3 namespace BookStack\Http\Controllers;
5 use BookStack\Actions\ActivityQueries;
6 use BookStack\Actions\ActivityType;
7 use BookStack\Actions\View;
8 use BookStack\Entities\Models\Bookshelf;
9 use BookStack\Entities\Repos\BookRepo;
10 use BookStack\Entities\Tools\BookContents;
11 use BookStack\Entities\Tools\Cloner;
12 use BookStack\Entities\Tools\HierarchyTransformer;
13 use BookStack\Entities\Tools\PermissionsUpdater;
14 use BookStack\Entities\Tools\ShelfContext;
15 use BookStack\Exceptions\ImageUploadException;
16 use BookStack\Exceptions\NotFoundException;
17 use BookStack\Facades\Activity;
18 use BookStack\References\ReferenceFetcher;
19 use Illuminate\Http\Request;
20 use Illuminate\Validation\ValidationException;
23 class BookController extends Controller
25 protected BookRepo $bookRepo;
26 protected ShelfContext $shelfContext;
27 protected ReferenceFetcher $referenceFetcher;
29 public function __construct(ShelfContext $entityContextManager, BookRepo $bookRepo, ReferenceFetcher $referenceFetcher)
31 $this->bookRepo = $bookRepo;
32 $this->shelfContext = $entityContextManager;
33 $this->referenceFetcher = $referenceFetcher;
37 * Display a listing of the book.
39 public function index()
41 $view = setting()->getForCurrentUser('books_view_type');
42 $sort = setting()->getForCurrentUser('books_sort', 'name');
43 $order = setting()->getForCurrentUser('books_sort_order', 'asc');
45 $books = $this->bookRepo->getAllPaginated(18, $sort, $order);
46 $recents = $this->isSignedIn() ? $this->bookRepo->getRecentlyViewed(4) : false;
47 $popular = $this->bookRepo->getPopular(4);
48 $new = $this->bookRepo->getRecentlyCreated(4);
50 $this->shelfContext->clearShelfContext();
52 $this->setPageTitle(trans('entities.books'));
54 return view('books.index', [
56 'recents' => $recents,
57 'popular' => $popular,
66 * Show the form for creating a new book.
68 public function create(string $shelfSlug = null)
70 $this->checkPermission('book-create-all');
73 if ($shelfSlug !== null) {
74 $bookshelf = Bookshelf::visible()->where('slug', '=', $shelfSlug)->firstOrFail();
75 $this->checkOwnablePermission('bookshelf-update', $bookshelf);
78 $this->setPageTitle(trans('entities.books_create'));
80 return view('books.create', [
81 'bookshelf' => $bookshelf,
86 * Store a newly created book in storage.
88 * @throws ImageUploadException
89 * @throws ValidationException
91 public function store(Request $request, string $shelfSlug = null)
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()),
102 if ($shelfSlug !== null) {
103 $bookshelf = Bookshelf::visible()->where('slug', '=', $shelfSlug)->firstOrFail();
104 $this->checkOwnablePermission('bookshelf-update', $bookshelf);
107 $book = $this->bookRepo->create($validated);
110 $bookshelf->appendBook($book);
111 Activity::add(ActivityType::BOOKSHELF_UPDATE, $bookshelf);
114 return redirect($book->getUrl());
118 * Display the specified book.
120 public function show(Request $request, ActivityQueries $activities, string $slug)
122 $book = $this->bookRepo->getBySlug($slug);
123 $bookChildren = (new BookContents($book))->getTree(true);
124 $bookParentShelves = $book->shelves()->scopes('visible')->get();
126 View::incrementFor($book);
127 if ($request->has('shelf')) {
128 $this->shelfContext->setShelfContext(intval($request->get('shelf')));
131 $this->setPageTitle($book->getShortName());
133 return view('books.show', [
136 'bookChildren' => $bookChildren,
137 'bookParentShelves' => $bookParentShelves,
138 'activity' => $activities->entityActivity($book, 20, 1),
139 'referenceCount' => $this->referenceFetcher->getPageReferenceCountToEntity($book),
144 * Show the form for editing the specified book.
146 public function edit(string $slug)
148 $book = $this->bookRepo->getBySlug($slug);
149 $this->checkOwnablePermission('book-update', $book);
150 $this->setPageTitle(trans('entities.books_edit_named', ['bookName'=>$book->getShortName()]));
152 return view('books.edit', ['book' => $book, 'current' => $book]);
156 * Update the specified book in storage.
158 * @throws ImageUploadException
159 * @throws ValidationException
162 public function update(Request $request, string $slug)
164 $book = $this->bookRepo->getBySlug($slug);
165 $this->checkOwnablePermission('book-update', $book);
167 $validated = $this->validate($request, [
168 'name' => ['required', 'string', 'max:255'],
169 'description' => ['string', 'max:1000'],
170 'image' => array_merge(['nullable'], $this->getImageValidationRules()),
174 if ($request->has('image_reset')) {
175 $validated['image'] = null;
176 } elseif (array_key_exists('image', $validated) && is_null($validated['image'])) {
177 unset($validated['image']);
180 $book = $this->bookRepo->update($book, $validated);
182 return redirect($book->getUrl());
186 * Shows the page to confirm deletion.
188 public function showDelete(string $bookSlug)
190 $book = $this->bookRepo->getBySlug($bookSlug);
191 $this->checkOwnablePermission('book-delete', $book);
192 $this->setPageTitle(trans('entities.books_delete_named', ['bookName' => $book->getShortName()]));
194 return view('books.delete', ['book' => $book, 'current' => $book]);
198 * Remove the specified book from the system.
202 public function destroy(string $bookSlug)
204 $book = $this->bookRepo->getBySlug($bookSlug);
205 $this->checkOwnablePermission('book-delete', $book);
207 $this->bookRepo->destroy($book);
209 return redirect('/books');
213 * Show the permissions view.
215 public function showPermissions(string $bookSlug)
217 $book = $this->bookRepo->getBySlug($bookSlug);
218 $this->checkOwnablePermission('restrictions-manage', $book);
220 return view('books.permissions', [
226 * Set the restrictions for this book.
230 public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $bookSlug)
232 $book = $this->bookRepo->getBySlug($bookSlug);
233 $this->checkOwnablePermission('restrictions-manage', $book);
235 $permissionsUpdater->updateFromPermissionsForm($book, $request);
237 $this->showSuccessNotification(trans('entities.books_permissions_updated'));
239 return redirect($book->getUrl());
243 * Show the view to copy a book.
245 * @throws NotFoundException
247 public function showCopy(string $bookSlug)
249 $book = $this->bookRepo->getBySlug($bookSlug);
250 $this->checkOwnablePermission('book-view', $book);
252 session()->flashInput(['name' => $book->name]);
254 return view('books.copy', [
260 * Create a copy of a book within the requested target destination.
262 * @throws NotFoundException
264 public function copy(Request $request, Cloner $cloner, string $bookSlug)
266 $book = $this->bookRepo->getBySlug($bookSlug);
267 $this->checkOwnablePermission('book-view', $book);
268 $this->checkPermission('book-create-all');
270 $newName = $request->get('name') ?: $book->name;
271 $bookCopy = $cloner->cloneBook($book, $newName);
272 $this->showSuccessNotification(trans('entities.books_copy_success'));
274 return redirect($bookCopy->getUrl());
278 * Convert the chapter to a book.
280 public function convertToShelf(HierarchyTransformer $transformer, string $bookSlug)
282 $book = $this->bookRepo->getBySlug($bookSlug);
283 $this->checkOwnablePermission('book-update', $book);
284 $this->checkOwnablePermission('book-delete', $book);
285 $this->checkPermission('bookshelf-create-all');
286 $this->checkPermission('book-create-all');
288 $shelf = $transformer->transformBookToShelf($book);
290 return redirect($shelf->getUrl());