Architecture
app/{Enums,Extensions,Plugins,Site,Themes}/
Modules/{Admin,Charity,Core,Site}/
plugins/{ContactForm,Events,Fundraising,KaramBanner}/
resources/views/themes/{karam,najda}/
Themes/plugins depend on stable application contracts and tenant context. Themes do not import plugins; current plugins do not import one another.
Discovery
ThemeRegistry iterates ThemeKey::cases() and reads resources/views/themes/{slug}/theme.json. PluginRegistry iterates PluginKey::cases(), reads plugin.json, and resolves the statically mapped driver. PluginServiceProvider registers plugin.{slug} views and package routes. Catalog rows are snapshots and cannot discover code.
Public request
organization.routepopulatesCurrentOrganization.- Published delivery requires active organization and published site; signed preview skips that publication gate.
- CMS queries hide drafts outside preview.
PluginPageResolvertries an enabled plugin page before exact CMS slug lookup.- Plugin navigation merges with CMS navigation.
ThemeResolverresolves assignment or configured default.PluginRendererrenders all three slots by priority.- The static enum-backed theme view renders the response.
Every theme receives $organization, $page, $pluginPage, $navigationPages, $isPreview, $theme, and $pluginSlots.
Management surfaces
Organization requests resolve a tenant, then PluginDashboardResolver and PluginDashboardWidgetResolver use effective tenant plugins. Super Admin requests use super.admin, which establishes tenant bypass; PluginSuperAdminPageResolver and PluginSuperAdminWidgetResolver use only global installed/active state. Neither Super Admin nor organization UI is rendered by a theme.
State
Public and organization contributions require allowlisted, installed, globally active, tenant-enabled, and compatible state. Super Admin contributions require only allowlisted, installed, and globally active state. These states are independent. Theme activation normalizes settings, makes one assignment active, and invalidates theme/plugin caches.
Install validates definitions, requires core migrations, runs package migrations and declared seeders, synchronizes catalog, marks installed/active, and invalidates affected caches. Organization configuration is separate.
Ordering and validation
Slot priorities are 0..1000, default 100; lower first, slug breaks ties. Definition validation rejects duplicate organization/Super Admin IDs and routes, missing routes, settings collisions, middleware/scope mismatches, duplicate public page IDs/normalized patterns, and plugin routes without plugin.{slug}.. Enable validation also rejects CMS slug collisions. Runtime resolvers fail closed when global or tenant availability changes.
Organization navigation has two extension layers. PluginDashboardResolver::groupsForOrganization() groups each plugin's top-level management destinations into one sidebar submenu. PluginDashboardWidgetResolver renders ordered summary widgets from enabled plugins implementing ContributesDashboardWidgets.
Super Admin navigation mirrors that shape through PluginSuperAdminPageResolver::groupsForPlatform(), while PluginSuperAdminWidgetResolver renders global widgets on admin.dashboard.