]> BookStack Code Mirror - bookstack/blob - app/Http/Controllers/PageRevisionController.php
85ee6c2bcccbaa0e1ed35ab63e2dec1e10698515
[bookstack] / app / Http / Controllers / PageRevisionController.php
1 <?php
2
3 namespace BookStack\Http\Controllers;
4
5 use BookStack\Actions\ActivityType;
6 use BookStack\Entities\Models\PageRevision;
7 use BookStack\Entities\Repos\PageRepo;
8 use BookStack\Entities\Tools\PageContent;
9 use BookStack\Exceptions\NotFoundException;
10 use BookStack\Facades\Activity;
11 use Ssddanbrown\HtmlDiff\Diff;
12
13 class PageRevisionController extends Controller
14 {
15     protected PageRepo $pageRepo;
16
17     public function __construct(PageRepo $pageRepo)
18     {
19         $this->pageRepo = $pageRepo;
20     }
21
22     /**
23      * Shows the last revisions for this page.
24      *
25      * @throws NotFoundException
26      */
27     public function index(string $bookSlug, string $pageSlug)
28     {
29         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
30         $revisions = $page->revisions()->select([
31             'id', 'page_id', 'name', 'created_at', 'created_by', 'updated_at',
32             'type', 'revision_number', 'summary',
33         ])
34             ->selectRaw("IF(markdown = '', false, true) as is_markdown")
35             ->with(['page.book', 'createdBy'])
36             ->get();
37
38         $this->setPageTitle(trans('entities.pages_revisions_named', ['pageName' => $page->getShortName()]));
39
40         return view('pages.revisions', [
41             'revisions' => $revisions,
42             'page'      => $page,
43         ]);
44     }
45
46     /**
47      * Shows a preview of a single revision.
48      *
49      * @throws NotFoundException
50      */
51     public function show(string $bookSlug, string $pageSlug, int $revisionId)
52     {
53         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
54         /** @var ?PageRevision $revision */
55         $revision = $page->revisions()->where('id', '=', $revisionId)->first();
56         if ($revision === null) {
57             throw new NotFoundException();
58         }
59
60         $page->fill($revision->toArray());
61         // TODO - Refactor PageContent so we don't need to juggle this
62         $page->html = $revision->html;
63         $page->html = (new PageContent($page))->render();
64
65         $this->setPageTitle(trans('entities.pages_revision_named', ['pageName' => $page->getShortName()]));
66
67         return view('pages.revision', [
68             'page'     => $page,
69             'book'     => $page->book,
70             'diff'     => null,
71             'revision' => $revision,
72         ]);
73     }
74
75     /**
76      * Shows the changes of a single revision.
77      *
78      * @throws NotFoundException
79      */
80     public function changes(string $bookSlug, string $pageSlug, int $revisionId)
81     {
82         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
83         /** @var ?PageRevision $revision */
84         $revision = $page->revisions()->where('id', '=', $revisionId)->first();
85         if ($revision === null) {
86             throw new NotFoundException();
87         }
88
89         $prev = $revision->getPrevious();
90         $prevContent = $prev->html ?? '';
91         $diff = Diff::excecute($prevContent, $revision->html);
92
93         $page->fill($revision->toArray());
94         // TODO - Refactor PageContent so we don't need to juggle this
95         $page->html = $revision->html;
96         $page->html = (new PageContent($page))->render();
97         $this->setPageTitle(trans('entities.pages_revision_named', ['pageName' => $page->getShortName()]));
98
99         return view('pages.revision', [
100             'page'     => $page,
101             'book'     => $page->book,
102             'diff'     => $diff,
103             'revision' => $revision,
104         ]);
105     }
106
107     /**
108      * Restores a page using the content of the specified revision.
109      *
110      * @throws NotFoundException
111      */
112     public function restore(string $bookSlug, string $pageSlug, int $revisionId)
113     {
114         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
115         $this->checkOwnablePermission('page-update', $page);
116
117         $page = $this->pageRepo->restoreRevision($page, $revisionId);
118
119         return redirect($page->getUrl());
120     }
121
122     /**
123      * Deletes a revision using the id of the specified revision.
124      *
125      * @throws NotFoundException
126      */
127     public function destroy(string $bookSlug, string $pageSlug, int $revId)
128     {
129         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
130         $this->checkOwnablePermission('page-delete', $page);
131
132         $revision = $page->revisions()->where('id', '=', $revId)->first();
133         if ($revision === null) {
134             throw new NotFoundException("Revision #{$revId} not found");
135         }
136
137         // Check if it's the latest revision, cannot delete the latest revision.
138         if (intval($page->currentRevision->id ?? null) === intval($revId)) {
139             $this->showErrorNotification(trans('entities.revision_cannot_delete_latest'));
140
141             return redirect($page->getUrl('/revisions'));
142         }
143
144         $revision->delete();
145         Activity::add(ActivityType::REVISION_DELETE, $revision);
146         $this->showSuccessNotification(trans('entities.revision_delete_success'));
147
148         return redirect($page->getUrl('/revisions'));
149     }
150 }