Files
newznab-tmux-NNTmux/app/Http/Controllers/Admin/AdminUserController.php
T
2025-12-01 14:43:40 +01:00

357 lines
14 KiB
PHP

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\BasePageController;
use App\Models\Invitation;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Jrean\UserVerification\Facades\UserVerification;
use Spatie\Permission\Models\Role;
use Stevebauman\Location\Facades\Location;
class AdminUserController extends BasePageController
{
/**
* @throws \Throwable
*/
public function index(Request $request)
{
$this->setAdminPrefs();
$meta_title = $title = 'User List';
$roles = [];
$userRoles = Role::cursor()->remember();
foreach ($userRoles as $userRole) {
$roles[$userRole->id] = $userRole->name;
}
$ordering = getUserBrowseOrdering();
$orderBy = $request->has('ob') && \in_array($request->input('ob'), $ordering, false) ? $request->input('ob') : '';
$page = $request->has('page') && is_numeric($request->input('page')) ? $request->input('page') : 1;
$offset = ($page - 1) * config('nntmux.items_per_page');
$variables = [
'username' => $request->has('username') ? $request->input('username') : '',
'email' => $request->has('email') ? $request->input('email') : '',
'host' => $request->has('host') ? $request->input('host') : '',
'role' => $request->has('role') ? $request->input('role') : '',
'created_from' => $request->has('created_from') ? $request->input('created_from') : '',
'created_to' => $request->has('created_to') ? $request->input('created_to') : '',
];
$result = User::getRange(
$offset,
config('nntmux.items_per_page'),
$orderBy,
$variables['username'],
$variables['email'],
$variables['host'],
$variables['role'],
true,
$variables['created_from'],
$variables['created_to']
);
$results = $this->paginate($result ?? [], User::getCount($variables['role'], $variables['username'], $variables['host'], $variables['email'], $variables['created_from'], $variables['created_to']) ?? 0, config('nntmux.items_per_page'), $page, $request->url(), $request->query());
// Add country data to each user based on their host IP
foreach ($results as $user) {
$position = null;
if (! empty($user->host) && filter_var($user->host, FILTER_VALIDATE_IP)) {
$position = Location::get($user->host);
}
$user->country_name = $position ? $position->countryName : null;
$user->country_code = $position ? $position->countryCode : null;
// Add daily API and download counts
try {
$user->daily_api_count = \App\Models\UserRequest::getApiRequests($user->id);
$user->daily_download_count = \App\Models\UserDownload::getDownloadRequests($user->id);
} catch (\Exception $e) {
$user->daily_api_count = 0;
$user->daily_download_count = 0;
}
}
// Build order by URLs
$orderByUrls = [];
foreach ($ordering as $orderType) {
$orderByUrls['orderby'.$orderType] = url('admin/user-list?ob='.$orderType);
}
$this->viewData = array_merge($this->viewData, [
'username' => $variables['username'],
'email' => $variables['email'],
'host' => $variables['host'],
'role' => $variables['role'],
'created_from' => $variables['created_from'],
'created_to' => $variables['created_to'],
'role_ids' => array_keys($roles),
'role_names' => $roles,
'userlist' => $results,
'title' => $title,
'meta_title' => $meta_title,
], $orderByUrls);
return view('admin.users.index', $this->viewData);
}
/**
* @return RedirectResponse|\Illuminate\View\View
*
* @throws \Exception
*/
public function edit(Request $request)
{
$this->setAdminPrefs();
$user = [
'id' => '',
'username' => '',
'email' => '',
'password' => '',
'role' => User::ROLE_USER,
'notes' => '',
'rate_limit' => 60,
];
$meta_title = $title = 'View User';
// set the current action
$action = $request->input('action') ?? 'view';
// get the user roles
$userRoles = Role::cursor()->remember();
$roles = [];
$defaultRole = 'User';
$defaultInvites = Invitation::DEFAULT_INVITES;
foreach ($userRoles as $r) {
$roles[$r->id] = $r->name;
if ($r->isdefault === 1) {
$defaultRole = $r->id;
$defaultInvites = $r->defaultinvites;
}
}
$error = null;
switch ($action) {
case 'add':
$user += [
'role' => $defaultRole,
'notes' => '',
'invites' => $defaultInvites,
'movieview' => 0,
'xxxview' => 0,
'musicview' => 0,
'consoleview' => 0,
'gameview' => 0,
'bookview' => 0,
];
break;
case 'submit':
if (empty($request->input('id'))) {
$invites = $defaultInvites;
foreach ($userRoles as $role) {
if ($role['id'] === $request->input('role')) {
$invites = $role['defaultinvites'];
}
}
$ret = User::signUp($request->input('username'), $request->input('password'), $request->input('email'), '', $request->input('notes'), $invites, '', true, $request->input('role'), false);
} else {
$editedUser = User::find($request->input('id'));
// Check if role is changing and get stack preference
$roleChanged = $editedUser->roles_id != $request->input('role');
$stackRole = $request->input('stack_role') ? true : false; // Check if checkbox is checked
$changedBy = auth()->check() ? auth()->id() : null;
// CRITICAL: Capture the ORIGINAL rolechangedate BEFORE any updates
// This is needed for accurate role history tracking
$originalRoleChangeDate = $editedUser->rolechangedate;
// Handle pending role cancellation
if ($request->has('cancel_pending_role') && $request->input('cancel_pending_role')) {
$editedUser->cancelPendingRole();
}
// Handle rolechangedate - Update the expiry for the CURRENT role FIRST
// This must happen BEFORE role change so the new expiry applies to the old role
if ($request->has('rolechangedate')) {
$roleChangeDate = $request->input('rolechangedate');
if (! empty($roleChangeDate)) {
User::updateUserRoleChangeDate($editedUser->id, $roleChangeDate);
$editedUser->refresh();
} else {
// Clear the rolechangedate if empty string is provided
$editedUser->update(['rolechangedate' => null]);
$editedUser->refresh();
}
}
// If role is changing, handle it with stacking logic
// Pass the original expiry so history records the correct old_expiry_date
if ($roleChanged && $request->input('role') !== null) {
User::updateUserRole(
$editedUser->id,
(int) $request->input('role'), // Cast to integer
true, // Apply promotions
$stackRole, // Stack role if requested
$changedBy,
$originalRoleChangeDate // Pass original expiry for history
);
$editedUser->refresh();
} elseif (!$roleChanged && $request->input('role') !== null) {
// Role isn't changing, but we should still apply promotions if there are any
// This handles the case where admin extends expiry date for existing role
User::updateUserRole(
$editedUser->id,
(int) $request->input('role'), // Same role
true, // Apply promotions
false, // Don't stack (not changing role)
$changedBy,
$originalRoleChangeDate
);
$editedUser->refresh();
}
// Update user basic information (but NOT the role - it's handled above)
// Use current role to avoid overwriting
$ret = User::updateUser(
$editedUser->id,
$request->input('username'),
$request->input('email'),
$editedUser->grabs,
$editedUser->roles_id, // Use current role, not the request role
$request->input('notes'),
$request->input('invites'),
($request->has('movieview') ? 1 : 0),
($request->has('musicview') ? 1 : 0),
($request->has('gameview') ? 1 : 0),
($request->has('xxxview') ? 1 : 0),
($request->has('consoleview') ? 1 : 0),
($request->has('bookview') ? 1 : 0)
);
if ($request->input('password') !== null) {
User::updatePassword($editedUser->id, $request->input('password'));
}
}
if ($ret >= 0) {
return redirect()->to('admin/user-list');
}
switch ($ret) {
case User::ERR_SIGNUP_BADUNAME:
$error = 'Bad username. Try a better one.';
break;
case User::ERR_SIGNUP_BADPASS:
$error = 'Bad password. Try a longer one.';
break;
case User::ERR_SIGNUP_BADEMAIL:
$error = 'Bad email.';
break;
case User::ERR_SIGNUP_UNAMEINUSE:
$error = 'Username in use.';
break;
case User::ERR_SIGNUP_EMAILINUSE:
$error = 'Email in use.';
break;
default:
$error = 'Unknown save error.';
break;
}
$user += [
'id' => $request->input('id'),
'username' => $request->input('username'),
'email' => $request->input('email'),
'role' => $request->input('role'),
'notes' => $request->input('notes'),
];
break;
case 'view':
default:
if ($request->has('id')) {
$title = 'User Edit';
$id = $request->input('id');
$user = User::find($id);
// Add daily API and download counts
if ($user) {
try {
$user->daily_api_count = \App\Models\UserRequest::getApiRequests($user->id);
$user->daily_download_count = \App\Models\UserDownload::getDownloadRequests($user->id);
} catch (\Exception $e) {
$user->daily_api_count = 0;
$user->daily_download_count = 0;
}
}
}
break;
}
$this->viewData = array_merge($this->viewData, [
'yesno_ids' => [1, 0],
'yesno_names' => ['Yes', 'No'],
'role_ids' => array_keys($roles),
'role_names' => $roles,
'user' => $user,
'error' => $error,
'title' => $title,
'meta_title' => $meta_title,
]);
return view('admin.users.edit', $this->viewData);
}
public function destroy(Request $request): RedirectResponse
{
if ($request->has('id')) {
$user = User::find($request->input('id'));
$username = $user->username; // Store username before deletion
$user->delete();
// Redirect with username to display in notification
return redirect()->to('admin/user-list?deleted=1&username='.urlencode($username));
}
if ($request->has('redir')) {
return redirect()->to($request->input('redir'));
}
return redirect()->to($request->server('HTTP_REFERER'));
}
public function resendVerification(Request $request): RedirectResponse
{
if ($request->has('id')) {
$user = User::find($request->input('id'));
UserVerification::generate($user);
UserVerification::send($user, 'User email verification required');
return redirect()->back()->with('success', 'Email verification for '.$user->username.' sent');
}
return redirect()->back()->with('error', 'User is invalid');
}
public function verify(Request $request): RedirectResponse
{
if ($request->has('id')) {
$user = User::find($request->input('id'));
User::query()->where('id', $request->input('id'))->update(['verified' => 1, 'email_verified_at' => now()]);
return redirect()->back()->with('success', 'Email verification for '.$user->username.' sent');
}
return redirect()->back()->with('error', 'User is invalid');
}
}