3 namespace BookStack\Http\Controllers;
5 use BookStack\Actions\ActivityType;
6 use BookStack\Entities\Repos\PageRepo;
7 use BookStack\Entities\Tools\PageContent;
8 use BookStack\Exceptions\NotFoundException;
9 use BookStack\Facades\Activity;
10 use Ssddanbrown\HtmlDiff\Diff;
12 class PageRevisionController extends Controller
14 protected PageRepo $pageRepo;
16 public function __construct(PageRepo $pageRepo)
18 $this->pageRepo = $pageRepo;
22 * Shows the last revisions for this page.
24 * @throws NotFoundException
26 public function index(string $bookSlug, string $pageSlug)
28 $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
29 $revisions = $page->revisions()->select([
30 'id', 'page_id', 'name', 'created_at', 'created_by', 'updated_at',
31 'type', 'revision_number', 'summary',
33 ->selectRaw("IF(markdown = '', false, true) as is_markdown")
34 ->with(['page.book', 'createdBy'])
37 $this->setPageTitle(trans('entities.pages_revisions_named', ['pageName' => $page->getShortName()]));
39 return view('pages.revisions', [
40 'revisions' => $revisions,
46 * Shows a preview of a single revision.
48 * @throws NotFoundException
50 public function show(string $bookSlug, string $pageSlug, int $revisionId)
52 $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
53 $revision = $page->revisions()->where('id', '=', $revisionId)->first();
54 if ($revision === null) {
55 throw new NotFoundException();
58 $page->fill($revision->toArray());
59 // TODO - Refactor PageContent so we don't need to juggle this
60 $page->html = $revision->html;
61 $page->html = (new PageContent($page))->render();
63 $this->setPageTitle(trans('entities.pages_revision_named', ['pageName' => $page->getShortName()]));
65 return view('pages.revision', [
67 'book' => $page->book,
69 'revision' => $revision,
74 * Shows the changes of a single revision.
76 * @throws NotFoundException
78 public function changes(string $bookSlug, string $pageSlug, int $revisionId)
80 $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
81 $revision = $page->revisions()->where('id', '=', $revisionId)->first();
82 if ($revision === null) {
83 throw new NotFoundException();
86 $prev = $revision->getPrevious();
87 $prevContent = $prev->html ?? '';
88 $diff = Diff::excecute($prevContent, $revision->html);
90 $page->fill($revision->toArray());
91 // TODO - Refactor PageContent so we don't need to juggle this
92 $page->html = $revision->html;
93 $page->html = (new PageContent($page))->render();
94 $this->setPageTitle(trans('entities.pages_revision_named', ['pageName'=>$page->getShortName()]));
96 return view('pages.revision', [
98 'book' => $page->book,
100 'revision' => $revision,
105 * Restores a page using the content of the specified revision.
107 * @throws NotFoundException
109 public function restore(string $bookSlug, string $pageSlug, int $revisionId)
111 $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
112 $this->checkOwnablePermission('page-update', $page);
114 $page = $this->pageRepo->restoreRevision($page, $revisionId);
116 return redirect($page->getUrl());
120 * Deletes a revision using the id of the specified revision.
122 * @throws NotFoundException
124 public function destroy(string $bookSlug, string $pageSlug, int $revId)
126 $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
127 $this->checkOwnablePermission('page-delete', $page);
129 $revision = $page->revisions()->where('id', '=', $revId)->first();
130 if ($revision === null) {
131 throw new NotFoundException("Revision #{$revId} not found");
134 // Check if it's the latest revision, cannot delete the latest revision.
135 if (intval($page->currentRevision->id ?? null) === intval($revId)) {
136 $this->showErrorNotification(trans('entities.revision_cannot_delete_latest'));
138 return redirect($page->getUrl('/revisions'));
142 Activity::add(ActivityType::REVISION_DELETE, $revision);
143 $this->showSuccessNotification(trans('entities.revision_delete_success'));
145 return redirect($page->getUrl('/revisions'));