Skip to content

Commit 275b916

Browse files
Merge pull request #107 from imbus/SIT174-advanced-search-permissions
Sit174 advanced search permissions
2 parents d778a24 + cb52239 commit 275b916

File tree

17 files changed

+242
-106
lines changed

17 files changed

+242
-106
lines changed

app/Http/Controllers/Api/PredefinedFilterController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public function index(Request $request) : JsonResponse | array
6161

6262
public function show(int $id)
6363
{
64-
$filter = $this->service->getFilterById($id);
64+
$filter = $this->service->getFilterWithOptionalPermissionsById($id);
6565

6666
if (!$filter) {
6767
return response()->json(['message' => trans('admin/predefinedFilters/message.does_not_exist')], 404);

app/Http/Controllers/Assets/AssetsController.php

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use App\Models\Actionlog;
1212
use App\Http\Requests\UploadFileRequest;
1313
use Illuminate\Support\Facades\Log;
14+
use App\Models\AdvancedSearch;
1415
use App\Models\Asset;
1516
use App\Models\AssetModel;
1617
use App\Models\CheckoutRequest;
@@ -68,26 +69,35 @@ public function index(Request $request): View
6869
{
6970
$this->authorize('index', Asset::class);
7071
$company = Company::find($request->input('company_id'));
72+
$user = auth()->user();
7173

74+
$advancedSearchViewPermission = AdvancedSearch::userHasViewPermission($user);
7275
$predefined_filter_id = $request->input('predefinedFilterId');
7376

74-
// Validate if it's a valid integer
75-
if (filter_var($predefined_filter_id, FILTER_VALIDATE_INT) === false && $predefined_filter_id != null) {
76-
throw new InvalidArgumentException('You provided an invalid parameter for predefinedFilterId (must be an integer).');
77-
}
78-
79-
$predefined_filter_name = ""; // Just an empty string to not fail other stuff because it is only needed when a predefined filter is set using the url
80-
81-
if ($predefined_filter_id !== null) {
82-
$filter = $this->predefinedFilterService->getFilterById($predefined_filter_id);
83-
if (!($filter)) {
84-
$predefined_filter_id = null;
85-
} else {
86-
$predefined_filter_name = $filter->name;
77+
if($advancedSearchViewPermission) {
78+
// Validate if it's a valid integer
79+
if (filter_var($predefined_filter_id, FILTER_VALIDATE_INT) === false && $predefined_filter_id != null) {
80+
throw new InvalidArgumentException('You provided an invalid parameter for predefinedFilterId (must be an integer).');
8781
}
88-
}
82+
83+
$predefined_filter_name = ""; // Just an empty string to not fail other stuff because it is only needed when a predefined filter is set using the url
84+
85+
if ($predefined_filter_id !== null) {
86+
$filter = $this->predefinedFilterService->getFilterWithOptionalPermissionsById($predefined_filter_id);
87+
if (!($filter)) {
88+
$predefined_filter_id = null;
89+
abort(404, "Predefined filter not found");
90+
} else {
91+
$predefined_filter_name = $filter->name;
92+
}
93+
}
94+
} else {
95+
$predefined_filter_id = null;
96+
$predefined_filter_name = null;
97+
}
8998

9099
return view('hardware/index')->with('company', $company)
100+
->with('advanced_search_permission', $advancedSearchViewPermission)
91101
->with('predefined_filter_id', $predefined_filter_id)
92102
->with('predefined_filter_name', $predefined_filter_name);
93103
}

app/Livewire/Partials/AdvancedSearch/Modal.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public function openPredefinedFiltersModal(
7777
$this->modalActionType === AdvancedsearchModalAction::Edit &&
7878
$predefinedFilterId !== null
7979
) {
80-
$predefinedFilter = $predefinedFilterService->getFilterById(
80+
$predefinedFilter = $predefinedFilterService->getFilterWithOptionalPermissionsById(
8181
$predefinedFilterId
8282
);
8383
$this->name = $predefinedFilter["name"];
@@ -285,7 +285,7 @@ public function deletePredefinedFiltersModal(
285285
) {
286286

287287

288-
$predefinedFilter = $predefinedFilterService->getFilterById($this->filterId);
288+
$predefinedFilter = $predefinedFilterService->getFilterWithOptionalPermissionsById($this->filterId);
289289

290290
if (!isset($predefinedFilter)) {
291291
$this->dispatch('showNotificationInFrontend', [

app/Models/AdvancedSearch.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
7+
class AdvancedSearch extends Model
8+
{
9+
static function userHasViewPermission($user): bool {
10+
return $user->hasAccess('advancedsearch');
11+
}
12+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace App\Policies;
4+
5+
use App\Models\User;
6+
use App\Models\Asset;
7+
8+
class AdvancedSearchPolicy extends SnipePermissionsPolicy
9+
{
10+
protected function columnName()
11+
{
12+
return 'advancedSearch';
13+
}
14+
}

app/Providers/AuthServiceProvider.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Providers;
44

55
use App\Models\Accessory;
6+
use App\Models\AdvancedSearch;
67
use App\Models\Asset;
78
use App\Models\AssetModel;
89
use App\Models\Category;
@@ -22,6 +23,7 @@
2223
use App\Models\Supplier;
2324
use App\Models\User;
2425
use App\Policies\AccessoryPolicy;
26+
use App\Policies\AdvancedSearchPolicy;
2527
use App\Policies\AssetModelPolicy;
2628
use App\Policies\AssetPolicy;
2729
use App\Policies\CategoryPolicy;
@@ -56,6 +58,7 @@ class AuthServiceProvider extends ServiceProvider
5658
*/
5759
protected $policies = [
5860
Accessory::class => AccessoryPolicy::class,
61+
AdvancedSearch::class => AdvancedSearchPolicy::class,
5962
Asset::class => AssetPolicy::class,
6063
AssetModel::class => AssetModelPolicy::class,
6164
Category::class => CategoryPolicy::class,
@@ -290,6 +293,7 @@ public function boot()
290293
|| $user->can('create', Accessory::class)
291294
|| $user->can('update', User::class)
292295
|| $user->can('create', User::class)
296+
|| ($user->hasAccess('advancedsearch'))
293297
|| ($user->hasAccess('reports.view'));
294298
});
295299

app/Services/PredefinedFilterService.php

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@
22

33
namespace App\Services;
44

5+
use App\Models\Category;
6+
use App\Models\Company;
7+
use App\Models\AssetModel;
8+
use App\Models\Location;
9+
use App\Models\Manufacturer;
10+
use App\Models\Statuslabel;
11+
use App\Models\Supplier;
512
use DB;
613
use Exception;
714
use Throwable;
815
use App\Models\PredefinedFilter;
16+
use App\Services\FilterService\FilterService;
917
use App\Services\PredefinedFilterPermissionService;
1018
use Illuminate\Http\Request;
1119
use Illuminate\Pagination\LengthAwarePaginator;
@@ -22,6 +30,14 @@ public function __construct(PredefinedFilterPermissionService $predefinedFilterP
2230
$this->predefinedFilterPermissionService = $predefinedFilterPermissionService;
2331
}
2432

33+
protected ?FilterService $filterService = null;
34+
35+
public function filterService(): FilterService
36+
{
37+
return $this->filterService ??= app(FilterService::class);
38+
39+
}
40+
2541
public function getAllViewableFilters(): Collection
2642
{
2743
$user = Auth::user();
@@ -39,14 +55,101 @@ public function getAllViewableFilters(): Collection
3955
})->values();
4056
}
4157

42-
// TODO different Naming because it does more than only get a filter by ID - getFilterWithOptionalPermissionsById
43-
public function getFilterById(int $id, bool $include_predefined_filter_groups = true)
58+
public function getFilterWithOptionalPermissionsById(int $id, bool $include_predefined_filter_groups = true)
4459
{
4560
$predefinedFilter = PredefinedFilter::find($id);
4661
if ($include_predefined_filter_groups && $predefinedFilter) {
4762
$permissions = $this->predefinedFilterPermissionService->getPermissionsByPredefinedFilterId($id);
4863
$predefinedFilter['permissions'] = $permissions;
4964
}
65+
66+
if (!$predefinedFilter){
67+
return null;
68+
}
69+
70+
$filters = $predefinedFilter->filter_data;
71+
72+
foreach ($filters as &$filter) {
73+
74+
75+
if (!empty($filter['value']) && is_array($filter['value']) && is_int($filter['value'][0])) {
76+
$values =[];
77+
foreach ($filter['value'] as $valueId){
78+
switch ($filter['field']) {
79+
case 'company':
80+
$company = Company::find($valueId);
81+
if ($company) {
82+
$values[] = [
83+
'id' => $company->id,
84+
'name' => $company->name,
85+
];
86+
}
87+
break;
88+
case 'model':
89+
$model = AssetModel::find($valueId);
90+
if ($model) {
91+
$values[] = [
92+
'id' => $model->id,
93+
'name' => $model->name,
94+
];
95+
}
96+
break;
97+
case 'category':
98+
$category = Category::find($valueId);
99+
if ($category) {
100+
$values[] = [
101+
'id' => $category->id,
102+
'name' => $category->name,
103+
];
104+
}
105+
break;
106+
case 'statuslabel':
107+
$status = Statuslabel::find($valueId);
108+
if ($status) {
109+
$values[] = [
110+
'id' => $status->id,
111+
'name' => $status->name,
112+
];
113+
}
114+
break;
115+
case 'location':
116+
case 'default_location':
117+
$location = Location::find($valueId);
118+
if ($location) {
119+
$values[] = [
120+
'id' => $location->id,
121+
'name' => $location->name,
122+
];
123+
}
124+
break;
125+
case 'manufacturer':
126+
$manufacturer = Manufacturer::find($valueId);
127+
if ($manufacturer) {
128+
$values[] = [
129+
'id' => $manufacturer->id,
130+
'name' => $manufacturer->name,
131+
];
132+
}
133+
break;
134+
case 'supplier':
135+
$supplier = Supplier::find($valueId);
136+
if ($supplier) {
137+
$values[] = [
138+
'id' => $supplier->id,
139+
'name' => $supplier->name,
140+
];
141+
}
142+
break;
143+
default:
144+
break;
145+
}
146+
}
147+
148+
$filter['value'] = $values;
149+
$predefinedFilter->filter_data = $filters;
150+
151+
}
152+
}
50153
return $predefinedFilter;
51154
}
52155

@@ -152,8 +255,6 @@ public function selectList(Request $request, bool $visibilityInName = false): Le
152255
$query->where('name', 'LIKE', '%' . trim($search) . '%');
153256
}
154257

155-
156-
157258
$paginated = $query->orderBy('name')->paginate(50);
158259

159260
foreach ($paginated as $item) {

config/permissions.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,13 @@
234234
],
235235
],
236236

237+
'AdvancedSearch' => [
238+
[
239+
'permission' => 'advancedSearch.view',
240+
'display' => true,
241+
],
242+
],
243+
237244
'Users' => [
238245
[
239246
'permission' => 'users.view',

public/js/dist/filterInputs.js

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -138,52 +138,35 @@ class SelectFilterInput extends FilterInput {
138138

139139
setValue(newValues, logic, operator, type = this.getType()) {
140140
const requestPromises = newValues.map((newValue) => {
141-
// If it's a number, fetch from backend
142-
if (typeof newValue === "number") {
143-
return this.apiService.fetchItemFromBackendById(type, newValue)
144-
.then((response) => {
145-
return response.json().then((responseJson) => {
146-
const $existingOption = $(this.element).find(`option[value='${responseJson.id}']`);
147-
148-
if ($existingOption.length === 0) {
149-
const option = new Option(responseJson.name, responseJson.id, true, true);
150-
$(this.element).append(option);
151-
} else {
152-
$existingOption.prop('selected', true);
153-
}
154-
155-
this.setSearchOperator(logic, operator);
156-
157-
$(this.element).trigger('change');
158-
return responseJson;
159-
});
160-
});
161-
} else {
162-
queueMicrotask(() => {
163-
// Directly insert/select string value
164-
this.setSearchOperator(logic, operator);
165-
const existingOption = $(this.element).find(`option[value='${newValue}']`);
141+
return Promise.resolve().then(() => {
142+
this.setSearchOperator(logic, operator);
166143

167-
if (existingOption.length === 0) {
168-
const option = new Option(newValue, newValue, true, true);
169-
$(this.element).append(option);
170-
} else {
171-
existingOption.prop('selected', true);
172-
}
144+
// Normalize the newValue
145+
const isObject = typeof newValue === "object" && newValue !== null;
146+
const id = isObject ? newValue.id : newValue;
147+
const name = isObject ? newValue.name : newValue;
173148

174-
$(this.element).trigger('change');
149+
const $el = $(this.element);
150+
let existingOption = $el.find(`option[value='${id}']`);
175151

176-
// Return a resolved promise for consistency
177-
return Promise.resolve({ id: newValue, name: newValue });
178-
});
179-
}
152+
if (existingOption.length === 0) {
153+
const option = new Option(name, id, true, true);
154+
$el.append(option);
155+
} else {
156+
existingOption.prop("selected", true);
157+
}
158+
159+
$el.trigger("change");
160+
return { id, name };
161+
});
180162
});
181163

182164
return Promise.all(requestPromises);
183165
}
184166

185167

186168

169+
187170
clear() {
188171
$(this.element).val(null).trigger('change');
189172
super.clear();
@@ -340,7 +323,7 @@ class AssignedEntityFilterInput extends TextFilterInput {
340323
}
341324
resolve(newValue);
342325
})
343-
}
326+
}
344327

345328
clear() {
346329
this.element.value = "";

0 commit comments

Comments
 (0)