umbraco-dynamic-root
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUmbraco Dynamic Root
Umbraco 动态根
What is it?
什么是动态根?
Dynamic Roots allow content pickers to have a starting point (origin) that is determined dynamically rather than being a fixed node. This includes two extension types:
- Dynamic Root Origin: Defines where the picker starts (e.g., current page, site root, nearest ancestor of type)
- Dynamic Root Query Step: Defines navigation steps from the origin (e.g., find nearest ancestor, get children of type)
These enable flexible content picker configurations that adapt based on context.
动态根允许内容选择器拥有一个动态确定的起始点(源),而非固定节点。它包含两种扩展类型:
- 动态根源(Dynamic Root Origin):定义选择器的起始位置(例如当前页面、站点根目录、最近的指定类型祖先节点)
- 动态根查询步骤(Dynamic Root Query Step):定义从源开始的导航步骤(例如查找最近的祖先、获取指定类型的子节点)
这些功能让内容选择器的配置能够根据上下文灵活调整。
Documentation
文档
Always fetch the latest docs before implementing:
- Extension Types: https://docs.umbraco.com/umbraco-cms/customizing/extending-overview/extension-types
- Content Picker: https://docs.umbraco.com/umbraco-cms/fundamentals/backoffice/property-editors/built-in-umbraco-property-editors/content-picker
- Foundation: https://docs.umbraco.com/umbraco-cms/customizing/foundation
在实现前请务必获取最新文档:
Workflow
工作流程
- Fetch docs - Use WebFetch on the URLs above
- Ask questions - What starting point logic? What query steps needed?
- Generate files - Create manifest(s) based on latest docs
- Explain - Show what was created and how to test
- 获取文档 - 使用WebFetch获取上述URL的文档内容
- 明确需求 - 确定需要什么起始点逻辑?需要哪些查询步骤?
- 生成文件 - 根据最新文档创建清单文件(manifest)
- 解释说明 - 展示创建的内容以及测试方法
Minimal Examples
最小示例
Dynamic Root Origin Manifest
动态根源清单
typescript
import type { ManifestDynamicRootOrigin } from '@umbraco-cms/backoffice/extension-registry';
const manifest: ManifestDynamicRootOrigin = {
type: 'dynamicRootOrigin',
alias: 'My.DynamicRootOrigin.SiteRoot',
name: 'Site Root Origin',
meta: {
originAlias: 'SiteRoot',
label: 'Site Root',
description: 'Start from the root of the current site',
icon: 'icon-home',
},
};
export const manifests = [manifest];typescript
import type { ManifestDynamicRootOrigin } from '@umbraco-cms/backoffice/extension-registry';
const manifest: ManifestDynamicRootOrigin = {
type: 'dynamicRootOrigin',
alias: 'My.DynamicRootOrigin.SiteRoot',
name: 'Site Root Origin',
meta: {
originAlias: 'SiteRoot',
label: 'Site Root',
description: 'Start from the root of the current site',
icon: 'icon-home',
},
};
export const manifests = [manifest];Dynamic Root Query Step Manifest
动态根查询步骤清单
typescript
import type { ManifestDynamicRootQueryStep } from '@umbraco-cms/backoffice/extension-registry';
const manifest: ManifestDynamicRootQueryStep = {
type: 'dynamicRootQueryStep',
alias: 'My.DynamicRootQueryStep.NearestBlog',
name: 'Nearest Blog Query Step',
meta: {
queryStepAlias: 'NearestBlog',
label: 'Nearest Blog',
description: 'Find the nearest blog ancestor',
icon: 'icon-article',
},
};
export const manifests = [manifest];typescript
import type { ManifestDynamicRootQueryStep } from '@umbraco-cms/backoffice/extension-registry';
const manifest: ManifestDynamicRootQueryStep = {
type: 'dynamicRootQueryStep',
alias: 'My.DynamicRootQueryStep.NearestBlog',
name: 'Nearest Blog Query Step',
meta: {
queryStepAlias: 'NearestBlog',
label: 'Nearest Blog',
description: 'Find the nearest blog ancestor',
icon: 'icon-article',
},
};
export const manifests = [manifest];Multiple Origins and Steps
多源与多查询步骤
typescript
import type { ManifestDynamicRootOrigin, ManifestDynamicRootQueryStep } from '@umbraco-cms/backoffice/extension-registry';
const originManifests: ManifestDynamicRootOrigin[] = [
{
type: 'dynamicRootOrigin',
alias: 'My.DynamicRootOrigin.CurrentPage',
name: 'Current Page Origin',
meta: {
originAlias: 'CurrentPage',
label: 'Current Page',
description: 'Start from the page being edited',
icon: 'icon-document',
},
},
{
type: 'dynamicRootOrigin',
alias: 'My.DynamicRootOrigin.Parent',
name: 'Parent Page Origin',
meta: {
originAlias: 'Parent',
label: 'Parent Page',
description: 'Start from the parent of current page',
icon: 'icon-arrow-up',
},
},
];
const queryStepManifests: ManifestDynamicRootQueryStep[] = [
{
type: 'dynamicRootQueryStep',
alias: 'My.DynamicRootQueryStep.Children',
name: 'Children Query Step',
meta: {
queryStepAlias: 'Children',
label: 'Children',
description: 'Get all child pages',
icon: 'icon-tree',
},
},
{
type: 'dynamicRootQueryStep',
alias: 'My.DynamicRootQueryStep.Ancestors',
name: 'Ancestors Query Step',
meta: {
queryStepAlias: 'Ancestors',
label: 'Ancestors',
description: 'Get ancestor pages',
icon: 'icon-navigation-up',
},
},
];
export const manifests = [...originManifests, ...queryStepManifests];typescript
import type { ManifestDynamicRootOrigin, ManifestDynamicRootQueryStep } from '@umbraco-cms/backoffice/extension-registry';
const originManifests: ManifestDynamicRootOrigin[] = [
{
type: 'dynamicRootOrigin',
alias: 'My.DynamicRootOrigin.CurrentPage',
name: 'Current Page Origin',
meta: {
originAlias: 'CurrentPage',
label: 'Current Page',
description: 'Start from the page being edited',
icon: 'icon-document',
},
},
{
type: 'dynamicRootOrigin',
alias: 'My.DynamicRootOrigin.Parent',
name: 'Parent Page Origin',
meta: {
originAlias: 'Parent',
label: 'Parent Page',
description: 'Start from the parent of current page',
icon: 'icon-arrow-up',
},
},
];
const queryStepManifests: ManifestDynamicRootQueryStep[] = [
{
type: 'dynamicRootQueryStep',
alias: 'My.DynamicRootQueryStep.Children',
name: 'Children Query Step',
meta: {
queryStepAlias: 'Children',
label: 'Children',
description: 'Get all child pages',
icon: 'icon-tree',
},
},
{
type: 'dynamicRootQueryStep',
alias: 'My.DynamicRootQueryStep.Ancestors',
name: 'Ancestors Query Step',
meta: {
queryStepAlias: 'Ancestors',
label: 'Ancestors',
description: 'Get ancestor pages',
icon: 'icon-navigation-up',
},
},
];
export const manifests = [...originManifests, ...queryStepManifests];Backend Implementation Required
需要后端C#实现
Dynamic roots require backend C# implementation to handle the actual query logic:
csharp
// Example C# implementation for a custom origin
public class SiteRootDynamicRootOrigin : IDynamicRootOrigin
{
public string OriginAlias => "SiteRoot";
public Task<Guid?> GetOriginAsync(Guid contentKey, string? entityType)
{
// Return the site root GUID based on the content's position
// Implementation depends on your site structure
return Task.FromResult<Guid?>(GetSiteRootForContent(contentKey));
}
}
// Example C# implementation for a custom query step
public class NearestBlogQueryStep : IDynamicRootQueryStep
{
public string QueryStepAlias => "NearestBlog";
public Task<IEnumerable<Guid>> ExecuteAsync(Guid originKey, string? entityType)
{
// Find nearest blog ancestor from the origin
// Return matching content GUIDs
return Task.FromResult(FindNearestBlogAncestors(originKey));
}
}
// Register in Composer
public class DynamicRootComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.DynamicRootSteps()
.AddOrigin<SiteRootDynamicRootOrigin>()
.AddQueryStep<NearestBlogQueryStep>();
}
}动态根需要后端C#代码来处理实际的查询逻辑:
csharp
// Example C# implementation for a custom origin
public class SiteRootDynamicRootOrigin : IDynamicRootOrigin
{
public string OriginAlias => "SiteRoot";
public Task<Guid?> GetOriginAsync(Guid contentKey, string? entityType)
{
// Return the site root GUID based on the content's position
// Implementation depends on your site structure
return Task.FromResult<Guid?>(GetSiteRootForContent(contentKey));
}
}
// Example C# implementation for a custom query step
public class NearestBlogQueryStep : IDynamicRootQueryStep
{
public string QueryStepAlias => "NearestBlog";
public Task<IEnumerable<Guid>> ExecuteAsync(Guid originKey, string? entityType)
{
// Find nearest blog ancestor from the origin
// Return matching content GUIDs
return Task.FromResult(FindNearestBlogAncestors(originKey));
}
}
// Register in Composer
public class DynamicRootComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.DynamicRootSteps()
.AddOrigin<SiteRootDynamicRootOrigin>()
.AddQueryStep<NearestBlogQueryStep>();
}
}Origin Meta Properties
动态根源元属性
| Property | Description |
|---|---|
| Unique identifier matching backend implementation |
| Display name in picker configuration |
| Help text explaining the origin |
| Icon shown in configuration UI |
| 属性 | 说明 |
|---|---|
| 与后端实现匹配的唯一标识符 |
| 选择器配置界面中的显示名称 |
| 解释该源的帮助文本 |
| 配置界面中显示的图标 |
Query Step Meta Properties
动态根查询步骤元属性
| Property | Description |
|---|---|
| Unique identifier matching backend implementation |
| Display name in picker configuration |
| Help text explaining the query step |
| Icon shown in configuration UI |
That's it! Always fetch fresh docs, keep examples minimal, generate complete working code.
| 属性 | 说明 |
|---|---|
| 与后端实现匹配的唯一标识符 |
| 选择器配置界面中的显示名称 |
| 解释该查询步骤的帮助文本 |
| 配置界面中显示的图标 |
就是这样!请始终获取最新文档,保持示例简洁,生成完整可运行的代码。