diff --git a/resources/views/layouts/main.blade.php b/resources/views/layouts/main.blade.php index bdd324f52..0af6d178c 100644 --- a/resources/views/layouts/main.blade.php +++ b/resources/views/layouts/main.blade.php @@ -12,7 +12,7 @@ {{-- Apply dark mode BEFORE any CSS loads to prevent white flash --}} @include('partials.theme-init') - {{ $meta_title ?? config('app.name') }}@if(isset($meta_title) && $meta_title != '' && $site['metatitle'] != '') - @endif{{ $site['metatitle'] ?? '' }} + {{ $meta_title ?? config('app.name') }}@if(isset($meta_title) && $meta_title !== '' && (($site['metatitle'] ?? '') !== '')) - @endif{{ $site['metatitle'] ?? '' }} diff --git a/routes/web.php b/routes/web.php index 5029a2b13..192d65a7f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -120,6 +120,10 @@ Route::get('api/search/assist', [SearchSuggestController::class, 'searchAssist'] Route::match(['GET', 'POST'], 'getnzb/{guid}', [GetNzbController::class, 'getNzb'])->name('getnzb.guid'); Route::match(['GET', 'POST'], 'getnzb', [GetNzbController::class, 'getNzb'])->name('getnzb'); +// Contact form must remain publicly reachable for guests. +Route::match(['GET', 'POST'], 'contact-us', [ContactUsController::class, 'showContactForm'])->name('contact-us'); +Route::post('contact-us', [ContactUsController::class, 'contact']); + Route::middleware('isVerified')->group(function () { Route::match(['GET', 'POST'], 'resetpassword', [ResetPasswordController::class, 'reset'])->name('resetpassword'); Route::match(['GET', 'POST'], 'profile', [ProfileController::class, 'show'])->name('profile'); @@ -169,8 +173,6 @@ Route::middleware('isVerified')->group(function () { Route::match(['GET', 'POST'], 'nfo/{id?}', [NfoController::class, 'showNfo'])->name('nfo'); - Route::match(['GET', 'POST'], 'contact-us', [ContactUsController::class, 'showContactForm'])->name('contact-us'); - Route::post('contact-us', [ContactUsController::class, 'contact']); Route::match(['GET', 'POST'], 'profileedit', [ProfileController::class, 'edit'])->name('profileedit'); Route::match(['GET', 'POST'], 'profile_delete', [ProfileController::class, 'destroy'])->name('profile_delete'); Route::post('profile/update-theme', [ProfileController::class, 'updateTheme'])->name('profile.update-theme'); diff --git a/tests/Feature/NzbAndRssAccessTest.php b/tests/Feature/NzbAndRssAccessTest.php index 9d7e7f68e..18becfd09 100644 --- a/tests/Feature/NzbAndRssAccessTest.php +++ b/tests/Feature/NzbAndRssAccessTest.php @@ -4,10 +4,14 @@ declare(strict_types=1); namespace Tests\Feature; +use App\View\Composers\GlobalDataComposer; use Illuminate\Contracts\Console\Kernel; +use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; use PDO; +use ReflectionClass; use Tests\TestCase; class NzbAndRssAccessTest extends TestCase @@ -67,6 +71,9 @@ class NzbAndRssAccessTest extends TestCase DB::purge(); DB::reconnect(); Cache::flush(); + + $this->createSchema(); + $this->resetGlobalComposerState(); } protected function tearDown(): void @@ -92,6 +99,16 @@ class NzbAndRssAccessTest extends TestCase $response->assertDontSee('Login', false); } + public function test_legacy_api_get_without_apikey_returns_api_error_instead_of_login_redirect(): void + { + $response = $this->get('/api/v1/api?t=get&id=test-guid'); + + $response->assertOk(); + $response->assertSee('<error code="200" description="Missing parameter (apikey)"/>', false); + $response->assertDontSee('name="login"', false); + $response->assertDontSee('<title>Login', false); + } + public function test_rss_feed_without_api_token_returns_403_error_instead_of_login_redirect(): void { $response = $this->get('/rss/full-feed'); @@ -104,6 +121,16 @@ class NzbAndRssAccessTest extends TestCase $response->assertDontSee('<title>Login', false); } + public function test_contact_form_is_publicly_accessible_to_guests(): void + { + $response = $this->get('/contact-us'); + + $response->assertOk(); + $response->assertSee('Contact '.config('app.name')); + $response->assertDontSee('name="login"', false); + $response->assertDontSee('<title>Login', false); + } + private function setEnvironmentValue(string $key, ?string $value): void { if ($value === null) { @@ -117,4 +144,30 @@ class NzbAndRssAccessTest extends TestCase $_ENV[$key] = $value; $_SERVER[$key] = $value; } + + private function createSchema(): void + { + if (! Schema::hasTable('content')) { + Schema::create('content', function (Blueprint $table): void { + $table->increments('id'); + $table->string('title')->default(''); + $table->string('url', 2000)->nullable(); + $table->text('body')->nullable(); + $table->string('metadescription', 1000)->default(''); + $table->string('metakeywords', 1000)->default(''); + $table->integer('contenttype')->default(2); + $table->integer('status')->default(1); + $table->integer('ordinal')->nullable(); + $table->integer('role')->default(0); + $table->timestamps(); + }); + } + } + + private function resetGlobalComposerState(): void + { + $reflection = new ReflectionClass(GlobalDataComposer::class); + $property = $reflection->getProperty('resolvedData'); + $property->setValue(null, null); + } }