]> BookStack Code Mirror - bookstack/blob - app/Entities/Controllers/BookController.php
12df935b0a649a258e1c080fc907bb317c11a12e
[bookstack] / app / Entities / Controllers / BookController.php
1 <?php
2
3 namespace BookStack\Entities\Controllers;
4
5 use BookStack\Activity\ActivityQueries;
6 use BookStack\Activity\ActivityType;
7 use BookStack\Activity\Models\View;
8 use BookStack\Activity\Tools\UserEntityWatchOptions;
9 use BookStack\Entities\Models\Bookshelf;
10 use BookStack\Entities\Repos\BookRepo;
11 use BookStack\Entities\Tools\BookContents;
12 use BookStack\Entities\Tools\Cloner;
13 use BookStack\Entities\Tools\HierarchyTransformer;
14 use BookStack\Entities\Tools\ShelfContext;
15 use BookStack\Exceptions\ImageUploadException;
16 use BookStack\Exceptions\NotFoundException;
17 use BookStack\Facades\Activity;
18 use BookStack\Http\Controller;
19 use BookStack\References\ReferenceFetcher;
20 use BookStack\Util\SimpleListOptions;
21 use Illuminate\Http\Request;
22 use Illuminate\Validation\ValidationException;
23 use Throwable;
24
25 class BookController extends Controller
26 {
27     public function __construct(
28         protected ShelfContext $shelfContext,
29         protected BookRepo $bookRepo,
30         protected ReferenceFetcher $referenceFetcher
31     ) {
32     }
33
34     /**
35      * Display a listing of the book.
36      */
37     public function index(Request $request)
38     {
39         $view = setting()->getForCurrentUser('books_view_type');
40         $listOptions = SimpleListOptions::fromRequest($request, 'books')->withSortOptions([
41             'name' => trans('common.sort_name'),
42             'created_at' => trans('common.sort_created_at'),
43             'updated_at' => trans('common.sort_updated_at'),
44         ]);
45
46         $books = $this->bookRepo->getAllPaginated(18, $listOptions->getSort(), $listOptions->getOrder());
47         $recents = $this->isSignedIn() ? $this->bookRepo->getRecentlyViewed(4) : false;
48         $popular = $this->bookRepo->getPopular(4);
49         $new = $this->bookRepo->getRecentlyCreated(4);
50
51         $this->shelfContext->clearShelfContext();
52
53         $this->setPageTitle(trans('entities.books'));
54
55         return view('books.index', [
56             'books'   => $books,
57             'recents' => $recents,
58             'popular' => $popular,
59             'new'     => $new,
60             'view'    => $view,
61             'listOptions' => $listOptions,
62         ]);
63     }
64
65     /**
66      * Show the form for creating a new book.
67      */
68     public function create(string $shelfSlug = null)
69     {
70         $this->checkPermission('book-create-all');
71
72         $bookshelf = null;
73         if ($shelfSlug !== null) {
74             $bookshelf = Bookshelf::visible()->where('slug', '=', $shelfSlug)->firstOrFail();
75             $this->checkOwnablePermission('bookshelf-update', $bookshelf);
76         }
77
78         $this->setPageTitle(trans('entities.books_create'));
79
80         return view('books.create', [
81             'bookshelf' => $bookshelf,
82         ]);
83     }
84
85     /**
86      * Store a newly created book in storage.
87      *
88      * @throws ImageUploadException
89      * @throws ValidationException
90      */
91     public function store(Request $request, string $shelfSlug = null)
92     {
93         $this->checkPermission('book-create-all');
94         $validated = $this->validate($request, [
95             'name'              => ['required', 'string', 'max:255'],
96             'description'       => ['string', 'max:1000'],
97             'image'             => array_merge(['nullable'], $this->getImageValidationRules()),
98             'tags'              => ['array'],
99             'default_template'  => ['nullable', 'integer'],
100         ]);
101
102         $bookshelf = null;
103         if ($shelfSlug !== null) {
104             $bookshelf = Bookshelf::visible()->where('slug', '=', $shelfSlug)->firstOrFail();
105             $this->checkOwnablePermission('bookshelf-update', $bookshelf);
106         }
107
108         $book = $this->bookRepo->create($validated);
109
110         if ($bookshelf) {
111             $bookshelf->appendBook($book);
112             Activity::add(ActivityType::BOOKSHELF_UPDATE, $bookshelf);
113         }
114
115         return redirect($book->getUrl());
116     }
117
118     /**
119      * Display the specified book.
120      */
121     public function show(Request $request, ActivityQueries $activities, string $slug)
122     {
123         $book = $this->bookRepo->getBySlug($slug);
124         $bookChildren = (new BookContents($book))->getTree(true);
125         $bookParentShelves = $book->shelves()->scopes('visible')->get();
126
127         View::incrementFor($book);
128         if ($request->has('shelf')) {
129             $this->shelfContext->setShelfContext(intval($request->get('shelf')));
130         }
131
132         $this->setPageTitle($book->getShortName());
133
134         return view('books.show', [
135             'book'              => $book,
136             'current'           => $book,
137             'bookChildren'      => $bookChildren,
138             'bookParentShelves' => $bookParentShelves,
139             'watchOptions'      => new UserEntityWatchOptions(user(), $book),
140             'activity'          => $activities->entityActivity($book, 20, 1),
141             'referenceCount'    => $this->referenceFetcher->getPageReferenceCountToEntity($book),
142         ]);
143     }
144
145     /**
146      * Show the form for editing the specified book.
147      */
148     public function edit(string $slug)
149     {
150         $book = $this->bookRepo->getBySlug($slug);
151         $this->checkOwnablePermission('book-update', $book);
152         $this->setPageTitle(trans('entities.books_edit_named', ['bookName' => $book->getShortName()]));
153
154         return view('books.edit', ['book' => $book, 'current' => $book]);
155     }
156
157     /**
158      * Update the specified book in storage.
159      *
160      * @throws ImageUploadException
161      * @throws ValidationException
162      * @throws Throwable
163      */
164     public function update(Request $request, string $slug)
165     {
166         $book = $this->bookRepo->getBySlug($slug);
167         $this->checkOwnablePermission('book-update', $book);
168
169         $validated = $this->validate($request, [
170             'name'              => ['required', 'string', 'max:255'],
171             'description'       => ['string', 'max:1000'],
172             'image'             => array_merge(['nullable'], $this->getImageValidationRules()),
173             'tags'              => ['array'],
174             'default_template'  => ['nullable', 'integer'],
175         ]);
176
177         if ($request->has('image_reset')) {
178             $validated['image'] = null;
179         } elseif (array_key_exists('image', $validated) && is_null($validated['image'])) {
180             unset($validated['image']);
181         }
182
183         $book = $this->bookRepo->update($book, $validated);
184
185         return redirect($book->getUrl());
186     }
187
188     /**
189      * Shows the page to confirm deletion.
190      */
191     public function showDelete(string $bookSlug)
192     {
193         $book = $this->bookRepo->getBySlug($bookSlug);
194         $this->checkOwnablePermission('book-delete', $book);
195         $this->setPageTitle(trans('entities.books_delete_named', ['bookName' => $book->getShortName()]));
196
197         return view('books.delete', ['book' => $book, 'current' => $book]);
198     }
199
200     /**
201      * Remove the specified book from the system.
202      *
203      * @throws Throwable
204      */
205     public function destroy(string $bookSlug)
206     {
207         $book = $this->bookRepo->getBySlug($bookSlug);
208         $this->checkOwnablePermission('book-delete', $book);
209
210         $this->bookRepo->destroy($book);
211
212         return redirect('/books');
213     }
214
215     /**
216      * Show the view to copy a book.
217      *
218      * @throws NotFoundException
219      */
220     public function showCopy(string $bookSlug)
221     {
222         $book = $this->bookRepo->getBySlug($bookSlug);
223         $this->checkOwnablePermission('book-view', $book);
224
225         session()->flashInput(['name' => $book->name]);
226
227         return view('books.copy', [
228             'book' => $book,
229         ]);
230     }
231
232     /**
233      * Create a copy of a book within the requested target destination.
234      *
235      * @throws NotFoundException
236      */
237     public function copy(Request $request, Cloner $cloner, string $bookSlug)
238     {
239         $book = $this->bookRepo->getBySlug($bookSlug);
240         $this->checkOwnablePermission('book-view', $book);
241         $this->checkPermission('book-create-all');
242
243         $newName = $request->get('name') ?: $book->name;
244         $bookCopy = $cloner->cloneBook($book, $newName);
245         $this->showSuccessNotification(trans('entities.books_copy_success'));
246
247         return redirect($bookCopy->getUrl());
248     }
249
250     /**
251      * Convert the chapter to a book.
252      */
253     public function convertToShelf(HierarchyTransformer $transformer, string $bookSlug)
254     {
255         $book = $this->bookRepo->getBySlug($bookSlug);
256         $this->checkOwnablePermission('book-update', $book);
257         $this->checkOwnablePermission('book-delete', $book);
258         $this->checkPermission('bookshelf-create-all');
259         $this->checkPermission('book-create-all');
260
261         $shelf = $transformer->transformBookToShelf($book);
262
263         return redirect($shelf->getUrl());
264     }
265 }