Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions resources/views/admin/events/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
<a href="<?= route_path('admin_events_create') ?>" class="btn btn-primary">Create New Event</a>
</div>

<?php if($success): ?>
<?php if($Success): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<?= htmlspecialchars($success) ?>
<?= htmlspecialchars($Success) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>

<?php if($error): ?>
<?php if($Error): ?>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<?= htmlspecialchars($error) ?>
<?= htmlspecialchars($Error) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
Expand Down
4 changes: 2 additions & 2 deletions resources/views/auth/login/login.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
<input type="hidden" name="redirect_url" value="<?= htmlspecialchars($RedirectUrl ?? route_path('admin_dashboard')) ?>">

<div class="mb-3">
<label for="username" class="form-label">Username</label>
<label for="username" class="form-label">Username or Email</label>
<input
type="text"
id="username"
name="username"
class="form-control"
placeholder="Enter your username"
placeholder="Enter your username or email"
required
autofocus
>
Expand Down
4 changes: 2 additions & 2 deletions src/Cms/Controllers/Admin/EventCategories.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ public function index( Request $request ): string
->withCsrfToken()
->with([
'categories' => $this->_repository->all(),
FlashMessageType::SUCCESS->value => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->value => $sessionManager->getFlash( FlashMessageType::ERROR->value )
FlashMessageType::SUCCESS->viewKey() => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->viewKey() => $sessionManager->getFlash( FlashMessageType::ERROR->value )
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing view updates break flash messages on multiple pages

High Severity

The controllers now pass flash message data using viewKey() which returns capitalized keys ('Success', 'Error'), but several view templates still expect lowercase variables ($success, $error). Only resources/views/admin/events/index.php was updated. The views for Event Categories, Media, Pages, and Profile still use lowercase variables and will silently fail to display any flash messages to users.

Additional Locations (2)

Fix in Cursor Fix in Web

])
->render( 'index', 'admin' );
}
Expand Down
4 changes: 2 additions & 2 deletions src/Cms/Controllers/Admin/Events.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ public function index( Request $request ): string
->withCsrfToken()
->with([
'events' => $events,
FlashMessageType::SUCCESS->value => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->value => $sessionManager->getFlash( FlashMessageType::ERROR->value )
FlashMessageType::SUCCESS->viewKey() => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->viewKey() => $sessionManager->getFlash( FlashMessageType::ERROR->value )
])
->render( 'index', 'admin' );
}
Expand Down
8 changes: 4 additions & 4 deletions src/Cms/Controllers/Admin/Media.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ public function index( Request $request ): string
'resources' => $result['resources'],
'nextCursor' => $result['next_cursor'],
'totalCount' => $result['total_count'],
FlashMessageType::SUCCESS->value => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->value => $sessionManager->getFlash( FlashMessageType::ERROR->value )
FlashMessageType::SUCCESS->viewKey() => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->viewKey() => $sessionManager->getFlash( FlashMessageType::ERROR->value )
])
->render( 'index', 'admin' );
}
Expand All @@ -135,8 +135,8 @@ public function index( Request $request ): string
'resources' => [],
'nextCursor' => null,
'totalCount' => 0,
FlashMessageType::SUCCESS->value => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->value => 'Failed to load media library. Please try again.'
FlashMessageType::SUCCESS->viewKey() => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->viewKey() => 'Failed to load media library. Please try again.'
])
->render( 'index', 'admin' );
}
Expand Down
4 changes: 2 additions & 2 deletions src/Cms/Controllers/Admin/Pages.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ public function index( Request $request ): string
->withCsrfToken()
->with([
'pages' => $pages,
FlashMessageType::SUCCESS->value => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->value => $sessionManager->getFlash( FlashMessageType::ERROR->value )
FlashMessageType::SUCCESS->viewKey() => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->viewKey() => $sessionManager->getFlash( FlashMessageType::ERROR->value )
])
->render( 'index', 'admin' );
}
Expand Down
4 changes: 2 additions & 2 deletions src/Cms/Controllers/Admin/Posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ public function index( Request $request ): string
->withCsrfToken()
->with([
'posts' => $posts,
FlashMessageType::SUCCESS->value => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->value => $sessionManager->getFlash( FlashMessageType::ERROR->value )
FlashMessageType::SUCCESS->viewKey() => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->viewKey() => $sessionManager->getFlash( FlashMessageType::ERROR->value )
])
->render( 'index', 'admin' );
}
Expand Down
4 changes: 2 additions & 2 deletions src/Cms/Controllers/Admin/Profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ public function edit( Request $request ): string
->withCsrfToken()
->with([
'groupedTimezones' => $groupedTimezones,
FlashMessageType::SUCCESS->value => $this->getSessionManager()->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->value => $this->getSessionManager()->getFlash( FlashMessageType::ERROR->value )
FlashMessageType::SUCCESS->viewKey() => $this->getSessionManager()->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->viewKey() => $this->getSessionManager()->getFlash( FlashMessageType::ERROR->value )
])
->render( 'edit', 'admin' );
}
Expand Down
4 changes: 2 additions & 2 deletions src/Cms/Controllers/Admin/Users.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ public function index( Request $request ): string
->withCsrfToken()
->with([
'users' => $this->_repository->all(),
FlashMessageType::SUCCESS->value => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->value => $sessionManager->getFlash( FlashMessageType::ERROR->value )
FlashMessageType::SUCCESS->viewKey() => $sessionManager->getFlash( FlashMessageType::SUCCESS->value ),
FlashMessageType::ERROR->viewKey() => $sessionManager->getFlash( FlashMessageType::ERROR->value )
])
->render( 'index', 'admin' );
}
Expand Down
4 changes: 2 additions & 2 deletions src/Cms/Controllers/Auth/Login.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ public function showLoginForm( Request $request ): string
->title( 'Login' )
->description( 'Login to ' . $this->getName() )
->withCsrfToken()
->with( FlashMessageType::ERROR->value, $this->getSessionManager()->getFlash( 'error' ) )
->with( FlashMessageType::SUCCESS->value, $this->getSessionManager()->getFlash( 'success' ) )
->with( FlashMessageType::ERROR->viewKey(), $this->getSessionManager()->getFlash( FlashMessageType::ERROR->value ) )
->with( FlashMessageType::SUCCESS->viewKey(), $this->getSessionManager()->getFlash( FlashMessageType::SUCCESS->value ) )
->with( 'RedirectUrl', $redirectUrl )
->render( 'login', 'auth' );
}
Expand Down
15 changes: 15 additions & 0 deletions src/Cms/Enums/FlashMessageType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
/**
* Flash message types used throughout the CMS
*
* Provides consistent keys for both session storage and view variables.
*
* @package Neuron\Cms\Enums
*/
enum FlashMessageType: string
Expand All @@ -13,4 +15,17 @@ enum FlashMessageType: string
case ERROR = 'error';
case WARNING = 'warning';
case INFO = 'info';

/**
* Get the view variable name for this flash message type
*
* Views expect capitalized variable names (e.g., $Success, $Error)
* while session storage uses lowercase keys (e.g., 'success', 'error')
*
* @return string The capitalized view variable name
*/
public function viewKey(): string
{
return ucfirst( $this->value );
}
}
16 changes: 15 additions & 1 deletion src/Cms/Services/Auth/Authentication.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,24 @@ public function __construct(

/**
* Attempt to authenticate a user
*
* Accepts either username or email address for login.
* Automatically detects if the input is an email and searches accordingly.
*/
public function attempt( string $username, string $password, bool $remember = false ): bool
{
$user = $this->_userRepository->findByUsername( $username );
// Detect if input is an email address
$isEmail = filter_var( $username, FILTER_VALIDATE_EMAIL );

// Try to find user by email or username
if( $isEmail )
{
$user = $this->_userRepository->findByEmail( $username );
}
else
{
$user = $this->_userRepository->findByUsername( $username );
}

if( !$user )
{
Expand Down
59 changes: 59 additions & 0 deletions tests/Unit/Cms/Enums/FlashMessageTypeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Tests\Unit\Cms\Enums;

use Neuron\Cms\Enums\FlashMessageType;
use PHPUnit\Framework\TestCase;

/**
* Tests for FlashMessageType enum
*
* @package Tests\Unit\Cms\Enums
*/
class FlashMessageTypeTest extends TestCase
{
/**
* Test that viewKey() returns capitalized version of the enum value
*
* The viewKey() method provides consistent view variable names
* (e.g., $Success, $Error) for use in templates.
*/
public function testViewKeyReturnsCapitalizedValue(): void
{
$this->assertEquals( 'Success', FlashMessageType::SUCCESS->viewKey() );
$this->assertEquals( 'Error', FlashMessageType::ERROR->viewKey() );
$this->assertEquals( 'Warning', FlashMessageType::WARNING->viewKey() );
$this->assertEquals( 'Info', FlashMessageType::INFO->viewKey() );
}

/**
* Test that enum values remain lowercase for session storage
*
* The value property should remain lowercase for consistency
* with session flash key storage.
*/
public function testEnumValuesAreLowercase(): void
{
$this->assertEquals( 'success', FlashMessageType::SUCCESS->value );
$this->assertEquals( 'error', FlashMessageType::ERROR->value );
$this->assertEquals( 'warning', FlashMessageType::WARNING->value );
$this->assertEquals( 'info', FlashMessageType::INFO->value );
}

/**
* Test that all enum cases exist
*
* Ensures all expected flash message types are defined.
*/
public function testAllCasesExist(): void
{
$cases = FlashMessageType::cases();
$this->assertCount( 4, $cases );

$values = array_map( fn( $case ) => $case->value, $cases );
$this->assertContains( 'success', $values );
$this->assertContains( 'error', $values );
$this->assertContains( 'warning', $values );
$this->assertContains( 'info', $values );
}
}
54 changes: 54 additions & 0 deletions tests/Unit/Cms/Services/AuthenticationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -608,4 +608,58 @@ public function testValidateCredentialsWithInvalidPassword(): void

$this->assertFalse($result);
}

/**
* Test attempt() with email address instead of username
*/
public function testAttemptWithEmailAddress(): void
{
$user = $this->createTestUser('emailuser', 'TestPass123');

// Attempt login using email instead of username
$result = $this->_authentication->attempt($user->getEmail(), 'TestPass123');

$this->assertTrue($result);
$this->assertTrue($this->_authentication->check());
$this->assertEquals('emailuser', $this->_authentication->user()->getUsername());
}

/**
* Test attempt() with username still works
*/
public function testAttemptWithUsernameStillWorks(): void
{
$this->createTestUser('usernametest', 'TestPass123');

// Attempt login using username (traditional method)
$result = $this->_authentication->attempt('usernametest', 'TestPass123');

$this->assertTrue($result);
$this->assertTrue($this->_authentication->check());
$this->assertEquals('usernametest', $this->_authentication->user()->getUsername());
}

/**
* Test attempt() with nonexistent email returns false
*/
public function testAttemptWithNonexistentEmail(): void
{
$result = $this->_authentication->attempt('nobody@example.com', 'TestPass123');

$this->assertFalse($result);
$this->assertFalse($this->_authentication->check());
}

/**
* Test attempt() with incorrect password using email
*/
public function testAttemptWithEmailAndIncorrectPassword(): void
{
$user = $this->createTestUser('emailpasstest', 'TestPass123');

$result = $this->_authentication->attempt($user->getEmail(), 'WrongPassword');

$this->assertFalse($result);
$this->assertFalse($this->_authentication->check());
}
}