Skip to content

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 under Api/.
  • The Dashboard SPA (see Admin.Web workspace), served at /admin/* (or wherever Cofoundry:Admin:DirectoryName points).
  • 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. ICustomEntityFieldEditor for custom admin field UIs on a custom entity (see the example site's CalendarEventFields project 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:

  • PreviewRenderScope marking a render as a preview for the duration of the request.
  • PreviewPageTemplateRegionTagBuilderFactory / PreviewCustomEntityTemplateRegionTagBuilderFactory overriding Cofoundry's normal region tag builders (registered globally in PeachStateAdminDependencyRegistration) to render placeholder markup instead of real page blocks whenever PreviewRenderScope.IsActive is 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:

  • AzureDevOpsTokenProvider authenticates app-only to Entra ID (via Microsoft.Identity.Client) to get a token for the Azure DevOps REST API. AzureDevOpsHttpClientStartupConfigurationTask wires up the named HttpClient used for those calls.
  • IAzureDevOpsTemplateSourceService/AzureDevOpsTemplateSourceService reads/writes template file content via the Azure DevOps Git Items/Pushes REST APIs.
  • UpdatePageTemplateSourceCommand/Handler handles the "save & commit" flow, gated behind EditPageTemplateSourcePermission (permission code PTSRCE, 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).

PeachGrove CMS Documentation