Hooks Reference
Hooks allow plugins to react to events and modify behavior throughout LusterCMS.
How Hooks Work
LusterCMS uses Pluggy for its hook system:
from core.hooks import hookimpl
class MyPlugin:
@hookimpl
def after_content_save(self, entry_id: str) -> None:
# Your code runs after content is saved
pass
Available Hooks
Content Lifecycle
| Hook | Trigger | Parameters |
|---|---|---|
before_content_save | Before saving content | entry_id, data |
after_content_save | After content is saved | entry_id |
before_content_publish | Before publishing | entry_id |
after_content_publish | After content is published | entry_id |
before_content_delete | Before deleting | entry_id |
after_content_delete | After content is deleted | entry_id |
Media Lifecycle
| Hook | Trigger | Parameters |
|---|---|---|
before_media_upload | Before upload | file, metadata |
after_media_upload | After upload | media_id, file_path |
before_media_delete | Before deleting | media_id |
after_media_delete | After deletion | media_id |
User Lifecycle
| Hook | Trigger | Parameters |
|---|---|---|
after_user_register | After registration | user_id |
after_user_login | After login | user_id |
before_user_delete | Before deleting | user_id |
Theme & Plugin Lifecycle
| Hook | Trigger | Parameters |
|---|---|---|
before_theme_switch | Before switching theme | theme_name |
after_theme_switch | After theme switch | theme_name |
before_plugin_activate | Before activating | plugin_name |
after_plugin_activate | After activation | plugin_name |
before_plugin_deactivate | Before deactivating | plugin_name |
after_plugin_deactivate | After deactivation | plugin_name |
AI Lifecycle
| Hook | Trigger | Parameters |
|---|---|---|
before_ai_content_suggest | Before AI generation | prompt, context |
after_ai_content_suggest | After AI generation | result, credits_used |
Hook Examples
Logging Content Changes
from core.hooks import hookimpl
import logging
logger = logging.getLogger(__name__)
class AuditPlugin:
@hookimpl
def after_content_save(self, entry_id: str) -> None:
logger.info(f"Content saved: {entry_id}")
@hookimpl
def after_content_publish(self, entry_id: str) -> None:
logger.info(f"Content published: {entry_id}")
Sending Notifications
from core.hooks import hookimpl
from myapp.notifications import send_email
class NotificationPlugin:
@hookimpl
def after_content_publish(self, entry_id: str) -> None:
# Send notification to subscribers
send_email(
to="team@example.com",
subject="New content published",
body=f"Entry {entry_id} was just published."
)
Validating Content
from core.hooks import hookimpl
from core.exceptions import ValidationError
class ValidationPlugin:
@hookimpl
def before_content_save(self, entry_id: str, data: dict) -> None:
# Validate content before saving
if len(data.get("title", "")) < 5:
raise ValidationError("Title must be at least 5 characters")
Modifying AI Prompts
from core.hooks import hookimpl
class AIEnhancerPlugin:
@hookimpl
def before_ai_content_suggest(self, prompt: str, context: dict) -> dict:
# Add brand guidelines to AI prompts
enhanced_prompt = f"""
{prompt}
Brand Guidelines:
- Use friendly, conversational tone
- Avoid jargon
- Keep sentences short
"""
return {"prompt": enhanced_prompt, "context": context}
Hook Priority
Control execution order with hookimpl options:
from core.hooks import hookimpl
class MyPlugin:
@hookimpl(tryfirst=True)
def before_content_save(self, entry_id: str, data: dict) -> None:
# Runs before other plugins
pass
@hookimpl(trylast=True)
def after_content_save(self, entry_id: str) -> None:
# Runs after other plugins
pass
Best Practices
- Keep hooks fast — Don't block the main request
- Handle errors gracefully — Log and continue
- Use async for I/O — Offload heavy work
- Be idempotent — Hooks may run multiple times
- Document side effects — Make behavior clear
⚠️ Important
Hooks run synchronously in the request cycle. For heavy operations, use background tasks or queues.