Passed
Push — master ( fa5159...8fd79e )
by Darko
11:05
created

DeletedUsersController::bulkAction()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 23
rs 9.2222
cc 6
nc 4
nop 1
1
<?php
2
3
namespace App\Http\Controllers\Admin;
4
5
use App\Http\Controllers\BasePageController;
6
use App\Models\User;
7
use Carbon\Carbon;
8
use Illuminate\Http\Request;
9
10
class DeletedUsersController extends BasePageController
11
{
12
    /**
13
     * Display a listing of soft-deleted users with filtering, sorting and pagination.
14
     */
15
    public function index(Request $request)
16
    {
17
        $this->setAdminPrefs();
18
19
        // Filters
20
        $username = $request->input('username', '');
21
        $email = $request->input('email', '');
22
        $host = $request->input('host', '');
23
        $orderBy = $request->filled('ob') ? $request->input('ob') : 'deleted_at_desc';
24
        $createdFrom = $request->input('created_from', '');
25
        $createdTo = $request->input('created_to', '');
26
        $deletedFrom = $request->input('deleted_from', '');
27
        $deletedTo = $request->input('deleted_to', '');
28
29
        $deletedUsers = User::onlyTrashed()
30
            ->when($username !== '', fn ($q) => $q->where('username', 'like', "%$username%"))
31
            ->when($email !== '', fn ($q) => $q->where('email', 'like', "%$email%"))
32
            ->when($host !== '', fn ($q) => $q->where('host', 'like', "%$host%"))
33
            // Created date filters
34
            ->when($createdFrom !== '' || $createdTo !== '', function ($q) use ($createdFrom, $createdTo) {
35
                try {
36
                    if ($createdFrom !== '' && $createdTo !== '') {
37
                        $from = Carbon::createFromFormat('Y-m-d', $createdFrom)->startOfDay();
0 ignored issues
show
Bug introduced by
The method startOfDay() does not exist on DateTime. It seems like you code against a sub-type of DateTime such as RectorPrefix202508\Nette\Utils\DateTime. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

37
                        $from = Carbon::createFromFormat('Y-m-d', $createdFrom)->/** @scrutinizer ignore-call */ startOfDay();
Loading history...
38
                        $to = Carbon::createFromFormat('Y-m-d', $createdTo)->endOfDay();
0 ignored issues
show
Bug introduced by
The method endOfDay() does not exist on DateTime. It seems like you code against a sub-type of DateTime such as RectorPrefix202508\Nette\Utils\DateTime. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

38
                        $to = Carbon::createFromFormat('Y-m-d', $createdTo)->/** @scrutinizer ignore-call */ endOfDay();
Loading history...
39
                        $q->whereBetween('created_at', [$from, $to]);
40
                    } elseif ($createdFrom !== '') {
41
                        $from = Carbon::createFromFormat('Y-m-d', $createdFrom)->startOfDay();
42
                        $q->where('created_at', '>=', $from);
43
                    } elseif ($createdTo !== '') {
44
                        $to = Carbon::createFromFormat('Y-m-d', $createdTo)->endOfDay();
45
                        $q->where('created_at', '<=', $to);
46
                    }
47
                } catch (\Exception $e) {
48
                    // ignore invalid dates
49
                }
50
            })
51
            // Deleted date filters
52
            ->when($deletedFrom !== '' || $deletedTo !== '', function ($q) use ($deletedFrom, $deletedTo) {
53
                try {
54
                    if ($deletedFrom !== '' && $deletedTo !== '') {
55
                        $from = Carbon::createFromFormat('Y-m-d', $deletedFrom)->startOfDay();
56
                        $to = Carbon::createFromFormat('Y-m-d', $deletedTo)->endOfDay();
57
                        $q->whereBetween('deleted_at', [$from, $to]);
58
                    } elseif ($deletedFrom !== '') {
59
                        $from = Carbon::createFromFormat('Y-m-d', $deletedFrom)->startOfDay();
60
                        $q->where('deleted_at', '>=', $from);
61
                    } elseif ($deletedTo !== '') {
62
                        $to = Carbon::createFromFormat('Y-m-d', $deletedTo)->endOfDay();
63
                        $q->where('deleted_at', '<=', $to);
64
                    }
65
                } catch (\Exception $e) {
66
                    // ignore invalid dates
67
                }
68
            });
69
70
        // Sorting
71
        [$orderField, $orderSort] = $this->getSortOrder($orderBy);
72
        $deletedUsers = $deletedUsers->orderBy($orderField, $orderSort)
73
            ->paginate(25)
74
            ->appends($request->except('page'));
75
76
        // Build query string (exclude ordering + pagination) for sort links
77
        $qsParams = $request->except(['ob', 'page']);
78
        $queryString = http_build_query(array_filter($qsParams, fn ($v) => $v !== '' && $v !== null));
79
80
        $this->smarty->assign([
81
            'deletedusers' => $deletedUsers,
82
            'username' => $username,
83
            'email' => $email,
84
            'host' => $host,
85
            'orderby' => $orderBy,
86
            'created_from' => $createdFrom,
87
            'created_to' => $createdTo,
88
            'deleted_from' => $deletedFrom,
89
            'deleted_to' => $deletedTo,
90
            'csrf_token' => csrf_token(),
91
            'queryString' => $queryString,
92
        ]);
93
94
        $meta_title = 'Deleted Users';
95
        $meta_keywords = 'view,deleted,users,softdeleted';
96
        $meta_description = 'View and restore soft-deleted user accounts';
97
98
        $content = $this->smarty->fetch('deleted_users.tpl');
99
        $this->smarty->assign(compact('content', 'meta_title', 'meta_keywords', 'meta_description'));
100
101
        $this->adminrender();
102
    }
103
104
    /**
105
     * Bulk restore or permanent delete.
106
     */
107
    public function bulkAction(Request $request)
108
    {
109
        $action = $request->input('action');
110
        $userIds = $request->input('user_ids', []);
111
112
        if (! in_array($action, ['restore', 'delete'], true) || empty($userIds) || ! is_array($userIds)) {
113
            return redirect()->route('admin.deleted.users.index')->with('error', 'Invalid bulk action request.');
114
        }
115
116
        $userIds = array_filter(array_map('intval', $userIds));
117
        if (empty($userIds)) {
118
            return redirect()->route('admin.deleted.users.index')->with('error', 'No valid users selected.');
119
        }
120
121
        if ($action === 'restore') {
122
            $count = User::onlyTrashed()->whereIn('id', $userIds)->restore();
123
124
            return redirect()->route('admin.deleted.users.index')->with('success', $count.' user(s) restored successfully.');
0 ignored issues
show
Bug introduced by
Are you sure $count of type Illuminate\Database\Eloq...ase\Query\Builder|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

124
            return redirect()->route('admin.deleted.users.index')->with('success', /** @scrutinizer ignore-type */ $count.' user(s) restored successfully.');
Loading history...
125
        }
126
127
        $count = User::onlyTrashed()->whereIn('id', $userIds)->forceDelete();
128
129
        return redirect()->route('admin.deleted.users.index')->with('success', $count.' user(s) permanently deleted.');
130
    }
131
132
    /**
133
     * Restore single user.
134
     */
135
    public function restore($id)
136
    {
137
        $user = User::onlyTrashed()->find($id);
138
        if ($user) {
139
            $user->restore();
140
141
            return redirect()->route('admin.deleted.users.index')->with('success', "User '{$user->username}' has been restored successfully.");
142
        }
143
144
        return redirect()->route('admin.deleted.users.index')->with('error', 'User not found.');
145
    }
146
147
    /**
148
     * Permanently delete single user.
149
     */
150
    public function permanentDelete($id)
151
    {
152
        $user = User::onlyTrashed()->find($id);
153
        if ($user) {
154
            $username = $user->username;
155
            $user->forceDelete();
156
157
            return redirect()->route('admin.deleted.users.index')->with('success', "User '{$username}' has been permanently deleted.");
158
        }
159
160
        return redirect()->route('admin.deleted.users.index')->with('error', 'User not found.');
161
    }
162
163
    /**
164
     * Parse and validate sort order.
165
     */
166
    private function getSortOrder(string $orderBy): array
167
    {
168
        $orderArr = explode('_', $orderBy);
169
        $fieldKey = $orderArr[0] ?? 'deletedat';
170
        $orderField = match ($fieldKey) {
171
            'email' => 'email',
172
            'host' => 'host',
173
            'createdat' => 'created_at',
174
            'deletedat' => 'deleted_at',
175
            'lastlogin' => 'lastlogin',
176
            'apiaccess' => 'apiaccess',
177
            'grabs' => 'grabs',
178
            'role' => 'roles_id',
179
            default => 'username',
180
        };
181
        $orderSort = (isset($orderArr[1]) && preg_match('/^(asc|desc)$/i', $orderArr[1])) ? strtolower($orderArr[1]) : 'desc';
182
183
        return [$orderField, $orderSort];
184
    }
185
}
186