From b897af2ed034088193986c8526be9606edaca7d5 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Tue, 4 Feb 2025 20:11:35 +0000 Subject: [PATCH] Sorting: Finished main sort set CRUD work --- app/Activity/ActivityType.php | 4 ++ app/Sorting/SortSet.php | 17 +++-- app/Sorting/SortSetController.php | 69 +++++++++++++++++++ app/Sorting/SortSetOperation.php | 17 ++++- lang/en/activities.php | 8 +++ lang/en/settings.php | 2 + .../settings/categories/sorting.blade.php | 13 +++- .../views/settings/sort-sets/edit.blade.php | 44 ++++++++++++ .../settings/sort-sets/parts/form.blade.php | 16 +++-- .../parts/sort-set-list-item.blade.php | 8 +++ 10 files changed, 187 insertions(+), 11 deletions(-) create mode 100644 resources/views/settings/sort-sets/edit.blade.php create mode 100644 resources/views/settings/sort-sets/parts/sort-set-list-item.blade.php diff --git a/app/Activity/ActivityType.php b/app/Activity/ActivityType.php index 5ec9b9cf0..4a648da6c 100644 --- a/app/Activity/ActivityType.php +++ b/app/Activity/ActivityType.php @@ -71,6 +71,10 @@ class ActivityType const IMPORT_RUN = 'import_run'; const IMPORT_DELETE = 'import_delete'; + const SORT_SET_CREATE = 'sort_set_create'; + const SORT_SET_UPDATE = 'sort_set_update'; + const SORT_SET_DELETE = 'sort_set_delete'; + /** * Get all the possible values. */ diff --git a/app/Sorting/SortSet.php b/app/Sorting/SortSet.php index ee45c211f..971b3e29a 100644 --- a/app/Sorting/SortSet.php +++ b/app/Sorting/SortSet.php @@ -2,6 +2,7 @@ namespace BookStack\Sorting; +use BookStack\Activity\Models\Loggable; use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; @@ -12,16 +13,14 @@ use Illuminate\Database\Eloquent\Model; * @property Carbon $created_at * @property Carbon $updated_at */ -class SortSet extends Model +class SortSet extends Model implements Loggable { /** * @return SortSetOperation[] */ public function getOperations(): array { - $strOptions = explode(',', $this->sequence); - $options = array_map(fn ($val) => SortSetOperation::tryFrom($val), $strOptions); - return array_filter($options); + return SortSetOperation::fromSequence($this->sequence); } /** @@ -32,4 +31,14 @@ class SortSet extends Model $values = array_map(fn (SortSetOperation $opt) => $opt->value, $options); $this->sequence = implode(',', $values); } + + public function logDescriptor(): string + { + return "({$this->id}) {$this->name}"; + } + + public function getUrl(): string + { + return url("/settings/sorting/sets/{$this->id}"); + } } diff --git a/app/Sorting/SortSetController.php b/app/Sorting/SortSetController.php index 4ef148295..0d77bd88f 100644 --- a/app/Sorting/SortSetController.php +++ b/app/Sorting/SortSetController.php @@ -2,7 +2,9 @@ namespace BookStack\Sorting; +use BookStack\Activity\ActivityType; use BookStack\Http\Controller; +use BookStack\Http\Request; class SortSetController extends Controller { @@ -14,6 +16,73 @@ class SortSetController extends Controller public function create() { + $this->setPageTitle(trans('settings.sort_set_create')); + return view('settings.sort-sets.create'); } + + public function store(Request $request) + { + $this->validate($request, [ + 'name' => ['required', 'string', 'min:1', 'max:200'], + 'sequence' => ['required', 'string', 'min:1'], + ]); + + $operations = SortSetOperation::fromSequence($request->input('sequence')); + if (count($operations) === 0) { + return redirect()->withInput()->withErrors(['sequence' => 'No operations set.']); + } + + $set = new SortSet(); + $set->name = $request->input('name'); + $set->setOperations($operations); + $set->save(); + + $this->logActivity(ActivityType::SORT_SET_CREATE, $set); + + return redirect('/settings/sorting'); + } + + public function edit(string $id) + { + $set = SortSet::query()->findOrFail($id); + + $this->setPageTitle(trans('settings.sort_set_edit')); + + return view('settings.sort-sets.edit', ['set' => $set]); + } + + public function update(string $id, Request $request) + { + $this->validate($request, [ + 'name' => ['required', 'string', 'min:1', 'max:200'], + 'sequence' => ['required', 'string', 'min:1'], + ]); + + $set = SortSet::query()->findOrFail($id); + $operations = SortSetOperation::fromSequence($request->input('sequence')); + if (count($operations) === 0) { + return redirect()->withInput()->withErrors(['sequence' => 'No operations set.']); + } + + $set->name = $request->input('name'); + $set->setOperations($operations); + $set->save(); + + $this->logActivity(ActivityType::SORT_SET_UPDATE, $set); + + return redirect('/settings/sorting'); + } + + public function destroy(string $id) + { + $set = SortSet::query()->findOrFail($id); + + // TODO - Check if it's in use + + $set->delete(); + $this->logActivity(ActivityType::SORT_SET_DELETE, $set); + + return redirect('/settings/sorting'); + } } diff --git a/app/Sorting/SortSetOperation.php b/app/Sorting/SortSetOperation.php index 12fda669f..a6dd860f5 100644 --- a/app/Sorting/SortSetOperation.php +++ b/app/Sorting/SortSetOperation.php @@ -39,6 +39,21 @@ enum SortSetOperation: string public static function allExcluding(array $operations): array { $all = SortSetOperation::cases(); - return array_diff($all, $operations); + $filtered = array_filter($all, function (SortSetOperation $operation) use ($operations) { + return !in_array($operation, $operations); + }); + return array_values($filtered); + } + + /** + * Create a set of operations from a string sequence representation. + * (values seperated by commas). + * @return SortSetOperation[] + */ + public static function fromSequence(string $sequence): array + { + $strOptions = explode(',', $sequence); + $options = array_map(fn ($val) => SortSetOperation::tryFrom($val), $strOptions); + return array_filter($options); } } diff --git a/lang/en/activities.php b/lang/en/activities.php index 7c3454d41..7db872c0c 100644 --- a/lang/en/activities.php +++ b/lang/en/activities.php @@ -127,6 +127,14 @@ return [ 'comment_update' => 'updated comment', 'comment_delete' => 'deleted comment', + // Sort Sets + 'sort_set_create' => 'created sort set', + 'sort_set_create_notification' => 'Sort set successfully created', + 'sort_set_update' => 'updated sort set', + 'sort_set_update_notification' => 'Sort set successfully update', + 'sort_set_delete' => 'deleted sort set', + 'sort_set_delete_notification' => 'Sort set successfully deleted', + // Other 'permissions_update' => 'updated permissions', ]; diff --git a/lang/en/settings.php b/lang/en/settings.php index 8bb2f6ef4..cda097590 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -82,6 +82,8 @@ return [ 'sorting_sets_desc' => 'These are predefined sorting operations which can be applied to content in the system.', 'sort_set_create' => 'Create Sort Set', 'sort_set_edit' => 'Edit Sort Set', + 'sort_set_delete' => 'Delete Sort Set', + 'sort_set_delete_desc' => 'Remove this sort set from the system. Deletion will only go ahead if the sort is not in active use.', 'sort_set_details' => 'Sort Set Details', 'sort_set_details_desc' => 'Set a name for this sort set, which will appear in lists when users are selecting a sort.', 'sort_set_operations' => 'Sort Operations', diff --git a/resources/views/settings/categories/sorting.blade.php b/resources/views/settings/categories/sorting.blade.php index 9de11bb6f..b5d613840 100644 --- a/resources/views/settings/categories/sorting.blade.php +++ b/resources/views/settings/categories/sorting.blade.php @@ -52,6 +52,17 @@ -{{-- TODO--}} + @php + $sortSets = \BookStack\Sorting\SortSet::query()->orderBy('name', 'asc')->get(); + @endphp + @if(empty($sortSets)) +

{{ trans('common.no_items') }}

+ @else +
+ @foreach($sortSets as $set) + @include('settings.sort-sets.parts.sort-set-list-item', ['set' => $set]) + @endforeach +
+ @endif @endsection \ No newline at end of file diff --git a/resources/views/settings/sort-sets/edit.blade.php b/resources/views/settings/sort-sets/edit.blade.php new file mode 100644 index 000000000..3b88c1243 --- /dev/null +++ b/resources/views/settings/sort-sets/edit.blade.php @@ -0,0 +1,44 @@ +@extends('layouts.simple') + +@section('body') + +
+ + @include('settings.parts.navbar', ['selected' => 'settings']) + +
+

{{ trans('settings.sort_set_edit') }}

+ +
+ {{ method_field('PUT') }} + {{ csrf_field() }} + + @include('settings.sort-sets.parts.form', ['model' => $set]) + +
+ {{ trans('common.cancel') }} + +
+
+
+ +
+
+
+

{{ trans('settings.sort_set_delete') }}

+

{{ trans('settings.sort_set_delete_desc') }}

+
+
+
+ {{ method_field('DELETE') }} + {{ csrf_field() }} +
+ +
+
+
+
+
+
+ +@stop diff --git a/resources/views/settings/sort-sets/parts/form.blade.php b/resources/views/settings/sort-sets/parts/form.blade.php index 3f2220947..38db840ac 100644 --- a/resources/views/settings/sort-sets/parts/form.blade.php +++ b/resources/views/settings/sort-sets/parts/form.blade.php @@ -15,9 +15,14 @@

{{ trans('settings.sort_set_operations_desc') }}

+ @include('form.errors', ['name' => 'sequence']) - + + + @php + $configuredOps = old('sequence') ? \BookStack\Sorting\SortSetOperation::fromSequence(old('sequence')) : ($model?->getOperations() ?? []); + @endphp
@@ -27,8 +32,9 @@ aria-labelledby="sort-set-configured-operations" class="scroll-box configured-option-list">
  • {{ trans('settings.sort_set_configured_operations_empty') }}
  • - @foreach(($model?->getOperations() ?? []) as $option) - @include('settings.sort-sets.parts.operation') + + @foreach($configuredOps as $operation) + @include('settings.sort-sets.parts.operation', ['operation' => $operation]) @endforeach
    @@ -40,7 +46,7 @@ aria-labelledby="sort-set-available-operations" class="scroll-box available-option-list">
  • {{ trans('settings.sort_set_available_operations_empty') }}
  • - @foreach(\BookStack\Sorting\SortSetOperation::allExcluding($model?->getOperations() ?? []) as $operation) + @foreach(\BookStack\Sorting\SortSetOperation::allExcluding($configuredOps) as $operation) @include('settings.sort-sets.parts.operation', ['operation' => $operation]) @endforeach diff --git a/resources/views/settings/sort-sets/parts/sort-set-list-item.blade.php b/resources/views/settings/sort-sets/parts/sort-set-list-item.blade.php new file mode 100644 index 000000000..e5ee1fb87 --- /dev/null +++ b/resources/views/settings/sort-sets/parts/sort-set-list-item.blade.php @@ -0,0 +1,8 @@ +
    + +
    + {{ implode(', ', array_map(fn ($op) => $op->getLabel(), $set->getOperations())) }} +
    +
    \ No newline at end of file