mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-09-09 06:29:32 +03:00
Maintenance: Continued work towards PHPstan level 2
Updated html description code to be behind a proper interface. Set new convention for mode traits/interfaces.
This commit is contained in:
@@ -26,10 +26,10 @@ use Illuminate\Support\Collection;
|
|||||||
* @property ?Page $defaultTemplate
|
* @property ?Page $defaultTemplate
|
||||||
* @property ?SortRule $sortRule
|
* @property ?SortRule $sortRule
|
||||||
*/
|
*/
|
||||||
class Book extends Entity implements HasCoverImage
|
class Book extends Entity implements CoverImageInterface, HtmlDescriptionInterface
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use HasHtmlDescription;
|
use HtmlDescriptionTrait;
|
||||||
|
|
||||||
public float $searchFactor = 1.2;
|
public float $searchFactor = 1.2;
|
||||||
|
|
||||||
@@ -111,6 +111,7 @@ class Book extends Entity implements HasCoverImage
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all chapters within this book.
|
* Get all chapters within this book.
|
||||||
|
* @return HasMany<Chapter>
|
||||||
*/
|
*/
|
||||||
public function chapters(): HasMany
|
public function chapters(): HasMany
|
||||||
{
|
{
|
||||||
|
@@ -8,10 +8,10 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
|
|
||||||
class Bookshelf extends Entity implements HasCoverImage
|
class Bookshelf extends Entity implements CoverImageInterface, HtmlDescriptionInterface
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use HasHtmlDescription;
|
use HtmlDescriptionTrait;
|
||||||
|
|
||||||
protected $table = 'bookshelves';
|
protected $table = 'bookshelves';
|
||||||
|
|
||||||
|
@@ -14,10 +14,10 @@ use Illuminate\Support\Collection;
|
|||||||
* @property ?int $default_template_id
|
* @property ?int $default_template_id
|
||||||
* @property ?Page $defaultTemplate
|
* @property ?Page $defaultTemplate
|
||||||
*/
|
*/
|
||||||
class Chapter extends BookChild
|
class Chapter extends BookChild implements HtmlDescriptionInterface
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use HasHtmlDescription;
|
use HtmlDescriptionTrait;
|
||||||
|
|
||||||
public float $searchFactor = 1.2;
|
public float $searchFactor = 1.2;
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@ namespace BookStack\Entities\Models;
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
interface HasCoverImage
|
interface CoverImageInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Get the cover image for this item.
|
* Get the cover image for this item.
|
@@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
|
|||||||
* A model that can be deleted in a manner that deletions
|
* A model that can be deleted in a manner that deletions
|
||||||
* are tracked to be part of the recycle bin system.
|
* are tracked to be part of the recycle bin system.
|
||||||
*/
|
*/
|
||||||
interface Deletable
|
interface DeletableInterface
|
||||||
{
|
{
|
||||||
public function deletions(): MorphMany;
|
public function deletions(): MorphMany;
|
||||||
}
|
}
|
@@ -13,7 +13,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
|
|||||||
* @property int $deleted_by
|
* @property int $deleted_by
|
||||||
* @property string $deletable_type
|
* @property string $deletable_type
|
||||||
* @property int $deletable_id
|
* @property int $deletable_id
|
||||||
* @property Deletable $deletable
|
* @property DeletableInterface $deletable
|
||||||
*/
|
*/
|
||||||
class Deletion extends Model implements Loggable
|
class Deletion extends Model implements Loggable
|
||||||
{
|
{
|
||||||
|
@@ -49,7 +49,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||||||
* @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, Deletable, Loggable
|
abstract class Entity extends Model implements Sluggable, Favouritable, Viewable, DeletableInterface, Loggable
|
||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
use HasCreatorAndUpdater;
|
use HasCreatorAndUpdater;
|
||||||
|
@@ -1,21 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace BookStack\Entities\Models;
|
|
||||||
|
|
||||||
use BookStack\Util\HtmlContentFilter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property string $description
|
|
||||||
* @property string $description_html
|
|
||||||
*/
|
|
||||||
trait HasHtmlDescription
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the HTML description for this book.
|
|
||||||
*/
|
|
||||||
public function descriptionHtml(): string
|
|
||||||
{
|
|
||||||
$html = $this->description_html ?: '<p>' . nl2br(e($this->description)) . '</p>';
|
|
||||||
return HtmlContentFilter::removeScriptsFromHtmlString($html);
|
|
||||||
}
|
|
||||||
}
|
|
17
app/Entities/Models/HtmlDescriptionInterface.php
Normal file
17
app/Entities/Models/HtmlDescriptionInterface.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BookStack\Entities\Models;
|
||||||
|
|
||||||
|
interface HtmlDescriptionInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the HTML-based description for this item.
|
||||||
|
* By default, the content should be sanitised unless raw is set to true.
|
||||||
|
*/
|
||||||
|
public function descriptionHtml(bool $raw = false): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the HTML-based description for this item.
|
||||||
|
*/
|
||||||
|
public function setDescriptionHtml(string $html, string|null $plaintext = null): void;
|
||||||
|
}
|
35
app/Entities/Models/HtmlDescriptionTrait.php
Normal file
35
app/Entities/Models/HtmlDescriptionTrait.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BookStack\Entities\Models;
|
||||||
|
|
||||||
|
use BookStack\Util\HtmlContentFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property string $description
|
||||||
|
* @property string $description_html
|
||||||
|
*/
|
||||||
|
trait HtmlDescriptionTrait
|
||||||
|
{
|
||||||
|
public function descriptionHtml(bool $raw = false): string
|
||||||
|
{
|
||||||
|
$html = $this->description_html ?: '<p>' . nl2br(e($this->description)) . '</p>';
|
||||||
|
if ($raw) {
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HtmlContentFilter::removeScriptsFromHtmlString($html);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDescriptionHtml(string $html, string|null $plaintext = null): void
|
||||||
|
{
|
||||||
|
$this->description_html = $html;
|
||||||
|
|
||||||
|
if ($plaintext !== null) {
|
||||||
|
$this->description = $plaintext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($html) && !empty($plaintext)) {
|
||||||
|
$this->description_html = $this->descriptionHtml();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -66,6 +66,9 @@ class PageQueries implements ProvidesEntityQueries
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Builder<Page>
|
||||||
|
*/
|
||||||
public function visibleForList(): Builder
|
public function visibleForList(): Builder
|
||||||
{
|
{
|
||||||
return $this->start()
|
return $this->start()
|
||||||
|
@@ -7,8 +7,9 @@ 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\Entity;
|
use BookStack\Entities\Models\Entity;
|
||||||
use BookStack\Entities\Models\HasCoverImage;
|
use BookStack\Entities\Models\CoverImageInterface;
|
||||||
use BookStack\Entities\Models\HasHtmlDescription;
|
use BookStack\Entities\Models\HtmlDescriptionInterface;
|
||||||
|
use BookStack\Entities\Models\HtmlDescriptionTrait;
|
||||||
use BookStack\Entities\Queries\PageQueries;
|
use BookStack\Entities\Queries\PageQueries;
|
||||||
use BookStack\Exceptions\ImageUploadException;
|
use BookStack\Exceptions\ImageUploadException;
|
||||||
use BookStack\References\ReferenceStore;
|
use BookStack\References\ReferenceStore;
|
||||||
@@ -88,7 +89,7 @@ class BaseRepo
|
|||||||
/**
|
/**
|
||||||
* Update the given items' cover image, or clear it.
|
* Update the given items' cover image, or clear it.
|
||||||
*
|
*
|
||||||
* @param Entity&HasCoverImage $entity
|
* @param Entity&CoverImageInterface $entity
|
||||||
*
|
*
|
||||||
* @throws ImageUploadException
|
* @throws ImageUploadException
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
@@ -150,18 +151,17 @@ class BaseRepo
|
|||||||
|
|
||||||
protected function updateDescription(Entity $entity, array $input): void
|
protected function updateDescription(Entity $entity, array $input): void
|
||||||
{
|
{
|
||||||
if (!in_array(HasHtmlDescription::class, class_uses($entity))) {
|
if (!($entity instanceof HtmlDescriptionInterface)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var HasHtmlDescription $entity */
|
|
||||||
if (isset($input['description_html'])) {
|
if (isset($input['description_html'])) {
|
||||||
$entity->description_html = HtmlDescriptionFilter::filterFromString($input['description_html']);
|
$entity->setDescriptionHtml(
|
||||||
$entity->description = html_entity_decode(strip_tags($input['description_html']));
|
HtmlDescriptionFilter::filterFromString($input['description_html']),
|
||||||
|
html_entity_decode(strip_tags($input['description_html']))
|
||||||
|
);
|
||||||
} else if (isset($input['description'])) {
|
} else if (isset($input['description'])) {
|
||||||
$entity->description = $input['description'];
|
$entity->setDescriptionHtml('', $input['description']);
|
||||||
$entity->description_html = '';
|
|
||||||
$entity->description_html = $entity->descriptionHtml();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@ use BookStack\Entities\Models\Book;
|
|||||||
use BookStack\Entities\Models\Bookshelf;
|
use BookStack\Entities\Models\Bookshelf;
|
||||||
use BookStack\Entities\Models\Chapter;
|
use BookStack\Entities\Models\Chapter;
|
||||||
use BookStack\Entities\Models\Entity;
|
use BookStack\Entities\Models\Entity;
|
||||||
use BookStack\Entities\Models\HasCoverImage;
|
use BookStack\Entities\Models\CoverImageInterface;
|
||||||
use BookStack\Entities\Models\Page;
|
use BookStack\Entities\Models\Page;
|
||||||
use BookStack\Entities\Repos\BookRepo;
|
use BookStack\Entities\Repos\BookRepo;
|
||||||
use BookStack\Entities\Repos\ChapterRepo;
|
use BookStack\Entities\Repos\ChapterRepo;
|
||||||
@@ -105,7 +105,7 @@ class Cloner
|
|||||||
$inputData['tags'] = $this->entityTagsToInputArray($entity);
|
$inputData['tags'] = $this->entityTagsToInputArray($entity);
|
||||||
|
|
||||||
// Add a cover to the data if existing on the original entity
|
// Add a cover to the data if existing on the original entity
|
||||||
if ($entity instanceof HasCoverImage) {
|
if ($entity instanceof CoverImageInterface) {
|
||||||
$cover = $entity->cover()->first();
|
$cover = $entity->cover()->first();
|
||||||
if ($cover) {
|
if ($cover) {
|
||||||
$inputData['image'] = $this->imageToUploadedFile($cover);
|
$inputData['image'] = $this->imageToUploadedFile($cover);
|
||||||
|
@@ -7,7 +7,6 @@ use Closure;
|
|||||||
use DOMDocument;
|
use DOMDocument;
|
||||||
use DOMElement;
|
use DOMElement;
|
||||||
use DOMNode;
|
use DOMNode;
|
||||||
use DOMText;
|
|
||||||
|
|
||||||
class PageIncludeParser
|
class PageIncludeParser
|
||||||
{
|
{
|
||||||
@@ -159,7 +158,7 @@ class PageIncludeParser
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits the given $parentNode at the location of the $domNode within it.
|
* Splits the given $parentNode at the location of the $domNode within it.
|
||||||
* Attempts replicate the original $parentNode, moving some of their parent
|
* Attempts to replicate the original $parentNode, moving some of their parent
|
||||||
* children in where needed, before adding the $domNode between.
|
* children in where needed, before adding the $domNode between.
|
||||||
*/
|
*/
|
||||||
protected function splitNodeAtChildNode(DOMElement $parentNode, DOMNode $domNode): void
|
protected function splitNodeAtChildNode(DOMElement $parentNode, DOMNode $domNode): void
|
||||||
@@ -171,6 +170,10 @@ class PageIncludeParser
|
|||||||
}
|
}
|
||||||
|
|
||||||
$parentClone = $parentNode->cloneNode();
|
$parentClone = $parentNode->cloneNode();
|
||||||
|
if (!($parentClone instanceof DOMElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$parentNode->parentNode->insertBefore($parentClone, $parentNode);
|
$parentNode->parentNode->insertBefore($parentClone, $parentNode);
|
||||||
$parentClone->removeAttribute('id');
|
$parentClone->removeAttribute('id');
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@ use BookStack\Entities\Models\Bookshelf;
|
|||||||
use BookStack\Entities\Models\Chapter;
|
use BookStack\Entities\Models\Chapter;
|
||||||
use BookStack\Entities\Models\Deletion;
|
use BookStack\Entities\Models\Deletion;
|
||||||
use BookStack\Entities\Models\Entity;
|
use BookStack\Entities\Models\Entity;
|
||||||
use BookStack\Entities\Models\HasCoverImage;
|
use BookStack\Entities\Models\CoverImageInterface;
|
||||||
use BookStack\Entities\Models\Page;
|
use BookStack\Entities\Models\Page;
|
||||||
use BookStack\Entities\Queries\EntityQueries;
|
use BookStack\Entities\Queries\EntityQueries;
|
||||||
use BookStack\Exceptions\NotifyException;
|
use BookStack\Exceptions\NotifyException;
|
||||||
@@ -398,7 +398,7 @@ class TrashCan
|
|||||||
$entity->referencesTo()->delete();
|
$entity->referencesTo()->delete();
|
||||||
$entity->referencesFrom()->delete();
|
$entity->referencesFrom()->delete();
|
||||||
|
|
||||||
if ($entity instanceof HasCoverImage && $entity->cover()->exists()) {
|
if ($entity instanceof CoverImageInterface && $entity->cover()->exists()) {
|
||||||
$imageService = app()->make(ImageService::class);
|
$imageService = app()->make(ImageService::class);
|
||||||
$imageService->destroy($entity->cover()->first());
|
$imageService->destroy($entity->cover()->first());
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,8 @@ namespace BookStack\References;
|
|||||||
|
|
||||||
use BookStack\Entities\Models\Book;
|
use BookStack\Entities\Models\Book;
|
||||||
use BookStack\Entities\Models\Entity;
|
use BookStack\Entities\Models\Entity;
|
||||||
use BookStack\Entities\Models\HasHtmlDescription;
|
use BookStack\Entities\Models\HtmlDescriptionInterface;
|
||||||
|
use BookStack\Entities\Models\HtmlDescriptionTrait;
|
||||||
use BookStack\Entities\Models\Page;
|
use BookStack\Entities\Models\Page;
|
||||||
use BookStack\Entities\Repos\RevisionRepo;
|
use BookStack\Entities\Repos\RevisionRepo;
|
||||||
use BookStack\Util\HtmlDocument;
|
use BookStack\Util\HtmlDocument;
|
||||||
@@ -61,20 +62,18 @@ class ReferenceUpdater
|
|||||||
{
|
{
|
||||||
if ($entity instanceof Page) {
|
if ($entity instanceof Page) {
|
||||||
$this->updateReferencesWithinPage($entity, $oldLink, $newLink);
|
$this->updateReferencesWithinPage($entity, $oldLink, $newLink);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array(HasHtmlDescription::class, class_uses($entity))) {
|
if ($entity instanceof HtmlDescriptionInterface) {
|
||||||
$this->updateReferencesWithinDescription($entity, $oldLink, $newLink);
|
$this->updateReferencesWithinDescription($entity, $oldLink, $newLink);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function updateReferencesWithinDescription(Entity $entity, string $oldLink, string $newLink): void
|
protected function updateReferencesWithinDescription(Entity&HtmlDescriptionInterface $entity, string $oldLink, string $newLink): void
|
||||||
{
|
{
|
||||||
/** @var HasHtmlDescription&Entity $entity */
|
|
||||||
$entity = (clone $entity)->refresh();
|
$entity = (clone $entity)->refresh();
|
||||||
$html = $this->updateLinksInHtml($entity->description_html ?: '', $oldLink, $newLink);
|
$html = $this->updateLinksInHtml($entity->descriptionHtml(true) ?: '', $oldLink, $newLink);
|
||||||
$entity->description_html = $html;
|
$entity->setDescriptionHtml($html);
|
||||||
$entity->save();
|
$entity->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,7 +29,7 @@ class SortRuleController extends Controller
|
|||||||
|
|
||||||
$operations = SortRuleOperation::fromSequence($request->input('sequence'));
|
$operations = SortRuleOperation::fromSequence($request->input('sequence'));
|
||||||
if (count($operations) === 0) {
|
if (count($operations) === 0) {
|
||||||
return redirect()->withInput()->withErrors(['sequence' => 'No operations set.']);
|
return redirect('/settings/sorting/rules/new')->withInput()->withErrors(['sequence' => 'No operations set.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$rule = new SortRule();
|
$rule = new SortRule();
|
||||||
|
@@ -146,10 +146,10 @@ class ImageGalleryApiController extends ApiController
|
|||||||
$data['content']['html'] = "<div drawio-diagram=\"{$image->id}\"><img src=\"{$escapedUrl}\"></div>";
|
$data['content']['html'] = "<div drawio-diagram=\"{$image->id}\"><img src=\"{$escapedUrl}\"></div>";
|
||||||
$data['content']['markdown'] = $data['content']['html'];
|
$data['content']['markdown'] = $data['content']['html'];
|
||||||
} else {
|
} else {
|
||||||
$escapedDisplayThumb = htmlentities($image->thumbs['display']);
|
$escapedDisplayThumb = htmlentities($image->getAttribute('thumbs')['display']);
|
||||||
$data['content']['html'] = "<a href=\"{$escapedUrl}\" target=\"_blank\"><img src=\"{$escapedDisplayThumb}\" alt=\"{$escapedName}\"></a>";
|
$data['content']['html'] = "<a href=\"{$escapedUrl}\" target=\"_blank\"><img src=\"{$escapedDisplayThumb}\" alt=\"{$escapedName}\"></a>";
|
||||||
$mdEscapedName = str_replace(']', '', str_replace('[', '', $image->name));
|
$mdEscapedName = str_replace(']', '', str_replace('[', '', $image->name));
|
||||||
$mdEscapedThumb = str_replace(']', '', str_replace('[', '', $image->thumbs['display']));
|
$mdEscapedThumb = str_replace(']', '', str_replace('[', '', $image->getAttribute('thumbs')['display']));
|
||||||
$data['content']['markdown'] = "";
|
$data['content']['markdown'] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user