Comprehensive example plugin for PteroCA demonstrating all major plugin system capabilities.
This plugin serves as a reference implementation for plugin developers, showcasing best practices and patterns for extending PteroCA functionality.
For complete plugin development documentation, visit:
- Main Documentation: https://docs.pteroca.com
- Plugin Getting Started Guide: https://docs.pteroca.com/for-developers/plugins/getting-started
The Hello World plugin demonstrates:
- All 7 plugin capabilities (routes, entities, migrations, ui, eda, console, cron)
- Plugin settings integration across all components
- Multiple widget types and contexts
- Essential event subscriptions
- Entity relationships and custom repositories
- Custom services and bootstrap initialization
- Professional code documentation
Demonstrates all setting types and hierarchies:
General Settings:
greeting_message(string) - Customizable greetingenable_widget(boolean) - Toggle widget visibilitymax_visits_to_display(integer) - Limit for visit listswelcome_text(string) - Multi-line welcome messagenotification_level(string) - Log level: debug, info, warning, error
API Settings:
api_enabled(boolean) - Toggle API accessapi_key(string) - Secure API key storageapi_rate_limit(integer) - Request limit
Advanced Settings:
enable_statistics(boolean) - Toggle statistics trackingcleanup_days(integer) - Data retention perioddebug_mode(boolean) - Enable debug logging
HelloWidget
- Context: DASHBOARD
- Position: RIGHT
- Demonstrates: Settings integration (greeting_message, enable_widget)
StatsWidget
- Contexts: DASHBOARD, ADMIN_OVERVIEW
- Position: LEFT
- Demonstrates: Multi-context support, context-aware data, EntityManager injection
QuickActionsWidget
- Context: DASHBOARD
- Position: NAVBAR
- Demonstrates: Global navigation widget, dropdown menu
UserEventSubscriber
UserLoggedInEvent- Track user loginsUserRegisteredEvent- Welcome new users
ServerEventSubscriber
ServerCreatedOnPterodactylEvent- Log server creationServerPurchaseCompletedEvent- Track purchases
MenuEventSubscriber
MenuItemsCollectedEvent- Add navigation menu items (Hello World link, Visit Logs CRUD)
ServerTabSubscriber
ServerTabsCollectedEvent- Register custom server tabs
HelloEventSubscriber
DashboardAccessedEvent- Log dashboard access
VisitLog Entity
- Table:
plg_hello_visit_log - Fields: id, visitorName, ipAddress, userAgent, message, createdAt
- Relationship: OneToMany with Statistic
- Repository: VisitLogRepository with 7 custom query methods
Statistic Entity
- Table:
plg_hello_statistic - Fields: id, type, value, metadata (JSON), createdAt
- Relationship: ManyToOne with VisitLog
- Demonstrates: JSON storage, entity relationships, indexed queries
Custom query methods:
findRecentVisits()- Get latest visitsgetStatsByPeriod()- Aggregated statisticscleanupOldVisits()- Bulk delete old recordsfindByVisitorName()- Case-insensitive searchgetDailyVisitCounts()- Trend analysisfindVisitsWithStatistics()- Eager loading (avoid N+1)countTotal()- Simple count query
Business logic for statistics:
trackEvent()- Create statistics with metadatagetStatsByType()- Filter by type and periodaggregateStats()- Calculate sum, avg, min, maxgetAllTypes()- List unique typescleanupOldStatistics()- Data retention
Plugin initialization:
- Sets default settings on enable
- Validates configuration
- Initializes plugin defaults
- Provides cleanup method
- Comprehensive error handling
Console Command:
hello-world:greet- Demonstrates CLI integration
Cron Tasks:
HelloCleanupTask- Daily cleanup (3:00 AM)- Schedule:
0 3 * * * - Uses configurable
cleanup_dayssetting
HelloController (4 routes):
GET /- Main plugin pageGET /greet/{name}- JSON greeting endpointGET /info- Plugin metadataGET /visits- Visit statistics
VisitLogCrudController (Admin CRUD):
- Full EasyAdmin CRUD interface for Visit Logs
- Demonstrates: List, detail, delete operations
- Shows how plugins can create admin panel interfaces
ExampleTab
- ID:
hello_world_example - Label: "Hello World"
- Priority: 10 (appears at the end)
- Demonstrates: Custom tabs on server management pages
- Template:
@PluginHelloWorld/tabs/example.html.twig
Translations:
plugin_hello_world.en.yaml- English translationsplugin_hello_world.pl.yaml- Polish translations (auto-generated)
Assets:
css/hello-world.css- Widget and page stylesjs/hello-world.js- Interactive featuresimages/hello-logo.svg- Plugin logo
# Place plugin in plugins directory
cp -r hello-world /path/to/panel/plugins/# Scan for new plugins
php bin/console plugin:scan
# Enable the plugin
php bin/console plugin:enable hello-worldMigrations run automatically on enable:
Version20251101000001.php- Createsplg_hello_visit_logtableVersion20251214000001.php- Createsplg_hello_statistictable with relationshipsVersion20251214000002.php- Initializes default plugin settings in database- Sets up indexes for performance
Access via: Admin Panel → Settings → Plugin: hello-world
Configure:
- Greeting messages
- Widget visibility
- Statistics tracking
- API access
- Data retention
For Settings Integration:
// In any class with PluginSettingService injected
$value = $this->pluginSettingService->get('plugin-name', 'setting_key', 'default');For Event Subscription:
class MyEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [EventClass::class => 'onEventMethod'];
}
}For Custom Widgets:
class MyWidget implements WidgetInterface
{
public function getSupportedContexts(): array
{
return [WidgetContext::DASHBOARD];
}
public function isVisible(WidgetContext $context, array $contextData): bool
{
// Check settings, user roles, etc.
return true;
}
}For Entity Relationships:
#[ORM\ManyToOne(targetEntity: ParentEntity::class, inversedBy: 'children')]
#[ORM\JoinColumn(onDelete: 'SET NULL')]
private ?ParentEntity $parent = null;For Custom Repositories:
#[ORM\Entity(repositoryClass: 'Plugins\MyPlugin\Entity\Repository\MyRepository')]
class MyEntity { }For Bootstrap Initialization:
{
"bootstrap_class": "Plugins\\MyPlugin\\Bootstrap"
}hello-world/
├── plugin.json # Manifest (11 settings, bootstrap config)
├── Bootstrap.php # Plugin initialization
├── Migrations/
│ ├── Version20251101000001.php # Create visit_log table
│ ├── Version20251214000001.php # Create statistic table with relationships
│ └── Version20251214000002.php # Initialize default settings
├── src/
│ ├── Controller/
│ │ ├── HelloController.php # 4 routes (GET only)
│ │ └── Admin/
│ │ └── VisitLogCrudController.php # EasyAdmin CRUD for Visit Logs
│ ├── Entity/
│ │ ├── VisitLog.php # Main entity with relationships
│ │ ├── Statistic.php # Related entity with JSON metadata
│ │ └── Repository/
│ │ └── VisitLogRepository.php # 7 custom query methods
│ ├── EventSubscriber/
│ │ ├── HelloEventSubscriber.php # Dashboard events
│ │ ├── UserEventSubscriber.php # User lifecycle events
│ │ ├── ServerEventSubscriber.php # Server events
│ │ ├── MenuEventSubscriber.php # Navigation customization
│ │ └── ServerTabSubscriber.php # Server tabs registration
│ ├── Tab/
│ │ └── ExampleTab.php # Custom server tab
│ ├── Widget/
│ │ ├── HelloWidget.php # DASHBOARD/RIGHT widget
│ │ ├── StatsWidget.php # Multi-context/LEFT widget
│ │ └── QuickActionsWidget.php # NAVBAR widget
│ ├── Service/
│ │ └── StatisticsService.php # Business logic service
│ ├── Command/
│ │ └── HelloGreetCommand.php # CLI command
│ └── CronTask/
│ └── HelloCleanupTask.php # Scheduled task
├── Resources/
│ ├── config/
│ │ └── services.yaml # Service definitions
│ ├── views/
│ │ ├── hello/
│ │ │ └── index.html.twig # Main page template
│ │ └── widgets/
│ │ ├── hello.html.twig # Widget templates
│ │ ├── stats.html.twig
│ │ └── quick_actions.html.twig
│ └── translations/
│ └── messages.en.yaml # Base translations
├── assets/
│ ├── css/hello-world.css # Plugin styles
│ ├── js/hello-world.js # Plugin JavaScript
│ └── images/hello-logo.svg # Plugin logo
├── templates/
│ ├── index.html.twig # Additional templates
│ ├── tabs/
│ │ └── example.html.twig # Server tab template
│ └── widgets/
│ ├── hello.html.twig # Widget templates (duplicates for compatibility)
│ ├── stats.html.twig
│ └── quick_actions.html.twig
├── translations/
│ ├── plugin_hello_world.en.yaml # Plugin-specific translations
│ └── plugin_hello_world.pl.yaml # Auto-generated Polish
└── README.md
-
Settings:
- Go to Settings → Plugin: hello-world
- Change greeting_message and verify in widget
- Toggle enable_widget and check dashboard
-
Widgets:
- Check dashboard for all 3 widgets
- Verify navbar dropdown appears
- Check admin overview for stats widget
-
Events:
- Login to trigger UserLoggedInEvent
- Create server to trigger ServerCreatedOnPterodactylEvent
- Check menu for "Hello World" items and "Visit Logs" link
-
Database & CRUD:
- Visit
/plugins/hello-world/to create visit logs - Check tables:
plg_hello_visit_log,plg_hello_statistic - Access Visit Logs CRUD via admin panel menu
- Visit
-
Server Tabs:
- Visit any server management page
- Look for "Hello World" tab at the end of tabs list
# Test console command
php bin/console hello-world:greet World --yell
# Test cron task manually
php bin/console plugin:cron:run- Fixed
HelloCleanupTask.php:36- Changedv.visitedAttov.createdAt - Resolved TODOs in
HelloWidget.php- Settings now fully integrated
- All types demonstrated: string, integer, boolean
- Three hierarchies: general, api, advanced
- Settings used throughout all components
- Default values initialized via migration
- Different data per context (user vs system stats)
- Conditional visibility based on settings
- Multiple positions (LEFT, RIGHT, NAVBAR)
- User lifecycle events (login, registration)
- Server events (creation, purchase)
- Menu customization (navigation injection)
- Server tabs registration (custom tabs on server pages)
- Bidirectional relationships (OneToMany/ManyToOne)
- JSON metadata storage
- Custom repository methods
- Query optimization with indexes
- EasyAdmin CRUD integration for entity management
- Business logic separation
- Dependency injection
- Error handling and logging
- Settings integration
- Comprehensive PHPDoc comments
- Type hints throughout
- Error handling
- Logging at appropriate levels
- Clean code organization
$value = $this->pluginSettingService->get('hello-world', 'setting_name', 'default_value');public function isVisible(WidgetContext $context, array $contextData): bool
{
return (bool) $this->pluginSettingService->get('hello-world', 'enable_widget', true);
}$this->logger->info('Plugin: Event occurred', [
'event_id' => $event->getEventId(),
'data' => $event->getData(),
]);$qb = $this->entityManager->createQueryBuilder();
$results = $qb->select('e')
->from(Entity::class, 'e')
->where('e.field = :value')
->setParameter('value', $value)
->getQuery()
->getResult();To extend this plugin or create your own:
- Study the Code: Read through each file to understand patterns
- Modify Settings: Add your own config_schema entries
- Create Widgets: Copy widget structure for your use case
- Subscribe to Events: Find events in
src/Core/Event/and subscribe - Add Entities: Create tables following naming convention
- Build Services: Extract business logic into services
- Test Thoroughly: Test settings, events, widgets, database
- Official Documentation: https://docs.pteroca.com
- Plugin Getting Started: https://docs.pteroca.com/for-developers/plugins/getting-started
- Core Events: Browse
src/Core/Event/for available events in PteroCA core - Service Registration: Use
Resources/config/services.yamlfor custom services
MIT License - Free to use and modify for your own plugins.
This is a reference plugin for learning purposes. For questions about the PteroCA plugin system, consult the official documentation or visit the community forums.
Version: 1.0.0 Author: PteroCA Team Updated: 2025-12-28 Compatible: PteroCA 0.6.0 - 1.0.0