3
0
mirror of https://github.com/snipe/snipe-it.git synced 2026-08-18 11:15:42 +00:00
Files
snipe-it/app/Http/Controllers/BulkMaintenancesController.php
2026-07-28 11:59:55 +01:00

119 lines
4.7 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Enums\ActionType;
use App\Helpers\Helper;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\Maintenance;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
/**
* Handles bulk actions from the maintenances index table. The single POST
* endpoint branches on the bulk_actions form field so the same dropdown
* can grow additional actions later without a new route per verb.
*/
class BulkMaintenancesController extends Controller
{
public function store(Request $request): RedirectResponse
{
// Top-level gate mirrors MaintenancesController::complete()/destroy(),
// because maintenance keys on the asset edit permission (see
// MaintenancePolicy). This blocks users without any assets.edit rights
// from hitting the endpoint at all; per-row policy still runs below.
// FMCS company scoping is applied automatically on the Maintenance
// lookups further down via the model's CompanyableChildTrait global
// scope, so a scoped user can't reach maintenance rows for assets
// outside their company set even if they submit those ids directly.
// We should probably consider making maintenances their own permission though
$this->authorize('update', Asset::class);
$action = $request->input('bulk_actions');
$ids = array_values(array_unique(array_filter(array_map('intval', (array) $request->input('ids', [])))));
// Prefer the Referer header (rather than a hidden form field,
// which clients can forge freely) so the user lands back on the
// surface they submitted from, whether that was the standalone
// index with its ?completed=true filter or the asset-detail
// maintenance tab. Helper::sameOriginUrl() rejects off-host and
// non-http(s) schemes; fall back to the plain index on rejection.
$backUrl = Helper::sameOriginUrl($request->headers->get('referer')) ?? route('maintenances.index');
if (empty($ids)) {
return redirect($backUrl)
->with('warning', trans('general.bulk_checkin_delete.nothing_selected', ['object_type' => trans('general.maintenances')]));
}
return match ($action) {
'delete' => $this->bulkDelete($ids, $backUrl),
'complete' => $this->bulkComplete($ids, $backUrl),
default => redirect($backUrl)->with('error', trans('general.something_went_wrong')),
};
}
/**
* Soft-delete the selected maintenance records after per-row policy check.
* Silently skips already-deleted rows and any the actor can't touch.
*/
private function bulkDelete(array $ids, string $backUrl): RedirectResponse
{
$success = 0;
$skipped = 0;
Maintenance::whereIn('id', $ids)->with('asset')->get()->each(function (Maintenance $maintenance) use (&$success, &$skipped) {
if (! auth()->user()->can('delete', $maintenance)) {
$skipped++;
return;
}
$maintenance->delete();
$success++;
});
return redirect($backUrl)->with(
$success > 0 ? 'success' : 'warning',
trans_choice('admin/maintenances/message.bulk_delete', $success, ['count' => $success, 'skipped' => $skipped])
);
}
/**
* Mark the selected maintenance records complete. Skips rows that are
* already completed (idempotent) and any the actor can't update.
*/
private function bulkComplete(array $ids, string $backUrl): RedirectResponse
{
$success = 0;
$skipped = 0;
Maintenance::whereIn('id', $ids)->whereNull('completed_at')->with('asset')->get()->each(function (Maintenance $maintenance) use (&$success, &$skipped) {
if (! auth()->user()->can('update', $maintenance)) {
$skipped++;
return;
}
$maintenance->completed_at = now();
$maintenance->completed_by = auth()->id();
$maintenance->asset_maintenance_time = (int) $maintenance->created_at->diffInDays(now(), true);
$maintenance->saveQuietly();
$log = new Actionlog;
$log->item_type = Maintenance::class;
$log->item_id = $maintenance->id;
$log->target_type = Asset::class;
$log->target_id = $maintenance->asset_id;
$log->created_by = auth()->id();
$log->logaction(ActionType::MaintenanceComplete);
$success++;
});
return redirect($backUrl)->with(
$success > 0 ? 'success' : 'warning',
trans_choice('admin/maintenances/message.bulk_complete', $success, ['count' => $success, 'skipped' => $skipped])
);
}
}