]> BookStack Code Mirror - bookstack/blob - app/Entities/Controllers/ChapterController.php
Input WYSIWYG: Added description_html field, added store logic
[bookstack] / app / Entities / Controllers / ChapterController.php
1 <?php
2
3 namespace BookStack\Entities\Controllers;
4
5 use BookStack\Activity\Models\View;
6 use BookStack\Activity\Tools\UserEntityWatchOptions;
7 use BookStack\Entities\Models\Book;
8 use BookStack\Entities\Repos\ChapterRepo;
9 use BookStack\Entities\Tools\BookContents;
10 use BookStack\Entities\Tools\Cloner;
11 use BookStack\Entities\Tools\HierarchyTransformer;
12 use BookStack\Entities\Tools\NextPreviousContentLocator;
13 use BookStack\Exceptions\MoveOperationException;
14 use BookStack\Exceptions\NotFoundException;
15 use BookStack\Exceptions\NotifyException;
16 use BookStack\Exceptions\PermissionsException;
17 use BookStack\Http\Controller;
18 use BookStack\References\ReferenceFetcher;
19 use Illuminate\Http\Request;
20 use Illuminate\Validation\ValidationException;
21 use Throwable;
22
23 class ChapterController extends Controller
24 {
25     public function __construct(
26         protected ChapterRepo $chapterRepo,
27         protected ReferenceFetcher $referenceFetcher
28     ) {
29     }
30
31     /**
32      * Show the form for creating a new chapter.
33      */
34     public function create(string $bookSlug)
35     {
36         $book = Book::visible()->where('slug', '=', $bookSlug)->firstOrFail();
37         $this->checkOwnablePermission('chapter-create', $book);
38
39         $this->setPageTitle(trans('entities.chapters_create'));
40
41         return view('chapters.create', ['book' => $book, 'current' => $book]);
42     }
43
44     /**
45      * Store a newly created chapter in storage.
46      *
47      * @throws ValidationException
48      */
49     public function store(Request $request, string $bookSlug)
50     {
51         $validated = $this->validate($request, [
52             'name'             => ['required', 'string', 'max:255'],
53             'description_html' => ['string', 'max:2000'],
54             'tags'             => ['array'],
55         ]);
56
57         $book = Book::visible()->where('slug', '=', $bookSlug)->firstOrFail();
58         $this->checkOwnablePermission('chapter-create', $book);
59
60         $chapter = $this->chapterRepo->create($validated, $book);
61
62         return redirect($chapter->getUrl());
63     }
64
65     /**
66      * Display the specified chapter.
67      */
68     public function show(string $bookSlug, string $chapterSlug)
69     {
70         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
71         $this->checkOwnablePermission('chapter-view', $chapter);
72
73         $sidebarTree = (new BookContents($chapter->book))->getTree();
74         $pages = $chapter->getVisiblePages();
75         $nextPreviousLocator = new NextPreviousContentLocator($chapter, $sidebarTree);
76         View::incrementFor($chapter);
77
78         $this->setPageTitle($chapter->getShortName());
79
80         return view('chapters.show', [
81             'book'           => $chapter->book,
82             'chapter'        => $chapter,
83             'current'        => $chapter,
84             'sidebarTree'    => $sidebarTree,
85             'watchOptions'   => new UserEntityWatchOptions(user(), $chapter),
86             'pages'          => $pages,
87             'next'           => $nextPreviousLocator->getNext(),
88             'previous'       => $nextPreviousLocator->getPrevious(),
89             'referenceCount' => $this->referenceFetcher->getPageReferenceCountToEntity($chapter),
90         ]);
91     }
92
93     /**
94      * Show the form for editing the specified chapter.
95      */
96     public function edit(string $bookSlug, string $chapterSlug)
97     {
98         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
99         $this->checkOwnablePermission('chapter-update', $chapter);
100
101         $this->setPageTitle(trans('entities.chapters_edit_named', ['chapterName' => $chapter->getShortName()]));
102
103         return view('chapters.edit', ['book' => $chapter->book, 'chapter' => $chapter, 'current' => $chapter]);
104     }
105
106     /**
107      * Update the specified chapter in storage.
108      *
109      * @throws NotFoundException
110      */
111     public function update(Request $request, string $bookSlug, string $chapterSlug)
112     {
113         $validated = $this->validate($request, [
114             'name'             => ['required', 'string', 'max:255'],
115             'description_html' => ['string', 'max:2000'],
116             'tags'             => ['array'],
117         ]);
118
119         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
120         $this->checkOwnablePermission('chapter-update', $chapter);
121
122         $this->chapterRepo->update($chapter, $validated);
123
124         return redirect($chapter->getUrl());
125     }
126
127     /**
128      * Shows the page to confirm deletion of this chapter.
129      *
130      * @throws NotFoundException
131      */
132     public function showDelete(string $bookSlug, string $chapterSlug)
133     {
134         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
135         $this->checkOwnablePermission('chapter-delete', $chapter);
136
137         $this->setPageTitle(trans('entities.chapters_delete_named', ['chapterName' => $chapter->getShortName()]));
138
139         return view('chapters.delete', ['book' => $chapter->book, 'chapter' => $chapter, 'current' => $chapter]);
140     }
141
142     /**
143      * Remove the specified chapter from storage.
144      *
145      * @throws NotFoundException
146      * @throws Throwable
147      */
148     public function destroy(string $bookSlug, string $chapterSlug)
149     {
150         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
151         $this->checkOwnablePermission('chapter-delete', $chapter);
152
153         $this->chapterRepo->destroy($chapter);
154
155         return redirect($chapter->book->getUrl());
156     }
157
158     /**
159      * Show the page for moving a chapter.
160      *
161      * @throws NotFoundException
162      */
163     public function showMove(string $bookSlug, string $chapterSlug)
164     {
165         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
166         $this->setPageTitle(trans('entities.chapters_move_named', ['chapterName' => $chapter->getShortName()]));
167         $this->checkOwnablePermission('chapter-update', $chapter);
168         $this->checkOwnablePermission('chapter-delete', $chapter);
169
170         return view('chapters.move', [
171             'chapter' => $chapter,
172             'book'    => $chapter->book,
173         ]);
174     }
175
176     /**
177      * Perform the move action for a chapter.
178      *
179      * @throws NotFoundException|NotifyException
180      */
181     public function move(Request $request, string $bookSlug, string $chapterSlug)
182     {
183         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
184         $this->checkOwnablePermission('chapter-update', $chapter);
185         $this->checkOwnablePermission('chapter-delete', $chapter);
186
187         $entitySelection = $request->get('entity_selection', null);
188         if ($entitySelection === null || $entitySelection === '') {
189             return redirect($chapter->getUrl());
190         }
191
192         try {
193             $this->chapterRepo->move($chapter, $entitySelection);
194         } catch (PermissionsException $exception) {
195             $this->showPermissionError();
196         } catch (MoveOperationException $exception) {
197             $this->showErrorNotification(trans('errors.selected_book_not_found'));
198
199             return redirect($chapter->getUrl('/move'));
200         }
201
202         return redirect($chapter->getUrl());
203     }
204
205     /**
206      * Show the view to copy a chapter.
207      *
208      * @throws NotFoundException
209      */
210     public function showCopy(string $bookSlug, string $chapterSlug)
211     {
212         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
213         $this->checkOwnablePermission('chapter-view', $chapter);
214
215         session()->flashInput(['name' => $chapter->name]);
216
217         return view('chapters.copy', [
218             'book'    => $chapter->book,
219             'chapter' => $chapter,
220         ]);
221     }
222
223     /**
224      * Create a copy of a chapter within the requested target destination.
225      *
226      * @throws NotFoundException
227      * @throws Throwable
228      */
229     public function copy(Request $request, Cloner $cloner, string $bookSlug, string $chapterSlug)
230     {
231         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
232         $this->checkOwnablePermission('chapter-view', $chapter);
233
234         $entitySelection = $request->get('entity_selection') ?: null;
235         $newParentBook = $entitySelection ? $this->chapterRepo->findParentByIdentifier($entitySelection) : $chapter->getParent();
236
237         if (is_null($newParentBook)) {
238             $this->showErrorNotification(trans('errors.selected_book_not_found'));
239
240             return redirect($chapter->getUrl('/copy'));
241         }
242
243         $this->checkOwnablePermission('chapter-create', $newParentBook);
244
245         $newName = $request->get('name') ?: $chapter->name;
246         $chapterCopy = $cloner->cloneChapter($chapter, $newParentBook, $newName);
247         $this->showSuccessNotification(trans('entities.chapters_copy_success'));
248
249         return redirect($chapterCopy->getUrl());
250     }
251
252     /**
253      * Convert the chapter to a book.
254      */
255     public function convertToBook(HierarchyTransformer $transformer, string $bookSlug, string $chapterSlug)
256     {
257         $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
258         $this->checkOwnablePermission('chapter-update', $chapter);
259         $this->checkOwnablePermission('chapter-delete', $chapter);
260         $this->checkPermission('book-create-all');
261
262         $book = $transformer->transformChapterToBook($chapter);
263
264         return redirect($book->getUrl());
265     }
266 }