]> BookStack Code Mirror - bookstack/blob - app/Http/Controllers/PageController.php
Started work towards adding role view permissions
[bookstack] / app / Http / Controllers / PageController.php
1 <?php namespace BookStack\Http\Controllers;
2
3 use Activity;
4 use BookStack\Exceptions\NotFoundException;
5 use BookStack\Repos\UserRepo;
6 use BookStack\Services\ExportService;
7 use Illuminate\Http\Request;
8 use BookStack\Http\Requests;
9 use BookStack\Repos\BookRepo;
10 use BookStack\Repos\ChapterRepo;
11 use BookStack\Repos\PageRepo;
12 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
13 use Views;
14
15 class PageController extends Controller
16 {
17
18     protected $pageRepo;
19     protected $bookRepo;
20     protected $chapterRepo;
21     protected $exportService;
22     protected $userRepo;
23
24     /**
25      * PageController constructor.
26      * @param PageRepo $pageRepo
27      * @param BookRepo $bookRepo
28      * @param ChapterRepo $chapterRepo
29      * @param ExportService $exportService
30      * @param UserRepo $userRepo
31      */
32     public function __construct(PageRepo $pageRepo, BookRepo $bookRepo, ChapterRepo $chapterRepo, ExportService $exportService, UserRepo $userRepo)
33     {
34         $this->pageRepo = $pageRepo;
35         $this->bookRepo = $bookRepo;
36         $this->chapterRepo = $chapterRepo;
37         $this->exportService = $exportService;
38         $this->userRepo = $userRepo;
39         parent::__construct();
40     }
41
42     /**
43      * Show the form for creating a new page.
44      * @param      $bookSlug
45      * @param bool $chapterSlug
46      * @return Response
47      * @internal param bool $pageSlug
48      */
49     public function create($bookSlug, $chapterSlug = false)
50     {
51         $book = $this->bookRepo->getBySlug($bookSlug);
52         $chapter = $chapterSlug ? $this->chapterRepo->getBySlug($chapterSlug, $book->id) : null;
53         $parent = $chapter ? $chapter : $book;
54         $this->checkOwnablePermission('page-create', $parent);
55         $this->setPageTitle('Create New Page');
56
57         $draft = $this->pageRepo->getDraftPage($book, $chapter);
58         return redirect($draft->getUrl());
59     }
60
61     /**
62      * Show form to continue editing a draft page.
63      * @param $bookSlug
64      * @param $pageId
65      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
66      */
67     public function editDraft($bookSlug, $pageId)
68     {
69         $book = $this->bookRepo->getBySlug($bookSlug);
70         $draft = $this->pageRepo->getById($pageId, true);
71         $this->checkOwnablePermission('page-create', $draft);
72         $this->setPageTitle('Edit Page Draft');
73
74         return view('pages/create', ['draft' => $draft, 'book' => $book]);
75     }
76
77     /**
78      * Store a new page by changing a draft into a page.
79      * @param  Request $request
80      * @param  string $bookSlug
81      * @return Response
82      */
83     public function store(Request $request, $bookSlug, $pageId)
84     {
85         $this->validate($request, [
86             'name' => 'required|string|max:255'
87         ]);
88
89         $input = $request->all();
90         $book = $this->bookRepo->getBySlug($bookSlug);
91
92         $draftPage = $this->pageRepo->getById($pageId, true);
93
94         $chapterId = $draftPage->chapter_id;
95         $parent = $chapterId !== 0 ? $this->chapterRepo->getById($chapterId) : $book;
96         $this->checkOwnablePermission('page-create', $parent);
97
98         if ($parent->isA('chapter')) {
99             $input['priority'] = $this->chapterRepo->getNewPriority($parent);
100         } else {
101             $input['priority'] = $this->bookRepo->getNewPriority($parent);
102         }
103
104         $page = $this->pageRepo->publishDraft($draftPage, $input);
105
106         Activity::add($page, 'page_create', $book->id);
107         return redirect($page->getUrl());
108     }
109
110     /**
111      * Display the specified page.
112      * If the page is not found via the slug the
113      * revisions are searched for a match.
114      * @param $bookSlug
115      * @param $pageSlug
116      * @return Response
117      */
118     public function show($bookSlug, $pageSlug)
119     {
120         $book = $this->bookRepo->getBySlug($bookSlug);
121
122         try {
123             $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
124         } catch (NotFoundException $e) {
125             $page = $this->pageRepo->findPageUsingOldSlug($pageSlug, $bookSlug);
126             if ($page === null) abort(404);
127             return redirect($page->getUrl());
128         }
129
130         $this->checkOwnablePermission('page-view', $page);
131
132         $sidebarTree = $this->bookRepo->getChildren($book);
133         Views::add($page);
134         $this->setPageTitle($page->getShortName());
135         return view('pages/show', ['page' => $page, 'book' => $book, 'current' => $page, 'sidebarTree' => $sidebarTree]);
136     }
137
138     /**
139      * Get page from an ajax request.
140      * @param $pageId
141      * @return \Illuminate\Http\JsonResponse
142      */
143     public function getPageAjax($pageId)
144     {
145         $page = $this->pageRepo->getById($pageId);
146         return response()->json($page);
147     }
148
149     /**
150      * Show the form for editing the specified page.
151      * @param $bookSlug
152      * @param $pageSlug
153      * @return Response
154      */
155     public function edit($bookSlug, $pageSlug)
156     {
157         $book = $this->bookRepo->getBySlug($bookSlug);
158         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
159         $this->checkOwnablePermission('page-update', $page);
160         $this->setPageTitle('Editing Page ' . $page->getShortName());
161         $page->isDraft = false;
162
163         // Check for active editing
164         $warnings = [];
165         if ($this->pageRepo->isPageEditingActive($page, 60)) {
166             $warnings[] = $this->pageRepo->getPageEditingActiveMessage($page, 60);
167         }
168
169         // Check for a current draft version for this user
170         if ($this->pageRepo->hasUserGotPageDraft($page, $this->currentUser->id)) {
171             $draft = $this->pageRepo->getUserPageDraft($page, $this->currentUser->id);
172             $page->name = $draft->name;
173             $page->html = $draft->html;
174             $page->markdown = $draft->markdown;
175             $page->isDraft = true;
176             $warnings [] = $this->pageRepo->getUserPageDraftMessage($draft);
177         }
178
179         if (count($warnings) > 0) session()->flash('warning', implode("\n", $warnings));
180
181         return view('pages/edit', ['page' => $page, 'book' => $book, 'current' => $page]);
182     }
183
184     /**
185      * Update the specified page in storage.
186      * @param  Request $request
187      * @param          $bookSlug
188      * @param          $pageSlug
189      * @return Response
190      */
191     public function update(Request $request, $bookSlug, $pageSlug)
192     {
193         $this->validate($request, [
194             'name' => 'required|string|max:255'
195         ]);
196         $book = $this->bookRepo->getBySlug($bookSlug);
197         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
198         $this->checkOwnablePermission('page-update', $page);
199         $this->pageRepo->updatePage($page, $book->id, $request->all());
200         Activity::add($page, 'page_update', $book->id);
201         return redirect($page->getUrl());
202     }
203
204     /**
205      * Save a draft update as a revision.
206      * @param Request $request
207      * @param $pageId
208      * @return \Illuminate\Http\JsonResponse
209      */
210     public function saveDraft(Request $request, $pageId)
211     {
212         $page = $this->pageRepo->getById($pageId, true);
213         $this->checkOwnablePermission('page-update', $page);
214         if ($page->draft) {
215             $draft = $this->pageRepo->updateDraftPage($page, $request->only(['name', 'html', 'markdown']));
216         } else {
217             $draft = $this->pageRepo->saveUpdateDraft($page, $request->only(['name', 'html', 'markdown']));
218         }
219         $updateTime = $draft->updated_at->format('H:i');
220         return response()->json(['status' => 'success', 'message' => 'Draft saved at ' . $updateTime]);
221     }
222
223     /**
224      * Redirect from a special link url which
225      * uses the page id rather than the name.
226      * @param $pageId
227      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
228      */
229     public function redirectFromLink($pageId)
230     {
231         $page = $this->pageRepo->getById($pageId);
232         return redirect($page->getUrl());
233     }
234
235     /**
236      * Show the deletion page for the specified page.
237      * @param $bookSlug
238      * @param $pageSlug
239      * @return \Illuminate\View\View
240      */
241     public function showDelete($bookSlug, $pageSlug)
242     {
243         $book = $this->bookRepo->getBySlug($bookSlug);
244         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
245         $this->checkOwnablePermission('page-delete', $page);
246         $this->setPageTitle('Delete Page ' . $page->getShortName());
247         return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
248     }
249
250
251     /**
252      * Show the deletion page for the specified page.
253      * @param $bookSlug
254      * @param $pageId
255      * @return \Illuminate\View\View
256      * @throws NotFoundException
257      */
258     public function showDeleteDraft($bookSlug, $pageId)
259     {
260         $book = $this->bookRepo->getBySlug($bookSlug);
261         $page = $this->pageRepo->getById($pageId, true);
262         $this->checkOwnablePermission('page-update', $page);
263         $this->setPageTitle('Delete Draft Page ' . $page->getShortName());
264         return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
265     }
266
267     /**
268      * Remove the specified page from storage.
269      * @param $bookSlug
270      * @param $pageSlug
271      * @return Response
272      * @internal param int $id
273      */
274     public function destroy($bookSlug, $pageSlug)
275     {
276         $book = $this->bookRepo->getBySlug($bookSlug);
277         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
278         $this->checkOwnablePermission('page-delete', $page);
279         Activity::addMessage('page_delete', $book->id, $page->name);
280         session()->flash('success', 'Page deleted');
281         $this->pageRepo->destroy($page);
282         return redirect($book->getUrl());
283     }
284
285     /**
286      * Remove the specified draft page from storage.
287      * @param $bookSlug
288      * @param $pageId
289      * @return Response
290      * @throws NotFoundException
291      */
292     public function destroyDraft($bookSlug, $pageId)
293     {
294         $book = $this->bookRepo->getBySlug($bookSlug);
295         $page = $this->pageRepo->getById($pageId, true);
296         $this->checkOwnablePermission('page-update', $page);
297         session()->flash('success', 'Draft deleted');
298         $this->pageRepo->destroy($page);
299         return redirect($book->getUrl());
300     }
301
302     /**
303      * Shows the last revisions for this page.
304      * @param $bookSlug
305      * @param $pageSlug
306      * @return \Illuminate\View\View
307      */
308     public function showRevisions($bookSlug, $pageSlug)
309     {
310         $book = $this->bookRepo->getBySlug($bookSlug);
311         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
312         $this->setPageTitle('Revisions For ' . $page->getShortName());
313         return view('pages/revisions', ['page' => $page, 'book' => $book, 'current' => $page]);
314     }
315
316     /**
317      * Shows a preview of a single revision
318      * @param $bookSlug
319      * @param $pageSlug
320      * @param $revisionId
321      * @return \Illuminate\View\View
322      */
323     public function showRevision($bookSlug, $pageSlug, $revisionId)
324     {
325         $book = $this->bookRepo->getBySlug($bookSlug);
326         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
327         $revision = $this->pageRepo->getRevisionById($revisionId);
328         $page->fill($revision->toArray());
329         $this->setPageTitle('Page Revision For ' . $page->getShortName());
330         return view('pages/revision', ['page' => $page, 'book' => $book]);
331     }
332
333     /**
334      * Restores a page using the content of the specified revision.
335      * @param $bookSlug
336      * @param $pageSlug
337      * @param $revisionId
338      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
339      */
340     public function restoreRevision($bookSlug, $pageSlug, $revisionId)
341     {
342         $book = $this->bookRepo->getBySlug($bookSlug);
343         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
344         $this->checkOwnablePermission('page-update', $page);
345         $page = $this->pageRepo->restoreRevision($page, $book, $revisionId);
346         Activity::add($page, 'page_restore', $book->id);
347         return redirect($page->getUrl());
348     }
349
350     /**
351      * Exports a page to pdf format using barryvdh/laravel-dompdf wrapper.
352      * https://github.com/barryvdh/laravel-dompdf
353      * @param $bookSlug
354      * @param $pageSlug
355      * @return \Illuminate\Http\Response
356      */
357     public function exportPdf($bookSlug, $pageSlug)
358     {
359         $book = $this->bookRepo->getBySlug($bookSlug);
360         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
361         $pdfContent = $this->exportService->pageToPdf($page);
362         return response()->make($pdfContent, 200, [
363             'Content-Type'        => 'application/octet-stream',
364             'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.pdf'
365         ]);
366     }
367
368     /**
369      * Export a page to a self-contained HTML file.
370      * @param $bookSlug
371      * @param $pageSlug
372      * @return \Illuminate\Http\Response
373      */
374     public function exportHtml($bookSlug, $pageSlug)
375     {
376         $book = $this->bookRepo->getBySlug($bookSlug);
377         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
378         $containedHtml = $this->exportService->pageToContainedHtml($page);
379         return response()->make($containedHtml, 200, [
380             'Content-Type'        => 'application/octet-stream',
381             'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.html'
382         ]);
383     }
384
385     /**
386      * Export a page to a simple plaintext .txt file.
387      * @param $bookSlug
388      * @param $pageSlug
389      * @return \Illuminate\Http\Response
390      */
391     public function exportPlainText($bookSlug, $pageSlug)
392     {
393         $book = $this->bookRepo->getBySlug($bookSlug);
394         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
395         $containedHtml = $this->exportService->pageToPlainText($page);
396         return response()->make($containedHtml, 200, [
397             'Content-Type'        => 'application/octet-stream',
398             'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.txt'
399         ]);
400     }
401
402     /**
403      * Show a listing of recently created pages
404      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
405      */
406     public function showRecentlyCreated()
407     {
408         $pages = $this->pageRepo->getRecentlyCreatedPaginated(20);
409         return view('pages/detailed-listing', [
410             'title' => 'Recently Created Pages',
411             'pages' => $pages
412         ]);
413     }
414
415     /**
416      * Show a listing of recently created pages
417      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
418      */
419     public function showRecentlyUpdated()
420     {
421         $pages = $this->pageRepo->getRecentlyUpdatedPaginated(20);
422         return view('pages/detailed-listing', [
423             'title' => 'Recently Updated Pages',
424             'pages' => $pages
425         ]);
426     }
427
428     /**
429      * Show the Restrictions view.
430      * @param $bookSlug
431      * @param $pageSlug
432      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
433      */
434     public function showRestrict($bookSlug, $pageSlug)
435     {
436         $book = $this->bookRepo->getBySlug($bookSlug);
437         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
438         $this->checkOwnablePermission('restrictions-manage', $page);
439         $roles = $this->userRepo->getRestrictableRoles();
440         return view('pages/restrictions', [
441             'page'  => $page,
442             'roles' => $roles
443         ]);
444     }
445
446     /**
447      * Set the restrictions for this page.
448      * @param $bookSlug
449      * @param $pageSlug
450      * @param Request $request
451      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
452      */
453     public function restrict($bookSlug, $pageSlug, Request $request)
454     {
455         $book = $this->bookRepo->getBySlug($bookSlug);
456         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
457         $this->checkOwnablePermission('restrictions-manage', $page);
458         $this->pageRepo->updateRestrictionsFromRequest($request, $page);
459         session()->flash('success', 'Page Restrictions Updated');
460         return redirect($page->getUrl());
461     }
462
463 }