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 $sidebarTree = $this->bookRepo->getChildren($book);
132 $this->setPageTitle($page->getShortName());
133 return view('pages/show', ['page' => $page, 'book' => $book, 'current' => $page, 'sidebarTree' => $sidebarTree]);
137 * Get page from an ajax request.
139 * @return \Illuminate\Http\JsonResponse
141 public function getPageAjax($pageId)
143 $page = $this->pageRepo->getById($pageId);
144 return response()->json($page);
148 * Show the form for editing the specified page.
153 public function edit($bookSlug, $pageSlug)
155 $book = $this->bookRepo->getBySlug($bookSlug);
156 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
157 $this->checkOwnablePermission('page-update', $page);
158 $this->setPageTitle('Editing Page ' . $page->getShortName());
159 $page->isDraft = false;
161 // Check for active editing
163 if ($this->pageRepo->isPageEditingActive($page, 60)) {
164 $warnings[] = $this->pageRepo->getPageEditingActiveMessage($page, 60);
167 // Check for a current draft version for this user
168 if ($this->pageRepo->hasUserGotPageDraft($page, $this->currentUser->id)) {
169 $draft = $this->pageRepo->getUserPageDraft($page, $this->currentUser->id);
170 $page->name = $draft->name;
171 $page->html = $draft->html;
172 $page->markdown = $draft->markdown;
173 $page->isDraft = true;
174 $warnings [] = $this->pageRepo->getUserPageDraftMessage($draft);
177 if (count($warnings) > 0) session()->flash('warning', implode("\n", $warnings));
179 return view('pages/edit', ['page' => $page, 'book' => $book, 'current' => $page]);
183 * Update the specified page in storage.
184 * @param Request $request
189 public function update(Request $request, $bookSlug, $pageSlug)
191 $this->validate($request, [
192 'name' => 'required|string|max:255'
194 $book = $this->bookRepo->getBySlug($bookSlug);
195 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
196 $this->checkOwnablePermission('page-update', $page);
197 $this->pageRepo->updatePage($page, $book->id, $request->all());
198 Activity::add($page, 'page_update', $book->id);
199 return redirect($page->getUrl());
203 * Save a draft update as a revision.
204 * @param Request $request
206 * @return \Illuminate\Http\JsonResponse
208 public function saveDraft(Request $request, $pageId)
210 $page = $this->pageRepo->getById($pageId, true);
211 $this->checkOwnablePermission('page-update', $page);
213 $draft = $this->pageRepo->updateDraftPage($page, $request->only(['name', 'html', 'markdown']));
215 $draft = $this->pageRepo->saveUpdateDraft($page, $request->only(['name', 'html', 'markdown']));
217 $updateTime = $draft->updated_at->format('H:i');
218 return response()->json(['status' => 'success', 'message' => 'Draft saved at ' . $updateTime]);
222 * Redirect from a special link url which
223 * uses the page id rather than the name.
225 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
227 public function redirectFromLink($pageId)
229 $page = $this->pageRepo->getById($pageId);
230 return redirect($page->getUrl());
234 * Show the deletion page for the specified page.
237 * @return \Illuminate\View\View
239 public function showDelete($bookSlug, $pageSlug)
241 $book = $this->bookRepo->getBySlug($bookSlug);
242 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
243 $this->checkOwnablePermission('page-delete', $page);
244 $this->setPageTitle('Delete Page ' . $page->getShortName());
245 return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
250 * Show the deletion page for the specified page.
253 * @return \Illuminate\View\View
254 * @throws NotFoundException
256 public function showDeleteDraft($bookSlug, $pageId)
258 $book = $this->bookRepo->getBySlug($bookSlug);
259 $page = $this->pageRepo->getById($pageId, true);
260 $this->checkOwnablePermission('page-update', $page);
261 $this->setPageTitle('Delete Draft Page ' . $page->getShortName());
262 return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
266 * Remove the specified page from storage.
270 * @internal param int $id
272 public function destroy($bookSlug, $pageSlug)
274 $book = $this->bookRepo->getBySlug($bookSlug);
275 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
276 $this->checkOwnablePermission('page-delete', $page);
277 Activity::addMessage('page_delete', $book->id, $page->name);
278 session()->flash('success', 'Page deleted');
279 $this->pageRepo->destroy($page);
280 return redirect($book->getUrl());
284 * Remove the specified draft page from storage.
288 * @throws NotFoundException
290 public function destroyDraft($bookSlug, $pageId)
292 $book = $this->bookRepo->getBySlug($bookSlug);
293 $page = $this->pageRepo->getById($pageId, true);
294 $this->checkOwnablePermission('page-update', $page);
295 session()->flash('success', 'Draft deleted');
296 $this->pageRepo->destroy($page);
297 return redirect($book->getUrl());
301 * Shows the last revisions for this page.
304 * @return \Illuminate\View\View
306 public function showRevisions($bookSlug, $pageSlug)
308 $book = $this->bookRepo->getBySlug($bookSlug);
309 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
310 $this->setPageTitle('Revisions For ' . $page->getShortName());
311 return view('pages/revisions', ['page' => $page, 'book' => $book, 'current' => $page]);
315 * Shows a preview of a single revision
319 * @return \Illuminate\View\View
321 public function showRevision($bookSlug, $pageSlug, $revisionId)
323 $book = $this->bookRepo->getBySlug($bookSlug);
324 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
325 $revision = $this->pageRepo->getRevisionById($revisionId);
326 $page->fill($revision->toArray());
327 $this->setPageTitle('Page Revision For ' . $page->getShortName());
328 return view('pages/revision', ['page' => $page, 'book' => $book]);
332 * Restores a page using the content of the specified revision.
336 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
338 public function restoreRevision($bookSlug, $pageSlug, $revisionId)
340 $book = $this->bookRepo->getBySlug($bookSlug);
341 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
342 $this->checkOwnablePermission('page-update', $page);
343 $page = $this->pageRepo->restoreRevision($page, $book, $revisionId);
344 Activity::add($page, 'page_restore', $book->id);
345 return redirect($page->getUrl());
349 * Exports a page to pdf format using barryvdh/laravel-dompdf wrapper.
350 * https://github.com/barryvdh/laravel-dompdf
353 * @return \Illuminate\Http\Response
355 public function exportPdf($bookSlug, $pageSlug)
357 $book = $this->bookRepo->getBySlug($bookSlug);
358 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
359 $pdfContent = $this->exportService->pageToPdf($page);
360 return response()->make($pdfContent, 200, [
361 'Content-Type' => 'application/octet-stream',
362 'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.pdf'
367 * Export a page to a self-contained HTML file.
370 * @return \Illuminate\Http\Response
372 public function exportHtml($bookSlug, $pageSlug)
374 $book = $this->bookRepo->getBySlug($bookSlug);
375 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
376 $containedHtml = $this->exportService->pageToContainedHtml($page);
377 return response()->make($containedHtml, 200, [
378 'Content-Type' => 'application/octet-stream',
379 'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.html'
384 * Export a page to a simple plaintext .txt file.
387 * @return \Illuminate\Http\Response
389 public function exportPlainText($bookSlug, $pageSlug)
391 $book = $this->bookRepo->getBySlug($bookSlug);
392 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
393 $containedHtml = $this->exportService->pageToPlainText($page);
394 return response()->make($containedHtml, 200, [
395 'Content-Type' => 'application/octet-stream',
396 'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.txt'
401 * Show a listing of recently created pages
402 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
404 public function showRecentlyCreated()
406 $pages = $this->pageRepo->getRecentlyCreatedPaginated(20);
407 return view('pages/detailed-listing', [
408 'title' => 'Recently Created Pages',
414 * Show a listing of recently created pages
415 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
417 public function showRecentlyUpdated()
419 $pages = $this->pageRepo->getRecentlyUpdatedPaginated(20);
420 return view('pages/detailed-listing', [
421 'title' => 'Recently Updated Pages',
427 * Show the Restrictions view.
430 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
432 public function showRestrict($bookSlug, $pageSlug)
434 $book = $this->bookRepo->getBySlug($bookSlug);
435 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
436 $this->checkOwnablePermission('restrictions-manage', $page);
437 $roles = $this->userRepo->getRestrictableRoles();
438 return view('pages/restrictions', [
445 * Set the restrictions for this page.
448 * @param Request $request
449 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
451 public function restrict($bookSlug, $pageSlug, Request $request)
453 $book = $this->bookRepo->getBySlug($bookSlug);
454 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
455 $this->checkOwnablePermission('restrictions-manage', $page);
456 $this->pageRepo->updateRestrictionsFromRequest($request, $page);
457 session()->flash('success', 'Page Restrictions Updated');
458 return redirect($page->getUrl());