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