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->markdown = $draft->markdown;
168 $page->isDraft = true;
169 $warnings [] = $this->pageRepo->getUserPageDraftMessage($draft);
172 if (count($warnings) > 0) session()->flash('warning', implode("\n", $warnings));
174 return view('pages/edit', ['page' => $page, 'book' => $book, 'current' => $page]);
178 * Update the specified page in storage.
179 * @param Request $request
184 public function update(Request $request, $bookSlug, $pageSlug)
186 $this->validate($request, [
187 'name' => 'required|string|max:255'
189 $book = $this->bookRepo->getBySlug($bookSlug);
190 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
191 $this->checkOwnablePermission('page-update', $page);
192 $this->pageRepo->updatePage($page, $book->id, $request->all());
193 Activity::add($page, 'page_update', $book->id);
194 return redirect($page->getUrl());
198 * Save a draft update as a revision.
199 * @param Request $request
201 * @return \Illuminate\Http\JsonResponse
203 public function saveDraft(Request $request, $pageId)
205 $page = $this->pageRepo->getById($pageId, true);
206 $this->checkOwnablePermission('page-update', $page);
208 $draft = $this->pageRepo->updateDraftPage($page, $request->only(['name', 'html', 'markdown']));
210 $draft = $this->pageRepo->saveUpdateDraft($page, $request->only(['name', 'html', 'markdown']));
212 $updateTime = $draft->updated_at->format('H:i');
213 return response()->json(['status' => 'success', 'message' => 'Draft saved at ' . $updateTime]);
217 * Redirect from a special link url which
218 * uses the page id rather than the name.
220 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
222 public function redirectFromLink($pageId)
224 $page = $this->pageRepo->getById($pageId);
225 return redirect($page->getUrl());
229 * Show the deletion page for the specified page.
232 * @return \Illuminate\View\View
234 public function showDelete($bookSlug, $pageSlug)
236 $book = $this->bookRepo->getBySlug($bookSlug);
237 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
238 $this->checkOwnablePermission('page-delete', $page);
239 $this->setPageTitle('Delete Page ' . $page->getShortName());
240 return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
245 * Show the deletion page for the specified page.
248 * @return \Illuminate\View\View
249 * @throws NotFoundException
251 public function showDeleteDraft($bookSlug, $pageId)
253 $book = $this->bookRepo->getBySlug($bookSlug);
254 $page = $this->pageRepo->getById($pageId, true);
255 $this->checkOwnablePermission('page-update', $page);
256 $this->setPageTitle('Delete Draft Page ' . $page->getShortName());
257 return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
261 * Remove the specified page from storage.
265 * @internal param int $id
267 public function destroy($bookSlug, $pageSlug)
269 $book = $this->bookRepo->getBySlug($bookSlug);
270 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
271 $this->checkOwnablePermission('page-delete', $page);
272 Activity::addMessage('page_delete', $book->id, $page->name);
273 session()->flash('success', 'Page deleted');
274 $this->pageRepo->destroy($page);
275 return redirect($book->getUrl());
279 * Remove the specified draft page from storage.
283 * @throws NotFoundException
285 public function destroyDraft($bookSlug, $pageId)
287 $book = $this->bookRepo->getBySlug($bookSlug);
288 $page = $this->pageRepo->getById($pageId, true);
289 $this->checkOwnablePermission('page-update', $page);
290 session()->flash('success', 'Draft deleted');
291 $this->pageRepo->destroy($page);
292 return redirect($book->getUrl());
296 * Shows the last revisions for this page.
299 * @return \Illuminate\View\View
301 public function showRevisions($bookSlug, $pageSlug)
303 $book = $this->bookRepo->getBySlug($bookSlug);
304 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
305 $this->setPageTitle('Revisions For ' . $page->getShortName());
306 return view('pages/revisions', ['page' => $page, 'book' => $book, 'current' => $page]);
310 * Shows a preview of a single revision
314 * @return \Illuminate\View\View
316 public function showRevision($bookSlug, $pageSlug, $revisionId)
318 $book = $this->bookRepo->getBySlug($bookSlug);
319 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
320 $revision = $this->pageRepo->getRevisionById($revisionId);
321 $page->fill($revision->toArray());
322 $this->setPageTitle('Page Revision For ' . $page->getShortName());
323 return view('pages/revision', ['page' => $page, 'book' => $book]);
327 * Restores a page using the content of the specified revision.
331 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
333 public function restoreRevision($bookSlug, $pageSlug, $revisionId)
335 $book = $this->bookRepo->getBySlug($bookSlug);
336 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
337 $this->checkOwnablePermission('page-update', $page);
338 $page = $this->pageRepo->restoreRevision($page, $book, $revisionId);
339 Activity::add($page, 'page_restore', $book->id);
340 return redirect($page->getUrl());
344 * Exports a page to pdf format using barryvdh/laravel-dompdf wrapper.
345 * https://github.com/barryvdh/laravel-dompdf
348 * @return \Illuminate\Http\Response
350 public function exportPdf($bookSlug, $pageSlug)
352 $book = $this->bookRepo->getBySlug($bookSlug);
353 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
354 $pdfContent = $this->exportService->pageToPdf($page);
355 return response()->make($pdfContent, 200, [
356 'Content-Type' => 'application/octet-stream',
357 'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.pdf'
362 * Export a page to a self-contained HTML file.
365 * @return \Illuminate\Http\Response
367 public function exportHtml($bookSlug, $pageSlug)
369 $book = $this->bookRepo->getBySlug($bookSlug);
370 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
371 $containedHtml = $this->exportService->pageToContainedHtml($page);
372 return response()->make($containedHtml, 200, [
373 'Content-Type' => 'application/octet-stream',
374 'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.html'
379 * Export a page to a simple plaintext .txt file.
382 * @return \Illuminate\Http\Response
384 public function exportPlainText($bookSlug, $pageSlug)
386 $book = $this->bookRepo->getBySlug($bookSlug);
387 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
388 $containedHtml = $this->exportService->pageToPlainText($page);
389 return response()->make($containedHtml, 200, [
390 'Content-Type' => 'application/octet-stream',
391 'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.txt'
396 * Show a listing of recently created pages
397 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
399 public function showRecentlyCreated()
401 $pages = $this->pageRepo->getRecentlyCreatedPaginated(20);
402 return view('pages/detailed-listing', [
403 'title' => 'Recently Created Pages',
409 * Show a listing of recently created pages
410 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
412 public function showRecentlyUpdated()
414 $pages = $this->pageRepo->getRecentlyUpdatedPaginated(20);
415 return view('pages/detailed-listing', [
416 'title' => 'Recently Updated Pages',
422 * Show the Restrictions view.
425 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
427 public function showRestrict($bookSlug, $pageSlug)
429 $book = $this->bookRepo->getBySlug($bookSlug);
430 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
431 $this->checkOwnablePermission('restrictions-manage', $page);
432 $roles = $this->userRepo->getRestrictableRoles();
433 return view('pages/restrictions', [
440 * Set the restrictions for this page.
443 * @param Request $request
444 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
446 public function restrict($bookSlug, $pageSlug, Request $request)
448 $book = $this->bookRepo->getBySlug($bookSlug);
449 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
450 $this->checkOwnablePermission('restrictions-manage', $page);
451 $this->pageRepo->updateRestrictionsFromRequest($request, $page);
452 session()->flash('success', 'Page Restrictions Updated');
453 return redirect($page->getUrl());