Plugin providers and contribution contracts
Implement only the provider methods your plugin uses. All get… methods below return arrays. Definition methods return structs; contributor methods return CFC objects implementing callbacks. [] means no contribution.
| Provider method | Returns |
|---|---|
getAdminRouteDefinitions() |
Admin route structs. |
getAdminPermissionDefinitions() |
Permission structs. |
getAdminSettingsDefinitions() |
Settings item structs. |
getAdminNavigationDefinitions() |
Navigation item structs. |
getIntegrationDefinitions() |
Integration entry structs. |
getIntegrationStatusResolvers() |
Objects with resolveIntegrationStatus(integrationDefinition, requestContext). |
getAdminOrderContributors() |
Objects providing order cards or columns. |
getStorefrontRouteDefinitions() |
Storefront route structs. |
getStorefrontHeaderActionContributors() |
Header-action callback objects. |
getStorefrontProductBadgeContributors() |
Product-badge callback objects. |
getLineItemConfigurationContributors() |
Product-detail/configuration callback objects. |
getLineItemPricingContributors() |
Line-price adjustment callback objects. |
The last four are described under Storefront contributions.
Admin routes
Section titled “Admin routes”{ 'name' = "plugin.example.hello.index", 'methods' = ["GET"], 'path' = "/plugins/example.hello", 'controller' = "netseller_plugin_example_hello.controllers.hello_controller", 'action' = "index", 'middleware' = ["admin.auth", "admin.permission:plugin.example.hello.view"]}| Field | Type | Contract |
|---|---|---|
name |
string | Required; starts with plugin.<plugin-id>.. Use a unique stable name. |
path |
string | Required; under /plugins/<plugin-id>. NetSeller prefixes the configured admin base path. |
methods |
string[] | HTTP methods; defaults to ["GET"]. Declare POST explicitly for mutations. |
controller |
string | Required CFC component path, using the plugin mapping. |
action |
string | Required public controller method, called without arguments. |
middleware |
string[] | Must contain admin.auth and an admin.permission:<key> entry. |
area |
string | Normalized to admin for this provider method. |
Use {recordId} in a path for a route parameter. Read it from request.currentRoute.params.recordId and validate it. Generate URLs with request.buildUrl(routeName, params), not a hardcoded /admin prefix.
Permissions
Section titled “Permissions”{ 'key' = "plugin.example.hello.view", 'action' = "view", 'defaultName' = "View Hello", 'defaultDescription' = "Open the Hello plugin.", 'translationKey' = "plugin.example.hello.permission_view"}key is required and must begin with plugin.<plugin-id>.. Optional action defaults to the last key segment. defaultName and defaultDescription provide labels; translationKey and descriptionKey translate them. The module is plugin.<plugin-id>; a plugin cannot register permissions under a different module.
The loader also accepts permissionKey, actionKey, labelKey, name and description as the corresponding normalized fields. Prefer the example’s naming consistently.
Settings and navigation
Section titled “Settings and navigation”{ 'id' = "plugin_example_hello", 'translationKey' = "plugin.example.hello.title", 'defaultName' = "Hello", 'icon' = "box", 'routeName' = "plugin.example.hello.index", 'permissionsAll' = ["plugin.example.hello.view"]}Return this from getAdminSettingsDefinitions() for a configuration page. Plugin settings entries appear in the System settings tab and are sorted by their translated names.
For a main navigation entry, return the same basic fields from getAdminNavigationDefinitions() and add:
'routePrefixes' = ["plugin.example.hello."]| Field | Meaning |
|---|---|
id |
Required stable item identifier; use a plugin-specific value. |
translationKey, defaultName |
Label key and fallback. |
icon |
Shared admin icon name, for example box or settings. |
routeName |
Named target route. Without one, the item has no actionable link. |
permissionsAll |
Every listed permission is required. |
permissionsAny |
At least one listed permission is required. If both arrays are present, both rules apply. |
routePrefixes |
Navigation only: route-name prefixes that mark the item active. |
children |
Navigation only: child items with IDs, labels, routes and permission rules. |
searchKeywords |
Settings only: additional searchable strings. |
searchKeywordTranslationKey |
Settings only: translated search terms. |
Visibility filtering is not authorization. Protect the destination route independently.
Integration entries
Section titled “Integration entries”Use an integration entry for an external connection or status-bearing capability. It is separate from the plugin manager.
{ 'id' = "example.hello", 'groupKey' = "other", 'translationKey' = "plugin.example.hello.title", 'defaultName' = "Hello", 'descriptionKey' = "plugin.example.hello.description", 'defaultDescription' = "Example connection.", 'icon' = "box", 'routeName' = "plugin.example.hello.index", 'sortOrder' = 100, 'ownerType' = "plugin", 'ownerKey' = "example.hello", 'statusResolverKey' = "plugin.example.hello", 'capabilities' = ["example"], 'permissionsAll' = ["plugin.example.hello.view"]}Required fields are id, groupKey, at least one label (translationKey or defaultName), ownerType, ownerKey and statusResolverKey. Supply an icon as the visual fallback. IDs must be unique.
Use ownerType = "plugin" and your plugin ID as ownerKey. Group keys include ai, payments, shipping, accounting, search, crm and other. Optional routeParams is a struct of route arguments; logo is {src, alt} for a locally served image. capabilities is an array of descriptive capability strings. Permission arrays follow the same rules as navigation.
Integration status
Section titled “Integration status”The demo returns [this] from getIntegrationStatusResolvers() and implements the callback in the same provider:
public struct function resolveIntegrationStatus( required struct integrationDefinition, struct requestContext = {}) { if (arguments.integrationDefinition.statusResolverKey neq "plugin.example.hello") { return {}; } return { 'status' = "configured", 'details' = "Connection configured." };}Return {} for entries you do not own. For your entry return status as configured, missing_configuration, warning or error, and a user-facing details string. Resolve the real state in your plugin service; the constant above illustrates the shape only. Do not expose credentials or raw exceptions.
Activation hook
Section titled “Activation hook”An optional onPluginEnable(context) method runs after successful migrations and translation import, before activation completes. context.datasource is the target datasource and context.plugin describes the package, including its id.
Use this hook for idempotent provisioning through documented services. It can run again on reactivation or update. Throwing an exception aborts activation. There are no corresponding documented disable/delete hooks.
Boundaries
Section titled “Boundaries”Registry definitions contain metadata, not inline SQL or HTML. Callback-specific payloads may contain HTML only where an explicit storefront contract calls for it. Do not modify Core files or call arbitrary shared services.