179 lines
7.0 KiB
Python
179 lines
7.0 KiB
Python
|
|
"""
|
||
|
|
Plugin Template — Example plugin demonstrating all manifest fields.
|
||
|
|
|
||
|
|
Copy this directory to app/plugins/builtins/<your_plugin_name>/ 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,
|
||
|
|
},
|
||
|
|
]
|