]> BookStack Code Mirror - bookstack/blob - app/Http/Controllers/PageController.php
Merged branch autosaving_drafts into master
[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) : false;
53         $parent = $chapter ? $chapter : $book;
54         $this->checkOwnablePermission('page-create', $parent);
55         $this->setPageTitle('Create New Page');
56         return view('pages/create', ['book' => $book, 'chapter' => $chapter]);
57     }
58
59     /**
60      * Store a newly created page in storage.
61      * @param  Request $request
62      * @param          $bookSlug
63      * @return Response
64      */
65     public function store(Request $request, $bookSlug)
66     {
67         $this->validate($request, [
68             'name'   => 'required|string|max:255'
69         ]);
70
71         $input = $request->all();
72         $book = $this->bookRepo->getBySlug($bookSlug);
73         $chapterId = ($request->has('chapter') && $this->chapterRepo->idExists($request->get('chapter'))) ? $request->get('chapter') : null;
74         $parent = $chapterId !== null ? $this->chapterRepo->getById($chapterId) : $book;
75         $this->checkOwnablePermission('page-create', $parent);
76         $input['priority'] = $this->bookRepo->getNewPriority($book);
77
78         $page = $this->pageRepo->saveNew($input, $book, $chapterId);
79
80         Activity::add($page, 'page_create', $book->id);
81         return redirect($page->getUrl());
82     }
83
84     /**
85      * Display the specified page.
86      * If the page is not found via the slug the
87      * revisions are searched for a match.
88      * @param $bookSlug
89      * @param $pageSlug
90      * @return Response
91      */
92     public function show($bookSlug, $pageSlug)
93     {
94         $book = $this->bookRepo->getBySlug($bookSlug);
95
96         try {
97             $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
98         } catch (NotFoundException $e) {
99             $page = $this->pageRepo->findPageUsingOldSlug($pageSlug, $bookSlug);
100             if ($page === null) abort(404);
101             return redirect($page->getUrl());
102         }
103
104         $sidebarTree = $this->bookRepo->getChildren($book);
105         Views::add($page);
106         $this->setPageTitle($page->getShortName());
107         return view('pages/show', ['page' => $page, 'book' => $book, 'current' => $page, 'sidebarTree' => $sidebarTree]);
108     }
109
110     /**
111      * Get page from an ajax request.
112      * @param $pageId
113      * @return \Illuminate\Http\JsonResponse
114      */
115     public function getPageAjax($pageId)
116     {
117         $page = $this->pageRepo->getById($pageId);
118         return response()->json($page);
119     }
120
121     /**
122      * Show the form for editing the specified page.
123      * @param $bookSlug
124      * @param $pageSlug
125      * @return Response
126      */
127     public function edit($bookSlug, $pageSlug)
128     {
129         $book = $this->bookRepo->getBySlug($bookSlug);
130         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
131         $this->checkOwnablePermission('page-update', $page);
132         $this->setPageTitle('Editing Page ' . $page->getShortName());
133         $page->isDraft = false;
134
135         // Check for active editing and drafts
136         $warnings = [];
137         if ($this->pageRepo->isPageEditingActive($page, 60)) {
138             $warnings[] = $this->pageRepo->getPageEditingActiveMessage($page, 60);
139         }
140
141         if ($this->pageRepo->hasUserGotPageDraft($page, $this->currentUser->id)) {
142             $draft = $this->pageRepo->getUserPageDraft($page, $this->currentUser->id);
143             $page->name = $draft->name;
144             $page->html = $draft->html;
145             $page->isDraft = true;
146             $warnings [] = $this->pageRepo->getUserPageDraftMessage($draft);
147         }
148
149         if (count($warnings) > 0) session()->flash('warning', implode("\n", $warnings));
150
151         return view('pages/edit', ['page' => $page, 'book' => $book, 'current' => $page]);
152     }
153
154     /**
155      * Update the specified page in storage.
156      * @param  Request $request
157      * @param          $bookSlug
158      * @param          $pageSlug
159      * @return Response
160      */
161     public function update(Request $request, $bookSlug, $pageSlug)
162     {
163         $this->validate($request, [
164             'name'   => 'required|string|max:255'
165         ]);
166         $book = $this->bookRepo->getBySlug($bookSlug);
167         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
168         $this->checkOwnablePermission('page-update', $page);
169         $this->pageRepo->updatePage($page, $book->id, $request->all());
170         Activity::add($page, 'page_update', $book->id);
171         return redirect($page->getUrl());
172     }
173
174     /**
175      * Save a draft update as a revision.
176      * @param Request $request
177      * @param $pageId
178      * @return \Illuminate\Http\JsonResponse
179      */
180     public function saveUpdateDraft(Request $request, $pageId)
181     {
182         $this->validate($request, [
183             'name' => 'required|string|max:255'
184         ]);
185         $page = $this->pageRepo->getById($pageId);
186         $this->checkOwnablePermission('page-update', $page);
187         $draft = $this->pageRepo->saveUpdateDraft($page, $request->only(['name', 'html']));
188         $updateTime = $draft->updated_at->format('H:i');
189         return response()->json(['status' => 'success', 'message' => 'Draft saved at ' . $updateTime]);
190     }
191
192     /**
193      * Redirect from a special link url which
194      * uses the page id rather than the name.
195      * @param $pageId
196      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
197      */
198     public function redirectFromLink($pageId)
199     {
200         $page = $this->pageRepo->getById($pageId);
201         return redirect($page->getUrl());
202     }
203
204     /**
205      * Show the deletion page for the specified page.
206      * @param $bookSlug
207      * @param $pageSlug
208      * @return \Illuminate\View\View
209      */
210     public function showDelete($bookSlug, $pageSlug)
211     {
212         $book = $this->bookRepo->getBySlug($bookSlug);
213         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
214         $this->checkOwnablePermission('page-delete', $page);
215         $this->setPageTitle('Delete Page ' . $page->getShortName());
216         return view('pages/delete', ['book' => $book, 'page' => $page, 'current' => $page]);
217     }
218
219     /**
220      * Remove the specified page from storage.
221      *
222      * @param $bookSlug
223      * @param $pageSlug
224      * @return Response
225      * @internal param int $id
226      */
227     public function destroy($bookSlug, $pageSlug)
228     {
229         $book = $this->bookRepo->getBySlug($bookSlug);
230         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
231         $this->checkOwnablePermission('page-delete', $page);
232         Activity::addMessage('page_delete', $book->id, $page->name);
233         $this->pageRepo->destroy($page);
234         return redirect($book->getUrl());
235     }
236
237     /**
238      * Shows the last revisions for this page.
239      * @param $bookSlug
240      * @param $pageSlug
241      * @return \Illuminate\View\View
242      */
243     public function showRevisions($bookSlug, $pageSlug)
244     {
245         $book = $this->bookRepo->getBySlug($bookSlug);
246         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
247         $this->setPageTitle('Revisions For ' . $page->getShortName());
248         return view('pages/revisions', ['page' => $page, 'book' => $book, 'current' => $page]);
249     }
250
251     /**
252      * Shows a preview of a single revision
253      * @param $bookSlug
254      * @param $pageSlug
255      * @param $revisionId
256      * @return \Illuminate\View\View
257      */
258     public function showRevision($bookSlug, $pageSlug, $revisionId)
259     {
260         $book = $this->bookRepo->getBySlug($bookSlug);
261         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
262         $revision = $this->pageRepo->getRevisionById($revisionId);
263         $page->fill($revision->toArray());
264         $this->setPageTitle('Page Revision For ' . $page->getShortName());
265         return view('pages/revision', ['page' => $page, 'book' => $book]);
266     }
267
268     /**
269      * Restores a page using the content of the specified revision.
270      * @param $bookSlug
271      * @param $pageSlug
272      * @param $revisionId
273      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
274      */
275     public function restoreRevision($bookSlug, $pageSlug, $revisionId)
276     {
277         $book = $this->bookRepo->getBySlug($bookSlug);
278         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
279         $this->checkOwnablePermission('page-update', $page);
280         $page = $this->pageRepo->restoreRevision($page, $book, $revisionId);
281         Activity::add($page, 'page_restore', $book->id);
282         return redirect($page->getUrl());
283     }
284
285     /**
286      * Exports a page to pdf format using barryvdh/laravel-dompdf wrapper.
287      * https://github.com/barryvdh/laravel-dompdf
288      * @param $bookSlug
289      * @param $pageSlug
290      * @return \Illuminate\Http\Response
291      */
292     public function exportPdf($bookSlug, $pageSlug)
293     {
294         $book = $this->bookRepo->getBySlug($bookSlug);
295         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
296         $pdfContent = $this->exportService->pageToPdf($page);
297         return response()->make($pdfContent, 200, [
298             'Content-Type' => 'application/octet-stream',
299             'Content-Disposition' => 'attachment; filename="'.$pageSlug.'.pdf'
300         ]);
301     }
302
303     /**
304      * Export a page to a self-contained HTML file.
305      * @param $bookSlug
306      * @param $pageSlug
307      * @return \Illuminate\Http\Response
308      */
309     public function exportHtml($bookSlug, $pageSlug)
310     {
311         $book = $this->bookRepo->getBySlug($bookSlug);
312         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
313         $containedHtml = $this->exportService->pageToContainedHtml($page);
314         return response()->make($containedHtml, 200, [
315             'Content-Type' => 'application/octet-stream',
316             'Content-Disposition' => 'attachment; filename="'.$pageSlug.'.html'
317         ]);
318     }
319
320     /**
321      * Export a page to a simple plaintext .txt file.
322      * @param $bookSlug
323      * @param $pageSlug
324      * @return \Illuminate\Http\Response
325      */
326     public function exportPlainText($bookSlug, $pageSlug)
327     {
328         $book = $this->bookRepo->getBySlug($bookSlug);
329         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
330         $containedHtml = $this->exportService->pageToPlainText($page);
331         return response()->make($containedHtml, 200, [
332             'Content-Type' => 'application/octet-stream',
333             'Content-Disposition' => 'attachment; filename="'.$pageSlug.'.txt'
334         ]);
335     }
336
337     /**
338      * Show a listing of recently created pages
339      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
340      */
341     public function showRecentlyCreated()
342     {
343         $pages = $this->pageRepo->getRecentlyCreatedPaginated(20);
344         return view('pages/detailed-listing', [
345             'title' => 'Recently Created Pages',
346             'pages' => $pages
347         ]);
348     }
349
350     /**
351      * Show a listing of recently created pages
352      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
353      */
354     public function showRecentlyUpdated()
355     {
356         $pages = $this->pageRepo->getRecentlyUpdatedPaginated(20);
357         return view('pages/detailed-listing', [
358             'title' => 'Recently Updated Pages',
359             'pages' => $pages
360         ]);
361     }
362
363     /**
364      * Show the Restrictions view.
365      * @param $bookSlug
366      * @param $pageSlug
367      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
368      */
369     public function showRestrict($bookSlug, $pageSlug)
370     {
371         $book = $this->bookRepo->getBySlug($bookSlug);
372         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
373         $this->checkOwnablePermission('restrictions-manage', $page);
374         $roles = $this->userRepo->getRestrictableRoles();
375         return view('pages/restrictions', [
376             'page' => $page,
377             'roles' => $roles
378         ]);
379     }
380
381     /**
382      * Set the restrictions for this page.
383      * @param $bookSlug
384      * @param $pageSlug
385      * @param Request $request
386      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
387      */
388     public function restrict($bookSlug, $pageSlug, Request $request)
389     {
390         $book = $this->bookRepo->getBySlug($bookSlug);
391         $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
392         $this->checkOwnablePermission('restrictions-manage', $page);
393         $this->pageRepo->updateRestrictionsFromRequest($request, $page);
394         session()->flash('success', 'Page Restrictions Updated');
395         return redirect($page->getUrl());
396     }
397
398 }