Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 25 additions & 28 deletions app/Http/Controllers/BookshelfController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,19 @@
use BookStack\Entities\Tools\ShelfContext;
use BookStack\Exceptions\ImageUploadException;
use BookStack\Exceptions\NotFoundException;
use BookStack\Uploads\ImageRepo;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;

class BookshelfController extends Controller
{
protected $bookshelfRepo;
protected $entityContextManager;
protected $imageRepo;
protected BookshelfRepo $shelfRepo;
protected ShelfContext $shelfContext;

public function __construct(BookshelfRepo $bookshelfRepo, ShelfContext $entityContextManager, ImageRepo $imageRepo)
public function __construct(BookshelfRepo $shelfRepo, ShelfContext $shelfContext)
{
$this->bookshelfRepo = $bookshelfRepo;
$this->entityContextManager = $entityContextManager;
$this->imageRepo = $imageRepo;
$this->shelfRepo = $shelfRepo;
$this->shelfContext = $shelfContext;
}

/**
Expand All @@ -42,12 +39,12 @@ public function index()
'updated_at' => trans('common.sort_updated_at'),
];

$shelves = $this->bookshelfRepo->getAllPaginated(18, $sort, $order);
$recents = $this->isSignedIn() ? $this->bookshelfRepo->getRecentlyViewed(4) : false;
$popular = $this->bookshelfRepo->getPopular(4);
$new = $this->bookshelfRepo->getRecentlyCreated(4);
$shelves = $this->shelfRepo->getAllPaginated(18, $sort, $order);
$recents = $this->isSignedIn() ? $this->shelfRepo->getRecentlyViewed(4) : false;
$popular = $this->shelfRepo->getPopular(4);
$new = $this->shelfRepo->getRecentlyCreated(4);

$this->entityContextManager->clearShelfContext();
$this->shelfContext->clearShelfContext();
$this->setPageTitle(trans('entities.shelves'));

return view('shelves.index', [
Expand All @@ -68,7 +65,7 @@ public function index()
public function create()
{
$this->checkPermission('bookshelf-create-all');
$books = Book::visible()->get();
$books = Book::visible()->orderBy('name')->get(['name', 'id', 'slug']);
$this->setPageTitle(trans('entities.shelves_create'));

return view('shelves.create', ['books' => $books]);
Expand All @@ -91,7 +88,7 @@ public function store(Request $request)
]);

$bookIds = explode(',', $request->get('books', ''));
$shelf = $this->bookshelfRepo->create($validated, $bookIds);
$shelf = $this->shelfRepo->create($validated, $bookIds);

return redirect($shelf->getUrl());
}
Expand All @@ -103,7 +100,7 @@ public function store(Request $request)
*/
public function show(ActivityQueries $activities, string $slug)
{
$shelf = $this->bookshelfRepo->getBySlug($slug);
$shelf = $this->shelfRepo->getBySlug($slug);
$this->checkOwnablePermission('bookshelf-view', $shelf);

$sort = setting()->getForCurrentUser('shelf_books_sort', 'default');
Expand All @@ -115,7 +112,7 @@ public function show(ActivityQueries $activities, string $slug)
->all();

View::incrementFor($shelf);
$this->entityContextManager->setShelfContext($shelf->id);
$this->shelfContext->setShelfContext($shelf->id);
$view = setting()->getForCurrentUser('bookshelf_view_type');

$this->setPageTitle($shelf->getShortName());
Expand All @@ -135,11 +132,11 @@ public function show(ActivityQueries $activities, string $slug)
*/
public function edit(string $slug)
{
$shelf = $this->bookshelfRepo->getBySlug($slug);
$shelf = $this->shelfRepo->getBySlug($slug);
$this->checkOwnablePermission('bookshelf-update', $shelf);

$shelfBookIds = $shelf->books()->get(['id'])->pluck('id');
$books = Book::visible()->whereNotIn('id', $shelfBookIds)->get();
$books = Book::visible()->whereNotIn('id', $shelfBookIds)->orderBy('name')->get(['name', 'id', 'slug']);

$this->setPageTitle(trans('entities.shelves_edit_named', ['name' => $shelf->getShortName()]));

Expand All @@ -158,7 +155,7 @@ public function edit(string $slug)
*/
public function update(Request $request, string $slug)
{
$shelf = $this->bookshelfRepo->getBySlug($slug);
$shelf = $this->shelfRepo->getBySlug($slug);
$this->checkOwnablePermission('bookshelf-update', $shelf);
$validated = $this->validate($request, [
'name' => ['required', 'string', 'max:255'],
Expand All @@ -174,7 +171,7 @@ public function update(Request $request, string $slug)
}

$bookIds = explode(',', $request->get('books', ''));
$shelf = $this->bookshelfRepo->update($shelf, $validated, $bookIds);
$shelf = $this->shelfRepo->update($shelf, $validated, $bookIds);

return redirect($shelf->getUrl());
}
Expand All @@ -184,7 +181,7 @@ public function update(Request $request, string $slug)
*/
public function showDelete(string $slug)
{
$shelf = $this->bookshelfRepo->getBySlug($slug);
$shelf = $this->shelfRepo->getBySlug($slug);
$this->checkOwnablePermission('bookshelf-delete', $shelf);

$this->setPageTitle(trans('entities.shelves_delete_named', ['name' => $shelf->getShortName()]));
Expand All @@ -199,10 +196,10 @@ public function showDelete(string $slug)
*/
public function destroy(string $slug)
{
$shelf = $this->bookshelfRepo->getBySlug($slug);
$shelf = $this->shelfRepo->getBySlug($slug);
$this->checkOwnablePermission('bookshelf-delete', $shelf);

$this->bookshelfRepo->destroy($shelf);
$this->shelfRepo->destroy($shelf);

return redirect('/shelves');
}
Expand All @@ -212,7 +209,7 @@ public function destroy(string $slug)
*/
public function showPermissions(string $slug)
{
$shelf = $this->bookshelfRepo->getBySlug($slug);
$shelf = $this->shelfRepo->getBySlug($slug);
$this->checkOwnablePermission('restrictions-manage', $shelf);

return view('shelves.permissions', [
Expand All @@ -225,7 +222,7 @@ public function showPermissions(string $slug)
*/
public function permissions(Request $request, PermissionsUpdater $permissionsUpdater, string $slug)
{
$shelf = $this->bookshelfRepo->getBySlug($slug);
$shelf = $this->shelfRepo->getBySlug($slug);
$this->checkOwnablePermission('restrictions-manage', $shelf);

$permissionsUpdater->updateFromPermissionsForm($shelf, $request);
Expand All @@ -240,10 +237,10 @@ public function permissions(Request $request, PermissionsUpdater $permissionsUpd
*/
public function copyPermissions(string $slug)
{
$shelf = $this->bookshelfRepo->getBySlug($slug);
$shelf = $this->shelfRepo->getBySlug($slug);
$this->checkOwnablePermission('restrictions-manage', $shelf);

$updateCount = $this->bookshelfRepo->copyDownPermissions($shelf);
$updateCount = $this->shelfRepo->copyDownPermissions($shelf);
$this->showSuccessNotification(trans('entities.shelves_copy_permission_success', ['count' => $updateCount]));

return redirect($shelf->getUrl());
Expand Down
38 changes: 32 additions & 6 deletions resources/js/components/shelf-sort.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import Sortable from "sortablejs";

class ShelfSort {

constructor(elem) {
this.elem = elem;
this.input = document.getElementById('books-input');
this.shelfBooksList = elem.querySelector('[shelf-sort-assigned-books]');
setup() {
this.elem = this.$el;
this.input = this.$refs.input;
this.shelfBookList = this.$refs.shelfBookList;
this.allBookList = this.$refs.allBookList;
this.bookSearchInput = this.$refs.bookSearch;

this.initSortable();
this.setupListeners();
Expand All @@ -25,12 +27,36 @@ class ShelfSort {

setupListeners() {
this.elem.addEventListener('click', event => {
const sortItem = event.target.closest('.scroll-box-item:not(.instruction)');
const sortItem = event.target.closest('.scroll-box-item');
if (sortItem) {
event.preventDefault();
this.sortItemClick(sortItem);
}
});

this.bookSearchInput.addEventListener('input', event => {
this.filterBooksByName(this.bookSearchInput.value);
});
}

/**
* @param {String} filterVal
*/
filterBooksByName(filterVal) {

// Set height on first search, if not already set, to prevent the distraction
// of the list height jumping around
if (!this.allBookList.style.height) {
this.allBookList.style.height = this.allBookList.getBoundingClientRect().height + 'px';
}

const books = this.allBookList.children;
const lowerFilter = filterVal.trim().toLowerCase();

for (const bookEl of books) {
const show = !filterVal || bookEl.textContent.toLowerCase().includes(lowerFilter);
bookEl.style.display = show ? null : 'none';
}
}

/**
Expand All @@ -47,7 +73,7 @@ class ShelfSort {
}

onChange() {
const shelfBookElems = Array.from(this.shelfBooksList.querySelectorAll('[data-id]'));
const shelfBookElems = Array.from(this.shelfBookList.querySelectorAll('[data-id]'));
this.input.value = shelfBookElems.map(elem => elem.getAttribute('data-id')).join(',');
}

Expand Down
2 changes: 1 addition & 1 deletion resources/lang/en/entities.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
'shelves_save' => 'Save Shelf',
'shelves_books' => 'Books on this shelf',
'shelves_add_books' => 'Add books to this shelf',
'shelves_drag_books' => 'Drag books here to add them to this shelf',
'shelves_drag_books' => 'Drag books below to add them to this shelf',
'shelves_empty_contents' => 'This shelf has no books assigned to it',
'shelves_edit_and_assign' => 'Edit shelf to assign books',
'shelves_edit_named' => 'Edit Bookshelf :name',
Expand Down
3 changes: 2 additions & 1 deletion resources/sass/_forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,8 @@ input[type=color] {

.form-group[collapsible] {
padding: 0 $-m;
border: 1px solid #DDD;
border: 1px solid;
@include lightDark(border-color, #DDD, #000);
border-radius: 4px;
.collapse-title {
margin-inline-start: -$-m;
Expand Down
42 changes: 34 additions & 8 deletions resources/sass/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -241,28 +241,54 @@ $btt-size: 40px;
.scroll-box {
max-height: 250px;
overflow-y: scroll;
border: 1px solid #DDD;
border: 1px solid;
@include lightDark(border-color, #DDD, #000);
border-radius: 3px;
min-height: 20px;
@include lightDark(background-color, #EEE, #000);
.scroll-box-item {
padding: $-xs $-m;
border-bottom: 1px solid #DDD;
border-top: 1px solid #DDD;
border-bottom: 1px solid;
border-top: 1px solid;
@include lightDark(border-color, #DDD, #000);
margin-top: -1px;
@include lightDark(background-color, #FFF, #222);
display: flex;
gap: $-xs;
&:last-child {
border-bottom: 0;
}
&:hover {
cursor: pointer;
@include lightDark(background-color, #f8f8f8, #333);
}
.handle {
color: #AAA;
cursor: grab;
}
}
}

.scroll-box[data-instruction]:before {
content: attr(data-instruction);
input.scroll-box-search, .scroll-box-header-item {
font-size: 0.8rem;
padding: $-xs $-m;
border-bottom: 1px solid #DDD;
display: block;
font-size: 0.75rem;
border: 1px solid;
@include lightDark(border-color, #DDD, #000);
@include lightDark(background-color, #FFF, #222);
margin-bottom: -1px;
border-radius: 3px 3px 0 0;
width: 100%;
max-width: 100%;
height: auto;
line-height: 1.4;
color: #666;
}

.scroll-box-search + .scroll-box,
.scroll-box-header-item + .scroll-box {
border-radius: 0 0 3px 3px;
}

.fullscreen {
border:0;
position:fixed;
Expand Down
12 changes: 8 additions & 4 deletions resources/views/shelves/parts/form.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@
@include('form.textarea', ['name' => 'description'])
</div>

<div shelf-sort class="grid half gap-xl">
<div component="shelf-sort" class="grid half gap-xl">
<div class="form-group">
<label for="books">{{ trans('entities.shelves_books') }}</label>
<input type="hidden" id="books-input" name="books"
<input refs="shelf-sort@input" type="hidden" name="books"
value="{{ isset($shelf) ? $shelf->visibleBooks->implode('id', ',') : '' }}">
<div class="scroll-box" shelf-sort-assigned-books data-instruction="{{ trans('entities.shelves_drag_books') }}">
<div class="scroll-box-header-item">{{ trans('entities.shelves_drag_books') }}</div>
<div refs="shelf-sort@shelf-book-list" class="scroll-box">
@if (count($shelf->visibleBooks ?? []) > 0)
@foreach ($shelf->visibleBooks as $book)
<div data-id="{{ $book->id }}" class="scroll-box-item">
<div class="handle">@icon('grip')</div>
<a href="{{ $book->getUrl() }}" class="text-book">@icon('book'){{ $book->name }}</a>
</div>
@endforeach
Expand All @@ -27,9 +29,11 @@
</div>
<div class="form-group">
<label for="books">{{ trans('entities.shelves_add_books') }}</label>
<div class="scroll-box">
<input type="text" refs="shelf-sort@book-search" class="scroll-box-search" placeholder="{{ trans('common.search') }}">
<div refs="shelf-sort@all-book-list" class="scroll-box">
@foreach ($books as $book)
<div data-id="{{ $book->id }}" class="scroll-box-item">
<div class="handle">@icon('grip')</div>
<a href="{{ $book->getUrl() }}" class="text-book">@icon('book'){{ $book->name }}</a>
</div>
@endforeach
Expand Down