mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 01:08:56 +00:00
Update message when user disables category
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Models\RootCategory;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -18,66 +20,236 @@ class ClearanceMiddleware
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
if ($user->hasAnyRole(['Admin', 'Moderator']) && ! $request->is(['Admin', 'Admin/*'])) {
|
||||
// Admin/Moderator bypass only applies to Admin section pages
|
||||
// They should still respect their personal category permission settings
|
||||
if ($request->is(['Admin', 'Admin/*'])) {
|
||||
if (! $user->hasAnyRole(['Admin', 'Moderator'])) {
|
||||
abort(403, 'Unauthorized access to admin area');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ($request->is(['Movies', 'browse/Movies', 'browse/Movies/*'])) {
|
||||
if (! $user->hasDirectPermission('view movies') || ! $user->hasPermissionTo('view movies')) {
|
||||
abort(401);
|
||||
} else {
|
||||
return $next($request);
|
||||
// Get the path for pattern matching
|
||||
$path = $request->path();
|
||||
|
||||
// Check for browse routes with subcategory - e.g., browse/Movies/HD
|
||||
if (preg_match('#^browse/([^/]+)/([^/]+)$#i', $path, $matches)) {
|
||||
$parentCategoryName = $matches[1];
|
||||
$subcategoryName = $matches[2];
|
||||
|
||||
// First check if the main category is blocked
|
||||
$blockedCategory = $this->checkMainCategoryPermission($user, $parentCategoryName);
|
||||
if ($blockedCategory) {
|
||||
return $this->abortCategoryDisabled($blockedCategory);
|
||||
}
|
||||
|
||||
// Then check if the subcategory is excluded (unless it's "All")
|
||||
if (strtolower($subcategoryName) !== 'all') {
|
||||
$blockedSubcategory = $this->checkSubcategoryExclusion($user, $parentCategoryName, $subcategoryName);
|
||||
if ($blockedSubcategory) {
|
||||
return $this->abortSubcategoryDisabled($parentCategoryName, $blockedSubcategory);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ($request->is(['Console', 'browse/Console', 'browse/Console/*'])) {
|
||||
if (! $user->hasDirectPermission('view console') || ! $user->hasPermissionTo('view console')) {
|
||||
abort(401);
|
||||
} else {
|
||||
return $next($request);
|
||||
// Check for browse routes without subcategory - e.g., browse/Movies
|
||||
if (preg_match('#^browse/([^/]+)$#i', $path, $matches)) {
|
||||
$parentCategoryName = $matches[1];
|
||||
|
||||
$blockedCategory = $this->checkMainCategoryPermission($user, $parentCategoryName);
|
||||
if ($blockedCategory) {
|
||||
return $this->abortCategoryDisabled($blockedCategory);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ($request->is(['Books', 'browse/Books', 'browse/Books/*'])) {
|
||||
if (! $user->hasDirectPermission('view books') || ! $user->hasPermissionTo('view books')) {
|
||||
abort(401);
|
||||
} else {
|
||||
return $next($request);
|
||||
// Movies category
|
||||
if ($this->matchesCategoryPath($path, 'Movies')
|
||||
|| $this->matchesCategoryPath($path, 'movie')
|
||||
|| $this->matchesCategoryPath($path, 'trending-movies')
|
||||
|| $this->matchesCategoryPath($path, 'movietrailers')
|
||||
|| $this->matchesCategoryPath($path, 'mymovies')) {
|
||||
if (! $user->hasDirectPermission('view movies')) {
|
||||
return $this->abortCategoryDisabled('Movies');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ($request->is(['Audio', 'browse/Audio', 'browse/Audio/*'])) {
|
||||
if (! $user->hasDirectPermission('view audio') || ! $user->hasPermissionTo('view audio')) {
|
||||
abort(401);
|
||||
} else {
|
||||
return $next($request);
|
||||
// Console category
|
||||
if ($this->matchesCategoryPath($path, 'Console')) {
|
||||
if (! $user->hasDirectPermission('view console')) {
|
||||
return $this->abortCategoryDisabled('Console');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ($request->is(['XXX', 'browse/XXX', 'browse/XXX/*'])) {
|
||||
if (! $user->hasDirectPermission('view adult') || ! $user->hasPermissionTo('view adult')) {
|
||||
abort(401);
|
||||
} else {
|
||||
return $next($request);
|
||||
// Books category
|
||||
if ($this->matchesCategoryPath($path, 'Books')) {
|
||||
if (! $user->hasDirectPermission('view books')) {
|
||||
return $this->abortCategoryDisabled('Books');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ($request->is(['Games', 'browse/PC', 'browse/PC/*'])) {
|
||||
if (! $user->hasDirectPermission('view pc') || ! $user->hasPermissionTo('view pc')) {
|
||||
abort(401);
|
||||
} else {
|
||||
return $next($request);
|
||||
// Audio category
|
||||
if ($this->matchesCategoryPath($path, 'Audio')) {
|
||||
if (! $user->hasDirectPermission('view audio')) {
|
||||
return $this->abortCategoryDisabled('Audio');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ($request->is(['TV', 'browse/TV', 'browse/TV/*'])) {
|
||||
if (! $user->hasDirectPermission('view tv') || ! $user->hasPermissionTo('view tv')) {
|
||||
abort(401);
|
||||
} else {
|
||||
return $next($request);
|
||||
// Adult (XXX) category
|
||||
if ($this->matchesCategoryPath($path, 'XXX')) {
|
||||
if (! $user->hasDirectPermission('view adult')) {
|
||||
return $this->abortCategoryDisabled('Adult');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// PC/Games category
|
||||
if ($this->matchesCategoryPath($path, 'Games') || $this->matchesCategoryPath($path, 'PC')) {
|
||||
if (! $user->hasDirectPermission('view pc')) {
|
||||
return $this->abortCategoryDisabled('PC');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// TV category
|
||||
if ($this->matchesCategoryPath($path, 'TV')
|
||||
|| $this->matchesCategoryPath($path, 'series')
|
||||
|| $this->matchesCategoryPath($path, 'trending-tv')
|
||||
|| $this->matchesCategoryPath($path, 'myshows')) {
|
||||
if (! $user->hasDirectPermission('view tv')) {
|
||||
return $this->abortCategoryDisabled('TV');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user has permission to view a main category.
|
||||
*
|
||||
* @return string|null The blocked category name, or null if allowed
|
||||
*/
|
||||
protected function checkMainCategoryPermission($user, string $parentCategoryName): ?string
|
||||
{
|
||||
$categoryPermissions = [
|
||||
'movies' => 'view movies',
|
||||
'console' => 'view console',
|
||||
'books' => 'view books',
|
||||
'audio' => 'view audio',
|
||||
'xxx' => 'view adult',
|
||||
'games' => 'view pc',
|
||||
'pc' => 'view pc',
|
||||
'tv' => 'view tv',
|
||||
];
|
||||
|
||||
$categoryDisplayNames = [
|
||||
'movies' => 'Movies',
|
||||
'console' => 'Console',
|
||||
'books' => 'Books',
|
||||
'audio' => 'Audio',
|
||||
'xxx' => 'Adult',
|
||||
'games' => 'PC',
|
||||
'pc' => 'PC',
|
||||
'tv' => 'TV',
|
||||
];
|
||||
|
||||
$lowerName = strtolower($parentCategoryName);
|
||||
if (isset($categoryPermissions[$lowerName])) {
|
||||
if (! $user->hasDirectPermission($categoryPermissions[$lowerName])) {
|
||||
return $categoryDisplayNames[$lowerName];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user has excluded a specific subcategory.
|
||||
*
|
||||
* @return string|null The blocked subcategory name, or null if allowed
|
||||
*/
|
||||
protected function checkSubcategoryExclusion($user, string $parentCategoryName, string $subcategoryName): ?string
|
||||
{
|
||||
// Get the root category ID
|
||||
$rootCategory = RootCategory::query()
|
||||
->whereRaw('LOWER(title) = ?', [strtolower($parentCategoryName)])
|
||||
->first();
|
||||
|
||||
if (! $rootCategory) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the subcategory
|
||||
$subcategory = Category::query()
|
||||
->where('root_categories_id', $rootCategory->id)
|
||||
->whereRaw('LOWER(title) = ?', [strtolower($subcategoryName)])
|
||||
->first();
|
||||
|
||||
if (! $subcategory) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if this subcategory is in the user's exclusion list
|
||||
$isExcluded = $user->excludedCategories()
|
||||
->where('categories_id', $subcategory->id)
|
||||
->exists();
|
||||
|
||||
if ($isExcluded) {
|
||||
return $subcategory->title;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the path matches a category pattern (case-insensitive).
|
||||
*/
|
||||
protected function matchesCategoryPath(string $path, string $category): bool
|
||||
{
|
||||
$lowerPath = strtolower($path);
|
||||
$lowerCategory = strtolower($category);
|
||||
|
||||
// Match: Category, Category/*, browse/Category, browse/Category/*
|
||||
return $lowerPath === $lowerCategory
|
||||
|| str_starts_with($lowerPath, $lowerCategory.'/')
|
||||
|| $lowerPath === 'browse/'.$lowerCategory
|
||||
|| str_starts_with($lowerPath, 'browse/'.$lowerCategory.'/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort with a category disabled response.
|
||||
*/
|
||||
protected function abortCategoryDisabled(string $category): Response
|
||||
{
|
||||
return response()->view('errors.category-disabled', [
|
||||
'category' => $category,
|
||||
], 403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort with a subcategory disabled response.
|
||||
*/
|
||||
protected function abortSubcategoryDisabled(string $parentCategory, string $subcategory): Response
|
||||
{
|
||||
return response()->view('errors.category-disabled', [
|
||||
'category' => $parentCategory.' - '.$subcategory,
|
||||
'isSubcategory' => true,
|
||||
], 403);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>{{ __('Category Disabled') }}</title>
|
||||
|
||||
<style>
|
||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}code{font-family:monospace,monospace;font-size:1em}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
||||
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.container {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #374151;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #f9fafb;
|
||||
}
|
||||
|
||||
.message {
|
||||
color: #d1d5db;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
background-color: #fef3c7;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
max-width: 28rem;
|
||||
margin: 1rem;
|
||||
padding: 2rem;
|
||||
background-color: #ffffff;
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
margin: 0 auto 1.5rem;
|
||||
border-radius: 9999px;
|
||||
background-color: #fef3c7;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.message {
|
||||
color: #6b7280;
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background-color: #3b82f6;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
border-radius: 0.5rem;
|
||||
text-decoration: none;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #2563eb;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
display: block;
|
||||
margin-top: 1rem;
|
||||
color: #6b7280;
|
||||
text-decoration: underline;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.back-link {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
color: #d1d5db;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="antialiased">
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
<div class="icon-container">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="title">
|
||||
<span class="category-name">{{ $category }}</span> {{ isset($isSubcategory) && $isSubcategory ? 'Subcategory' : 'Category' }} Disabled
|
||||
</h1>
|
||||
|
||||
<p class="message">
|
||||
@if(isset($isSubcategory) && $isSubcategory)
|
||||
You have excluded the <strong>{{ $category }}</strong> subcategory in your profile settings.
|
||||
To access this content, please remove this subcategory from your exclusions in your profile.
|
||||
@else
|
||||
You have disabled viewing of the <strong>{{ $category }}</strong> category in your profile settings.
|
||||
To access this content, please enable the category in your profile.
|
||||
@endif
|
||||
</p>
|
||||
|
||||
<a href="{{ url('profileedit') }}" class="button">
|
||||
Go to Profile Settings
|
||||
</a>
|
||||
|
||||
<a href="{{ url('/') }}" class="back-link">
|
||||
← Back to Home
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+7
-5
@@ -143,6 +143,13 @@ Route::middleware('isVerified')->group(function () {
|
||||
Route::match(['GET', 'POST'], 'Console/{id?}', [ConsoleController::class, 'show'])->name('Console');
|
||||
Route::match(['GET', 'POST'], 'XXX/{id?}', [AdultController::class, 'show'])->name('XXX');
|
||||
Route::match(['GET', 'POST'], 'Books/{id?}', [BooksController::class, 'index'])->name('Books');
|
||||
// TV-related routes
|
||||
Route::match(['GET', 'POST'], 'series/{id?}', [SeriesController::class, 'index'])->name('series');
|
||||
Route::match(['GET', 'POST'], 'trending-tv', [SeriesController::class, 'showTrending'])->name('trending-tv');
|
||||
Route::match(['GET', 'POST'], 'myshows', [MyShowsController::class, 'show'])->name('myshows');
|
||||
Route::match(['GET', 'POST'], 'myshows/browse', [MyShowsController::class, 'browse'])->name('myshows.browse');
|
||||
// Movies-related routes
|
||||
Route::match(['GET', 'POST'], 'mymovies', [MyMoviesController::class, 'show'])->name('mymovies');
|
||||
});
|
||||
|
||||
Route::match(['GET', 'POST'], 'nfo/{id?}', [NfoController::class, 'showNfo'])->name('nfo');
|
||||
@@ -159,12 +166,7 @@ Route::middleware('isVerified')->group(function () {
|
||||
Route::get('release-report/reasons', [\App\Http\Controllers\ReleaseReportController::class, 'getReasons'])->name('release-report.reasons');
|
||||
Route::get('release-report/check', [\App\Http\Controllers\ReleaseReportController::class, 'checkReported'])->name('release-report.check');
|
||||
|
||||
Route::match(['GET', 'POST'], 'mymovies', [MyMoviesController::class, 'show'])->name('mymovies');
|
||||
Route::match(['GET', 'POST'], 'myshows', [MyShowsController::class, 'show'])->name('myshows');
|
||||
Route::match(['GET', 'POST'], 'myshows/browse', [MyShowsController::class, 'browse'])->name('myshows.browse');
|
||||
Route::get('api/release/{guid}/filelist', [\App\Http\Controllers\Api\FileListApiController::class, 'getFileList'])->name('api.filelist');
|
||||
Route::match(['GET', 'POST'], 'series/{id?}', [SeriesController::class, 'index'])->name('series');
|
||||
Route::match(['GET', 'POST'], 'trending-tv', [SeriesController::class, 'showTrending'])->name('trending-tv');
|
||||
Route::match(['GET', 'POST'], 'ajax_profile', [AjaxController::class, 'profile'])->name('ajax_profile');
|
||||
Route::match(['GET', 'POST'], '2fa', [PasswordSecurityController::class, 'show2faForm'])->name('2fa');
|
||||
Route::get('2fa/enable', [PasswordSecurityController::class, 'showEnable2faForm'])->name('2fa.enable');
|
||||
|
||||
Reference in New Issue
Block a user