From f0e8455079d21bca082df1ab77d51c8e6a50f42e Mon Sep 17 00:00:00 2001 From: Rastislav Chynoransky Date: Mon, 19 Aug 2024 16:09:40 +0200 Subject: [PATCH] Scope admin items/collections by user frontend --- app/Http/Kernel.php | 10 +- app/Http/Middleware/ApplyFrontendScope.php | 8 +- ...st.php => ConfigureFrontendFromHeader.php} | 2 +- .../Middleware/ConfigureFrontendFromUser.php | 23 ++++ app/Providers/RouteServiceProvider.php | 6 +- app/Services/Frontend.php | 6 +- database/factories/UserFactory.php | 3 + routes/admin.php | 99 ++++++++++++++ routes/web.php | 124 ------------------ 9 files changed, 149 insertions(+), 132 deletions(-) rename app/Http/Middleware/{ConfigureFrontendFromRequest.php => ConfigureFrontendFromHeader.php} (92%) create mode 100644 app/Http/Middleware/ConfigureFrontendFromUser.php create mode 100644 routes/admin.php diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index c8100ae14..be5d704f4 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -2,6 +2,8 @@ namespace App\Http; +use App\Http\Middleware\ApplyFrontendScope; +use App\Http\Middleware\ConfigureFrontendFromUser; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel @@ -44,9 +46,15 @@ class Kernel extends HttpKernel // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \App\Http\Middleware\AcceptLanguage::class, - \App\Http\Middleware\ConfigureFrontendFromRequest::class, + \App\Http\Middleware\ConfigureFrontendFromHeader::class, \App\Http\Middleware\ApplyFrontendScope::class, ], + + 'admin' => [ + 'auth', + ConfigureFrontendFromUser::class, + ApplyFrontendScope::class, + ], ]; /** diff --git a/app/Http/Middleware/ApplyFrontendScope.php b/app/Http/Middleware/ApplyFrontendScope.php index 892677883..1af47bc6c 100644 --- a/app/Http/Middleware/ApplyFrontendScope.php +++ b/app/Http/Middleware/ApplyFrontendScope.php @@ -12,8 +12,12 @@ class ApplyFrontendScope { public function handle(Request $request, \Closure $next): Response { - Collection::addGlobalScope('frontend', fn ($query) => $query->whereJsonContains('collections.frontends', Frontend::get())); - Item::addGlobalScope('frontend', fn ($query) => $query->whereJsonContains('items.frontends', Frontend::get())); + $frontend = Frontend::get(); + + if ($frontend) { + Collection::addGlobalScope('frontend', fn($query) => $query->whereJsonContains('collections.frontends', $frontend)); + Item::addGlobalScope('frontend', fn($query) => $query->whereJsonContains('items.frontends', $frontend)); + } return $next($request); } diff --git a/app/Http/Middleware/ConfigureFrontendFromRequest.php b/app/Http/Middleware/ConfigureFrontendFromHeader.php similarity index 92% rename from app/Http/Middleware/ConfigureFrontendFromRequest.php rename to app/Http/Middleware/ConfigureFrontendFromHeader.php index 9c6ddb1cb..4dd1fdeaf 100644 --- a/app/Http/Middleware/ConfigureFrontendFromRequest.php +++ b/app/Http/Middleware/ConfigureFrontendFromHeader.php @@ -8,7 +8,7 @@ use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; -class ConfigureFrontendFromRequest +class ConfigureFrontendFromHeader { public function handle(Request $request, Closure $next): Response { diff --git a/app/Http/Middleware/ConfigureFrontendFromUser.php b/app/Http/Middleware/ConfigureFrontendFromUser.php new file mode 100644 index 000000000..2842944ad --- /dev/null +++ b/app/Http/Middleware/ConfigureFrontendFromUser.php @@ -0,0 +1,23 @@ +user()->can_administer + ? FrontendEnum::from($request->user()->frontend) + : null; + + Frontend::set($frontend); + + return $next($request); + } +} diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 7d93bc7d1..02058e27e 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -35,7 +35,11 @@ public function boot() ->prefix('api') ->group(base_path('routes/api.php')); - Route::middleware('web')->group(base_path('routes/web.php')); + Route::middleware('web') + ->group(base_path('routes/web.php')); + + Route::middleware(['web', 'admin']) + ->group(base_path('routes/admin.php')); }); } diff --git a/app/Services/Frontend.php b/app/Services/Frontend.php index 8263ef04c..dde83033b 100644 --- a/app/Services/Frontend.php +++ b/app/Services/Frontend.php @@ -6,16 +6,16 @@ class Frontend { - public function __construct(private FrontendEnum $current) + public function __construct(private ?FrontendEnum $current) { } - public function set(FrontendEnum $frontend): void + public function set(?FrontendEnum $frontend): void { $this->current = $frontend; } - public function get(): FrontendEnum + public function get(): ?FrontendEnum { return $this->current; } diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 5771b37c8..9bfcbb043 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -2,6 +2,8 @@ namespace Database\Factories; +use App\Enums\FrontendEnum; +use App\Services\Frontend; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; @@ -23,6 +25,7 @@ public function definition() 'email' => fake()->safeEmail(), 'password' => bcrypt(Str::random(10)), 'remember_token' => Str::random(10), + 'frontend' => FrontendEnum::WEBUMENIA->value, ]; } } diff --git a/routes/admin.php b/routes/admin.php new file mode 100644 index 000000000..31a5d2cf3 --- /dev/null +++ b/routes/admin.php @@ -0,0 +1,99 @@ + ['can:edit']], function () { + Route::get('item/search', [ItemController::class, 'search'])->name('item.search'); + Route::get('item', [ItemController::class, 'index'])->name('item.index'); + Route::resource('item/tags', ItemTagsController::class)->names('item-tags'); + Route::get('item/{id}/show', [ItemController::class, 'show'])->name('item.show'); + Route::match(['get', 'post'], 'item/create', [ItemController::class, 'create'])->name('item.create'); + Route::match(['get', 'post'], 'item/{id}/edit', [ItemController::class, 'edit'])->name('item.edit'); + Route::post('item/destroySelected', [ItemController::class, 'destroySelected']); + Route::post('dielo/{id}/addTags', function($id) { + $item = Item::find($id); + $newTags = Request::input('tags'); + + if (empty($newTags)) { + Session::flash('message', trans('Neboli zadadné žiadne nové tagy.') ); + return Redirect::to($item->getUrl()); + } + + // @TODO take back captcha if opened for all users + foreach ($newTags as $newTag) { + $item->tag($newTag); + } + + Session::flash( 'message', trans('Bolo pridaných ' . count($newTags) . ' tagov. Ďakujeme!') ); + + return Redirect::to($item->getUrl()); + }); + + Route::resource('article', ArticleController::class); + Route::get('collection/{collection_id}/detach/{item_id}', [CollectionController::class, 'detach']); + Route::post('collection/fill', [CollectionController::class, 'fill']); + Route::post('collection/sort', [CollectionController::class, 'sort']); + Route::resource('collection', CollectionController::class); + Route::resource('user', UserController::class); + + Route::group(['prefix' => 'laravel-filemanager'], function () { + Lfm::routes(); + }); +}); + +Route::group(['middleware' => ['can:administer']], function () { + Route::resource('featured-pieces', FeaturedPieceController::class); + Route::resource('featured-artworks', FeaturedArtworkController::class); + Route::resource('shuffled-items', ShuffledItemController::class); + Route::get('harvests/launch/{id}', [SpiceHarvesterController::class, 'launch']); + Route::get('harvests/harvestFailed/{id}', [SpiceHarvesterController::class, 'harvestFailed']); + Route::get('harvests/orphaned/{id}', [SpiceHarvesterController::class, 'orphaned']); + Route::get('harvests/{record_id}/refreshRecord/', [SpiceHarvesterController::class, 'refreshRecord']); + Route::resource('harvests', SpiceHarvesterController::class); + Route::get('item/backup', [ItemController::class, 'backup']); + Route::get('item/geodata', [ItemController::class, 'geodata']); + Route::post('item/refreshSelected', [ItemController::class, 'refreshSelected']); + Route::get('item/reindex', [ItemController::class, 'reindex']); + Route::get('authority/reindex', [AuthorityController::class, 'reindex']); + Route::post('authority/destroySelected', [AuthorityController::class, 'destroySelected']); + Route::get('authority/search', [AuthorityController::class, 'search']); + Route::get('authority/role-translations', [RoleTranslationsController::class, 'index'])->name( + 'authority.role-translations.index' + ); + Route::get('authority/role-translations/download', [RoleTranslationsController::class, 'download'])->name( + 'authority.role-translations.download' + ); + Route::resource('authority', AuthorityController::class); + Route::resource('sketchbook', SketchbookController::class); + Route::resource('notices', NoticeController::class); + Route::resource('redirects', RedirectController::class); + Route::get('logs', [LogViewerController::class, 'index']); +}); diff --git a/routes/web.php b/routes/web.php index a2bcdde0e..72a072dfe 100644 --- a/routes/web.php +++ b/routes/web.php @@ -15,35 +15,19 @@ use App\Elasticsearch\Repositories\ItemRepository; use App\Facades\Experiment; use App\Filter\ItemFilter; -use App\Http\Controllers\Admin\Authority\RoleTranslationsController; -use App\Http\Controllers\Admin\FeaturedArtworkController; -use App\Http\Controllers\Admin\FeaturedPieceController; -use App\Http\Controllers\Admin\ItemTagsController; -use App\Http\Controllers\Admin\ShuffledItemController; -use App\Http\Controllers\AdminController; -use App\Http\Controllers\ArticleController; use App\Http\Controllers\AuthController; use App\Http\Controllers\AuthorController; -use App\Http\Controllers\AuthorityController; use App\Http\Controllers\CatalogController; use App\Http\Controllers\ClanokController; -use App\Http\Controllers\CollectionController; use App\Http\Controllers\EducationalArticleController; use App\Http\Controllers\HomeController; use App\Http\Controllers\ImageController; -use App\Http\Controllers\ImportController; -use App\Http\Controllers\ItemController; use App\Http\Controllers\KolekciaController; -use App\Http\Controllers\NoticeController; use App\Http\Controllers\PatternlibController; -use App\Http\Controllers\RedirectController; use App\Http\Controllers\SharedUserCollectionController; -use App\Http\Controllers\SketchbookController; use App\Http\Controllers\SkicareController; -use App\Http\Controllers\SpiceHarvesterController; use App\Http\Controllers\UserCollectionController; use App\Http\Controllers\NewCatalogController; -use App\Http\Controllers\UserController; use App\Http\Controllers\ZoomController; use App\Http\Middleware\ApplyFrontendScope; use App\Http\Middleware\RedirectLegacyCatalogRequest; @@ -59,19 +43,6 @@ use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Validator; use Mcamara\LaravelLocalization\Facades\LaravelLocalization; -use Rap2hpoutre\LaravelLogViewer\LogViewerController; - -Route::group(['domain' => 'media.webumenia.{tld}'], function () { - Route::get('/', function ($tld) { - return "webumenia media server"; - }); - Route::get('{id}', function ($tld, $id) { - $item = Item::find($id); - if ($item) { - return config('app.url') . $item->getImagePath(); - } - }); -}); Route::group([ 'prefix' => LaravelLocalization::setLocale(), @@ -405,98 +376,3 @@ function() Route::get('login', [AuthController::class, 'getLogin']); Route::post('login', [AuthController::class, 'postLogin']); }); - -Route::group(['middleware' => ['auth']], function () { - Route::get('admin', [AdminController::class, 'index']); - Route::get('logout', [AuthController::class, 'logout']); - Route::resource('imports', ImportController::class); - Route::get('imports/{import}/launch', [ImportController::class, 'launch']); -}); - -Route::group(['middleware' => ['auth', 'can:edit']], function () { - Route::get('item/search', [ItemController::class, 'search'])->name('item.search'); - Route::get('item', [ItemController::class, 'index'])->name('item.index'); - Route::resource('item/tags', ItemTagsController::class)->names('item-tags'); - Route::get('item/{id}/show', [ItemController::class, 'show'])->name('item.show'); - Route::match(['get', 'post'], 'item/create', [ItemController::class, 'create'])->name('item.create'); - Route::match(['get', 'post'], 'item/{id}/edit', [ItemController::class, 'edit'])->name('item.edit'); - - Route::post('item/destroySelected', [ItemController::class, 'destroySelected']); - - Route::post('dielo/{id}/addTags', function($id) - { - $item = Item::find($id); - $newTags = Request::input('tags'); - - if (empty($newTags)) { - Session::flash( 'message', trans('Neboli zadadné žiadne nové tagy.') ); - return Redirect::to($item->getUrl()); - } - - // @TODO take back captcha if opened for all users - foreach ($newTags as $newTag) { - $item->tag($newTag); - } - - Session::flash( 'message', trans('Bolo pridaných ' . count($newTags) . ' tagov. Ďakujeme!') ); - - // validate that user is human with recaptcha - // till it's for authorised users only, temporary disable - /* - $secret = config('app.google_recaptcha_secret'); - $recaptcha = new \ReCaptcha\ReCaptcha($secret); - $resp = $recaptcha->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']); - if ($resp->isSuccess()) { - // add new tags - foreach ($newTags as $newTag) { - $item->tag($newTag); - } - } else { - // validation unsuccessful - return Redirect::to($item->getUrl()); - } - */ - - return Redirect::to($item->getUrl()); - }); - - Route::resource('article', ArticleController::class); - Route::get('collection/{collection_id}/detach/{item_id}', [CollectionController::class, 'detach']); - Route::post('collection/fill', [CollectionController::class, 'fill']); - Route::post('collection/sort', [CollectionController::class, 'sort']); - Route::resource('collection', CollectionController::class); - Route::resource('user', UserController::class); - - Route::group(['prefix' => 'laravel-filemanager'], function () { - \UniSharp\LaravelFilemanager\Lfm::routes(); - }); -}); - -Route::group(['middleware' => ['auth', 'can:administer']], function () { - Route::resource('featured-pieces', FeaturedPieceController::class); - Route::resource('featured-artworks', FeaturedArtworkController::class); - Route::resource('shuffled-items', ShuffledItemController::class); - Route::get('harvests/launch/{id}', [SpiceHarvesterController::class, 'launch']); - Route::get('harvests/harvestFailed/{id}', [SpiceHarvesterController::class, 'harvestFailed']); - Route::get('harvests/orphaned/{id}', [SpiceHarvesterController::class, 'orphaned']); - Route::get('harvests/{record_id}/refreshRecord/', [SpiceHarvesterController::class, 'refreshRecord']); - Route::resource('harvests', SpiceHarvesterController::class); - Route::get('item/backup', [ItemController::class, 'backup']); - Route::get('item/geodata', [ItemController::class, 'geodata']); - Route::post('item/refreshSelected', [ItemController::class, 'refreshSelected']); - Route::get('item/reindex', [ItemController::class, 'reindex']); - Route::get('authority/reindex', [AuthorityController::class, 'reindex']); - Route::post('authority/destroySelected', [AuthorityController::class, 'destroySelected']); - Route::get('authority/search', [AuthorityController::class, 'search']); - Route::get('authority/role-translations', [RoleTranslationsController::class, 'index'])->name( - 'authority.role-translations.index' - ); - Route::get('authority/role-translations/download', [RoleTranslationsController::class, 'download'])->name( - 'authority.role-translations.download' - ); - Route::resource('authority', AuthorityController::class); - Route::resource('sketchbook', SketchbookController::class); - Route::resource('notices', NoticeController::class); - Route::resource('redirects', RedirectController::class); - Route::get('logs', [LogViewerController::class, 'index']); -});