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('', false);
+ $response->assertDontSee('name="login"', false);
+ $response->assertDontSee('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('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('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);
+ }
}