mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-10-29 16:09:29 +03:00
Maintenance: Reached PHPstan level 2
Reworked some stuff around slugs to use interface in a better way. Also standardised phpdoc to use @return instead of @returns
This commit is contained in:
@@ -51,7 +51,7 @@ class Saml2Service
|
|||||||
* Returns the SAML2 request ID, and the URL to redirect the user to.
|
* Returns the SAML2 request ID, and the URL to redirect the user to.
|
||||||
*
|
*
|
||||||
* @throws Error
|
* @throws Error
|
||||||
* @returns array{url: string, id: ?string}
|
* @return array{url: string, id: ?string}
|
||||||
*/
|
*/
|
||||||
public function logout(User $user): array
|
public function logout(User $user): array
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class SocialDriverManager
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the names of the active social drivers, keyed by driver id.
|
* Gets the names of the active social drivers, keyed by driver id.
|
||||||
* @returns array<string, string>
|
* @return array<string, string>
|
||||||
*/
|
*/
|
||||||
public function getActive(): array
|
public function getActive(): array
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ namespace BookStack\Activity\Models;
|
|||||||
|
|
||||||
use BookStack\App\Model;
|
use BookStack\App\Model;
|
||||||
use BookStack\Users\Models\HasCreatorAndUpdater;
|
use BookStack\Users\Models\HasCreatorAndUpdater;
|
||||||
|
use BookStack\Users\Models\OwnableInterface;
|
||||||
|
use BookStack\Users\Models\User;
|
||||||
use BookStack\Util\HtmlContentFilter;
|
use BookStack\Util\HtmlContentFilter;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
@@ -17,12 +19,10 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
|
|||||||
* @property int $local_id
|
* @property int $local_id
|
||||||
* @property string $entity_type
|
* @property string $entity_type
|
||||||
* @property int $entity_id
|
* @property int $entity_id
|
||||||
* @property int $created_by
|
|
||||||
* @property int $updated_by
|
|
||||||
* @property string $content_ref
|
* @property string $content_ref
|
||||||
* @property bool $archived
|
* @property bool $archived
|
||||||
*/
|
*/
|
||||||
class Comment extends Model implements Loggable
|
class Comment extends Model implements Loggable, OwnableInterface
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use HasCreatorAndUpdater;
|
use HasCreatorAndUpdater;
|
||||||
@@ -39,6 +39,7 @@ class Comment extends Model implements Loggable
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the parent comment this is in reply to (if existing).
|
* Get the parent comment this is in reply to (if existing).
|
||||||
|
* @return BelongsTo<Comment, Comment>
|
||||||
*/
|
*/
|
||||||
public function parent(): BelongsTo
|
public function parent(): BelongsTo
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class WatchLevels
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all the possible values as an option_name => value array.
|
* Get all the possible values as an option_name => value array.
|
||||||
* @returns array<string, int>
|
* @return array<string, int>
|
||||||
*/
|
*/
|
||||||
public static function all(): array
|
public static function all(): array
|
||||||
{
|
{
|
||||||
@@ -50,7 +50,7 @@ class WatchLevels
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the watch options suited for the given entity.
|
* Get the watch options suited for the given entity.
|
||||||
* @returns array<string, int>
|
* @return array<string, int>
|
||||||
*/
|
*/
|
||||||
public static function allSuitedFor(Entity $entity): array
|
public static function allSuitedFor(Entity $entity): array
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class Model extends EloquentModel
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Provides public access to get the raw attribute value from the model.
|
* Provides public access to get the raw attribute value from the model.
|
||||||
* Used in areas where no mutations are required but performance is critical.
|
* Used in areas where no mutations are required, but performance is critical.
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -5,11 +5,8 @@ namespace BookStack\App;
|
|||||||
/**
|
/**
|
||||||
* Assigned to models that can have slugs.
|
* Assigned to models that can have slugs.
|
||||||
* Must have the below properties.
|
* Must have the below properties.
|
||||||
*
|
|
||||||
* @property int $id
|
|
||||||
* @property string $name
|
|
||||||
*/
|
*/
|
||||||
interface Sluggable
|
interface SluggableInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Regenerate the slug for this model.
|
* Regenerate the slug for this model.
|
||||||
@@ -6,10 +6,10 @@ use BookStack\Entities\Models\Book;
|
|||||||
use BookStack\Entities\Models\BookChild;
|
use BookStack\Entities\Models\BookChild;
|
||||||
use BookStack\Entities\Models\Chapter;
|
use BookStack\Entities\Models\Chapter;
|
||||||
use BookStack\Entities\Models\Deletion;
|
use BookStack\Entities\Models\Deletion;
|
||||||
|
use BookStack\Entities\Models\Page;
|
||||||
use BookStack\Entities\Repos\DeletionRepo;
|
use BookStack\Entities\Repos\DeletionRepo;
|
||||||
use BookStack\Http\ApiController;
|
use BookStack\Http\ApiController;
|
||||||
use Closure;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
|
|
||||||
class RecycleBinApiController extends ApiController
|
class RecycleBinApiController extends ApiController
|
||||||
{
|
{
|
||||||
@@ -40,7 +40,7 @@ class RecycleBinApiController extends ApiController
|
|||||||
'updated_at',
|
'updated_at',
|
||||||
'deletable_type',
|
'deletable_type',
|
||||||
'deletable_id',
|
'deletable_id',
|
||||||
], [Closure::fromCallable([$this, 'listFormatter'])]);
|
], [$this->listFormatter(...)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,7 +72,6 @@ class RecycleBinApiController extends ApiController
|
|||||||
protected function listFormatter(Deletion $deletion)
|
protected function listFormatter(Deletion $deletion)
|
||||||
{
|
{
|
||||||
$deletable = $deletion->deletable;
|
$deletable = $deletion->deletable;
|
||||||
$withTrashedQuery = fn (Builder $query) => $query->withTrashed();
|
|
||||||
|
|
||||||
if ($deletable instanceof BookChild) {
|
if ($deletable instanceof BookChild) {
|
||||||
$parent = $deletable->getParent();
|
$parent = $deletable->getParent();
|
||||||
@@ -81,11 +80,19 @@ class RecycleBinApiController extends ApiController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($deletable instanceof Book || $deletable instanceof Chapter) {
|
if ($deletable instanceof Book || $deletable instanceof Chapter) {
|
||||||
$countsToLoad = ['pages' => $withTrashedQuery];
|
$countsToLoad = ['pages' => static::withTrashedQuery(...)];
|
||||||
if ($deletable instanceof Book) {
|
if ($deletable instanceof Book) {
|
||||||
$countsToLoad['chapters'] = $withTrashedQuery;
|
$countsToLoad['chapters'] = static::withTrashedQuery(...);
|
||||||
}
|
}
|
||||||
$deletable->loadCount($countsToLoad);
|
$deletable->loadCount($countsToLoad);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param HasMany<Chapter|Page, Book|Chapter> $query
|
||||||
|
*/
|
||||||
|
protected static function withTrashedQuery(HasMany $query): void
|
||||||
|
{
|
||||||
|
$query->withTrashed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ class Book extends Entity implements CoverImageInterface, HtmlDescriptionInterfa
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all pages within this book.
|
* Get all pages within this book.
|
||||||
|
* @return HasMany<Page, $this>
|
||||||
*/
|
*/
|
||||||
public function pages(): HasMany
|
public function pages(): HasMany
|
||||||
{
|
{
|
||||||
@@ -111,7 +112,7 @@ class Book extends Entity implements CoverImageInterface, HtmlDescriptionInterfa
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all chapters within this book.
|
* Get all chapters within this book.
|
||||||
* @return HasMany<Chapter>
|
* @return HasMany<Chapter, $this>
|
||||||
*/
|
*/
|
||||||
public function chapters(): HasMany
|
public function chapters(): HasMany
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ class Bookshelf extends Entity implements CoverImageInterface, HtmlDescriptionIn
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the cover image of the shelf.
|
* Get the cover image of the shelf.
|
||||||
|
* @return BelongsTo<Image, $this>
|
||||||
*/
|
*/
|
||||||
public function cover(): BelongsTo
|
public function cover(): BelongsTo
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class Chapter extends BookChild implements HtmlDescriptionInterface
|
|||||||
/**
|
/**
|
||||||
* Get the pages that this chapter contains.
|
* Get the pages that this chapter contains.
|
||||||
*
|
*
|
||||||
* @return HasMany<Page>
|
* @return HasMany<Page, $this>
|
||||||
*/
|
*/
|
||||||
public function pages(string $dir = 'ASC'): HasMany
|
public function pages(string $dir = 'ASC'): HasMany
|
||||||
{
|
{
|
||||||
@@ -60,7 +60,7 @@ class Chapter extends BookChild implements HtmlDescriptionInterface
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the visible pages in this chapter.
|
* Get the visible pages in this chapter.
|
||||||
* @returns Collection<Page>
|
* @return Collection<Page>
|
||||||
*/
|
*/
|
||||||
public function getVisiblePages(): Collection
|
public function getVisiblePages(): Collection
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use BookStack\Activity\Models\View;
|
|||||||
use BookStack\Activity\Models\Viewable;
|
use BookStack\Activity\Models\Viewable;
|
||||||
use BookStack\Activity\Models\Watch;
|
use BookStack\Activity\Models\Watch;
|
||||||
use BookStack\App\Model;
|
use BookStack\App\Model;
|
||||||
use BookStack\App\Sluggable;
|
use BookStack\App\SluggableInterface;
|
||||||
use BookStack\Entities\Tools\SlugGenerator;
|
use BookStack\Entities\Tools\SlugGenerator;
|
||||||
use BookStack\Permissions\JointPermissionBuilder;
|
use BookStack\Permissions\JointPermissionBuilder;
|
||||||
use BookStack\Permissions\Models\EntityPermission;
|
use BookStack\Permissions\Models\EntityPermission;
|
||||||
@@ -22,7 +22,8 @@ use BookStack\References\Reference;
|
|||||||
use BookStack\Search\SearchIndex;
|
use BookStack\Search\SearchIndex;
|
||||||
use BookStack\Search\SearchTerm;
|
use BookStack\Search\SearchTerm;
|
||||||
use BookStack\Users\Models\HasCreatorAndUpdater;
|
use BookStack\Users\Models\HasCreatorAndUpdater;
|
||||||
use BookStack\Users\Models\HasOwner;
|
use BookStack\Users\Models\OwnableInterface;
|
||||||
|
use BookStack\Users\Models\User;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
@@ -43,17 +44,23 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||||||
* @property Carbon $deleted_at
|
* @property Carbon $deleted_at
|
||||||
* @property int $created_by
|
* @property int $created_by
|
||||||
* @property int $updated_by
|
* @property int $updated_by
|
||||||
|
* @property int $owned_by
|
||||||
* @property Collection $tags
|
* @property Collection $tags
|
||||||
*
|
*
|
||||||
* @method static Entity|Builder visible()
|
* @method static Entity|Builder visible()
|
||||||
* @method static Builder withLastView()
|
* @method static Builder withLastView()
|
||||||
* @method static Builder withViewCount()
|
* @method static Builder withViewCount()
|
||||||
*/
|
*/
|
||||||
abstract class Entity extends Model implements Sluggable, Favouritable, Viewable, DeletableInterface, Loggable
|
abstract class Entity extends Model implements
|
||||||
|
SluggableInterface,
|
||||||
|
Favouritable,
|
||||||
|
Viewable,
|
||||||
|
DeletableInterface,
|
||||||
|
OwnableInterface,
|
||||||
|
Loggable
|
||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
use HasCreatorAndUpdater;
|
use HasCreatorAndUpdater;
|
||||||
use HasOwner;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string - Name of property where the main text content is found
|
* @var string - Name of property where the main text content is found
|
||||||
@@ -200,6 +207,20 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
|
|||||||
return $this->morphMany(JointPermission::class, 'entity');
|
return $this->morphMany(JointPermission::class, 'entity');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user who owns this entity.
|
||||||
|
* @return BelongsTo<User, $this>
|
||||||
|
*/
|
||||||
|
public function ownedBy(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class, 'owned_by');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOwnerFieldName(): string
|
||||||
|
{
|
||||||
|
return 'owned_by';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the related delete records for this entity.
|
* Get the related delete records for this entity.
|
||||||
*/
|
*/
|
||||||
@@ -318,7 +339,7 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
|
|||||||
*/
|
*/
|
||||||
public function refreshSlug(): string
|
public function refreshSlug(): string
|
||||||
{
|
{
|
||||||
$this->slug = app()->make(SlugGenerator::class)->generate($this);
|
$this->slug = app()->make(SlugGenerator::class)->generate($this, $this->name);
|
||||||
|
|
||||||
return $this->slug;
|
return $this->slug;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ class BookQueries implements ProvidesEntityQueries
|
|||||||
'created_at', 'updated_at', 'image_id', 'owned_by',
|
'created_at', 'updated_at', 'image_id', 'owned_by',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Builder<Book>
|
||||||
|
*/
|
||||||
public function start(): Builder
|
public function start(): Builder
|
||||||
{
|
{
|
||||||
return Book::query();
|
return Book::query();
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ class BookshelfQueries implements ProvidesEntityQueries
|
|||||||
'created_at', 'updated_at', 'image_id', 'owned_by',
|
'created_at', 'updated_at', 'image_id', 'owned_by',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Builder<Bookshelf>
|
||||||
|
*/
|
||||||
public function start(): Builder
|
public function start(): Builder
|
||||||
{
|
{
|
||||||
return Bookshelf::query();
|
return Bookshelf::query();
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class EntityQueries
|
|||||||
/**
|
/**
|
||||||
* Start a query of visible entities of the given type,
|
* Start a query of visible entities of the given type,
|
||||||
* suitable for listing display.
|
* suitable for listing display.
|
||||||
|
* @return Builder<Entity>
|
||||||
*/
|
*/
|
||||||
public function visibleForList(string $entityType): Builder
|
public function visibleForList(string $entityType): Builder
|
||||||
{
|
{
|
||||||
@@ -44,7 +45,6 @@ class EntityQueries
|
|||||||
|
|
||||||
protected function getQueriesForType(string $type): ProvidesEntityQueries
|
protected function getQueriesForType(string $type): ProvidesEntityQueries
|
||||||
{
|
{
|
||||||
/** @var ?ProvidesEntityQueries $queries */
|
|
||||||
$queries = match ($type) {
|
$queries = match ($type) {
|
||||||
'page' => $this->pages,
|
'page' => $this->pages,
|
||||||
'chapter' => $this->chapters,
|
'chapter' => $this->chapters,
|
||||||
|
|||||||
@@ -22,13 +22,14 @@ interface ProvidesEntityQueries
|
|||||||
public function start(): Builder;
|
public function start(): Builder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the entity of the given ID, or return null if not found.
|
* Find the entity of the given ID or return null if not found.
|
||||||
*/
|
*/
|
||||||
public function findVisibleById(int $id): ?Entity;
|
public function findVisibleById(int $id): ?Entity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a query for items that are visible, with selection
|
* Start a query for items that are visible, with selection
|
||||||
* configured for list display of this item.
|
* configured for list display of this item.
|
||||||
|
* @return Builder<Entity>
|
||||||
*/
|
*/
|
||||||
public function visibleForList(): Builder;
|
public function visibleForList(): Builder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,12 +89,10 @@ class BaseRepo
|
|||||||
/**
|
/**
|
||||||
* Update the given items' cover image, or clear it.
|
* Update the given items' cover image, or clear it.
|
||||||
*
|
*
|
||||||
* @param Entity&CoverImageInterface $entity
|
|
||||||
*
|
|
||||||
* @throws ImageUploadException
|
* @throws ImageUploadException
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function updateCoverImage($entity, ?UploadedFile $coverImage, bool $removeImage = false)
|
public function updateCoverImage(Entity&CoverImageInterface $entity, ?UploadedFile $coverImage, bool $removeImage = false)
|
||||||
{
|
{
|
||||||
if ($coverImage) {
|
if ($coverImage) {
|
||||||
$imageType = $entity->coverImageTypeKey();
|
$imageType = $entity->coverImageTypeKey();
|
||||||
@@ -106,7 +104,7 @@ class BaseRepo
|
|||||||
|
|
||||||
if ($removeImage) {
|
if ($removeImage) {
|
||||||
$this->imageRepo->destroyImage($entity->cover()->first());
|
$this->imageRepo->destroyImage($entity->cover()->first());
|
||||||
$entity->image_id = 0;
|
$entity->cover()->dissociate();
|
||||||
$entity->save();
|
$entity->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace BookStack\Entities\Tools;
|
namespace BookStack\Entities\Tools;
|
||||||
|
|
||||||
use BookStack\App\Model;
|
use BookStack\App\Model;
|
||||||
use BookStack\App\Sluggable;
|
use BookStack\App\SluggableInterface;
|
||||||
use BookStack\Entities\Models\BookChild;
|
use BookStack\Entities\Models\BookChild;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
@@ -13,9 +13,9 @@ class SlugGenerator
|
|||||||
* Generate a fresh slug for the given entity.
|
* Generate a fresh slug for the given entity.
|
||||||
* The slug will be generated so that it doesn't conflict within the same parent item.
|
* The slug will be generated so that it doesn't conflict within the same parent item.
|
||||||
*/
|
*/
|
||||||
public function generate(Sluggable $model): string
|
public function generate(SluggableInterface&Model $model, string $slugSource): string
|
||||||
{
|
{
|
||||||
$slug = $this->formatNameAsSlug($model->name);
|
$slug = $this->formatNameAsSlug($slugSource);
|
||||||
while ($this->slugInUse($slug, $model)) {
|
while ($this->slugInUse($slug, $model)) {
|
||||||
$slug .= '-' . Str::random(3);
|
$slug .= '-' . Str::random(3);
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ class SlugGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a name as a url slug.
|
* Format a name as a URL slug.
|
||||||
*/
|
*/
|
||||||
protected function formatNameAsSlug(string $name): string
|
protected function formatNameAsSlug(string $name): string
|
||||||
{
|
{
|
||||||
@@ -39,10 +39,8 @@ class SlugGenerator
|
|||||||
/**
|
/**
|
||||||
* Check if a slug is already in-use for this
|
* Check if a slug is already in-use for this
|
||||||
* type of model within the same parent.
|
* type of model within the same parent.
|
||||||
*
|
|
||||||
* @param Sluggable&Model $model
|
|
||||||
*/
|
*/
|
||||||
protected function slugInUse(string $slug, Sluggable $model): bool
|
protected function slugInUse(string $slug, SluggableInterface&Model $model): bool
|
||||||
{
|
{
|
||||||
$query = $model->newQuery()->where('slug', '=', $slug);
|
$query = $model->newQuery()->where('slug', '=', $slug);
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ use BookStack\Entities\EntityProvider;
|
|||||||
use BookStack\Entities\Models\Entity;
|
use BookStack\Entities\Models\Entity;
|
||||||
use BookStack\Entities\Models\Page;
|
use BookStack\Entities\Models\Page;
|
||||||
use BookStack\Permissions\Models\EntityPermission;
|
use BookStack\Permissions\Models\EntityPermission;
|
||||||
use BookStack\Users\Models\HasCreatorAndUpdater;
|
use BookStack\Users\Models\OwnableInterface;
|
||||||
use BookStack\Users\Models\HasOwner;
|
|
||||||
use BookStack\Users\Models\User;
|
use BookStack\Users\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Query\Builder as QueryBuilder;
|
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||||
@@ -24,10 +23,8 @@ class PermissionApplicator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an entity has a restriction set upon it.
|
* Checks if an entity has a restriction set upon it.
|
||||||
*
|
|
||||||
* @param Model&(HasCreatorAndUpdater|HasOwner) $ownable
|
|
||||||
*/
|
*/
|
||||||
public function checkOwnableUserAccess(Model $ownable, string $permission): bool
|
public function checkOwnableUserAccess(Model&OwnableInterface $ownable, string $permission): bool
|
||||||
{
|
{
|
||||||
$explodedPermission = explode('-', $permission);
|
$explodedPermission = explode('-', $permission);
|
||||||
$action = $explodedPermission[1] ?? $explodedPermission[0];
|
$action = $explodedPermission[1] ?? $explodedPermission[0];
|
||||||
@@ -39,7 +36,7 @@ class PermissionApplicator
|
|||||||
$allRolePermission = $user->can($fullPermission . '-all');
|
$allRolePermission = $user->can($fullPermission . '-all');
|
||||||
$ownRolePermission = $user->can($fullPermission . '-own');
|
$ownRolePermission = $user->can($fullPermission . '-own');
|
||||||
$nonJointPermissions = ['restrictions', 'image', 'attachment', 'comment'];
|
$nonJointPermissions = ['restrictions', 'image', 'attachment', 'comment'];
|
||||||
$ownerField = ($ownable instanceof Entity) ? 'owned_by' : 'created_by';
|
$ownerField = $ownable->getOwnerFieldName();
|
||||||
$ownableFieldVal = $ownable->getAttribute($ownerField);
|
$ownableFieldVal = $ownable->getAttribute($ownerField);
|
||||||
|
|
||||||
if (is_null($ownableFieldVal)) {
|
if (is_null($ownableFieldVal)) {
|
||||||
@@ -49,11 +46,15 @@ class PermissionApplicator
|
|||||||
$isOwner = $user->id === $ownableFieldVal;
|
$isOwner = $user->id === $ownableFieldVal;
|
||||||
$hasRolePermission = $allRolePermission || ($isOwner && $ownRolePermission);
|
$hasRolePermission = $allRolePermission || ($isOwner && $ownRolePermission);
|
||||||
|
|
||||||
// Handle non entity specific jointPermissions
|
// Handle non-entity-specific jointPermissions
|
||||||
if (in_array($explodedPermission[0], $nonJointPermissions)) {
|
if (in_array($explodedPermission[0], $nonJointPermissions)) {
|
||||||
return $hasRolePermission;
|
return $hasRolePermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!($ownable instanceof Entity)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$hasApplicableEntityPermissions = $this->hasEntityPermission($ownable, $userRoleIds, $action);
|
$hasApplicableEntityPermissions = $this->hasEntityPermission($ownable, $userRoleIds, $action);
|
||||||
|
|
||||||
return is_null($hasApplicableEntityPermissions) ? $hasRolePermission : $hasApplicableEntityPermissions;
|
return is_null($hasApplicableEntityPermissions) ? $hasRolePermission : $hasApplicableEntityPermissions;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class CrossLinkParser
|
|||||||
/**
|
/**
|
||||||
* Get a list of href values from the given document.
|
* Get a list of href values from the given document.
|
||||||
*
|
*
|
||||||
* @returns string[]
|
* @return string[]
|
||||||
*/
|
*/
|
||||||
protected function getLinksFromContent(string $html): array
|
protected function getLinksFromContent(string $html): array
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ class SearchIndex
|
|||||||
* Create a scored term array from the given text, where the keys are the terms
|
* Create a scored term array from the given text, where the keys are the terms
|
||||||
* and the values are their scores.
|
* and the values are their scores.
|
||||||
*
|
*
|
||||||
* @returns array<string, int>
|
* @return array<string, int>
|
||||||
*/
|
*/
|
||||||
protected function generateTermScoreMapFromText(string $text, float $scoreAdjustment = 1): array
|
protected function generateTermScoreMapFromText(string $text, float $scoreAdjustment = 1): array
|
||||||
{
|
{
|
||||||
@@ -136,7 +136,7 @@ class SearchIndex
|
|||||||
* Create a scored term array from the given HTML, where the keys are the terms
|
* Create a scored term array from the given HTML, where the keys are the terms
|
||||||
* and the values are their scores.
|
* and the values are their scores.
|
||||||
*
|
*
|
||||||
* @returns array<string, int>
|
* @return array<string, int>
|
||||||
*/
|
*/
|
||||||
protected function generateTermScoreMapFromHtml(string $html): array
|
protected function generateTermScoreMapFromHtml(string $html): array
|
||||||
{
|
{
|
||||||
@@ -177,7 +177,7 @@ class SearchIndex
|
|||||||
*
|
*
|
||||||
* @param Tag[] $tags
|
* @param Tag[] $tags
|
||||||
*
|
*
|
||||||
* @returns array<string, int>
|
* @return array<string, int>
|
||||||
*/
|
*/
|
||||||
protected function generateTermScoreMapFromTags(array $tags): array
|
protected function generateTermScoreMapFromTags(array $tags): array
|
||||||
{
|
{
|
||||||
@@ -199,7 +199,7 @@ class SearchIndex
|
|||||||
* For the given text, return an array where the keys are the unique term words
|
* For the given text, return an array where the keys are the unique term words
|
||||||
* and the values are the frequency of that term.
|
* and the values are the frequency of that term.
|
||||||
*
|
*
|
||||||
* @returns array<string, int>
|
* @return array<string, int>
|
||||||
*/
|
*/
|
||||||
protected function textToTermCountMap(string $text): array
|
protected function textToTermCountMap(string $text): array
|
||||||
{
|
{
|
||||||
@@ -243,7 +243,7 @@ class SearchIndex
|
|||||||
* For the given entity, Generate an array of term data details.
|
* For the given entity, Generate an array of term data details.
|
||||||
* Is the raw term data, not instances of SearchTerm models.
|
* Is the raw term data, not instances of SearchTerm models.
|
||||||
*
|
*
|
||||||
* @returns array{term: string, score: float, entity_id: int, entity_type: string}[]
|
* @return array{term: string, score: float, entity_id: int, entity_type: string}[]
|
||||||
*/
|
*/
|
||||||
protected function entityToTermDataArray(Entity $entity): array
|
protected function entityToTermDataArray(Entity $entity): array
|
||||||
{
|
{
|
||||||
@@ -279,7 +279,7 @@ class SearchIndex
|
|||||||
*
|
*
|
||||||
* @param array<string, int>[] ...$scoreMaps
|
* @param array<string, int>[] ...$scoreMaps
|
||||||
*
|
*
|
||||||
* @returns array<string, int>
|
* @return array<string, int>
|
||||||
*/
|
*/
|
||||||
protected function mergeTermScoreMaps(...$scoreMaps): array
|
protected function mergeTermScoreMaps(...$scoreMaps): array
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ class BookSorter
|
|||||||
* Sort the books content using the given sort map.
|
* Sort the books content using the given sort map.
|
||||||
* Returns a list of books that were involved in the operation.
|
* Returns a list of books that were involved in the operation.
|
||||||
*
|
*
|
||||||
* @returns Book[]
|
* @return Book[]
|
||||||
*/
|
*/
|
||||||
public function sortUsingMap(BookSortMap $sortMap): array
|
public function sortUsingMap(BookSortMap $sortMap): array
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class ThemeEvents
|
|||||||
*
|
*
|
||||||
* @param string $authSystem
|
* @param string $authSystem
|
||||||
* @param array $userData
|
* @param array $userData
|
||||||
* @returns bool|null
|
* @return bool|null
|
||||||
*/
|
*/
|
||||||
const AUTH_PRE_REGISTER = 'auth_pre_register';
|
const AUTH_PRE_REGISTER = 'auth_pre_register';
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ class ThemeEvents
|
|||||||
* If the listener returns a non-null value, that will be used as an environment instead.
|
* If the listener returns a non-null value, that will be used as an environment instead.
|
||||||
*
|
*
|
||||||
* @param \League\CommonMark\Environment\Environment $environment
|
* @param \League\CommonMark\Environment\Environment $environment
|
||||||
* @returns \League\CommonMark\Environment\Environment|null
|
* @return \League\CommonMark\Environment\Environment|null
|
||||||
*/
|
*/
|
||||||
const COMMONMARK_ENVIRONMENT_CONFIGURE = 'commonmark_environment_configure';
|
const COMMONMARK_ENVIRONMENT_CONFIGURE = 'commonmark_environment_configure';
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ class ThemeEvents
|
|||||||
*
|
*
|
||||||
* @param array $idTokenData
|
* @param array $idTokenData
|
||||||
* @param array $accessTokenData
|
* @param array $accessTokenData
|
||||||
* @returns array|null
|
* @return array|null
|
||||||
*/
|
*/
|
||||||
const OIDC_ID_TOKEN_PRE_VALIDATE = 'oidc_id_token_pre_validate';
|
const OIDC_ID_TOKEN_PRE_VALIDATE = 'oidc_id_token_pre_validate';
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ class ThemeEvents
|
|||||||
* Return values, if provided, will be used as a new response to use.
|
* Return values, if provided, will be used as a new response to use.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @returns \Illuminate\Http\Response|null
|
* @return \Illuminate\Http\Response|null
|
||||||
*/
|
*/
|
||||||
const WEB_MIDDLEWARE_BEFORE = 'web_middleware_before';
|
const WEB_MIDDLEWARE_BEFORE = 'web_middleware_before';
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ class ThemeEvents
|
|||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\BinaryFileResponse $response
|
* @param \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\BinaryFileResponse $response
|
||||||
* @returns \Illuminate\Http\Response|null
|
* @return \Illuminate\Http\Response|null
|
||||||
*/
|
*/
|
||||||
const WEB_MIDDLEWARE_AFTER = 'web_middleware_after';
|
const WEB_MIDDLEWARE_AFTER = 'web_middleware_after';
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ class ThemeEvents
|
|||||||
* @param string|\BookStack\Activity\Models\Loggable $detail
|
* @param string|\BookStack\Activity\Models\Loggable $detail
|
||||||
* @param \BookStack\Users\Models\User $initiator
|
* @param \BookStack\Users\Models\User $initiator
|
||||||
* @param int $initiatedTime
|
* @param int $initiatedTime
|
||||||
* @returns array|null
|
* @return array|null
|
||||||
*/
|
*/
|
||||||
const WEBHOOK_CALL_BEFORE = 'webhook_call_before';
|
const WEBHOOK_CALL_BEFORE = 'webhook_call_before';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ 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;
|
||||||
|
use BookStack\Users\Models\OwnableInterface;
|
||||||
use BookStack\Users\Models\User;
|
use BookStack\Users\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
@@ -27,7 +28,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||||||
*
|
*
|
||||||
* @method static Entity|Builder visible()
|
* @method static Entity|Builder visible()
|
||||||
*/
|
*/
|
||||||
class Attachment extends Model
|
class Attachment extends Model implements OwnableInterface
|
||||||
{
|
{
|
||||||
use HasCreatorAndUpdater;
|
use HasCreatorAndUpdater;
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ 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;
|
||||||
|
use BookStack\Users\Models\OwnableInterface;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
@@ -21,7 +22,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||||||
* @property int $created_by
|
* @property int $created_by
|
||||||
* @property int $updated_by
|
* @property int $updated_by
|
||||||
*/
|
*/
|
||||||
class Image extends Model
|
class Image extends Model implements OwnableInterface
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use HasCreatorAndUpdater;
|
use HasCreatorAndUpdater;
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ class ImageService
|
|||||||
* Get the raw data content from an image.
|
* Get the raw data content from an image.
|
||||||
*
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
* @returns ?resource
|
* @return ?resource
|
||||||
*/
|
*/
|
||||||
public function getImageStream(Image $image): mixed
|
public function getImageStream(Image $image): mixed
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class ImageStorageDisk
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a stream to the file at the given path.
|
* Get a stream to the file at the given path.
|
||||||
* @returns ?resource
|
* @return ?resource
|
||||||
*/
|
*/
|
||||||
public function stream(string $path): mixed
|
public function stream(string $path): mixed
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ class UserApiController extends ApiController
|
|||||||
return $this->apiListingResponse($users, [
|
return $this->apiListingResponse($users, [
|
||||||
'id', 'name', 'slug', 'email', 'external_auth_id',
|
'id', 'name', 'slug', 'email', 'external_auth_id',
|
||||||
'created_at', 'updated_at', 'last_activity_at',
|
'created_at', 'updated_at', 'last_activity_at',
|
||||||
], [Closure::fromCallable([$this, 'listFormatter'])]);
|
], [$this->listFormatter(...)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -27,4 +27,9 @@ trait HasCreatorAndUpdater
|
|||||||
{
|
{
|
||||||
return $this->belongsTo(User::class, 'updated_by');
|
return $this->belongsTo(User::class, 'updated_by');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getOwnerFieldName(): string
|
||||||
|
{
|
||||||
|
return 'created_by';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace BookStack\Users\Models;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property int $owned_by
|
|
||||||
*/
|
|
||||||
trait HasOwner
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Relation for the user that owns this entity.
|
|
||||||
*/
|
|
||||||
public function ownedBy(): BelongsTo
|
|
||||||
{
|
|
||||||
return $this->belongsTo(User::class, 'owned_by');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
8
app/Users/Models/OwnableInterface.php
Normal file
8
app/Users/Models/OwnableInterface.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BookStack\Users\Models;
|
||||||
|
|
||||||
|
interface OwnableInterface
|
||||||
|
{
|
||||||
|
public function getOwnerFieldName(): string;
|
||||||
|
}
|
||||||
@@ -53,6 +53,7 @@ class Role extends Model implements Loggable
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The RolePermissions that belong to the role.
|
* The RolePermissions that belong to the role.
|
||||||
|
* @return BelongsToMany<RolePermission, $this>
|
||||||
*/
|
*/
|
||||||
public function permissions(): BelongsToMany
|
public function permissions(): BelongsToMany
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use BookStack\Activity\Models\Loggable;
|
|||||||
use BookStack\Activity\Models\Watch;
|
use BookStack\Activity\Models\Watch;
|
||||||
use BookStack\Api\ApiToken;
|
use BookStack\Api\ApiToken;
|
||||||
use BookStack\App\Model;
|
use BookStack\App\Model;
|
||||||
use BookStack\App\Sluggable;
|
use BookStack\App\SluggableInterface;
|
||||||
use BookStack\Entities\Tools\SlugGenerator;
|
use BookStack\Entities\Tools\SlugGenerator;
|
||||||
use BookStack\Translation\LocaleDefinition;
|
use BookStack\Translation\LocaleDefinition;
|
||||||
use BookStack\Translation\LocaleManager;
|
use BookStack\Translation\LocaleManager;
|
||||||
@@ -47,7 +47,7 @@ use Illuminate\Support\Collection;
|
|||||||
* @property Collection $mfaValues
|
* @property Collection $mfaValues
|
||||||
* @property ?Image $avatar
|
* @property ?Image $avatar
|
||||||
*/
|
*/
|
||||||
class User extends Model implements AuthenticatableContract, CanResetPasswordContract, Loggable, Sluggable
|
class User extends Model implements AuthenticatableContract, CanResetPasswordContract, Loggable, SluggableInterface
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use Authenticatable;
|
use Authenticatable;
|
||||||
@@ -372,7 +372,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
|||||||
*/
|
*/
|
||||||
public function refreshSlug(): string
|
public function refreshSlug(): string
|
||||||
{
|
{
|
||||||
$this->slug = app()->make(SlugGenerator::class)->generate($this);
|
$this->slug = app()->make(SlugGenerator::class)->generate($this, $this->name);
|
||||||
|
|
||||||
return $this->slug;
|
return $this->slug;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ parameters:
|
|||||||
- app
|
- app
|
||||||
|
|
||||||
# The level 8 is the highest level
|
# The level 8 is the highest level
|
||||||
level: 1
|
level: 2
|
||||||
|
|
||||||
phpVersion:
|
phpVersion:
|
||||||
min: 80200
|
min: 80200
|
||||||
|
|||||||
Reference in New Issue
Block a user