1 <?php namespace BookStack\Http\Controllers;
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;
15 class PageController extends Controller
20 protected $chapterRepo;
21 protected $exportService;
25 * PageController constructor.
26 * @param PageRepo $pageRepo
27 * @param BookRepo $bookRepo
28 * @param ChapterRepo $chapterRepo
29 * @param ExportService $exportService
30 * @param UserRepo $userRepo
32 public function __construct(PageRepo $pageRepo, BookRepo $bookRepo, ChapterRepo $chapterRepo, ExportService $exportService, UserRepo $userRepo)
34 $this->pageRepo = $pageRepo;
35 $this->bookRepo = $bookRepo;
36 $this->chapterRepo = $chapterRepo;
37 $this->exportService = $exportService;
38 $this->userRepo = $userRepo;
39 parent::__construct();
43 * Show the form for creating a new page.
45 * @param bool $chapterSlug
47 * @internal param bool $pageSlug
49 public function create($bookSlug, $chapterSlug = false)
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');
57 $draft = $this->pageRepo->getDraftPage($book, $chapter);
58 return redirect($draft->getUrl());
62 * Show form to continue editing a draft page.
65 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
67 public function editDraft($bookSlug, $pageId)
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');
74 return view('pages/create', ['draft' => $draft, 'book' => $book]);
78 * Store a new page by changing a draft into a page.
79 * @param Request $request
80 * @param string $bookSlug
83 public function store(Request $request, $bookSlug, $pageId)
85 $this->validate($request, [
86 'name' => 'required|string|max:255'
89 $input = $request->all();
90 $book = $this->bookRepo->getBySlug($bookSlug);
92 $draftPage = $this->pageRepo->getById($pageId, true);
94 $chapterId = $draftPage->chapter_id;
95 $parent = $chapterId !== 0 ? $this->chapterRepo->getById($chapterId) : $book;
96 $this->checkOwnablePermission('page-create', $parent);
98 if ($parent->isA('chapter')) {
99 $input['priority'] = $this->chapterRepo->getNewPriority($parent);
101 $input['priority'] = $this->bookRepo->getNewPriority($parent);
104 $page = $this->pageRepo->publishDraft($draftPage, $input);
106 Activity::add($page, 'page_create', $book->id);
107 return redirect($page->getUrl());
111 * Display the specified page.
112 * If the page is not found via the slug the
113 * revisions are searched for a match.
118 public function show($bookSlug, $pageSlug)
120 $book = $this->bookRepo->getBySlug($bookSlug);
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());
130 $this->checkOwnablePermission('page-view', $page);
132 $sidebarTree = $this->bookRepo->getChildren($book);
134 $this->setPageTitle($page->getShortName());
135 return view('pages/show', ['page' => $page, 'book' => $book, 'current' => $page, 'sidebarTree' => $sidebarTree]);
139 * Get page from an ajax request.
141 * @return \Illuminate\Http\JsonResponse
143 public function getPageAjax($pageId)
145 $page = $this->pageRepo->getById($pageId);
146 return response()->json($page);
150 * Show the form for editing the specified page.
155 public function edit($bookSlug, $pageSlug)
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;
163 // Check for active editing
165 if ($this->pageRepo->isPageEditingActive($page, 60)) {
166 $warnings[] = $this->pageRepo->getPageEditingActiveMessage($page, 60);
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);
179 if (count($warnings) > 0) session()->flash('warning', implode("\n", $warnings));
181 return view('pages/edit', ['page' => $page, 'book' => $book, 'current' => $page]);
185 * Update the specified page in storage.
186 * @param Request $request
191 public function update(Request $request, $bookSlug, $pageSlug)
193 $this->validate($request, [
194 'name' => 'required|string|max:255'
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());
205 * Save a draft update as a revision.
206 * @param Request $request
208 * @return \Illuminate\Http\JsonResponse
210 public function saveDraft(Request $request, $pageId)
212 $page = $this->pageRepo->getById($pageId, true);
213 $this->checkOwnablePermission('page-update', $page);
215 $draft = $this->pageRepo->updateDraftPage($page, $request->only(['name', 'html', 'markdown']));
217 $draft = $this->pageRepo->saveUpdateDraft($page, $request->only(['name', 'html', 'markdown']));
219 $updateTime = $draft->updated_at->format('H:i');
220 return response()->json(['status' => 'success', 'message' => 'Draft saved at ' . $updateTime]);
224 * Redirect from a special link url which
225 * uses the page id rather than the name.
227 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
229 public function redirectFromLink($pageId)
231 $page = $this->pageRepo->getById($pageId);
232 return redirect($page->getUrl());
236 * Show the deletion page for the specified page.
239 * @return \Illuminate\View\View
241 public function showDelete($bookSlug, $pageSlug)
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]);
252 * Show the deletion page for the specified page.
255 * @return \Illuminate\View\View
256 * @throws NotFoundException
258 public function showDeleteDraft($bookSlug, $pageId)
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]);
268 * Remove the specified page from storage.
272 * @internal param int $id
274 public function destroy($bookSlug, $pageSlug)
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());
286 * Remove the specified draft page from storage.
290 * @throws NotFoundException
292 public function destroyDraft($bookSlug, $pageId)
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());
303 * Shows the last revisions for this page.
306 * @return \Illuminate\View\View
308 public function showRevisions($bookSlug, $pageSlug)
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]);
317 * Shows a preview of a single revision
321 * @return \Illuminate\View\View
323 public function showRevision($bookSlug, $pageSlug, $revisionId)
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]);
334 * Restores a page using the content of the specified revision.
338 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
340 public function restoreRevision($bookSlug, $pageSlug, $revisionId)
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());
351 * Exports a page to pdf format using barryvdh/laravel-dompdf wrapper.
352 * https://github.com/barryvdh/laravel-dompdf
355 * @return \Illuminate\Http\Response
357 public function exportPdf($bookSlug, $pageSlug)
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'
369 * Export a page to a self-contained HTML file.
372 * @return \Illuminate\Http\Response
374 public function exportHtml($bookSlug, $pageSlug)
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'
386 * Export a page to a simple plaintext .txt file.
389 * @return \Illuminate\Http\Response
391 public function exportPlainText($bookSlug, $pageSlug)
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'
403 * Show a listing of recently created pages
404 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
406 public function showRecentlyCreated()
408 $pages = $this->pageRepo->getRecentlyCreatedPaginated(20);
409 return view('pages/detailed-listing', [
410 'title' => 'Recently Created Pages',
416 * Show a listing of recently created pages
417 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
419 public function showRecentlyUpdated()
421 $pages = $this->pageRepo->getRecentlyUpdatedPaginated(20);
422 return view('pages/detailed-listing', [
423 'title' => 'Recently Updated Pages',
429 * Show the Restrictions view.
432 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
434 public function showRestrict($bookSlug, $pageSlug)
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', [
447 * Set the restrictions for this page.
450 * @param Request $request
451 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
453 public function restrict($bookSlug, $pageSlug, Request $request)
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());