use BookStack\Auth\Permissions\PermissionService;
use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
*/
public function entityActivity(Entity $entity, int $count = 20, int $page = 1): array
{
- /** @var [string => int[]] $queryIds */
+ /** @var array<string, int[]> $queryIds */
$queryIds = [$entity->getMorphClass() => [$entity->id]];
- if ($entity->isA('book')) {
+ if ($entity instanceof Book) {
$queryIds[(new Chapter())->getMorphClass()] = $entity->chapters()->visible()->pluck('id');
}
- if ($entity->isA('book') || $entity->isA('chapter')) {
+ if ($entity instanceof Book || $entity instanceof Chapter) {
$queryIds[(new Page())->getMorphClass()] = $entity->pages()->visible()->pluck('id');
}
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
+use Illuminate\Database\Eloquent\Model;
class ExternalBaseUserProvider implements UserProvider
{
/**
* LdapUserProvider constructor.
- *
- * @param $model
*/
public function __construct(string $model)
{
/**
* Create a new instance of the model.
*
- * @return \Illuminate\Database\Eloquent\Model
+ * @return Model
*/
public function createModel()
{
*
* @param mixed $identifier
*
- * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ * @return Authenticatable|null
*/
public function retrieveById($identifier)
{
* @param mixed $identifier
* @param string $token
*
- * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ * @return Authenticatable|null
*/
public function retrieveByToken($identifier, $token)
{
/**
* Update the "remember me" token for the given user in storage.
*
- * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param Authenticatable $user
* @param string $token
*
* @return void
*
* @param array $credentials
*
- * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ * @return Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
/**
* Validate a user against the given credentials.
*
- * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param Authenticatable $user
* @param array $credentials
*
* @return bool
use BookStack\Auth\Role;
use BookStack\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
/**
* @property int $id
/**
* The roles that belong to the permission.
*/
- public function roles()
+ public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class, 'permission_role', 'permission_id', 'role_id');
}
/**
* Get the permission object by name.
- *
- * @param $name
- *
- * @return mixed
*/
- public static function getByName($name)
+ public static function getByName(string $name): ?RolePermission
{
return static::where('name', '=', $name)->first();
}
DB::setDefaultConnection($this->option('database'));
}
- $this->searchIndex->indexAllEntities(function (Entity $model, int $processed, int $total) {
+ $this->searchIndex->indexAllEntities(function (Entity $model, int $processed, int $total): void {
$this->info('Indexed ' . class_basename($model) . ' entries (' . $processed . '/' . $total . ')');
});
namespace BookStack\Entities\Models;
use BookStack\Auth\User;
+use BookStack\Interfaces\Deletable;
use BookStack\Interfaces\Loggable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
- * @property Model $deletable
+ * @property Deletable $deletable
*/
class Deletion extends Model implements Loggable
{
use BookStack\Entities\Tools\SearchIndex;
use BookStack\Entities\Tools\SlugGenerator;
use BookStack\Facades\Permissions;
+use BookStack\Interfaces\Deletable;
use BookStack\Interfaces\Favouritable;
use BookStack\Interfaces\Sluggable;
use BookStack\Interfaces\Viewable;
* @method static Builder withLastView()
* @method static Builder withViewCount()
*/
-abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
+abstract class Entity extends Model implements Sluggable, Favouritable, Viewable, Deletable
{
use SoftDeletes;
use HasCreatorAndUpdater;
/**
* Check if this instance or class is a certain type of entity.
* Examples of $type are 'page', 'book', 'chapter'.
+ * @deprecated Use instanceof instead.
*/
public static function isA(string $type): bool
{
* Included here to align with entities in similar use cases.
* (Yup, Bit of an awkward hack).
*
- * @param $type
- *
- * @return bool
+ * @deprecated Use instanceof instead.
*/
- public static function isA($type)
+ public static function isA(string $type): bool
{
return $type === 'revision';
}
/**
* Update the given items' cover image, or clear it.
*
+ * @param Entity&HasCoverImage $entity
+ *
* @throws ImageUploadException
* @throws \Exception
*/
- public function updateCoverImage(HasCoverImage $entity, ?UploadedFile $coverImage, bool $removeImage = false)
+ public function updateCoverImage($entity, ?UploadedFile $coverImage, bool $removeImage = false)
{
if ($coverImage) {
$this->imageRepo->destroyImage($entity->cover);
public function restoreRevision(Page $page, int $revisionId): Page
{
$page->revision_count++;
+
+ /** @var PageRevision $revision */
$revision = $page->revisions()->where('id', '=', $revisionId)->first();
$page->fill($revision->toArray());
}
$page->chapter_id = ($parent instanceof Chapter) ? $parent->id : null;
- $page->changeBook($parent instanceof Book ? $parent->id : $parent->book->id);
+ $newBookId = ($parent instanceof Chapter) ? $parent->book->id : $parent->id;
+ $page->changeBook($newBookId);
$page->rebuildPermissions();
Activity::addForEntity($page, ActivityType::PAGE_MOVE);
*/
protected function changeParent(Page $page, Entity $parent)
{
- $book = ($parent instanceof Book) ? $parent : $parent->book;
+ $book = ($parent instanceof Chapter) ? $parent->book : $parent;
$page->chapter_id = ($parent instanceof Chapter) ? $parent->id : 0;
$page->save();
{
$parent = $page->getParent();
if ($parent instanceof Chapter) {
+ /** @var ?Page $lastPage */
$lastPage = $parent->pages('desc')->first();
return $lastPage ? $lastPage->priority + 1 : 0;
$all->each(function (Entity $entity) use ($renderPages) {
$entity->setRelation('book', $this->book);
- if ($renderPages && $entity->isA('page')) {
+ if ($renderPages && $entity instanceof Page) {
$entity->html = (new PageContent($entity))->render();
}
});
$priorityChanged = intval($model->priority) !== intval($sortMapItem->sort);
$bookChanged = intval($model->book_id) !== intval($sortMapItem->book);
- $chapterChanged = ($sortMapItem->type === 'page') && intval($model->chapter_id) !== $sortMapItem->parentChapter;
+ $chapterChanged = ($model instanceof Page) && intval($model->chapter_id) !== $sortMapItem->parentChapter;
if ($bookChanged) {
$model->changeBook($sortMapItem->book);
/** @var Entity $item */
foreach ($bookTree->all() as $item) {
$flatOrdered->push($item);
- $childPages = $item->visible_pages ?? [];
+ $childPages = $item->getAttribute('visible_pages') ?? [];
$flatOrdered = $flatOrdered->concat($childPages);
}
/**
* Parse a base64 image URI into the data and extension.
*
- * @return array{extension: array, data: string}
+ * @return array{extension: string, data: string}
*/
protected function parseBase64ImageUri(string $uri): array
{
*/
protected function setUniqueId(\DOMNode $element, array &$idMap): array
{
- if (get_class($element) !== 'DOMElement') {
+ if (!$element instanceof \DOMElement) {
return ['', ''];
}
return [$existingId, $existingId];
}
- // Create an unique id for the element
+ // Create a unique id for the element
// Uses the content as a basis to ensure output is the same every time
// the same content is passed through.
$contentId = 'bkmrk-' . mb_substr(strtolower(preg_replace('/\s+/', '-', trim($element->nodeValue))), 0, 20);
* - The number that have been processed so far.
* - The total number of that model to be processed.
*
- * @param callable(Entity, int, int)|null $progressCallback
+ * @param callable(Entity, int, int):void|null $progressCallback
*/
public function indexAllEntities(?callable $progressCallback = null)
{
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Models\SearchTerm;
+use Illuminate\Database\Connection;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
// We have to do a raw sql query for this since otherwise PDO will quote the value and MySQL will
// search the value as a string which prevents being able to do number-based operations
// on the tag values. We ensure it has a numeric value and then cast it just to be sure.
- $tagValue = (float) trim($query->getConnection()->getPdo()->quote($tagValue), "'");
+ /** @var Connection $connection */
+ $connection = $query->getConnection();
+ $tagValue = (float) trim($connection->getPdo()->quote($tagValue), "'");
$query->whereRaw("value ${tagOperator} ${tagValue}");
} else {
$query->where('value', $tagOperator, $tagValue);
use BookStack\Entities\EntityProvider;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Page;
use Illuminate\Support\Collection;
}
// Page in book or chapter
- if (($entity instanceof Page && !$entity->chapter) || $entity->isA('chapter')) {
+ if (($entity instanceof Page && !$entity->chapter) || $entity instanceof Chapter) {
$entities = $entity->book->getDirectChildren();
}
{
$shouldRestore = true;
$restoreCount = 0;
- $parent = $deletion->deletable->getParent();
- if ($parent && $parent->trashed()) {
- $shouldRestore = false;
+ if ($deletion->deletable instanceof Entity) {
+ $parent = $deletion->deletable->getParent();
+ if ($parent && $parent->trashed()) {
+ $shouldRestore = false;
+ }
}
- if ($shouldRestore) {
+ if ($deletion->deletable instanceof Entity && $shouldRestore) {
$restoreCount = $this->restoreEntity($deletion->deletable);
}
$searching = false;
}
}
+
/** @var ?Deletion $parentDeletion */
$parentDeletion = ($currentDeletable === $deletion->deletable) ? null : $currentDeletable->deletions()->first();
--- /dev/null
+<?php
+
+namespace BookStack\Interfaces;
+
+use Illuminate\Database\Eloquent\Relations\MorphMany;
+
+/**
+ * A model that can be deleted in a manner that deletions
+ * are tracked to be part of the recycle bin system.
+ */
+interface Deletable
+{
+ public function deletions(): MorphMany;
+}
\ No newline at end of file