FForm Platform
enzh-CN

Public content and CMS

Publish fast, indexable and bilingual public pages from editable CMS records rather than hard-coded site pages.

CMS module

FormPlatform CMS is a first-party extension package (`FormPlatform.Cms`), not a feature compiled into the Host. It adds immutable module migrations, CMS system forms, a server-rendered public-content pipeline, structured content blocks, navigation/taxonomy, media APIs, and a browser extension.

Public content

The public route is `/{culture}/{slug}` such as `/en/privacy-policy` or `/zh-CN/platform`. It renders HTML on the server before SPA fallback, so search engines receive the actual content, canonical URL, meta description, Open Graph metadata, `robots.txt`, and `sitemap.xml` without executing JavaScript.

Content Studio manages page title, slug, summary, status, publication dates, SEO title/description, page layout classes, translation key, page navigation tree, categories, tags, and ordered blocks. Matching translation keys connect published language variants; each language can keep its own slug, title, SEO, and body.

Available blocks are rich text, Markdown, callout, image, button, safe custom HTML, Hero, feature cards, and code example. CMS collects its Tailwind classes into the shared CSS-candidate index. The background style worker updates `/api/assets/forms.css`; requests do not compile Tailwind.

Media

New uploads use shared `IMediaService`, not a database BLOB or `wwwroot`. The Host supports local private storage and S3-compatible object storage. CMS retains folders, tags, alt text, descriptions, public state, and page-use protection in its own metadata tables. Historic CMS files still use a compatibility local-storage read fallback.

Media operations have separate role capabilities (`BrowseRoles`, `UploadRoles`, `EditRoles`, `DeleteRoles`, `ManageFolderRoles`) under `Cms:MediaPermissions`. `Administrator` always has all capabilities. Non-administrators additionally need Form Access Read access to `CMS_Media_Upload`.

For full storage, S3, credential, and role configuration see the module package files `MEDIA_STORAGE_AND_PERMISSIONS.md` and `MEDIA_STORAGE_AND_PERMISSIONS.zh-CN.md`.

Safety

Markdown is rendered through the restricted renderer. Custom HTML is server-sanitised: scripts, event attributes, embedded objects, unsafe URLs, and other active content are removed. Public pages may only use public media. An asset used by published content cannot be switched to private until its content references are removed or replaced.

Upgrade rules

Never edit an applied CMS migration. Add a new numbered migration when the schema changes. CMS seed content and system forms are created only when missing; later startup does not overwrite administrator edits.

Chinese edition: CMS.zh-CN.md.

---

Shared media service

`IMediaService` is the provider-neutral binary-media foundation for CMS, ordinary forms, survey attachments, commerce, documents, and third-party modules. It keeps bytes outside business records, records metadata in `app_media_asset`, and records protected usage in `app_media_reference`.

The default `IMediaStorage` writes private bytes below `Media:StorageRoot`. The built-in `s3` provider supports AWS S3 and S3-compatible services. Modules use a stable owner namespace such as `commerce` or `my-company.booking`; a module can only create references under its own owner. Use `ReplaceReferencesAsync` when saving a business record so deletion is refused while an asset is still used.

await using var stream = file.OpenReadStream();
var asset = await media.CreateAsync(stream, new MediaCreateRequest(
    Owner: "commerce", OriginalName: file.FileName,
    ContentType: file.ContentType, FileSize: file.Length,
    IsPublic: true, CreatedBy: userId), ct);

await media.ReplaceReferencesAsync("commerce", "product", productId,
    [new MediaReferenceInput(asset.Id, "primary-image")], ct);

Public assets may be served by the anonymous shared-media route. Private survey attachments remain protected by the deployment/response authorization chain even when their bytes are stored in S3. Existing historic CMS/survey data is read compatibly; switching a byte provider does not automatically move old files.

For FileUpload modes, form ACL rules, custom upload API contracts, and detailed Chinese guidance, see [SHARED_MEDIA_SERVICE.zh-CN.md](SHARED_MEDIA_SERVICE.zh-CN.md).