3 namespace BookStack\Entities\Models;
5 use BookStack\Entities\Tools\PageContent;
6 use BookStack\Permissions\PermissionApplicator;
7 use BookStack\Uploads\Attachment;
8 use Illuminate\Database\Eloquent\Builder;
9 use Illuminate\Database\Eloquent\Collection;
10 use Illuminate\Database\Eloquent\Factories\HasFactory;
11 use Illuminate\Database\Eloquent\Relations\BelongsTo;
12 use Illuminate\Database\Eloquent\Relations\HasMany;
13 use Illuminate\Database\Eloquent\Relations\HasOne;
17 * @property EntityPageData $pageData
18 * @property int $chapter_id
19 * @property string $html
20 * @property string $markdown
21 * @property string $text
22 * @property bool $template
23 * @property bool $draft
24 * @property int $revision_count
25 * @property string $editor
26 * @property Chapter $chapter
27 * @property Collection $attachments
28 * @property Collection $revisions
29 * @property PageRevision $currentRevision
31 class Page extends BookChild
35 public string $textField = 'text';
36 public string $htmlField = 'html';
37 protected $hidden = ['html', 'markdown', 'text', 'pivot', 'deleted_at', 'entity_id', 'entity_type'];
38 protected $fillable = ['name', 'priority'];
42 'template' => 'boolean',
46 * Get the entities that are visible to the current user.
48 public function scopeVisible(Builder $query): Builder
50 $query = app()->make(PermissionApplicator::class)->restrictDraftsOnPageQuery($query);
52 return parent::scopeVisible($query);
56 * Get the chapter that this page is in, If applicable.
58 public function chapter(): BelongsTo
60 return $this->belongsTo(Chapter::class);
64 * Check if this page has a chapter.
66 public function hasChapter(): bool
68 return $this->chapter()->count() > 0;
72 * Get the associated page revisions, ordered by created date.
73 * Only provides actual saved page revision instances, Not drafts.
75 public function revisions(): HasMany
77 return $this->allRevisions()
78 ->where('type', '=', 'version')
79 ->orderBy('created_at', 'desc')
80 ->orderBy('id', 'desc');
84 * Get the current revision for the page if existing.
86 public function currentRevision(): HasOne
88 return $this->hasOne(PageRevision::class)
89 ->where('type', '=', 'version')
90 ->orderBy('created_at', 'desc')
91 ->orderBy('id', 'desc');
95 * Get all revision instances assigned to this page.
96 * Includes all types of revisions.
98 public function allRevisions(): HasMany
100 return $this->hasMany(PageRevision::class);
104 * Get the attachments assigned to this page.
106 public function attachments(): HasMany
108 return $this->hasMany(Attachment::class, 'uploaded_to')->orderBy('order', 'asc');
112 * Get the url of this page.
114 public function getUrl(string $path = ''): string
118 urlencode($this->book_slug ?? $this->book->slug),
119 $this->draft ? 'draft' : 'page',
120 $this->draft ? $this->id : urlencode($this->slug),
124 return url('/' . implode('/', $parts));
128 * Get the ID-based permalink for this page.
130 public function getPermalink(): string
132 return url("/link/{$this->id}");
136 * Get this page for JSON display.
138 public function forJsonDisplay(): self
140 $refreshed = $this->refresh()->unsetRelations()->load(['tags', 'createdBy', 'updatedBy', 'ownedBy']);
141 $refreshed->setHidden(array_diff($refreshed->getHidden(), ['html', 'markdown']));
142 $refreshed->setAttribute('raw_html', $refreshed->html);
143 $refreshed->setAttribute('html', (new PageContent($refreshed))->render());
149 * @return HasOne<EntityPageData, $this>
151 public function relatedData(): HasOne
153 return $this->hasOne(EntityPageData::class, 'page_id', 'id');