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);
91 $input['priority'] = $this->bookRepo->getNewPriority($book);
93 $draftPage = $this->pageRepo->getById($pageId, true);
95 $chapterId = $draftPage->chapter_id;
96 $parent = $chapterId !== 0 ? $this->chapterRepo->getById($chapterId) : $book;
97 $this->checkOwnablePermission('page-create', $parent);
99 $page = $this->pageRepo->publishDraft($draftPage, $input);
101 Activity::add($page, 'page_create', $book->id);
102 return redirect($page->getUrl());
106 * Display the specified page.
107 * If the page is not found via the slug the
108 * revisions are searched for a match.
113 public function show($bookSlug, $pageSlug)
115 $book = $this->bookRepo->getBySlug($bookSlug);
118 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
119 } catch (NotFoundException $e) {
120 $page = $this->pageRepo->findPageUsingOldSlug($pageSlug, $bookSlug);
121 if ($page === null) abort(404);
122 return redirect($page->getUrl());
125 $sidebarTree = $this->bookRepo->getChildren($book);
127 $this->setPageTitle($page->getShortName());
128 return view('pages/show', ['page' => $page, 'book' => $book, 'current' => $page, 'sidebarTree' => $sidebarTree]);
132 * Get page from an ajax request.
134 * @return \Illuminate\Http\JsonResponse
136 public function getPageAjax($pageId)
138 $page = $this->pageRepo->getById($pageId);
139 return response()->json($page);
143 * Show the form for editing the specified page.
148 public function edit($bookSlug, $pageSlug)
150 $book = $this->bookRepo->getBySlug($bookSlug);
151 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
152 $this->checkOwnablePermission('page-update', $page);
153 $this->setPageTitle('Editing Page ' . $page->getShortName());
154 $page->isDraft = false;
156 // Check for active editing
158 if ($this->pageRepo->isPageEditingActive($page, 60)) {
159 $warnings[] = $this->pageRepo->getPageEditingActiveMessage($page, 60);
162 // Check for a current draft version for this user
163 if ($this->pageRepo->hasUserGotPageDraft($page, $this->currentUser->id)) {
164 $draft = $this->pageRepo->getUserPageDraft($page, $this->currentUser->id);
165 $page->name = $draft->name;
166 $page->html = $draft->html;
167 $page->isDraft = true;
168 $warnings [] = $this->pageRepo->getUserPageDraftMessage($draft);
171 if (count($warnings) > 0) session()->flash('warning', implode("\n", $warnings));
173 return view('pages/edit', ['page' => $page, 'book' => $book, 'current' => $page]);
177 * Update the specified page in storage.
178 * @param Request $request
183 public function update(Request $request, $bookSlug, $pageSlug)
185 $this->validate($request, [
186 'name' => 'required|string|max:255'
188 $book = $this->bookRepo->getBySlug($bookSlug);
189 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
190 $this->checkOwnablePermission('page-update', $page);
191 $this->pageRepo->updatePage($page, $book->id, $request->all());
192 Activity::add($page, 'page_update', $book->id);
193 return redirect($page->getUrl());
197 * Save a draft update as a revision.
198 * @param Request $request
200 * @return \Illuminate\Http\JsonResponse
202 public function saveDraft(Request $request, $pageId)
204 $page = $this->pageRepo->getById($pageId, true);
205 $this->checkOwnablePermission('page-update', $page);
207 $draft = $this->pageRepo->updateDraftPage($page, $request->only(['name', 'html']));
209 $draft = $this->pageRepo->saveUpdateDraft($page, $request->only(['name', 'html']));
211 $updateTime = $draft->updated_at->format('H:i');
212 return response()->json(['status' => 'success', 'message' => 'Draft saved at ' . $updateTime]);
216 * Redirect from a special link url which
217 * uses the page id rather than the name.
219 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
221 public function redirectFromLink($pageId)
223 $page = $this->pageRepo->getById($pageId);
224 return redirect($page->getUrl());
228 * Show the deletion page for the specified page.
231 * @return \Illuminate\View\View
233 public function showDelete($bookSlug, $pageSlug)
235 $book = $this->bookRepo->getBySlug($bookSlug);
236 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
237 $this->checkOwnablePermission('page-delete', $page);
238 $this->setPageTitle('Delete Page ' . $page->getShortName());
239 return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
244 * Show the deletion page for the specified page.
247 * @return \Illuminate\View\View
248 * @throws NotFoundException
250 public function showDeleteDraft($bookSlug, $pageId)
252 $book = $this->bookRepo->getBySlug($bookSlug);
253 $page = $this->pageRepo->getById($pageId, true);
254 $this->checkOwnablePermission('page-update', $page);
255 $this->setPageTitle('Delete Draft Page ' . $page->getShortName());
256 return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
260 * Remove the specified page from storage.
264 * @internal param int $id
266 public function destroy($bookSlug, $pageSlug)
268 $book = $this->bookRepo->getBySlug($bookSlug);
269 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
270 $this->checkOwnablePermission('page-delete', $page);
271 Activity::addMessage('page_delete', $book->id, $page->name);
272 session()->flash('success', 'Page deleted');
273 $this->pageRepo->destroy($page);
274 return redirect($book->getUrl());
278 * Remove the specified draft page from storage.
282 * @throws NotFoundException
284 public function destroyDraft($bookSlug, $pageId)
286 $book = $this->bookRepo->getBySlug($bookSlug);
287 $page = $this->pageRepo->getById($pageId, true);
288 $this->checkOwnablePermission('page-update', $page);
289 session()->flash('success', 'Draft deleted');
290 $this->pageRepo->destroy($page);
291 return redirect($book->getUrl());
295 * Shows the last revisions for this page.
298 * @return \Illuminate\View\View
300 public function showRevisions($bookSlug, $pageSlug)
302 $book = $this->bookRepo->getBySlug($bookSlug);
303 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
304 $this->setPageTitle('Revisions For ' . $page->getShortName());
305 return view('pages/revisions', ['page' => $page, 'book' => $book, 'current' => $page]);
309 * Shows a preview of a single revision
313 * @return \Illuminate\View\View
315 public function showRevision($bookSlug, $pageSlug, $revisionId)
317 $book = $this->bookRepo->getBySlug($bookSlug);
318 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
319 $revision = $this->pageRepo->getRevisionById($revisionId);
320 $page->fill($revision->toArray());
321 $this->setPageTitle('Page Revision For ' . $page->getShortName());
322 return view('pages/revision', ['page' => $page, 'book' => $book]);
326 * Restores a page using the content of the specified revision.
330 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
332 public function restoreRevision($bookSlug, $pageSlug, $revisionId)
334 $book = $this->bookRepo->getBySlug($bookSlug);
335 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
336 $this->checkOwnablePermission('page-update', $page);
337 $page = $this->pageRepo->restoreRevision($page, $book, $revisionId);
338 Activity::add($page, 'page_restore', $book->id);
339 return redirect($page->getUrl());
343 * Exports a page to pdf format using barryvdh/laravel-dompdf wrapper.
344 * https://github.com/barryvdh/laravel-dompdf
347 * @return \Illuminate\Http\Response
349 public function exportPdf($bookSlug, $pageSlug)
351 $book = $this->bookRepo->getBySlug($bookSlug);
352 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
353 $pdfContent = $this->exportService->pageToPdf($page);
354 return response()->make($pdfContent, 200, [
355 'Content-Type' => 'application/octet-stream',
356 'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.pdf'
361 * Export a page to a self-contained HTML file.
364 * @return \Illuminate\Http\Response
366 public function exportHtml($bookSlug, $pageSlug)
368 $book = $this->bookRepo->getBySlug($bookSlug);
369 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
370 $containedHtml = $this->exportService->pageToContainedHtml($page);
371 return response()->make($containedHtml, 200, [
372 'Content-Type' => 'application/octet-stream',
373 'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.html'
378 * Export a page to a simple plaintext .txt file.
381 * @return \Illuminate\Http\Response
383 public function exportPlainText($bookSlug, $pageSlug)
385 $book = $this->bookRepo->getBySlug($bookSlug);
386 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
387 $containedHtml = $this->exportService->pageToPlainText($page);
388 return response()->make($containedHtml, 200, [
389 'Content-Type' => 'application/octet-stream',
390 'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.txt'
395 * Show a listing of recently created pages
396 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
398 public function showRecentlyCreated()
400 $pages = $this->pageRepo->getRecentlyCreatedPaginated(20);
401 return view('pages/detailed-listing', [
402 'title' => 'Recently Created Pages',
408 * Show a listing of recently created pages
409 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
411 public function showRecentlyUpdated()
413 $pages = $this->pageRepo->getRecentlyUpdatedPaginated(20);
414 return view('pages/detailed-listing', [
415 'title' => 'Recently Updated Pages',
421 * Show the Restrictions view.
424 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
426 public function showRestrict($bookSlug, $pageSlug)
428 $book = $this->bookRepo->getBySlug($bookSlug);
429 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
430 $this->checkOwnablePermission('restrictions-manage', $page);
431 $roles = $this->userRepo->getRestrictableRoles();
432 return view('pages/restrictions', [
439 * Set the restrictions for this page.
442 * @param Request $request
443 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
445 public function restrict($bookSlug, $pageSlug, Request $request)
447 $book = $this->bookRepo->getBySlug($bookSlug);
448 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
449 $this->checkOwnablePermission('restrictions-manage', $page);
450 $this->pageRepo->updateRestrictionsFromRequest($request, $page);
451 session()->flash('success', 'Page Restrictions Updated');
452 return redirect($page->getUrl());