Skip to content
Merged
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
35 changes: 0 additions & 35 deletions examples/config/config.yaml

This file was deleted.

57 changes: 57 additions & 0 deletions examples/config/neuron.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
system:
timezone: US/Eastern
base_path: examples
routes_path: examples/config

logging:
destination: \Neuron\Log\Destination\File
format: \Neuron\Log\Format\PlainText
file: test.log
level: debug

cache:
enabled: true
storage: file
path: cache/views
ttl: 3600
html: true
markdown: true
json: false
xml: false
# Garbage collection settings (optional, defaults shown)
# gc_probability: 0.01 # 1% chance to run GC on cache write
# gc_divisor: 100 # Used with probability for fine-tuning

views:
path: views

site:
name: 'Neuron'
url: 'https://neuron.example.com'
description: 'A modern blogging platform built with Neuron.'

blog:
path: 'blog'
title: 'Neuron Blog'

# Member Registration and Management Configuration
member:
# Enable or disable user registration
# Set to false to prevent new users from signing up
registration_enabled: true

# Default role assigned to new users
# Options: admin, editor, author, subscriber
default_role: subscriber

# Require email verification before users can log in
# When true, users must verify their email before accessing member areas
require_email_verification: true

# Email verification URL path
# This is appended to the site URL to create the verification link
verification_url: /verify-email

# Email verification token expiration time (in minutes)
# Tokens older than this will be considered invalid
verification_token_expiration_minutes: 60
11 changes: 6 additions & 5 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ The installer will:
1. Create the complete directory structure (app/, config/, db/, public/, resources/, storage/)
2. Publish all view templates (admin panel, blog, auth, layouts)
3. Publish application initializers
4. Create configuration files (config.yaml, routes.yaml, auth.yaml, event-listeners.yaml)
4. Create configuration files (neuron.yaml, routes.yaml, auth.yaml, event-listeners.yaml)
5. Generate the front controller (public/index.php)
6. Set up database migrations
7. Optionally run migrations to create database tables
Expand All @@ -88,7 +88,8 @@ your-project/
│ ├── Initializers/ # Application initializers
│ │ ├── AuthInitializer.php
│ │ ├── MaintenanceInitializer.php
│ │ └── PasswordResetInitializer.php
│ │ ├── PasswordResetInitializer.php
│ │ └── ViewDataInitializer.php
│ ├── Jobs/ # Background jobs
│ ├── Listeners/ # Event listeners
│ ├── Models/ # Domain models
Expand All @@ -97,7 +98,7 @@ your-project/
│
├── config/
│ ├── auth.yaml # Authentication configuration
│ ├── config.yaml # Main application config
│ ├── neuron.yaml # Main application config
│ ├── event-listeners.yaml # Event listener configuration
│ └── routes.yaml # Route definitions
│
Expand Down Expand Up @@ -156,7 +157,7 @@ Log in with the admin credentials you created during installation.

### Optional Configuration

If you need to customize settings, edit `config/config.yaml`:
If you need to customize settings, edit `config/neuron.yaml`:

```yaml
site:
Expand Down Expand Up @@ -265,7 +266,7 @@ You can customize routes by editing `config/routes.yaml`.

### Email

Configure email in `config/config.yaml`:
Configure email in `config/neuron.yaml`:

```yaml
email:
Expand Down
128 changes: 128 additions & 0 deletions resources/app/Initializers/RegistrationInitializer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

namespace App\Initializers;

use Neuron\Cms\Auth\EmailVerificationManager;
use Neuron\Cms\Auth\Filters\MemberAuthenticationFilter;
use Neuron\Cms\Auth\AuthManager;
use Neuron\Cms\Auth\PasswordHasher;
use Neuron\Cms\Repositories\DatabaseEmailVerificationTokenRepository;
use Neuron\Cms\Repositories\DatabaseUserRepository;
use Neuron\Cms\Services\Member\RegistrationService;
use Neuron\Log\Log;
use Neuron\Patterns\Registry;
use Neuron\Patterns\IRunnable;

/**
* Initialize the member registration system
*
* Registers the registration services and member authentication filter
*/
class RegistrationInitializer implements IRunnable
{
/**
* Run the initializer
* @param array $argv
* @return mixed
* @throws \Exception
*/
public function run( array $argv = [] ): mixed
{
// Get Settings from Registry
$settings = Registry::getInstance()->get( 'Settings' );

if( !$settings || !$settings instanceof \Neuron\Data\Setting\SettingManager )
{
Log::error( "Settings not found in Registry, skipping registration initialization" );
return null;
}

// Get Application from Registry
$app = Registry::getInstance()->get( 'App' );

if( !$app || !$app instanceof \Neuron\Mvc\Application )
{
Log::error( "Application not found in Registry, skipping registration initialization" );
return null;
}

// Get AuthManager from Registry (must be initialized first by AuthInitializer)
$authManager = Registry::getInstance()->get( 'AuthManager' );

if( !$authManager )
{
Log::error( "AuthManager not found in Registry, skipping registration initialization" );
return null;
}

// Check if database is configured
try
{
$settingNames = $settings->getSectionSettingNames( 'database' );

if( !empty( $settingNames ) )
{
// Initialize registration components
$userRepository = new DatabaseUserRepository( $settings );
$tokenRepository = new DatabaseEmailVerificationTokenRepository( $settings );
$passwordHasher = new PasswordHasher();

// Get base path for email templates
$basePath = $settings->get( 'system', 'base_path' ) ?? getcwd();

// Get verification URL from settings
$siteUrl = $settings->get( 'site', 'url' ) ?? 'http://localhost:8000';
$verificationPath = $settings->get( 'member', 'verification_url' ) ?? '/verify-email';
$verificationUrl = rtrim( $siteUrl, '/' ) . '/' . ltrim( $verificationPath, '/' );

// Create EmailVerificationManager
$verificationManager = new EmailVerificationManager(
$tokenRepository,
$userRepository,
$settings,
$basePath,
$verificationUrl
);

// Set token expiration from settings
$tokenExpiration = $settings->get( 'member', 'verification_token_expiration_minutes' ) ?? 60;
$verificationManager->setTokenExpirationMinutes( $tokenExpiration );
Comment thread
ljonesfl marked this conversation as resolved.

// Get event emitter if available
$emitter = Registry::getInstance()->get( 'EventEmitter' );

// Create RegistrationService
$registrationService = new RegistrationService(
$userRepository,
$passwordHasher,
$verificationManager,
$settings,
$emitter
);

// Create member authentication filter
$requireVerification = $settings->get( 'member', 'require_email_verification' ) ?? true;
$memberFilter = new MemberAuthenticationFilter(
$authManager,
'/login',
$requireVerification
);

// Register the member filter with the Router
$app->getRouter()->registerFilter( 'member', $memberFilter );

// Store services in Registry for easy access
Registry::getInstance()->set( 'EmailVerificationManager', $verificationManager );
Registry::getInstance()->set( 'RegistrationService', $registrationService );

Log::info( "Registration system initialized successfully" );
}
}
catch( \Exception $e )
{
Log::error( "Failed to initialize registration system: " . $e->getMessage() );
}

return null;
}
}
74 changes: 74 additions & 0 deletions resources/app/Initializers/ViewDataInitializer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace App\Initializers;

use Neuron\Mvc\Views\ViewDataProvider;
use Neuron\Patterns\IRunnable;
use Neuron\Patterns\Registry;

/**
* Initialize global view data that should be available in all views.
*
* This initializer demonstrates how to use ViewDataProvider to inject
* commonly-needed data into all views without requiring controllers to
* manually pass it or views to access the Registry directly.
*
* The ViewDataProvider eliminates the need for views to directly access
* Registry::getInstance(), making views cleaner and more testable.
*
* Usage:
* - Static values are shared once and reused
* - Callables are executed on each request (lazy evaluation)
* - All shared data is automatically injected into view templates
*
* @package App\Initializers
*/
class ViewDataInitializer implements IRunnable
{
/**
* Run the initializer
*
* Register global view data that should be available in all views.
* Customize this method to share your application's global data.
*
* @param array $argv
* @return mixed
*/
public function run( array $argv = [] ): mixed
{
$registry = Registry::getInstance();
$provider = ViewDataProvider::getInstance();

// Site name - pulled from Registry 'name' key
$provider->share( 'siteName', function() use ( $registry ) {
return $registry->get( 'name' ) ?? 'Neuron CMS';
});

// Application version
$provider->share( 'appVersion', function() use ( $registry ) {
return $registry->get( 'version' ) ?? '';
});

// Current authenticated user
$provider->share( 'currentUser', function() use ( $registry ) {
return $registry->get( 'Auth.User' );
});

// Theme from settings with sensible defaults per context
$provider->share( 'theme', function() use ( $registry ) {
$settings = $registry->get( 'Settings' );
return $settings?->get( 'theme', 'default' ) ?? 'sandstone';
});

// Current year (useful for copyright notices)
$provider->share( 'currentYear', fn() => date( 'Y' ) );

// Check if user is authenticated (convenience helper)
$provider->share( 'isAuthenticated', function() use ( $registry ) {
$authManager = $registry->get( 'AuthManager' );
return $authManager && $authManager->isAuthenticated();
});

return null;
}
}
12 changes: 12 additions & 0 deletions resources/config/auth.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,15 @@ auth:
storage:
type: file # file or database (future)
path: storage/users # Path for file-based storage

# Member Portal Configuration
member:
# Registration settings
registration_enabled: true # Enable/disable public user registration
require_email_verification: true # Require email verification before account activation
default_role: subscriber # Default role for new members

# Email verification settings
verification_token_expiration_minutes: 60 # Token validity in minutes
verification_url: /verify-email # Verification endpoint URL
resend_throttle_seconds: 60 # Minimum time between resend requests
2 changes: 1 addition & 1 deletion resources/config/database.yaml.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Database Configuration
#
# This file provides database configuration for the CMS component.
# Copy sections to your config/config.yaml
# Copy sections to your config/neuron.yaml

database:
# Database adapter (mysql, pgsql, sqlite)
Expand Down
2 changes: 1 addition & 1 deletion resources/config/maintenance.yaml.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Maintenance Mode Configuration
#
# This file provides configuration defaults for the CMS maintenance mode system.
# Copy this file to your config directory and rename to config.yaml (or merge with existing config.yaml)
# Copy this file to your config directory and rename to neuron.yaml (or merge with existing neuron.yaml)

maintenance:
# Default maintenance message shown to users
Expand Down
Loading