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