Orchard Core Content Preview - Prompt Templates
Preview Unpublished Content
You are an Orchard Core expert. Generate configuration and content definitions for live preview of an editor's in-memory draft without publishing it.
Guidelines
- Enable the
OrchardCore.ContentPreview feature. Its module dependency is OrchardCore.Contents.
- The feature adds a preview button to content editors and requires the standard
PreviewContent permission.
- Attach
PreviewPart only when a decoupled frontend needs a custom URL that should render the draft. The core preview UI works without it.
- Set
PreviewPartSettings.Pattern to a Liquid path. The pattern is rendered with Model and ContentItem.
- Patterns should return a relative path such as
/articles/{{ ContentItem.ContentItemId }}. Do not include a host, scheme, or line breaks.
- Preview drafts are stored in distributed cache for five minutes with sliding expiration and are keyed by a preview token.
ContentPreviewFeature.Instance is placed in the current request features during a preview request. Drivers and handlers can inspect it to change preview-specific behavior.
- A configured
PreviewPart causes PreviewStartupFilter to re-execute the frontend pipeline with the preview path. This lets the real frontend route, theme, and scripts render the draft.
- All recipe JSON must be wrapped in the root
{ "steps": [...] } format.
- All C# classes must use the
sealed modifier, except View Models.
Enabling Content Preview
{
"steps": [
{
"name": "Feature",
"enable": [
"OrchardCore.ContentPreview"
],
"disable": []
}
]
}
Attaching PreviewPart with a Migration
PreviewPart has no persisted fields. Its type-part setting supplies the frontend path pattern.
using OrchardCore.ContentManagement.Metadata;
using OrchardCore.ContentPreview.Models;
using OrchardCore.Data.Migration;
namespace MyModule;
public sealed class Migrations : DataMigration
{
private readonly IContentDefinitionManager _contentDefinitionManager;
public Migrations(IContentDefinitionManager contentDefinitionManager)
{
_contentDefinitionManager = contentDefinitionManager;
}
public async Task<int> CreateAsync()
{
await _contentDefinitionManager.AlterTypeDefinitionAsync("LandingPage", type => type
.Draftable()
.Versionable()
.WithPart("TitlePart")
.WithPart(nameof(PreviewPart), part => part
.WithSettings(new PreviewPartSettings
{
Pattern = "/landing-pages/{{ ContentItem.ContentItemId }}",
})));
return 1;
}
}
PreviewPart Settings
| Setting |
Type |
Description |
Pattern |
string |
Liquid template that builds the frontend path for the draft. |
The handler populates PreviewAspect.PreviewUrl only when the pattern is non-empty. It strips line endings from the rendered result.
Content Definition Recipe
{
"steps": [
{
"name": "ContentDefinition",
"ContentTypes": [
{
"Name": "LandingPage",
"DisplayName": "Landing Page",
"Settings": {
"ContentTypeSettings": {
"Draftable": true,
"Versionable": true
}
},
"ContentTypePartDefinitionRecords": [
{
"PartName": "TitlePart",
"Name": "TitlePart"
},
{
"PartName": "PreviewPart",
"Name": "PreviewPart",
"Settings": {
"PreviewPartSettings": {
"Pattern": "/landing-pages/{{ ContentItem.ContentItemId }}"
}
}
}
]
}
]
}
]
}
How the Preview Request Works
- The editor posts form values to
PreviewController.Draft.
- The controller creates an in-memory
ContentItem, applies editor updates, validates it, and populates PreviewAspect.
- The draft and its frontend preview URL are saved to distributed cache under
contentpreview:<token>.
- The browser loads
PreviewController.Display with the token.
- If the type has a preview path,
PreviewStartupFilter reruns the request pipeline using that path. Otherwise the controller renders the detail shape through its MVC view.
Do not persist a preview draft from custom code. The feature deliberately keeps it transient and authorization-protected.
Detecting Preview Mode in a Driver
During both Draft and Display, the controller places ContentPreviewFeature.Instance in the current HttpContext.Features. A driver, handler, or underlying service that needs preview-specific behavior can inspect that feature from its IHttpContextAccessor request. Keep this logic narrowly scoped, and do not create a second preview cache or bypass the content manager session. If no alternate behavior is required, do not add preview-specific driver logic.
Frontend Considerations
- The configured route must load content through
IContentManager or the normal display pipeline so the IContentManagerSession can return the cached draft.
- Keep the configured preview URL within the current tenant and protected by the preview token flow. Do not expose it as a public draft route.
- Preview is authorized with
CommonPermissions.PreviewContent; a missing or expired cache entry returns 404.
- Content types without
PreviewPart still render their preview in the module's MVC display view with the detail display type.
Troubleshooting
| Symptom |
Check |
| Preview button is absent |
Enable OrchardCore.ContentPreview and verify the user can preview content. |
| Preview opens the fallback renderer |
Attach PreviewPart and configure a non-empty Pattern. |
| Frontend route shows published data |
Ensure the route resolves content through the normal request-scoped content manager session. |
| Preview expires unexpectedly |
The cache entry uses a five-minute sliding expiration; save or reload the preview to refresh it. |
1---2name: orchardcore-content-preview3description: Skill for configuring live draft preview in Orchard Core content editing. Covers ContentPreview, PreviewPart, PreviewPartSettings patterns, PreviewAspect URLs, the preview draft controller, and frontend preview pipeline handling. Use this skill when requests mention Orchard Core Content Preview, live preview, PreviewPart, draft rendering, preview URL patterns, or closely related Orchard Core implementation, setup, extension, or troubleshooting work. Strong matches include work with OrchardCore.ContentPreview, PreviewPart, PreviewPartSettings, PreviewAspect, ContentPreviewFeature, PreviewController, PreviewStartupFilter, and IContentItemDisplayManager. It also helps with preview migrations, recipes, Liquid patterns, and the code patterns captured in this skill.4license: Apache-2.05---67# Orchard Core Content Preview - Prompt Templates89## Preview Unpublished Content1011You are an Orchard Core expert. Generate configuration and content definitions for live preview of an editor's in-memory draft without publishing it.1213### Guidelines1415- Enable the `OrchardCore.ContentPreview` feature. Its module dependency is `OrchardCore.Contents`.16- The feature adds a preview button to content editors and requires the standard `PreviewContent` permission.17- Attach `PreviewPart` only when a decoupled frontend needs a custom URL that should render the draft. The core preview UI works without it.18- Set `PreviewPartSettings.Pattern` to a Liquid path. The pattern is rendered with `Model` and `ContentItem`.19- Patterns should return a relative path such as `/articles/{{ ContentItem.ContentItemId }}`. Do not include a host, scheme, or line breaks.20- Preview drafts are stored in distributed cache for five minutes with sliding expiration and are keyed by a preview token.21- `ContentPreviewFeature.Instance` is placed in the current request features during a preview request. Drivers and handlers can inspect it to change preview-specific behavior.22- A configured `PreviewPart` causes `PreviewStartupFilter` to re-execute the frontend pipeline with the preview path. This lets the real frontend route, theme, and scripts render the draft.23- All recipe JSON must be wrapped in the root `{ "steps": [...] }` format.24- All C# classes must use the `sealed` modifier, except View Models.2526### Enabling Content Preview2728```json29{30 "steps": [31 {32 "name": "Feature",33 "enable": [34 "OrchardCore.ContentPreview"35 ],36 "disable": []37 }38 ]39}40```4142### Attaching PreviewPart with a Migration4344`PreviewPart` has no persisted fields. Its type-part setting supplies the frontend path pattern.4546```csharp47using OrchardCore.ContentManagement.Metadata;48using OrchardCore.ContentPreview.Models;49using OrchardCore.Data.Migration;5051namespace MyModule;5253public sealed class Migrations : DataMigration54{55 private readonly IContentDefinitionManager _contentDefinitionManager;5657 public Migrations(IContentDefinitionManager contentDefinitionManager)58 {59 _contentDefinitionManager = contentDefinitionManager;60 }6162 public async Task<int> CreateAsync()63 {64 await _contentDefinitionManager.AlterTypeDefinitionAsync("LandingPage", type => type65 .Draftable()66 .Versionable()67 .WithPart("TitlePart")68 .WithPart(nameof(PreviewPart), part => part69 .WithSettings(new PreviewPartSettings70 {71 Pattern = "/landing-pages/{{ ContentItem.ContentItemId }}",72 })));7374 return 1;75 }76}77```7879### PreviewPart Settings8081| Setting | Type | Description |82|---|---|---|83| `Pattern` | `string` | Liquid template that builds the frontend path for the draft. |8485The handler populates `PreviewAspect.PreviewUrl` only when the pattern is non-empty. It strips line endings from the rendered result.8687### Content Definition Recipe8889```json90{91 "steps": [92 {93 "name": "ContentDefinition",94 "ContentTypes": [95 {96 "Name": "LandingPage",97 "DisplayName": "Landing Page",98 "Settings": {99 "ContentTypeSettings": {100 "Draftable": true,101 "Versionable": true102 }103 },104 "ContentTypePartDefinitionRecords": [105 {106 "PartName": "TitlePart",107 "Name": "TitlePart"108 },109 {110 "PartName": "PreviewPart",111 "Name": "PreviewPart",112 "Settings": {113 "PreviewPartSettings": {114 "Pattern": "/landing-pages/{{ ContentItem.ContentItemId }}"115 }116 }117 }118 ]119 }120 ]121 }122 ]123}124```125126### How the Preview Request Works1271281. The editor posts form values to `PreviewController.Draft`.1292. The controller creates an in-memory `ContentItem`, applies editor updates, validates it, and populates `PreviewAspect`.1303. The draft and its frontend preview URL are saved to distributed cache under `contentpreview:<token>`.1314. The browser loads `PreviewController.Display` with the token.1325. If the type has a preview path, `PreviewStartupFilter` reruns the request pipeline using that path. Otherwise the controller renders the detail shape through its MVC view.133134Do not persist a preview draft from custom code. The feature deliberately keeps it transient and authorization-protected.135136### Detecting Preview Mode in a Driver137138During both `Draft` and `Display`, the controller places `ContentPreviewFeature.Instance` in the current `HttpContext.Features`. A driver, handler, or underlying service that needs preview-specific behavior can inspect that feature from its `IHttpContextAccessor` request. Keep this logic narrowly scoped, and do not create a second preview cache or bypass the content manager session. If no alternate behavior is required, do not add preview-specific driver logic.139140### Frontend Considerations141142- The configured route must load content through `IContentManager` or the normal display pipeline so the `IContentManagerSession` can return the cached draft.143- Keep the configured preview URL within the current tenant and protected by the preview token flow. Do not expose it as a public draft route.144- Preview is authorized with `CommonPermissions.PreviewContent`; a missing or expired cache entry returns `404`.145- Content types without `PreviewPart` still render their preview in the module's MVC display view with the detail display type.146147### Troubleshooting148149| Symptom | Check |150|---|---|151| Preview button is absent | Enable `OrchardCore.ContentPreview` and verify the user can preview content. |152| Preview opens the fallback renderer | Attach `PreviewPart` and configure a non-empty `Pattern`. |153| Frontend route shows published data | Ensure the route resolves content through the normal request-scoped content manager session. |154| Preview expires unexpectedly | The cache entry uses a five-minute sliding expiration; save or reload the preview to refresh it. |