Skip to content

Repository files navigation

SilverStripe Content API

Content population layer for SilverStripe 5.2+, built on colymba/silverstripe-restfulapi (via dynamic/silverstripe-restfulapi, a maintained SS5-compatible fork — see Requirements below). The dependency provides token authentication and generic REST CRUD at /api; this module adds everything programmatic content population needs on top: atomic page compositions, batch operations with per-operation results, asset ingestion, stage-aware reads and publish actions, schema introspection, and field-level write guarding for the generic surface.

One token, one models map, two cooperating surfaces:

Surface Route Provides
colymba RESTfulAPI /api GET/POST/PUT/DELETE api/$Model(/$ID), api/auth/login|logout, token auth
this module /content-api/v1 stage-aware GET (_stage, ext:), publish/unpublish/archive actions, batch, compositions, assets, pages, schema, GET auth/session

Every endpoint maps 1:1 onto an MCP tool (schema/endpoints.json holds the tool definitions — see the companion silverstripe-content-api-mcp server). The module is the API-driven successor to Populate-YAML-fixture workflows — see docs/en/13_migrating-from-fixtures.md.

Requirements

This is the 1 branch (SilverStripe 5.2 line, default). Branch 2 targets SilverStripe 6 and requires colymba/silverstripe-restfulapi's feature/cms-6-compatibility branch instead.

Optional integrations (feature-gated at runtime; endpoints answer 501 FEATURE_UNAVAILABLE when absent): dnadesign/silverstripe-elemental (compositions), silverstripe/linkfield (link payloads), dynamic/silverstripe-essentials-tools ($palette()/$button() color tokens), dynamic/silverstripe-elemental-templates (apply-template).

Installation

The colymba dependency is consumed from dynamic/silverstripe-restfulapi's tagged 5.0.0 release (a package name unchanged from upstream, so it satisfies the same colymba/silverstripe-restfulapi constraint everywhere), so your project root composer.json must add the VCS entry (composer ignores a dependency's own repositories):

"repositories": [
    { "type": "vcs", "url": "https://github.com/dynamic/silverstripe-restfulapi" }
]
composer require colymba/silverstripe-restfulapi:^5.0
composer require dynamic/silverstripe-content-api

dynamic/silverstripe-restfulapi is Dynamic's maintained fork of silverstripeltd's feature/v5 branch, fixing 4 calls to methods removed in SilverStripe 4+ (Member::login()/logout(), DataObject::stat()). See docs/en/upstream-issues.md for background. ^5.0 resolves to a real tag, not a dev branch, so no minimum-stability workaround is needed for this dependency specifically.

Upgrading from an earlier version? See docs/en/00_installation.md for the 1.0.x and 1.1.x migration notes.

Quick start

  1. Expose classes — one map drives both surfaces (deny-by-default; a class must be mapped AND granted api_access):
Colymba\RESTfulAPI\QueryHandlers\DefaultQueryHandler:
  models:
    BlockPage: Dynamic\Base\Page\BlockPage
    ElementContent: DNADesign\Elemental\Models\ElementContent
    Image: SilverStripe\Assets\Image

# content-api-only refs (or overrides) go on the module's registry:
Dynamic\ContentApi\Registry\ClassRegistry:
  models:
    ElementalArea: DNADesign\Elemental\Models\ElementalArea

DNADesign\Elemental\Models\ElementContent:
  api_access: 'GET,POST,PUT'            # colymba HTTP verbs; the module maps them to
                                        # read/create/update (plus: delete, action)
  api_writable_fields: [Title, HTML, Sort, ShowTitle]
  extensions:
    - Dynamic\ContentApi\Write\WriteGuardExtension
  1. Apply extensions — the external-id extension to classes the API should upsert, and ContentApiGrantExtension to any class a service account needs to write without holding ADMIN:
SilverStripe\CMS\Model\SiteTree:
  extensions:
    - Dynamic\ContentApi\Identity\ExternalIdentifierExtension
    - Dynamic\ContentApi\Security\ContentApiGrantExtension
DNADesign\Elemental\Models\BaseElement:
  extensions:
    - Dynamic\ContentApi\Identity\ExternalIdentifierExtension
    - Dynamic\ContentApi\Security\ContentApiGrantExtension
SilverStripe\Assets\File:
  extensions: ['Dynamic\ContentApi\Identity\ExternalIdentifierExtension']
  1. Grant permissions and mint a token:
sake dev/tasks/MintContentApiToken email=agent@example.com
  1. Call it:
curl -H "X-Silverstripe-Apitoken: $TOKEN" https://site.test/content-api/v1/schema/site
curl -H "X-Silverstripe-Apitoken: $TOKEN" https://site.test/api/ElementContent

Full walkthrough with permission codes and next steps: docs/en/01_quickstart.md.

Security

SECURITY: never grant write verbs (POST,PUT) in api_access without WriteGuardExtension — colymba's write path natively applies every key in the payload with no writability check at all. See docs/en/04_security-model.md for the full class/record/field ACL model, write policies, and why the two write surfaces fail differently (revert-and-200 vs. reject-the-payload).

Branch policy

This repo carries two parallel lines: 1 (default, this branch, SilverStripe 5.2) and 2 (SilverStripe 6). 1 receives changes only via git merge origin/2 — never the reverse, and never a cherry-pick (which would leave no merge base and re-present the same commits as conflicts on the next sync). (This merge direction is itself scheduled to flip — see issue #106 — once branch 2 has its own doc-drift script; until then, 2 remains the source branch merges flow from.) The two branches are allowed to differ only in:

  • composer constraints (framework/cms/PHP versions, the colymba branch + patch)
  • task entry-point signatures (1's src/Tasks/*.php use SS5's legacy BuildTask::run($request); 2 uses SS6's execute(InputInterface, PolyOutput): int — each file carries a docblock noting the other branch's copy must be hand-ported. The branch-neutral business logic behind both lives in Tasks/Support/, kept behaviorally identical across branches — TaskResult/TaskStatus are byte-identical; ApiTokenMinter/ ServiceAccountProvisioner's class docblocks may legitimately differ in wording where they describe each branch's own adapter, but their code and returned messages must not)
  • requirement statements in this README and docs/en/00_installation.md
  • PHP-floor-dependent type declarations (e.g. Security/ContentApiGrantExtension.php's ?bool here vs true|null on branch 2 — standalone true as a return type needs PHP 8.2+, and this branch's floor is ^8.1)
  • SS6-only glue with no 1 equivalent (e.g. Tasks/Support/TaskResultRenderer.php, which wraps Symfony Console's PolyOutput/Command — not present on this branch at all, since its two adapters echo TaskResult::$lines directly instead)
  • one documented silverstripe/versioned behavioral divergence: branch 2's canDelete() is vetoed by an unmet canUnpublish() on an already-published record; this branch's silverstripe/versioned 2.4.x-dev has no such veto (see ContentApiGrantExtension's class docblock and docs/en/04_security-model.md#grant-extension)

Everything else — application logic, tests, docs content beyond the requirements blocks — should read identically on both branches after a sync. A CI check (.local-ci.json's doc-drift entry) greps this branch for SS6-only requirement text and the SS6 tasks: invocation namespace to catch regressions.

Documentation

Full reference lives in docs/en/:

Page Covers
Installation Requirements, install, upgrade notes
Quick start Expose a class, mint a token, first calls
Configuration reference Every config option, every class, with defaults
Authentication Token model, minting, hardened auth, colymba caveats
Security model Permission codes, ACL, write policies, trusted fields channel
Endpoint reference Every route, method, params
Write payloads fields/relations shape, polymorphic has_one, color tokens, links
Batch operations Ordered ops, atomic rollback, delete modes
Page compositions Atomic full-page writes
Assets Upload/read, conflict modes
Publishing & stages Draft/live, _stage, publish modes
Schema introspection GET schema family
Error codes The full machine-readable error reference
Migrating from fixtures Populate-YAML → content API
Architecture Request lifecycle, service map, the two write surfaces
Testing & contributing Test setup, running the suite, spec-sync
Upstream issues The colymba/silverstripe-restfulapi support workstream

schema/endpoints.json describes this module's endpoints as MCP-style tool definitions; GET /schema/$ClassRef supplies per-class payload contracts at runtime.

License

BSD-3-Clause. See LICENSE.md.

About

Token-authenticated REST content API: generic CRUD + batch page-composition population for SilverStripe 6

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages