mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-07-17 21:32:00 +03:00
Compare commits
3 Commits
developmen
...
phpstan_ju
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa3b7f77b9 | ||
|
|
03c9ddd14f | ||
|
|
5611c8c0da |
@@ -42,7 +42,7 @@ class ExternalBaseUserProvider implements UserProvider
|
|||||||
/**
|
/**
|
||||||
* Retrieve a user by the given credentials.
|
* Retrieve a user by the given credentials.
|
||||||
*/
|
*/
|
||||||
public function retrieveByCredentials(array $credentials): ?Authenticatable
|
public function retrieveByCredentials(array $credentials): ?User
|
||||||
{
|
{
|
||||||
return User::query()
|
return User::query()
|
||||||
->where('external_auth_id', $credentials['external_auth_id'])
|
->where('external_auth_id', $credentials['external_auth_id'])
|
||||||
|
|||||||
@@ -50,8 +50,12 @@ class ExternalBaseSessionGuard implements StatefulGuard
|
|||||||
/**
|
/**
|
||||||
* Create a new authentication guard.
|
* Create a new authentication guard.
|
||||||
*/
|
*/
|
||||||
public function __construct(string $name, UserProvider $provider, Session $session, RegistrationService $registrationService)
|
public function __construct(
|
||||||
{
|
string $name,
|
||||||
|
UserProvider $provider,
|
||||||
|
Session $session,
|
||||||
|
RegistrationService $registrationService
|
||||||
|
) {
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
$this->session = $session;
|
$this->session = $session;
|
||||||
$this->provider = $provider;
|
$this->provider = $provider;
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ class LdapSessionGuard extends ExternalBaseSessionGuard
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!($user instanceof User)) {
|
||||||
|
throw new LoginAttemptException('Could not find or create a user for LDAP login.');
|
||||||
|
}
|
||||||
|
|
||||||
// Sync LDAP groups if required
|
// Sync LDAP groups if required
|
||||||
if ($this->ldapService->shouldSyncGroups()) {
|
if ($this->ldapService->shouldSyncGroups()) {
|
||||||
$this->ldapService->syncGroups($user, $username);
|
$this->ldapService->syncGroups($user, $username);
|
||||||
|
|||||||
@@ -131,8 +131,6 @@ class OidcJwtWithClaims implements ProvidesClaims
|
|||||||
}
|
}
|
||||||
}, $this->keys);
|
}, $this->keys);
|
||||||
|
|
||||||
$parsedKeys = array_filter($parsedKeys);
|
|
||||||
|
|
||||||
$contentToSign = $this->tokenParts[0] . '.' . $this->tokenParts[1];
|
$contentToSign = $this->tokenParts[0] . '.' . $this->tokenParts[1];
|
||||||
/** @var OidcJwtSigningKey $parsedKey */
|
/** @var OidcJwtSigningKey $parsedKey */
|
||||||
foreach ($parsedKeys as $parsedKey) {
|
foreach ($parsedKeys as $parsedKey) {
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ class OidcService
|
|||||||
$provider->setPkceCode($pkceCode);
|
$provider->setPkceCode($pkceCode);
|
||||||
|
|
||||||
// Try to exchange authorization code for access token
|
// Try to exchange authorization code for access token
|
||||||
|
/** @var OidcAccessToken $accessToken */
|
||||||
$accessToken = $provider->getAccessToken('authorization_code', [
|
$accessToken = $provider->getAccessToken('authorization_code', [
|
||||||
'code' => $authorizationCode,
|
'code' => $authorizationCode,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use BookStack\Entities\Tools\MixedEntityListLoader;
|
|||||||
use BookStack\Permissions\PermissionApplicator;
|
use BookStack\Permissions\PermissionApplicator;
|
||||||
use BookStack\Users\Models\User;
|
use BookStack\Users\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@ class ActivityQueries
|
|||||||
*/
|
*/
|
||||||
public function latest(int $count = 20, int $page = 0): array
|
public function latest(int $count = 20, int $page = 0): array
|
||||||
{
|
{
|
||||||
|
/** @var Collection<int, Activity> $activityList */
|
||||||
$activityList = $this->permissions
|
$activityList = $this->permissions
|
||||||
->restrictEntityRelationQuery(Activity::query(), 'activities', 'loggable_id', 'loggable_type')
|
->restrictEntityRelationQuery(Activity::query(), 'activities', 'loggable_id', 'loggable_type')
|
||||||
->orderBy('created_at', 'desc')
|
->orderBy('created_at', 'desc')
|
||||||
@@ -83,6 +85,7 @@ class ActivityQueries
|
|||||||
*/
|
*/
|
||||||
public function userActivity(User $user, int $count = 20, int $page = 0): array
|
public function userActivity(User $user, int $count = 20, int $page = 0): array
|
||||||
{
|
{
|
||||||
|
/** @var Collection<int, Activity> $activityList */
|
||||||
$activityList = $this->permissions
|
$activityList = $this->permissions
|
||||||
->restrictEntityRelationQuery(Activity::query(), 'activities', 'loggable_id', 'loggable_type')
|
->restrictEntityRelationQuery(Activity::query(), 'activities', 'loggable_id', 'loggable_type')
|
||||||
->orderBy('created_at', 'desc')
|
->orderBy('created_at', 'desc')
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace BookStack\Activity\Models;
|
namespace BookStack\Activity\Models;
|
||||||
|
|
||||||
use BookStack\App\Model;
|
use BookStack\App\Model;
|
||||||
|
use BookStack\Entities\Models\Page;
|
||||||
use BookStack\Permissions\Models\JointPermission;
|
use BookStack\Permissions\Models\JointPermission;
|
||||||
use BookStack\Permissions\PermissionApplicator;
|
use BookStack\Permissions\PermissionApplicator;
|
||||||
use BookStack\Users\Models\HasCreatorAndUpdater;
|
use BookStack\Users\Models\HasCreatorAndUpdater;
|
||||||
@@ -40,6 +41,9 @@ class Comment extends Model implements Loggable, OwnableInterface
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the entity that this comment belongs to.
|
* Get the entity that this comment belongs to.
|
||||||
|
* It's only pages right now hence the typing below.
|
||||||
|
* Would need a deeper audit if that changes as many areas assume this is always a page.
|
||||||
|
* @return MorphTo<Page, $this>
|
||||||
*/
|
*/
|
||||||
public function entity(): MorphTo
|
public function entity(): MorphTo
|
||||||
{
|
{
|
||||||
@@ -55,7 +59,9 @@ class Comment extends Model implements Loggable, OwnableInterface
|
|||||||
|
|
||||||
// Ultimately, we could just align the method name to 'commentable' but that would be a potential
|
// Ultimately, we could just align the method name to 'commentable' but that would be a potential
|
||||||
// breaking change and not really worthwhile in a patch due to the risk of creating extra problems.
|
// breaking change and not really worthwhile in a patch due to the risk of creating extra problems.
|
||||||
return $this->morphTo(null, 'commentable_type', 'commentable_id');
|
/** @var MorphTo<Page, $this> $relation */
|
||||||
|
$relation = $this->morphTo(null, 'commentable_type', 'commentable_id');
|
||||||
|
return $relation;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -73,13 +73,17 @@ class WebhookFormatter
|
|||||||
// Load entity owner, creator, updater details
|
// Load entity owner, creator, updater details
|
||||||
$this->addModelFormatter(
|
$this->addModelFormatter(
|
||||||
fn ($event, $model) => ($model instanceof Entity),
|
fn ($event, $model) => ($model instanceof Entity),
|
||||||
fn ($model) => $model->load(['ownedBy', 'createdBy', 'updatedBy'])
|
function ($model) {
|
||||||
|
$model->load(['ownedBy', 'createdBy', 'updatedBy']);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Load revision detail for page update and create events
|
// Load revision detail for page update and create events
|
||||||
$this->addModelFormatter(
|
$this->addModelFormatter(
|
||||||
fn ($event, $model) => ($model instanceof Page && ($event === ActivityType::PAGE_CREATE || $event === ActivityType::PAGE_UPDATE)),
|
fn ($event, $model) => ($model instanceof Page && ($event === ActivityType::PAGE_CREATE || $event === ActivityType::PAGE_UPDATE)),
|
||||||
fn ($model) => $model->load('currentRevision')
|
function ($model) {
|
||||||
|
$model->load('currentRevision');
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -138,8 +138,8 @@ class ApiDocsGenerator
|
|||||||
return $validation;
|
return $validation;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_object($validation) && method_exists($validation, '__toString')) {
|
if (is_object($validation) && $validation instanceof \Stringable) {
|
||||||
return strval($validation);
|
return $validation->__toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($validation instanceof Password) {
|
if ($validation instanceof Password) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use BookStack\Theming\ThemeService;
|
|||||||
use BookStack\Theming\ThemeViews;
|
use BookStack\Theming\ThemeViews;
|
||||||
use Illuminate\Support\Facades\Blade;
|
use Illuminate\Support\Facades\Blade;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Illuminate\View\FileViewFinder;
|
||||||
|
|
||||||
class ThemeServiceProvider extends ServiceProvider
|
class ThemeServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
@@ -27,7 +28,11 @@ class ThemeServiceProvider extends ServiceProvider
|
|||||||
// Boot up the theme system
|
// Boot up the theme system
|
||||||
$themeService = $this->app->make(ThemeService::class);
|
$themeService = $this->app->make(ThemeService::class);
|
||||||
$viewFactory = $this->app->make('view');
|
$viewFactory = $this->app->make('view');
|
||||||
$themeViews = new ThemeViews($viewFactory->getFinder());
|
$viewFinder = $viewFactory->getFinder();
|
||||||
|
if (!($viewFinder instanceof FileViewFinder)) {
|
||||||
|
throw new \Exception('Only the file view finder is supported for the theme system');
|
||||||
|
}
|
||||||
|
$themeViews = new ThemeViews($viewFinder);
|
||||||
|
|
||||||
// Use a custom include so that we can insert theme views before/after includes.
|
// Use a custom include so that we can insert theme views before/after includes.
|
||||||
// This is done, even if no theme is active, so that view caching does not create problems
|
// This is done, even if no theme is active, so that view caching does not create problems
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use BookStack\Facades\Theme;
|
|||||||
use BookStack\Permissions\Permission;
|
use BookStack\Permissions\Permission;
|
||||||
use BookStack\Permissions\PermissionApplicator;
|
use BookStack\Permissions\PermissionApplicator;
|
||||||
use BookStack\Settings\SettingService;
|
use BookStack\Settings\SettingService;
|
||||||
|
use BookStack\Users\Models\OwnableInterface;
|
||||||
use BookStack\Users\Models\User;
|
use BookStack\Users\Models\User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,7 +41,7 @@ function user(): User
|
|||||||
* Check if the current user has a permission. If an ownable element
|
* Check if the current user has a permission. If an ownable element
|
||||||
* is passed in the jointPermissions are checked against that particular item.
|
* is passed in the jointPermissions are checked against that particular item.
|
||||||
*/
|
*/
|
||||||
function userCan(string|Permission $permission, ?Model $ownable = null): bool
|
function userCan(string|Permission $permission, (Model&OwnableInterface)|null $ownable = null): bool
|
||||||
{
|
{
|
||||||
if (is_null($ownable)) {
|
if (is_null($ownable)) {
|
||||||
return user()->can($permission);
|
return user()->can($permission);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace BookStack\Entities\Controllers;
|
namespace BookStack\Entities\Controllers;
|
||||||
|
|
||||||
use BookStack\Activity\Tools\CommentTree;
|
use BookStack\Activity\Tools\CommentTree;
|
||||||
@@ -10,7 +12,9 @@ use BookStack\Exceptions\PermissionsException;
|
|||||||
use BookStack\Http\ApiController;
|
use BookStack\Http\ApiController;
|
||||||
use BookStack\Permissions\Permission;
|
use BookStack\Permissions\Permission;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
|
||||||
class PageApiController extends ApiController
|
class PageApiController extends ApiController
|
||||||
{
|
{
|
||||||
@@ -45,7 +49,7 @@ class PageApiController extends ApiController
|
|||||||
/**
|
/**
|
||||||
* Get a listing of pages visible to the user.
|
* Get a listing of pages visible to the user.
|
||||||
*/
|
*/
|
||||||
public function list()
|
public function list(): JsonResponse
|
||||||
{
|
{
|
||||||
$pages = $this->queries->visibleForList()
|
$pages = $this->queries->visibleForList()
|
||||||
->addSelect(['created_by', 'updated_by', 'revision_count', 'editor']);
|
->addSelect(['created_by', 'updated_by', 'revision_count', 'editor']);
|
||||||
@@ -69,7 +73,7 @@ class PageApiController extends ApiController
|
|||||||
* Any images included via base64 data URIs will be extracted and saved as gallery
|
* Any images included via base64 data URIs will be extracted and saved as gallery
|
||||||
* images against the page during upload.
|
* images against the page during upload.
|
||||||
*/
|
*/
|
||||||
public function create(Request $request)
|
public function create(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$this->validate($request, $this->rules['create']);
|
$this->validate($request, $this->rules['create']);
|
||||||
|
|
||||||
@@ -102,9 +106,9 @@ class PageApiController extends ApiController
|
|||||||
* Comments for the page are provided in a tree-structure representing the hierarchy of top-level
|
* Comments for the page are provided in a tree-structure representing the hierarchy of top-level
|
||||||
* comments and replies, for both archived and active comments.
|
* comments and replies, for both archived and active comments.
|
||||||
*/
|
*/
|
||||||
public function read(string $id)
|
public function read(string $id): JsonResponse
|
||||||
{
|
{
|
||||||
$page = $this->queries->findVisibleByIdOrFail($id);
|
$page = $this->queries->findVisibleByIdOrFail(intval($id));
|
||||||
|
|
||||||
$page = $page->forJsonDisplay();
|
$page = $page->forJsonDisplay();
|
||||||
$commentTree = (new CommentTree($page));
|
$commentTree = (new CommentTree($page));
|
||||||
@@ -124,11 +128,11 @@ class PageApiController extends ApiController
|
|||||||
* Providing a 'book_id' or 'chapter_id' property will essentially move
|
* Providing a 'book_id' or 'chapter_id' property will essentially move
|
||||||
* the page into that parent element if you have permissions to do so.
|
* the page into that parent element if you have permissions to do so.
|
||||||
*/
|
*/
|
||||||
public function update(Request $request, string $id)
|
public function update(Request $request, string $id): JsonResponse
|
||||||
{
|
{
|
||||||
$requestData = $this->validate($request, $this->rules['update']);
|
$requestData = $this->validate($request, $this->rules['update']);
|
||||||
|
|
||||||
$page = $this->queries->findVisibleByIdOrFail($id);
|
$page = $this->queries->findVisibleByIdOrFail(intval($id));
|
||||||
$this->checkOwnablePermission(Permission::PageUpdate, $page);
|
$this->checkOwnablePermission(Permission::PageUpdate, $page);
|
||||||
|
|
||||||
$parent = null;
|
$parent = null;
|
||||||
@@ -161,9 +165,9 @@ class PageApiController extends ApiController
|
|||||||
* Delete a page.
|
* Delete a page.
|
||||||
* This will typically send the page to the recycle bin.
|
* This will typically send the page to the recycle bin.
|
||||||
*/
|
*/
|
||||||
public function delete(string $id)
|
public function delete(string $id): Response
|
||||||
{
|
{
|
||||||
$page = $this->queries->findVisibleByIdOrFail($id);
|
$page = $this->queries->findVisibleByIdOrFail(intval($id));
|
||||||
$this->checkOwnablePermission(Permission::PageDelete, $page);
|
$this->checkOwnablePermission(Permission::PageDelete, $page);
|
||||||
|
|
||||||
$this->pageRepo->destroy($page);
|
$this->pageRepo->destroy($page);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use BookStack\Util\HtmlContentFilter;
|
|||||||
use BookStack\Util\HtmlContentFilterConfig;
|
use BookStack\Util\HtmlContentFilterConfig;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
@@ -358,8 +359,8 @@ class PageController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function showRecentlyUpdated()
|
public function showRecentlyUpdated()
|
||||||
{
|
{
|
||||||
$visibleBelongsScope = function (BelongsTo $query) {
|
$visibleBelongsScope = function (Relation $relation): void {
|
||||||
$query->scopes('visible');
|
$relation->scopes('visible');
|
||||||
};
|
};
|
||||||
|
|
||||||
$pages = $this->queries->visibleForList()
|
$pages = $this->queries->visibleForList()
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ class PageRevisionController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function destroyUserDraft(string $pageId)
|
public function destroyUserDraft(string $pageId)
|
||||||
{
|
{
|
||||||
$page = $this->pageQueries->findVisibleByIdOrFail($pageId);
|
$page = $this->pageQueries->findVisibleByIdOrFail(intval($pageId));
|
||||||
$this->revisionRepo->deleteDraftsForCurrentUser($page);
|
$this->revisionRepo->deleteDraftsForCurrentUser($page);
|
||||||
|
|
||||||
return response('', 200);
|
return response('', 200);
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ use Illuminate\Support\Collection;
|
|||||||
* @property ?int $image_id
|
* @property ?int $image_id
|
||||||
* @property ?int $default_template_id
|
* @property ?int $default_template_id
|
||||||
* @property ?int $sort_rule_id
|
* @property ?int $sort_rule_id
|
||||||
* @property \Illuminate\Database\Eloquent\Collection $chapters
|
* @property \Illuminate\Database\Eloquent\Collection<int, Chapter> $chapters
|
||||||
* @property \Illuminate\Database\Eloquent\Collection $pages
|
* @property \Illuminate\Database\Eloquent\Collection<int, Page> $pages
|
||||||
* @property \Illuminate\Database\Eloquent\Collection $directPages
|
* @property \Illuminate\Database\Eloquent\Collection<int, Page> $directPages
|
||||||
* @property \Illuminate\Database\Eloquent\Collection $shelves
|
* @property \Illuminate\Database\Eloquent\Collection<int, Bookshelf> $shelves
|
||||||
* @property ?SortRule $sortRule
|
* @property ?SortRule $sortRule
|
||||||
*/
|
*/
|
||||||
class Book extends Entity implements HasDescriptionInterface, HasCoverInterface, HasDefaultTemplateInterface
|
class Book extends Entity implements HasDescriptionInterface, HasCoverInterface, HasDefaultTemplateInterface
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||||||
* @property int|null $created_by
|
* @property int|null $created_by
|
||||||
* @property int|null $updated_by
|
* @property int|null $updated_by
|
||||||
* @property int|null $owned_by
|
* @property int|null $owned_by
|
||||||
* @property Collection $tags
|
* @property Collection<int, Tag> $tags
|
||||||
*
|
*
|
||||||
* @method static Entity|Builder visible()
|
* @method static Entity|Builder visible()
|
||||||
* @method static Builder withLastView()
|
* @method static Builder withLastView()
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
|
|||||||
* @property int $revision_count
|
* @property int $revision_count
|
||||||
* @property string $editor
|
* @property string $editor
|
||||||
* @property Chapter|null $chapter
|
* @property Chapter|null $chapter
|
||||||
* @property Collection $attachments
|
* @property Collection<int, Attachment> $attachments
|
||||||
* @property Collection $revisions
|
* @property Collection<int, PageRevision> $revisions
|
||||||
* @property PageRevision $currentRevision
|
* @property PageRevision $currentRevision
|
||||||
*/
|
*/
|
||||||
class Page extends BookChild
|
class Page extends BookChild
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use BookStack\Activity\Models\View;
|
|||||||
use BookStack\Entities\EntityProvider;
|
use BookStack\Entities\EntityProvider;
|
||||||
use BookStack\Entities\Tools\MixedEntityListLoader;
|
use BookStack\Entities\Tools\MixedEntityListLoader;
|
||||||
use BookStack\Permissions\PermissionApplicator;
|
use BookStack\Permissions\PermissionApplicator;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ class QueryPopular
|
|||||||
|
|
||||||
public function run(int $count, int $page, array $filterModels): Collection
|
public function run(int $count, int $page, array $filterModels): Collection
|
||||||
{
|
{
|
||||||
|
/** @var Builder<View> $query */
|
||||||
$query = $this->permissions
|
$query = $this->permissions
|
||||||
->restrictEntityRelationQuery(View::query(), 'views', 'viewable_id', 'viewable_type')
|
->restrictEntityRelationQuery(View::query(), 'views', 'viewable_id', 'viewable_type')
|
||||||
->select('*', 'viewable_id', 'viewable_type', DB::raw('SUM(views) as view_count'))
|
->select('*', 'viewable_id', 'viewable_type', DB::raw('SUM(views) as view_count'))
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace BookStack\Entities\Queries;
|
|||||||
use BookStack\Activity\Models\View;
|
use BookStack\Activity\Models\View;
|
||||||
use BookStack\Entities\Tools\MixedEntityListLoader;
|
use BookStack\Entities\Tools\MixedEntityListLoader;
|
||||||
use BookStack\Permissions\PermissionApplicator;
|
use BookStack\Permissions\PermissionApplicator;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
class QueryRecentlyViewed
|
class QueryRecentlyViewed
|
||||||
@@ -22,6 +23,7 @@ class QueryRecentlyViewed
|
|||||||
return collect();
|
return collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @var Builder<View> $query */
|
||||||
$query = $this->permissions->restrictEntityRelationQuery(
|
$query = $this->permissions->restrictEntityRelationQuery(
|
||||||
View::query(),
|
View::query(),
|
||||||
'views',
|
'views',
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace BookStack\Entities\Queries;
|
|||||||
use BookStack\Activity\Models\Favourite;
|
use BookStack\Activity\Models\Favourite;
|
||||||
use BookStack\Entities\Tools\MixedEntityListLoader;
|
use BookStack\Entities\Tools\MixedEntityListLoader;
|
||||||
use BookStack\Permissions\PermissionApplicator;
|
use BookStack\Permissions\PermissionApplicator;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Query\JoinClause;
|
use Illuminate\Database\Query\JoinClause;
|
||||||
|
|
||||||
class QueryTopFavourites
|
class QueryTopFavourites
|
||||||
@@ -22,6 +23,7 @@ class QueryTopFavourites
|
|||||||
return collect();
|
return collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @var Builder<Favourite> $query */
|
||||||
$query = $this->permissions
|
$query = $this->permissions
|
||||||
->restrictEntityRelationQuery(Favourite::query(), 'favourites', 'favouritable_id', 'favouritable_type')
|
->restrictEntityRelationQuery(Favourite::query(), 'favourites', 'favouritable_id', 'favouritable_type')
|
||||||
->select('favourites.*')
|
->select('favourites.*')
|
||||||
|
|||||||
@@ -25,9 +25,13 @@ class CustomListItemRenderer implements NodeRendererInterface
|
|||||||
*/
|
*/
|
||||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer)
|
public function render(Node $node, ChildNodeRendererInterface $childRenderer)
|
||||||
{
|
{
|
||||||
|
if (!($node instanceof ListItem)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$listItem = $this->baseRenderer->render($node, $childRenderer);
|
$listItem = $this->baseRenderer->render($node, $childRenderer);
|
||||||
|
|
||||||
if ($node instanceof ListItem && $this->startsTaskListItem($node) && $listItem instanceof HtmlElement) {
|
if ($this->startsTaskListItem($node) && $listItem instanceof HtmlElement) {
|
||||||
$listItem->setAttribute('class', 'task-list-item');
|
$listItem->setAttribute('class', 'task-list-item');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -421,28 +421,41 @@ class PageContent
|
|||||||
*/
|
*/
|
||||||
protected function headerNodesToLevelList(DOMNodeList $nodeList): array
|
protected function headerNodesToLevelList(DOMNodeList $nodeList): array
|
||||||
{
|
{
|
||||||
$tree = collect($nodeList)->map(function (DOMElement $header) {
|
$minLevel = 6;
|
||||||
|
|
||||||
|
$headerDetails = array_map(function (DOMNode $header) use (&$minLevel) {
|
||||||
|
if (!$header instanceof DOMElement) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$text = trim(str_replace("\xc2\xa0", ' ', $header->nodeValue));
|
$text = trim(str_replace("\xc2\xa0", ' ', $header->nodeValue));
|
||||||
$text = mb_substr($text, 0, 100);
|
$text = mb_substr($text, 0, 100);
|
||||||
|
|
||||||
|
if (empty($text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$level = intval(str_replace('h', '', $header->nodeName));
|
||||||
|
if ($level < $minLevel) {
|
||||||
|
$minLevel = $level;
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'nodeName' => strtolower($header->nodeName),
|
'nodeName' => strtolower($header->nodeName),
|
||||||
'level' => intval(str_replace('h', '', $header->nodeName)),
|
'level' => $level,
|
||||||
'link' => '#' . $header->getAttribute('id'),
|
'link' => '#' . $header->getAttribute('id'),
|
||||||
'text' => $text,
|
'text' => $text,
|
||||||
];
|
];
|
||||||
})->filter(function ($header) {
|
}, [...$nodeList]);
|
||||||
return mb_strlen($header['text']) > 0;
|
|
||||||
});
|
$filtered = array_values(array_filter($headerDetails));
|
||||||
|
|
||||||
// Shift headers if only smaller headers have been used
|
// Shift headers if only smaller headers have been used
|
||||||
$levelChange = ($tree->pluck('level')->min() - 1);
|
$levelChange = ($minLevel - 1);
|
||||||
$tree = $tree->map(function ($header) use ($levelChange) {
|
foreach ($filtered as $index => $header) {
|
||||||
$header['level'] -= ($levelChange);
|
$filtered[$index]['level'] -= $levelChange;
|
||||||
|
}
|
||||||
|
|
||||||
return $header;
|
return $filtered;
|
||||||
});
|
|
||||||
|
|
||||||
return $tree->toArray();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,10 +192,10 @@ class PageIncludeParser
|
|||||||
/**
|
/**
|
||||||
* Get the parent paragraph of the given node, if existing.
|
* Get the parent paragraph of the given node, if existing.
|
||||||
*/
|
*/
|
||||||
protected function getParentParagraph(DOMNode $parent): ?DOMNode
|
protected function getParentParagraph(DOMNode $parent): ?DOMElement
|
||||||
{
|
{
|
||||||
do {
|
do {
|
||||||
if (strtolower($parent->nodeName) === 'p') {
|
if (strtolower($parent->nodeName) === 'p' && $parent instanceof DOMElement) {
|
||||||
return $parent;
|
return $parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class SlugGenerator
|
|||||||
{
|
{
|
||||||
$slug = Str::slug($name);
|
$slug = Str::slug($name);
|
||||||
if ($slug === '') {
|
if ($slug === '') {
|
||||||
$slug = substr(md5(rand(1, 500)), 0, 5);
|
$slug = substr(md5(strval(rand(1, 500))), 0, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $slug;
|
return $slug;
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
namespace BookStack\Exports;
|
namespace BookStack\Exports;
|
||||||
|
|
||||||
use BookStack\Activity\ActivityType;
|
use BookStack\Activity\ActivityType;
|
||||||
|
use BookStack\Entities\Models\Book;
|
||||||
|
use BookStack\Entities\Models\Chapter;
|
||||||
use BookStack\Entities\Models\Entity;
|
use BookStack\Entities\Models\Entity;
|
||||||
use BookStack\Entities\Queries\EntityQueries;
|
use BookStack\Entities\Queries\EntityQueries;
|
||||||
use BookStack\Exceptions\FileUploadException;
|
use BookStack\Exceptions\FileUploadException;
|
||||||
@@ -119,6 +121,9 @@ class ImportRepo
|
|||||||
$parentModel = null;
|
$parentModel = null;
|
||||||
if ($import->type === 'page' || $import->type === 'chapter') {
|
if ($import->type === 'page' || $import->type === 'chapter') {
|
||||||
$parentModel = $parent ? $this->entityQueries->findVisibleByStringIdentifier($parent) : null;
|
$parentModel = $parent ? $this->entityQueries->findVisibleByStringIdentifier($parent) : null;
|
||||||
|
if ($parentModel && !($parentModel instanceof Book || $parentModel instanceof Chapter)) {
|
||||||
|
throw new ZipImportException(['Selected parent is not a book or chapter']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::beginTransaction();
|
DB::beginTransaction();
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class ZipImportRunner
|
|||||||
* Returns the top-level entity item which was imported.
|
* Returns the top-level entity item which was imported.
|
||||||
* @throws ZipImportException
|
* @throws ZipImportException
|
||||||
*/
|
*/
|
||||||
public function run(Import $import, ?Entity $parent = null): Entity
|
public function run(Import $import, Book|Chapter|null $parent = null): Entity
|
||||||
{
|
{
|
||||||
$zipPath = $this->getZipPath($import);
|
$zipPath = $this->getZipPath($import);
|
||||||
$reader = new ZipExportReader($zipPath);
|
$reader = new ZipExportReader($zipPath);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use BookStack\App\Model;
|
|||||||
use BookStack\Exceptions\NotifyException;
|
use BookStack\Exceptions\NotifyException;
|
||||||
use BookStack\Facades\Activity;
|
use BookStack\Facades\Activity;
|
||||||
use BookStack\Permissions\Permission;
|
use BookStack\Permissions\Permission;
|
||||||
|
use BookStack\Users\Models\OwnableInterface;
|
||||||
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;
|
||||||
@@ -80,7 +81,7 @@ abstract class Controller extends BaseController
|
|||||||
/**
|
/**
|
||||||
* Check the current user's permissions against an ownable item otherwise throw an exception.
|
* Check the current user's permissions against an ownable item otherwise throw an exception.
|
||||||
*/
|
*/
|
||||||
protected function checkOwnablePermission(string|Permission $permission, Model $ownable, string $redirectLocation = '/'): void
|
protected function checkOwnablePermission(string|Permission $permission, Model&OwnableInterface $ownable, string $redirectLocation = '/'): void
|
||||||
{
|
{
|
||||||
if (!userCan($permission, $ownable)) {
|
if (!userCan($permission, $ownable)) {
|
||||||
$this->showPermissionError($redirectLocation);
|
$this->showPermissionError($redirectLocation);
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ class JointPermissionBuilder
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a query for fetching a book with its children.
|
* Get a query for fetching a book with its children.
|
||||||
|
* @return Builder<Book>
|
||||||
*/
|
*/
|
||||||
protected function bookFetchQuery(): Builder
|
protected function bookFetchQuery(): Builder
|
||||||
{
|
{
|
||||||
@@ -117,9 +118,11 @@ class JointPermissionBuilder
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Build joint permissions for the given book and role combinations.
|
* Build joint permissions for the given book and role combinations.
|
||||||
|
* @param EloquentCollection<int, Book> $books
|
||||||
*/
|
*/
|
||||||
protected function buildJointPermissionsForBooks(EloquentCollection $books, array $roles, bool $deleteOld = false): void
|
protected function buildJointPermissionsForBooks(EloquentCollection $books, array $roles, bool $deleteOld = false): void
|
||||||
{
|
{
|
||||||
|
/** @var EloquentCollection<int, Entity> $entities */
|
||||||
$entities = clone $books;
|
$entities = clone $books;
|
||||||
|
|
||||||
/** @var Book $book */
|
/** @var Book $book */
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace BookStack\References;
|
namespace BookStack\References;
|
||||||
|
|
||||||
|
use BookStack\App\Model;
|
||||||
use BookStack\Permissions\Models\JointPermission;
|
use BookStack\Permissions\Models\JointPermission;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class ReferenceFetcher
|
|||||||
*/
|
*/
|
||||||
public function getReferencesToEntity(Entity $entity, bool $withContents = false): Collection
|
public function getReferencesToEntity(Entity $entity, bool $withContents = false): Collection
|
||||||
{
|
{
|
||||||
|
/** @var Collection<int, Reference> $references */
|
||||||
$references = $this->queryReferencesToEntity($entity)->get();
|
$references = $this->queryReferencesToEntity($entity)->get();
|
||||||
$this->mixedEntityListLoader->loadIntoRelations($references->all(), 'from', false, $withContents);
|
$this->mixedEntityListLoader->loadIntoRelations($references->all(), 'from', false, $withContents);
|
||||||
|
|
||||||
@@ -37,6 +38,9 @@ class ReferenceFetcher
|
|||||||
return $this->queryReferencesToEntity($entity)->count();
|
return $this->queryReferencesToEntity($entity)->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Builder<Reference>
|
||||||
|
*/
|
||||||
protected function queryReferencesToEntity(Entity $entity): Builder
|
protected function queryReferencesToEntity(Entity $entity): Builder
|
||||||
{
|
{
|
||||||
$baseQuery = Reference::query()
|
$baseQuery = Reference::query()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace BookStack\Search;
|
|||||||
|
|
||||||
use BookStack\Entities\EntityProvider;
|
use BookStack\Entities\EntityProvider;
|
||||||
use BookStack\Entities\Models\Entity;
|
use BookStack\Entities\Models\Entity;
|
||||||
|
use BookStack\Entities\Models\EntityTable;
|
||||||
use BookStack\Entities\Queries\EntityQueries;
|
use BookStack\Entities\Queries\EntityQueries;
|
||||||
use BookStack\Entities\Tools\EntityHydrator;
|
use BookStack\Entities\Tools\EntityHydrator;
|
||||||
use BookStack\Permissions\PermissionApplicator;
|
use BookStack\Permissions\PermissionApplicator;
|
||||||
@@ -90,6 +91,8 @@ class SearchRunner
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a page of result data from the given query based on the provided page parameters.
|
* Get a page of result data from the given query based on the provided page parameters.
|
||||||
|
* @param EloquentBuilder<EntityTable> $query
|
||||||
|
* @return Collection<Entity>
|
||||||
*/
|
*/
|
||||||
protected function getPageOfDataFromQuery(EloquentBuilder $query, int $page, int $count): Collection
|
protected function getPageOfDataFromQuery(EloquentBuilder $query, int $page, int $count): Collection
|
||||||
{
|
{
|
||||||
@@ -106,6 +109,7 @@ class SearchRunner
|
|||||||
/**
|
/**
|
||||||
* Create a search query for an entity.
|
* Create a search query for an entity.
|
||||||
* @param string[] $entityTypes
|
* @param string[] $entityTypes
|
||||||
|
* @return EloquentBuilder<EntityTable>
|
||||||
*/
|
*/
|
||||||
protected function buildQuery(SearchOptions $searchOpts, array $entityTypes): EloquentBuilder
|
protected function buildQuery(SearchOptions $searchOpts, array $entityTypes): EloquentBuilder
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -243,9 +243,9 @@ class SettingService
|
|||||||
/**
|
/**
|
||||||
* Convert a setting key into a user-specific key.
|
* Convert a setting key into a user-specific key.
|
||||||
*/
|
*/
|
||||||
protected function userKey(string $userId, string $key = ''): string
|
protected function userKey(int $userId, string $key = ''): string
|
||||||
{
|
{
|
||||||
return 'user:' . $userId . ':' . $key;
|
return 'user:' . strval($userId) . ':' . $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -267,7 +267,7 @@ class SettingService
|
|||||||
/**
|
/**
|
||||||
* Delete settings for a given user id.
|
* Delete settings for a given user id.
|
||||||
*/
|
*/
|
||||||
public function deleteUserSettings(string $userId): void
|
public function deleteUserSettings(int $userId): void
|
||||||
{
|
{
|
||||||
Setting::query()
|
Setting::query()
|
||||||
->where('setting_key', 'like', $this->userKey($userId) . '%')
|
->where('setting_key', 'like', $this->userKey($userId) . '%')
|
||||||
|
|||||||
@@ -38,21 +38,35 @@ class SortRule extends Model implements Loggable
|
|||||||
$this->sequence = implode(',', $values);
|
$this->sequence = implode(',', $values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
public function logDescriptor(): string
|
public function logDescriptor(): string
|
||||||
{
|
{
|
||||||
return "({$this->id}) {$this->name}";
|
return "({$this->id}) {$this->name}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the URL to where this rule can be managed.
|
||||||
|
*/
|
||||||
public function getUrl(): string
|
public function getUrl(): string
|
||||||
{
|
{
|
||||||
return url("/settings/sorting/rules/{$this->id}");
|
return url("/settings/sorting/rules/{$this->id}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the books which are specifically set to use this sort rule.
|
||||||
|
* @return HasMany<Book, $this>
|
||||||
|
*/
|
||||||
public function books(): HasMany
|
public function books(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(Book::class, 'entity_container_data.sort_rule_id', 'id');
|
return $this->hasMany(Book::class, 'entity_container_data.sort_rule_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the available sort rules, ordered by name, with the number of books using each.
|
||||||
|
* @return Collection<SortRule>
|
||||||
|
*/
|
||||||
public static function allByName(): Collection
|
public static function allByName(): Collection
|
||||||
{
|
{
|
||||||
return static::query()
|
return static::query()
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ class AttachmentService
|
|||||||
/**
|
/**
|
||||||
* Updates the ordering for a listing of attached files.
|
* Updates the ordering for a listing of attached files.
|
||||||
*/
|
*/
|
||||||
public function updateFileOrderWithinPage(array $attachmentOrder, string $pageId)
|
public function updateFileOrderWithinPage(array $attachmentOrder, int $pageId)
|
||||||
{
|
{
|
||||||
foreach ($attachmentOrder as $index => $attachmentId) {
|
foreach ($attachmentOrder as $index => $attachmentId) {
|
||||||
Attachment::query()->where('uploaded_to', '=', $pageId)
|
Attachment::query()->where('uploaded_to', '=', $pageId)
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class RoleApiController extends ApiController
|
|||||||
*/
|
*/
|
||||||
public function read(string $id)
|
public function read(string $id)
|
||||||
{
|
{
|
||||||
$role = $this->permissionsRepo->getRoleById($id);
|
$role = $this->permissionsRepo->getRoleById(intval($id));
|
||||||
$this->singleFormatter($role);
|
$this->singleFormatter($role);
|
||||||
|
|
||||||
return response()->json($role);
|
return response()->json($role);
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ class RoleController extends Controller
|
|||||||
public function edit(string $id)
|
public function edit(string $id)
|
||||||
{
|
{
|
||||||
$this->checkPermission(Permission::UserRolesManage);
|
$this->checkPermission(Permission::UserRolesManage);
|
||||||
$role = $this->permissionsRepo->getRoleById($id);
|
$role = $this->permissionsRepo->getRoleById(intval($id));
|
||||||
|
|
||||||
$this->setPageTitle(trans('settings.role_edit'));
|
$this->setPageTitle(trans('settings.role_edit'));
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ class RoleController extends Controller
|
|||||||
public function showDelete(string $id)
|
public function showDelete(string $id)
|
||||||
{
|
{
|
||||||
$this->checkPermission(Permission::UserRolesManage);
|
$this->checkPermission(Permission::UserRolesManage);
|
||||||
$role = $this->permissionsRepo->getRoleById($id);
|
$role = $this->permissionsRepo->getRoleById(intval($id));
|
||||||
$roles = $this->permissionsRepo->getAllRolesExcept($role);
|
$roles = $this->permissionsRepo->getAllRolesExcept($role);
|
||||||
$blankRole = $role->newInstance(['display_name' => trans('settings.role_delete_no_migration')]);
|
$blankRole = $role->newInstance(['display_name' => trans('settings.role_delete_no_migration')]);
|
||||||
$roles->prepend($blankRole);
|
$roles->prepend($blankRole);
|
||||||
@@ -151,7 +151,7 @@ class RoleController extends Controller
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$migrateRoleId = intval($request->input('migrate_role_id') ?: "0");
|
$migrateRoleId = intval($request->input('migrate_role_id') ?: "0");
|
||||||
$this->permissionsRepo->deleteRole($id, $migrateRoleId);
|
$this->permissionsRepo->deleteRole(intval($id), $migrateRoleId);
|
||||||
} catch (PermissionsException $e) {
|
} catch (PermissionsException $e) {
|
||||||
$this->showErrorNotification($e->getMessage());
|
$this->showErrorNotification($e->getMessage());
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class UserApiController extends ApiController
|
|||||||
*/
|
*/
|
||||||
public function read(string $id)
|
public function read(string $id)
|
||||||
{
|
{
|
||||||
$user = $this->userRepo->getById($id);
|
$user = $this->userRepo->getById(intval($id));
|
||||||
$this->singleFormatter($user);
|
$this->singleFormatter($user);
|
||||||
|
|
||||||
return response()->json($user);
|
return response()->json($user);
|
||||||
@@ -124,8 +124,8 @@ class UserApiController extends ApiController
|
|||||||
*/
|
*/
|
||||||
public function update(Request $request, string $id)
|
public function update(Request $request, string $id)
|
||||||
{
|
{
|
||||||
$data = $this->validate($request, $this->rules($id)['update']);
|
$data = $this->validate($request, $this->rules(intval($id))['update']);
|
||||||
$user = $this->userRepo->getById($id);
|
$user = $this->userRepo->getById(intval($id));
|
||||||
$this->userRepo->update($user, $data, userCan(Permission::UsersManage));
|
$this->userRepo->update($user, $data, userCan(Permission::UsersManage));
|
||||||
$this->singleFormatter($user);
|
$this->singleFormatter($user);
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ class UserApiController extends ApiController
|
|||||||
*/
|
*/
|
||||||
public function delete(Request $request, string $id)
|
public function delete(Request $request, string $id)
|
||||||
{
|
{
|
||||||
$user = $this->userRepo->getById($id);
|
$user = $this->userRepo->getById(intval($id));
|
||||||
$newOwnerId = $request->input('migrate_ownership_id', null);
|
$newOwnerId = $request->input('migrate_ownership_id', null);
|
||||||
|
|
||||||
$this->userRepo->destroy($user, $newOwnerId);
|
$this->userRepo->destroy($user, $newOwnerId);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace BookStack\Util;
|
namespace BookStack\Util;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use Illuminate\Database\Connection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ use Throwable;
|
|||||||
class DatabaseTransaction
|
class DatabaseTransaction
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param (Closure(static): TReturn) $callback
|
* @param (Closure(Connection): TReturn) $callback
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected Closure $callback
|
protected Closure $callback
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ parameters:
|
|||||||
- app
|
- app
|
||||||
|
|
||||||
# The level 8 is the highest level
|
# The level 8 is the highest level
|
||||||
level: 4
|
level: 5
|
||||||
|
|
||||||
phpVersion:
|
phpVersion:
|
||||||
min: 80200
|
min: 80200
|
||||||
|
|||||||
Reference in New Issue
Block a user