mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-12-13 07:42:23 +03:00
Slugs: Added lookup system using history
Switched page lookup to use this.
This commit is contained in:
@@ -17,7 +17,6 @@ use BookStack\Entities\Tools\PageContent;
|
|||||||
use BookStack\Entities\Tools\PageEditActivity;
|
use BookStack\Entities\Tools\PageEditActivity;
|
||||||
use BookStack\Entities\Tools\PageEditorData;
|
use BookStack\Entities\Tools\PageEditorData;
|
||||||
use BookStack\Exceptions\NotFoundException;
|
use BookStack\Exceptions\NotFoundException;
|
||||||
use BookStack\Exceptions\NotifyException;
|
|
||||||
use BookStack\Exceptions\PermissionsException;
|
use BookStack\Exceptions\PermissionsException;
|
||||||
use BookStack\Http\Controller;
|
use BookStack\Http\Controller;
|
||||||
use BookStack\Permissions\Permission;
|
use BookStack\Permissions\Permission;
|
||||||
@@ -140,9 +139,7 @@ class PageController extends Controller
|
|||||||
try {
|
try {
|
||||||
$page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
|
$page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
|
||||||
} catch (NotFoundException $e) {
|
} catch (NotFoundException $e) {
|
||||||
$revision = $this->entityQueries->revisions->findLatestVersionBySlugs($bookSlug, $pageSlug);
|
$page = $this->entityQueries->findVisibleByOldSlugs('page', $pageSlug, $bookSlug);
|
||||||
$page = $revision->page ?? null;
|
|
||||||
|
|
||||||
if (is_null($page)) {
|
if (is_null($page)) {
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|||||||
25
app/Entities/Models/SlugHistory.php
Normal file
25
app/Entities/Models/SlugHistory.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BookStack\Entities\Models;
|
||||||
|
|
||||||
|
use BookStack\App\Model;
|
||||||
|
use BookStack\Permissions\Models\JointPermission;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property int $sluggable_id
|
||||||
|
* @property string $sluggable_type
|
||||||
|
* @property string $slug
|
||||||
|
* @property ?string $parent_slug
|
||||||
|
*/
|
||||||
|
class SlugHistory extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'slug_history';
|
||||||
|
|
||||||
|
public function jointPermissions(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(JointPermission::class, 'entity_id', 'sluggable_id')
|
||||||
|
->whereColumn('joint_permissions.entity_type', '=', 'slug_history.sluggable_type');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ namespace BookStack\Entities\Queries;
|
|||||||
|
|
||||||
use BookStack\Entities\Models\Entity;
|
use BookStack\Entities\Models\Entity;
|
||||||
use BookStack\Entities\Models\EntityTable;
|
use BookStack\Entities\Models\EntityTable;
|
||||||
|
use BookStack\Entities\Tools\SlugHistory;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Query\Builder as QueryBuilder;
|
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||||
use Illuminate\Database\Query\JoinClause;
|
use Illuminate\Database\Query\JoinClause;
|
||||||
@@ -18,6 +19,7 @@ class EntityQueries
|
|||||||
public ChapterQueries $chapters,
|
public ChapterQueries $chapters,
|
||||||
public PageQueries $pages,
|
public PageQueries $pages,
|
||||||
public PageRevisionQueries $revisions,
|
public PageRevisionQueries $revisions,
|
||||||
|
protected SlugHistory $slugHistory,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,9 +33,30 @@ class EntityQueries
|
|||||||
$explodedId = explode(':', $identifier);
|
$explodedId = explode(':', $identifier);
|
||||||
$entityType = $explodedId[0];
|
$entityType = $explodedId[0];
|
||||||
$entityId = intval($explodedId[1]);
|
$entityId = intval($explodedId[1]);
|
||||||
$queries = $this->getQueriesForType($entityType);
|
|
||||||
|
|
||||||
return $queries->findVisibleById($entityId);
|
return $this->findVisibleById($entityType, $entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an entity by its ID.
|
||||||
|
*/
|
||||||
|
public function findVisibleById(string $type, int $id): ?Entity
|
||||||
|
{
|
||||||
|
$queries = $this->getQueriesForType($type);
|
||||||
|
return $queries->findVisibleById($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an entity by looking up old slugs in the slug history.
|
||||||
|
*/
|
||||||
|
public function findVisibleByOldSlugs(string $type, string $slug, string $parentSlug = ''): ?Entity
|
||||||
|
{
|
||||||
|
$id = $this->slugHistory->lookupEntityIdUsingSlugs($type, $slug, $parentSlug);
|
||||||
|
if ($id === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->findVisibleById($type, $id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,10 +4,16 @@ namespace BookStack\Entities\Tools;
|
|||||||
|
|
||||||
use BookStack\Entities\Models\BookChild;
|
use BookStack\Entities\Models\BookChild;
|
||||||
use BookStack\Entities\Models\Entity;
|
use BookStack\Entities\Models\Entity;
|
||||||
use Illuminate\Support\Facades\DB;
|
use BookStack\Entities\Models\SlugHistory as SlugHistoryModel;
|
||||||
|
use BookStack\Permissions\PermissionApplicator;
|
||||||
|
|
||||||
class SlugHistory
|
class SlugHistory
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected PermissionApplicator $permissions,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record the current slugs for the given entity.
|
* Record the current slugs for the given entity.
|
||||||
*/
|
*/
|
||||||
@@ -17,31 +23,52 @@ class SlugHistory
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$latest = $this->getLatestEntryForEntity($entity);
|
|
||||||
if ($latest && $latest->slug === $entity->slug && $latest->parent_slug === $entity->getParent()?->slug) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$parentSlug = null;
|
$parentSlug = null;
|
||||||
if ($entity instanceof BookChild) {
|
if ($entity instanceof BookChild) {
|
||||||
$parentSlug = $entity->book()->first()?->slug;
|
$parentSlug = $entity->book()->first()?->slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$latest = $this->getLatestEntryForEntity($entity);
|
||||||
|
if ($latest && $latest->slug === $entity->slug && $latest->parent_slug === $parentSlug) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$info = [
|
$info = [
|
||||||
'sluggable_type' => $entity->getMorphClass(),
|
'sluggable_type' => $entity->getMorphClass(),
|
||||||
'sluggable_id' => $entity->id,
|
'sluggable_id' => $entity->id,
|
||||||
'slug' => $entity->slug,
|
'slug' => $entity->slug,
|
||||||
'parent_slug' => $parentSlug,
|
'parent_slug' => $parentSlug,
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now(),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
DB::table('slug_history')->insert($info);
|
$entry = new SlugHistoryModel();
|
||||||
|
$entry->forceFill($info);
|
||||||
|
$entry->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getLatestEntryForEntity(Entity $entity): \stdClass|null
|
/**
|
||||||
|
* Find the latest visible entry for an entity which uses the given slug(s) in the history.
|
||||||
|
*/
|
||||||
|
public function lookupEntityIdUsingSlugs(string $type, string $slug, string $parentSlug = ''): ?int
|
||||||
{
|
{
|
||||||
return DB::table('slug_history')
|
$query = SlugHistoryModel::query()
|
||||||
|
->where('sluggable_type', '=', $type)
|
||||||
|
->where('slug', '=', $slug);
|
||||||
|
|
||||||
|
if ($parentSlug) {
|
||||||
|
$query->where('parent_slug', '=', $parentSlug);
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = $this->permissions->restrictEntityRelationQuery($query, 'slug_history', 'sluggable_id', 'sluggable_type');
|
||||||
|
|
||||||
|
/** @var SlugHistoryModel|null $result */
|
||||||
|
$result = $query->orderBy('created_at', 'desc')->first();
|
||||||
|
|
||||||
|
return $result?->sluggable_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getLatestEntryForEntity(Entity $entity): SlugHistoryModel|null
|
||||||
|
{
|
||||||
|
return SlugHistoryModel::query()
|
||||||
->where('sluggable_type', '=', $entity->getMorphClass())
|
->where('sluggable_type', '=', $entity->getMorphClass())
|
||||||
->where('sluggable_id', '=', $entity->id)
|
->where('sluggable_id', '=', $entity->id)
|
||||||
->orderBy('created_at', 'desc')
|
->orderBy('created_at', 'desc')
|
||||||
|
|||||||
Reference in New Issue
Block a user