Appearance
PeachGrove Admin (PeachStateTechnologies.Cms.Plugins.Admin)
This is the core plugin: it replaces Cofoundry's built-in admin panel with a custom Angular Dashboard SPA (branded "PeachGrove") and a rebuilt Visual Editor, and exposes the REST API both front-ends talk to.
What it adds to a site
- A REST API (
Api/*) covering Auth, Account, Users, Roles, Pages, PageDirectories, PageTemplates, CustomEntities, Images, Documents, Settings, Setup, Locales, and DynamicData — one folder per area underApi/. - The Dashboard SPA (see Admin.Web workspace), served at
/admin/*(or whereverCofoundry:Admin:DirectoryNamepoints). - A rebuilt Visual Editor toolbar and outline, injected into live front-end pages in place of Cofoundry's default AngularJS-based one.
- Extension points a consuming site can implement, e.g.
ICustomEntityFieldEditorfor custom admin field UIs on a custom entity (see the example site'sCalendarEventFieldsproject for a worked example).
Installing it
Reference the plugin project/package from your Cofoundry site and call AddCofoundry(...) as normal — auto-registration picks it up (see the developer docs overview for how that works). The example site's Program.cs shows the two extra things a consuming site needs beyond a stock Cofoundry setup:
csharp
// Required for the Dashboard SPA's static web assets to be discoverable in Development,
// since they're published from a referenced project rather than the site's own wwwroot.
builder.WebHost.UseStaticWebAssets();
builder.Services.AddControllersWithViews().AddCofoundry(builder.Configuration);
// Required for the Dashboard SPA to be able to call any POST/PUT/PATCH/DELETE Api endpoint.
builder.Services.AddAntiforgery(options =>
{
options.HeaderName = "X-XSRF-TOKEN";
});UseStaticWebAssets() is only needed in Development — in a published/deployed build the SPA's files are copied straight into the output directory. See AdminSpaFallbackRouteRegistration for the full explanation of how static asset serving and SPA deep-link routing are wired up (MapStaticAssets() for the built files, a URL-rewrite fallback for Angular's own routes so they don't lose to Cofoundry's catch-all page route).
No manual step is required to build the Angular apps — referencing the plugin's .csproj triggers an MSBuild target that runs npm ci/npm run build against the Admin.Web workspace the first time the SPA's dist/ output doesn't already exist, then copies it into the plugin's own wwwroot/admin. Delete PeachStateTechnologies.Cms.Plugins.Admin.Web/dist to force a rebuild after pulling front-end changes.
Page Template Preview, Source Viewing & Azure DevOps Editing
This is the newest feature area, added with no prior documentation — it lives under PageTemplates/ and is exposed via PageTemplatesApiController. For the end-user workflow, see the user docs.
Preview
PageTemplatePreviewRenderer and PreviewViewModelFactory (in PageTemplates/Preview/) render a template with either placeholder region content or a real, user-selected entity, without needing an actual Page/PageVersion to exist. This works by:
PreviewRenderScopemarking a render as a preview for the duration of the request.PreviewPageTemplateRegionTagBuilderFactory/PreviewCustomEntityTemplateRegionTagBuilderFactoryoverriding Cofoundry's normal region tag builders (registered globally inPeachStateAdminDependencyRegistration) to render placeholder markup instead of real page blocks wheneverPreviewRenderScope.IsActiveis true — safe to register permanently since they only change behavior during that scope.
Viewing source
GetPageTemplateSourceQuery/Handler (in PageTemplates/Source/) reads a template's raw .cshtml from disk (relative to the app's ~/Cofoundry/PageTemplates virtual root) and returns it as PageTemplateSourceDetails. The Dashboard renders it in a read-only Monaco editor with a Razor syntax grammar. Gated behind ViewPageTemplateSourcePermission (permission code PTSRCV).
Editing via Azure DevOps
Templates can optionally be edited and committed straight from the Dashboard if they're backed by an Azure DevOps git repo. This is off by default and only activates once fully configured — see AzureDevOpsTemplateSourceSettings.IsEnabled, bound from the Cofoundry:Plugins:AzureDevOpsTemplateSource configuration section:
jsonc
{
"Cofoundry": {
"Plugins": {
"AzureDevOpsTemplateSource": {
"TenantId": "<Entra ID tenant id>",
"ClientId": "<Entra ID app registration client id>",
// Exactly one auth method must be configured:
"ClientSecret": "<app registration client secret>",
// — or —
"CertificateThumbprint": "<thumbprint of a cert already in the OS cert store>",
"CertificateStoreLocation": "CurrentUser", // or LocalMachine
// — or, for hosts without an OS cert store (e.g. Linux containers) —
"CertificatePfxPath": "/path/to/cert.pfx",
"CertificatePfxPassword": "<pfx password>",
"Organization": "<Azure DevOps org name>",
"Project": "<Azure DevOps project name>",
"RepositoryId": "<repo id or name>",
"DefaultBranch": "main",
// Repo-relative folder that maps to this app's ~/Cofoundry/PageTemplates.
"RepositoryRelativeTemplateRootPath": "example/PeachStateTechnologies.Cms.Plugins.Admin.Example/Cofoundry/PageTemplates"
}
}
}
}In production, set these via environment variables/secrets rather than checking them into appsettings.json — prefer a certificate over a client secret per Microsoft's own guidance. Once enabled:
AzureDevOpsTokenProviderauthenticates app-only to Entra ID (viaMicrosoft.Identity.Client) to get a token for the Azure DevOps REST API.AzureDevOpsHttpClientStartupConfigurationTaskwires up the namedHttpClientused for those calls.IAzureDevOpsTemplateSourceService/AzureDevOpsTemplateSourceServicereads/writes template file content via the Azure DevOps Git Items/Pushes REST APIs.UpdatePageTemplateSourceCommand/Handlerhandles the "save & commit" flow, gated behindEditPageTemplateSourcePermission(permission codePTSRCE, distinct from view access since it pushes a real commit). Optimistic-concurrency conflicts (someone else committed to the file first) are detected and surfaced back to the Dashboard as a friendly error rather than silently overwriting.
Permissions
New permission types introduced by this plugin live under PageTemplates/Permissions/ (ViewPageTemplateSourcePermission / EditPageTemplateSourcePermission) and follow the standard Cofoundry IPermission pattern — assign them to roles the same way as any other Cofoundry permission.
Extending it
Dependency registrations are centralized in PeachStateAdminDependencyRegistration — a good starting point for seeing everything this plugin overrides or adds on top of stock Cofoundry (e.g. IVisualEditorStateService, region tag builder factories, page block type lookups for the Visual Editor's built-in block editors).