""" Plugin Template — Example plugin demonstrating all manifest fields. Copy this directory to app/plugins/builtins// and customize. """ from __future__ import annotations from app.plugins.base import BasePlugin from app.plugins.manifest import ( PluginManifest, PluginRouteDef, FieldDefinition, FrontendMenuItem, FrontendPageRoute, FrontendDetailTab, FrontendSettingsPage, FrontendDashboardWidget, ) class ExamplePlugin(BasePlugin): """ Example plugin demonstrating all manifest fields. Remove or comment out fields you don't need. """ manifest = PluginManifest( # ── Core Metadata ────────────────────────────────────────────── name="example_plugin", version="1.0.0", display_name="Example Plugin", description="A minimal example plugin demonstrating all manifest fields.", dependencies=[], is_core=False, # ── API Routes ───────────────────────────────────────────────── routes=[ PluginRouteDef( path="/api/v1/example", module="app.plugins.builtins.example.routes", router_attr="router", ), ], # ── Event Subscriptions ──────────────────────────────────────── events=[ "contact.created", "contact.updated", ], # ── Database Migrations ──────────────────────────────────────── migrations=[ "0001_initial.sql", ], # ── RBAC Permissions ─────────────────────────────────────────── permissions=[ "example:read", "example:write", ], # ── Field Definitions (Field-Level Permissions) ──────────────── field_definitions=[ FieldDefinition( module="contacts", field="custom_field", label="Custom Field", sensitivity="normal", ), ], # ── AI Agent Capabilities ────────────────────────────────────── agent_capabilities=[ "example:search", ], # ── Frontend UI: Sidebar Menu Items ──────────────────────────── menu_items=[ FrontendMenuItem( label_key="nav.example", label="Example", path="/example", icon="Sparkles", order=100, ), ], # ── Frontend UI: Page Routes ─────────────────────────────────── page_routes=[ FrontendPageRoute( path="/example", component="@/pages/Example", protected=True, ), ], # ── Frontend UI: Detail Tabs ─────────────────────────────────── detail_tabs=[ FrontendDetailTab( entity_type="contact", label_key="tabs.example", label="Example", component="@/components/ExampleTab", icon="Sparkles", order=50, permission="example:read", ), ], # ── Frontend UI: Settings Pages ──────────────────────────────── settings_pages=[ FrontendSettingsPage( path="example", label_key="settings.example", label="Example", component="@/pages/ExampleSettings", icon="Sparkles", order=100, permission="example:write", ), ], # ── Frontend UI: Dashboard Widgets ───────────────────────────── dashboard_widgets=[ FrontendDashboardWidget( id="example_stats", label_key="widgets.example", label="Example Stats", component="@/components/ExampleWidget", icon="LayoutDashboard", order=100, col_span=2, row_span=1, permission="example:read", ), ], ) # ─── Lifecycle Hooks ─────────────────────────────────────────────── async def on_install(self, db, service_container): """Called after migrations are run.""" # Perform seed data or initial setup here pass async def on_activate(self, db, service_container, event_bus): """Called when the plugin is activated.""" # Default implementation subscribes to manifest events await super().on_activate(db, service_container, event_bus) async def on_deactivate(self, db, service_container, event_bus): """Called when the plugin is deactivated.""" # Default implementation unsubscribes all event listeners await super().on_deactivate(db, service_container, event_bus) async def on_uninstall(self, db, service_container): """Called before data tables are dropped.""" # Clean up external resources here pass # ─── Event Handlers ──────────────────────────────────────────────── async def on_contact_created(self, event_data: dict) -> None: """Handle contact.created event.""" contact_id = event_data.get("contact_id") # React to new contact pass async def on_contact_updated(self, event_data: dict) -> None: """Handle contact.updated event.""" contact_id = event_data.get("contact_id") # React to contact update pass # ─── Notification Types ──────────────────────────────────────────── def get_notification_types(self) -> list[dict]: """Return notification types this plugin registers.""" return [ { "type_key": "example.notification", "label": "Example Notification", "category": "general", "description": "Notification from the example plugin", "is_enabled_by_default": True, }, ]