2022-08-16 08:23:53 -04:00
|
|
|
<?php
|
|
|
|
|
2022-08-17 09:39:53 -04:00
|
|
|
namespace BookStack\References;
|
2022-08-16 08:23:53 -04:00
|
|
|
|
2023-05-17 12:56:55 -04:00
|
|
|
use BookStack\App\Model;
|
2024-02-07 11:37:36 -05:00
|
|
|
use BookStack\Entities\Queries\EntityQueries;
|
2022-08-17 09:39:53 -04:00
|
|
|
use BookStack\References\ModelResolvers\BookLinkModelResolver;
|
|
|
|
use BookStack\References\ModelResolvers\BookshelfLinkModelResolver;
|
|
|
|
use BookStack\References\ModelResolvers\ChapterLinkModelResolver;
|
|
|
|
use BookStack\References\ModelResolvers\CrossLinkModelResolver;
|
|
|
|
use BookStack\References\ModelResolvers\PageLinkModelResolver;
|
|
|
|
use BookStack\References\ModelResolvers\PagePermalinkModelResolver;
|
2023-11-14 10:46:32 -05:00
|
|
|
use BookStack\Util\HtmlDocument;
|
2022-08-16 08:23:53 -04:00
|
|
|
|
|
|
|
class CrossLinkParser
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var CrossLinkModelResolver[]
|
|
|
|
*/
|
|
|
|
protected array $modelResolvers;
|
|
|
|
|
|
|
|
public function __construct(array $modelResolvers)
|
|
|
|
{
|
|
|
|
$this->modelResolvers = $modelResolvers;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract any found models within the given HTML content.
|
|
|
|
*
|
2022-08-17 09:39:53 -04:00
|
|
|
* @return Model[]
|
2022-08-16 08:23:53 -04:00
|
|
|
*/
|
|
|
|
public function extractLinkedModels(string $html): array
|
|
|
|
{
|
|
|
|
$models = [];
|
|
|
|
|
|
|
|
$links = $this->getLinksFromContent($html);
|
|
|
|
|
|
|
|
foreach ($links as $link) {
|
|
|
|
$model = $this->linkToModel($link);
|
|
|
|
if (!is_null($model)) {
|
|
|
|
$models[get_class($model) . ':' . $model->id] = $model;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array_values($models);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a list of href values from the given document.
|
|
|
|
*
|
|
|
|
* @returns string[]
|
|
|
|
*/
|
|
|
|
protected function getLinksFromContent(string $html): array
|
|
|
|
{
|
|
|
|
$links = [];
|
|
|
|
|
2023-11-14 10:46:32 -05:00
|
|
|
$doc = new HtmlDocument($html);
|
|
|
|
$anchors = $doc->queryXPath('//a[@href]');
|
2022-08-16 08:23:53 -04:00
|
|
|
|
|
|
|
/** @var \DOMElement $anchor */
|
|
|
|
foreach ($anchors as $anchor) {
|
|
|
|
$links[] = $anchor->getAttribute('href');
|
|
|
|
}
|
|
|
|
|
|
|
|
return $links;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempt to resolve the given link to a model using the instance model resolvers.
|
|
|
|
*/
|
|
|
|
protected function linkToModel(string $link): ?Model
|
|
|
|
{
|
|
|
|
foreach ($this->modelResolvers as $resolver) {
|
|
|
|
$model = $resolver->resolve($link);
|
|
|
|
if (!is_null($model)) {
|
|
|
|
return $model;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new instance with a pre-defined set of model resolvers, specifically for the
|
|
|
|
* default set of entities within BookStack.
|
|
|
|
*/
|
|
|
|
public static function createWithEntityResolvers(): self
|
|
|
|
{
|
2024-02-07 11:37:36 -05:00
|
|
|
$queries = app()->make(EntityQueries::class);
|
|
|
|
|
2022-08-29 12:39:50 -04:00
|
|
|
return new self([
|
2024-02-07 11:37:36 -05:00
|
|
|
new PagePermalinkModelResolver($queries->pages),
|
|
|
|
new PageLinkModelResolver($queries->pages),
|
|
|
|
new ChapterLinkModelResolver($queries->chapters),
|
|
|
|
new BookLinkModelResolver($queries->books),
|
|
|
|
new BookshelfLinkModelResolver($queries->shelves),
|
2022-08-16 08:23:53 -04:00
|
|
|
]);
|
|
|
|
}
|
2022-08-29 12:46:41 -04:00
|
|
|
}
|