Package:
Umbraco.Community.QuickBlocksVersion: 1.0.0 Target Framework: .NET 6.0 Author: Paul Seal (Umbraco MVP) License: MIT Umbraco Compatibility: v10.4.0+
- What Is QuickBlocks?
- How It Works — High Level
- Architecture Overview
- Data Attributes Reference
- Processing Pipeline (End-to-End)
- Service Layer
- Data Type Mapping System
- Domain Models
- API Controller
- Dashboard UI
- Dependency Injection & Composition
- Folder Structure Created in Umbraco
- Generated Output
- Extension Points
- Configuration
- Build Pipeline
- File Reference Map
QuickBlocks is an Umbraco back-office package that scaffolds an entire block-list-based content architecture from a single annotated HTML file. Instead of manually creating dozens of content types, data types, block list configurations, and Razor partial views, a developer annotates their HTML prototype with data-* attributes and submits it to QuickBlocks. Within seconds, the package:
- Creates element content types for each block/row
- Creates settings content types for block-level visibility control
- Creates block list data types with full configuration
- Creates partial view
.cshtmlfiles with Razor rendering logic - Creates page content types with the correct block list properties
- Creates master and content templates
- Organises everything into a logical folder structure in the Umbraco back-office
The result is a fully wired-up Umbraco block list setup ready for editors to use, with no manual configuration required.
flowchart TD
A[Developer writes HTML prototype] --> B[Annotate with data-* attributes]
B --> C[Open QuickBlocks dashboard in Umbraco Settings]
C --> D{Paste HTML or provide URL}
D --> E[Click Report - read-only preview]
D --> F[Click Submit - creates everything]
E --> G[Review JSON structure report]
F --> H[Content types created]
F --> I[Data types / block lists created]
F --> J[Partial views created]
F --> K[Templates created]
H & I & J & K --> L[Site ready for editing]
classDiagram
class QuickBlocksApiController {
+Build(QuickBlocksInstruction) ActionResult~ContentTypeModel~
}
class IBlockParsingService {
+GetContentType(HtmlNode) ContentTypeModel
+GetLists(string, bool, string) List~BlockListModel~
+GetRows(string, bool) List~RowModel~
+GetBlocks(string, string) List~BlockItemModel~
+GetProperties(string) List~PropertyModel~
+GetPartialViews(HtmlNode) List~PartialViewModel~
}
class IBlockCreationService {
+CreateFolderStructure() FolderStructure
+CreateSupportingDataTypes() int
+CreateSupportingContentTypes(int)
+CreateList(BlockListModel, FolderStructure, int)
+CreateBlockConfigurations(BlockListModel, FolderStructure) List
+CreateBlockConfiguration(RowModel, FolderStructure, BlockListModel)
+CreateBlockListDataType(BlockListModel, List, int)
+CreateContentType(string, string, ...) IContentType
+AddPropertiesToContentType(IContentType, IEnumerable, string)
+CreateRowPartial(RowModel) bool
+CreatePartialViews(List~PartialViewModel~)
+RemoveAllQuickBlocksAttributes(HtmlDocument)
+ReplaceAllPartialAttributesWithCalls(HtmlDocument)
+RenderProperties(HtmlNodeCollection, string)
+RenderListPropertyCalls(HtmlNodeCollection, string)
}
class IDataTypeNameResolver {
+GetDataTypeName(string) string
}
class DataTypeMappersCollection {
+IEnumerable~IDataTypeMapper~ Items
}
class IDataTypeMapper {
<<interface>>
+HtmlElements IEnumerable~string~
+DataTypeName string
}
class ImgDataTypeMapper
class HeadersDataTypeMapper
class ParagraphDataTypeMapper
class AnchorDataTypeMapper
QuickBlocksApiController --> IBlockParsingService
QuickBlocksApiController --> IBlockCreationService
IBlockParsingService --> IDataTypeNameResolver
IDataTypeNameResolver --> DataTypeMappersCollection
DataTypeMappersCollection --> IDataTypeMapper
IDataTypeMapper <|.. ImgDataTypeMapper
IDataTypeMapper <|.. HeadersDataTypeMapper
IDataTypeMapper <|.. ParagraphDataTypeMapper
IDataTypeMapper <|.. AnchorDataTypeMapper
QuickBlocks reads a standard HTML document with special data-* attributes. Below is the complete specification.
| Attribute | Purpose | Example |
|---|---|---|
data-content-type-name |
Defines the page content type name | "Home Page" |
| Attribute | Purpose | Default |
|---|---|---|
data-list-name |
Names the top-level block list | required |
data-sub-list-name |
Names a nested block list inside a row | required (nested) |
data-list-maxwidth |
Max width of the property editor | "" |
data-list-single |
Single block mode | false |
data-list-live |
Live editing mode | false |
data-list-inline |
Inline editing as default | false |
data-list-min |
Minimum number of blocks | 0 |
data-list-max |
Maximum number of blocks | 0 (unlimited) |
data-use-community-preview |
Enable community block preview | false |
data-preview-css |
Path to preview CSS file | "" |
data-preview-view |
Path to preview HTML/view | "" |
| Attribute | Purpose | Default |
|---|---|---|
data-row-name |
Names a top-level block type | required |
data-item-name |
Names a nested block type inside a sub-list | required (nested) |
data-settings-name |
Name of the settings content type | auto-generated |
data-has-settings |
Whether to create a settings type | true |
data-icon-class |
Icon CSS class for the block in the editor | "icon-science" |
data-icon-colour |
Icon colour class | "" |
data-label-property |
Property alias used as editor label | "Title" |
data-ignore-convention |
Skip appending "Row" suffix to alias | false |
| Attribute | Purpose | Notes |
|---|---|---|
data-prop-name |
Names the property | required |
data-prop-type |
Overrides the resolved data type name | optional; auto-resolved if omitted |
data-multiple |
Marks a link as multi-URL picker | for <a> elements |
data-replace-marker |
Marker string used in multiple URL iteration | e.g., [!icon!] |
data-replace-inner |
Replace inner HTML of each link | boolean |
data-replace-attribute |
Attribute on the link element to replace | e.g., class |
| Attribute | Purpose |
|---|---|
data-partial-name |
Marks an element as a standalone partial view to extract |
<!-- Page content type definition -->
<div data-content-type-name="Home Page">
<!-- Top-level block list -->
<div data-list-name="Main Content"
data-list-maxwidth="100%"
data-list-min="0"
data-list-max="0"
data-list-live="false">
<!-- Block type: Hero -->
<section data-row-name="Hero"
data-settings-name="Hero Settings"
data-has-settings="true"
data-icon-class="icon-landscape"
data-label-property="Title">
<h1 data-prop-name="Title">Hero Heading</h1>
<p data-prop-name="Body Text">Some description here</p>
<img data-prop-name="Background Image" src="#" />
<a data-prop-name="Call To Action" href="#">Learn More</a>
</section>
<!-- Block type: Services (with nested list) -->
<section data-row-name="Services"
data-icon-class="icon-settings"
data-label-property="Title">
<h2 data-prop-name="Title">Our Services</h2>
<!-- Nested sub-list -->
<div data-sub-list-name="Service Items"
data-prop-name="Services"
data-list-inline="true">
<div data-item-name="Service Item">
<h4 data-prop-name="Title">Service Name</h4>
<p data-prop-name="Description">Details</p>
<img data-prop-name="Icon" src="#" />
</div>
</div>
</section>
</div>
<!-- Standalone partial (e.g. footer) -->
<footer data-partial-name="Footer">
<p>© 2024 My Site</p>
</footer>
</div>flowchart TD
A[POST /umbraco/backoffice/api/quickblocksapi/build/] --> B[QuickBlocksApiController.Build]
B --> C{Input valid?}
C -- No --> ERR[400 Bad Request]
C -- Yes --> D[Load HTML via HtmlAgilityPack]
D --> E{ReadOnly mode?}
E -- Yes --> PARSE
E -- No --> F[CreateFolderStructure]
F --> G[CreateSupportingDataTypes\nSingle Url Picker]
G --> H[CreateSupportingContentTypes\nBlock Visibility Settings]
H --> PARSE
PARSE[BlockParsingService] --> I[GetContentType\ndata-content-type-name]
I --> J[GetLists — top-level\ndata-list-name]
J --> K[foreach list: GetRows\ndata-row-name]
K --> L[foreach row: GetProperties\ndata-prop-name]
L --> M{Has nested sub-list?\ndata-sub-list-name}
M -- Yes --> N[GetLists — nested\ndata-sub-list-name]
N --> O[foreach sub-list: GetRows\ndata-item-name]
O --> P[GetProperties for sub-rows]
M -- No --> Q
P --> Q{ReadOnly?}
Q -- Yes --> REPORT[Build ContentTypeModel\nreturn as JSON report]
Q -- No --> CREATE
CREATE[BlockCreationService] --> R[CreateList for each top-level list]
R --> R1[CreateRowPartial — cshtml per row]
R --> R2[CreateBlockConfigurations]
R2 --> R3[CreateBlockConfiguration per row\ncreates element + settings types]
R3 --> R4[CreateBlockListDataType\nUmbraco BlockList editor]
R4 --> S[GetPartialViews — data-partial-name]
S --> T[CreatePartialViews]
T --> U[ReplaceAllPartialAttributesWithCalls]
U --> V[RemoveAllQuickBlocksAttributes]
V --> W[RenderProperties — convert to Razor]
W --> X[RenderListPropertyCalls]
X --> Y[CreateContentType — page type]
Y --> Z[AddPropertiesToContentType]
Z --> AA[Create Master Template]
AA --> AB[Create Content Template]
AB --> REPORT
File: src/QuickBlocks/Services/BlockParsingService.cs
Responsible for reading the HTML document and extracting all structural information into domain models. It never writes to Umbraco — it only reads.
Searches for a single node with data-content-type-name. Returns a ContentTypeModel with the name, alias, and any direct data-prop-name children.
XPath query: //*[@data-list-name] (top-level) or //*[@data-sub-list-name] (nested)
For each matching node:
- Reads all
data-list-*attributes intoBlockListModel - Calls
GetRows()to populate.Rows
XPath query: //*[@data-row-name] or //*[@data-item-name]
For each matching node:
- Creates a
RowModelfromdata-row-name/data-item-name - Reads all row configuration attributes
- Calls
GetProperties()for the row's direct properties - Calls
GetLists(isNestedList: true)to find any sub-lists within the row
XPath query: //*[@data-prop-name]
For each matching node:
- Reads
data-prop-nameas the property name - If
data-prop-typeis present, uses that; otherwise callsIDataTypeNameResolver.GetDataTypeName(tagName)to resolve from the HTML element type - Returns a
PropertyModel
Finds block items (data-item-name) within a given row context.
XPath query: //*[@data-partial-name]
Returns a list of PartialViewModel — each containing the partial name and its outer HTML.
File: src/QuickBlocks/Services/BlockCreationService.cs
The heart of the package. Translates parsed models into real Umbraco entities.
flowchart LR
ROOT[Content Types Root] --> COMP[Components]
ROOT --> COMPS[Compositions]
COMPS --> CCB[Content Blocks]
CCB --> CCM[Content Models]
CCB --> CSM[Settings Models]
ROOT --> ELEM[Elements]
ELEM --> ECB[Content Blocks]
ECB --> ECM[Content Models]
ECB --> ESM[Settings Models]
ROOT --> FOLD[Folders]
ROOT --> PAGES[Pages]
All IDs are stored in a FolderStructure object and passed through the pipeline so content types are organised automatically.
- Checks if a data type with this name already exists — skips if so
- For each row in the list, calls
CreateRowPartial() - Calls
CreateBlockConfigurations()to build the block configuration array - Calls
CreateBlockListDataType()to persist the data type
flowchart TD
A[RowModel] --> B[CreateContentType\nelement type for row content]
A --> C{HasSettings?}
C -- Yes --> D[CreateContentType\nelement type for settings]
C -- No --> E[Use Block Visibility Settings]
B --> F[AddPropertiesToContentType\nrow properties]
D --> G[Add Hide property\nfrom Block Visibility Settings]
B & D --> H[Build BlockListConfiguration.BlockConfiguration]
H --> I[Set label template\ne.g. Title or fallback to row name]
I --> J[Apply previewView and previewCss\nfrom row or parent list]
Creates a Razor partial view at:
Views/Partials/blocklist/Components/{alias}.cshtml
The generated view:
- Casts
Modelto the correct element type - Wraps output in a null-check
- Calls
RenderProperties()to convertdata-prop-nameelements to Razor expressions - Calls
RenderListPropertyCalls()to convert nested block lists to@Html.GetBlockListHtml() - Removes all remaining
data-*attributes
Converts HTML property nodes to Razor output:
| Element | Generated Razor |
|---|---|
h1–h6, span |
@Model.Value("alias") as inner text |
p |
@Html.Raw(Model.Value("alias")) (rich text) |
img |
src="@Url.GetCropUrl(Model.Value<IPublishedContent>("alias"), 800, 600)" |
a (single) |
href="@Model.Value<Link>("alias")?.Url" and target attribute |
a (multi, data-multiple) |
foreach loop over IEnumerable<Link> with marker replacement |
| other | Attribute and inner text replacement with custom markers |
Creates an Umbraco data type using the Umbraco.BlockList property editor. The configuration object (BlockListConfiguration) is populated with:
- The array of
BlockConfigurationitems (content + settings key pairs) UseSingleBlockMode,UseLiveEditing,UseInlineEditingAsDefaultValidationLimit.Min/ValidationLimit.Max
File: src/QuickBlocks/Services/Resolvers/DataTypeNameResolver.cs
Iterates DataTypeMappersCollection (last registered wins) to find the mapper whose HtmlElements list includes the queried element name. Falls back to QuickBlocksDefaultOptions.DefaultDataTypeName ("Textstring") if no mapper matches.
flowchart LR
A[HTML element name\ne.g. img] --> B[DataTypeMappersCollection]
B --> C{Match found?}
C -- Yes --> D[Return mapper.DataTypeName\ne.g. Image Media Picker]
C -- No --> E[Return DefaultDataTypeName\nTextstring]
QuickBlocks ships with four built-in mappers:
| Mapper Class | HTML Elements | Umbraco Data Type |
|---|---|---|
ImgDataTypeMapper |
img |
Image Media Picker |
HeadersDataTypeMapper |
h1–h6 |
Textstring |
ParagraphDataTypeMapper |
p |
Richtext editor |
AnchorDataTypeMapper |
a |
Single Url Picker |
| (default fallback) | all others | Textstring |
Implement IDataTypeMapper and register it in your own IComposer:
public class MyDataTypeMapper : IDataTypeMapper
{
public IEnumerable<string> HtmlElements => new[] { "textarea" };
public string DataTypeName => "Textarea";
}
public class MyComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.QuickBlockDataTypeMappers().Append<MyDataTypeMapper>();
}
}Mappers use "last registered wins" ordering, so your custom mapper will override a built-in one if it targets the same element.
classDiagram
class QuickBlocksInstruction {
+string Url
+string HtmlBody
+bool ReadOnly
}
class ContentTypeModel {
+string Name
+string Alias
+string ConventionName
+IEnumerable~PropertyModel~ Properties
+IEnumerable~BlockListModel~ Lists
+string Html
+string Message
}
class BlockListModel {
+string Name
+IEnumerable~RowModel~ Rows
+string MaxPropertyWidth
+bool UseSingleBlockMode
+bool UseLiveEditing
+bool UseInlineEditingAsDefault
+int ValidationLimitMin
+int ValidationLimitMax
+string Html
+string PreviewView
+string PreviewCss
}
class RowModel {
+string Name
+string Alias
+string SettingsName
+string SettingsAlias
+IEnumerable~PropertyModel~ Properties
+bool IgnoreNamingConvention
+bool HasSettings
+string Html
+string IconClass
+List~BlockListModel~ SubLists
+string LabelProperty
+string PreviewView
+string PreviewCss
}
class PropertyModel {
+string Name
+string PropertyType
+string Html
+string Value
}
class BlockItemModel {
+string Name
+string Alias
+string ConventionName
+IEnumerable~PropertyModel~ Properties
+string Html
+string IconClass
}
class PartialViewModel {
+string Name
+string Html
}
class FolderStructure {
+int ComponentsId
+int CompositionsId
+int CompositionsContentBlocksId
+int CompositionsContentModelsId
+int CompositionsSettingsModelsId
+int ElementsId
+int ElementsContentBlocksId
+int ElementsContentModelsId
+int ElementsSettingsModelsId
+int FoldersId
+int PagesId
}
ContentTypeModel "1" --> "*" BlockListModel
ContentTypeModel "1" --> "*" PropertyModel
BlockListModel "1" --> "*" RowModel
RowModel "1" --> "*" PropertyModel
RowModel "1" --> "*" BlockListModel : SubLists
File: src/QuickBlocks/Controllers/QuickBlocksUmbracoApiController.cs
Route: /umbraco/backoffice/api/quickblocksapi/build/
Auth: Umbraco back-office authentication required (UmbracoAuthorizedApiController)
sequenceDiagram
participant UI as Dashboard JS
participant API as QuickBlocksApiController
participant Parse as BlockParsingService
participant Create as BlockCreationService
participant Umbraco as Umbraco Services
UI->>API: POST Build { HtmlBody, ReadOnly }
API->>API: Load HTML via HtmlAgilityPack
alt Not ReadOnly
API->>Create: CreateFolderStructure()
Create->>Umbraco: IContentTypeService.Save() x7 folders
API->>Create: CreateSupportingDataTypes()
Create->>Umbraco: IDataTypeService.Save() Single Url Picker
API->>Create: CreateSupportingContentTypes()
Create->>Umbraco: IContentTypeService.Save() Block Visibility Settings
end
API->>Parse: GetContentType(htmlNode)
API->>Parse: GetLists(html, isNested=false)
Parse-->>API: List~BlockListModel~
loop Each BlockList
API->>Parse: GetRows(html, isNested=false)
Parse-->>API: List~RowModel~
loop Each Row
API->>Parse: GetProperties(html)
API->>Parse: GetLists(html, isNested=true)
end
alt Not ReadOnly
API->>Create: CreateList(list, folderStructure, parentId)
Create->>Create: CreateRowPartial() per row
Create->>Create: CreateBlockConfigurations()
Create->>Umbraco: IContentTypeService.Save() element types
Create->>Umbraco: IDataTypeService.Save() block list data type
end
end
alt Not ReadOnly
API->>Parse: GetPartialViews(htmlNode)
API->>Create: CreatePartialViews()
API->>Create: ReplaceAllPartialAttributesWithCalls()
API->>Create: RemoveAllQuickBlocksAttributes()
API->>Create: RenderProperties()
API->>Create: RenderListPropertyCalls()
API->>Create: CreateContentType() page type
API->>Umbraco: IFileService.SaveTemplate() master + content
Create->>Umbraco: IFileService.SavePartialView() per row
end
API-->>UI: ContentTypeModel JSON
{
"name": "Home Page",
"alias": "homePage",
"conventionName": "HomePagePage",
"properties": [...],
"lists": [
{
"name": "Main Content",
"rows": [
{
"name": "Hero Row",
"alias": "heroRow",
"properties": [
{ "name": "Title", "propertyType": "Textstring" },
{ "name": "Body Text", "propertyType": "Richtext editor" },
{ "name": "Background Image", "propertyType": "Image Media Picker" }
],
"subLists": []
}
]
}
],
"message": "Content type created successfully"
}Location: src/QuickBlocks/App_Plugins/QuickBlocks/
The dashboard is an AngularJS (Umbraco back-office) view registered in the Settings section with weight -10 (appears early in the list). Access is granted to admin and denied to translator.
flowchart TD
DASH[QuickBlocks Dashboard\nSettings Section] --> TAB1[Tab: HTML Snippet]
DASH --> TAB2[Tab: Fetch URL]
DASH --> TAB3[Tab: Report]
TAB1 --> ACE[ACE Editor\nHTML input]
TAB2 --> URLINPUT[URL text input]
ACE --> BTN1[Submit button\ncreates everything]
ACE --> BTN2[Report button\nread-only preview]
URLINPUT --> BTN1
URLINPUT --> BTN2
BTN1 --> API[POST to QuickBlocksApi/Build\nReadOnly: false]
BTN2 --> API2[POST to QuickBlocksApi/Build\nReadOnly: true]
API --> NOTIFY[Umbraco notification\nsuccess/error]
API --> TAB3
API2 --> TAB3
TAB3 --> JSON[JSON report display\nshows all created entities]
// Submit — creates all Umbraco entities
$scope.submit = function() {
$scope.state = 'busy';
quickBlocksResource.build({
HtmlBody: $scope.model.htmlSnippet, // or
Url: $scope.model.url,
ReadOnly: false
}).then(function(response) {
$scope.state = 'success';
$scope.report = response.data;
// switches to Report tab
});
};
// Report — read-only analysis
$scope.report = function() {
quickBlocksResource.build({
HtmlBody: $scope.model.htmlSnippet,
ReadOnly: true
}).then(function(response) {
$scope.report = response.data;
});
};flowchart TD
QB[QuickBlocksComposer] --> MF[ManifestFilter\nQuickBlocksManifestFilter]
QB --> DM1[ImgDataTypeMapper]
QB --> DM2[HeadersDataTypeMapper]
QB --> DM3[ParagraphDataTypeMapper]
QB --> DM4[AnchorDataTypeMapper]
RS[RegisterServicesComposer] --> S1[IBlockCreationService\n→ BlockCreationService\ntransient]
RS --> S2[IBlockParsingService\n→ BlockParsingService\ntransient]
RS --> S3[IDataTypeNameResolver\n→ DataTypeNameResolver\ntransient]
NH[NotificationHandlersComposer] --> N1[ServerVariablesParsingNotificationHandler\nExposes API URL to JS]
The three composers (QuickBlocksComposer, RegisterServicesComposer, NotificationHandlersComposer) are all discovered automatically by Umbraco's composition system via the IComposer interface — no explicit registration in Startup.cs is needed.
ServerVariablesParsingNotificationHandler adds the build endpoint URL to Umbraco's server variables, making it available in JavaScript as:
Umbraco.Sys.ServerVariables.QuickBlocks.QuickBlocksApiWhen QuickBlocks runs (non-read-only), it creates the following content type folder hierarchy:
Content Types (root)
├── Components/
├── Compositions/
│ └── Content Blocks/
│ ├── Content Models/
│ └── Settings Models/
├── Elements/
│ └── Content Blocks/
│ ├── Content Models/ ← element types for block content go here
│ └── Settings Models/ ← element types for block settings go here
├── Folders/
└── Pages/ ← page content types go here
For a block named "Hero", QuickBlocks generates:
- Placed in
Elements/Content Blocks/Content Models/ - Properties:
title(Textstring),bodyText(Richtext editor),backgroundImage(Image Media Picker),callToAction(Single Url Picker) - Is element type:
true
- Placed in
Elements/Content Blocks/Settings Models/ - Properties:
hide(checkbox) — inherited from Block Visibility Settings composition - Is element type:
true
- Editor:
Umbraco.BlockList - Contains block configuration referencing
heroRow(content) andheroSettings(settings) - Label template:
{{ !title || title == '' ? 'Hero Row' : title }}
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<Umbraco.Cms.Core.Models.Blocks.BlockListItem>
@{
var content = Model.Content as ContentModels.HeroRow;
if (content == null) { return; }
}
<section>
<h1>@content.Title</h1>
<p>@Html.Raw(content.BodyText)</p>
<img src="@Url.GetCropUrl(content.BackgroundImage, 800, 600)" />
<a href="@content.CallToAction?.Url" target="@content.CallToAction?.Target">
@content.CallToAction?.Name
</a>
</section>- Placed in
Pages/ - Property:
mainContenttyped to the "Main Content" block list data type
Master.cshtml— base layout templateHome Page.cshtml— content template inheriting master, renders block list
Add support for additional HTML elements:
// Map <video> elements to a "Video Picker" data type
public class VideoDataTypeMapper : IDataTypeMapper
{
public IEnumerable<string> HtmlElements => new[] { "video" };
public string DataTypeName => "Video Picker";
}
public class MyComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.QuickBlockDataTypeMappers().Append<VideoDataTypeMapper>();
}
}Change what unknown elements default to:
// In Startup.cs or a composer
services.Configure<QuickBlocksDefaultOptions>(options =>
{
options.DefaultDataTypeName = "Textarea";
});Because the collection uses "last registered wins", you can override built-in mappers:
// Override img to use a different picker
public class CustomImgMapper : IDataTypeMapper
{
public IEnumerable<string> HtmlElements => new[] { "img" };
public string DataTypeName => "Media Picker v3";
}public class QuickBlocksDefaultOptions
{
public string DefaultDataTypeName { get; set; } = "Textstring";
}Override in appsettings.json or via services.Configure<QuickBlocksDefaultOptions>().
{
"name": "QuickBlocks",
"version": "1.0.0",
"allowPackageTelemetry": true,
"dashboards": [{
"alias": "quickBlocks",
"view": "/App_Plugins/QuickBlocks/quickBlocks.html",
"sections": ["settings"],
"weight": -10,
"access": [
{ "deny": "translator" },
{ "grant": "admin" }
]
}]
}buildTransitive/Umbraco.Community.QuickBlocks.targets contains MSBuild targets that ensure App_Plugins files are copied into consuming projects at build time:
flowchart LR
BUILD[dotnet build] --> COPY[CopyPackageMsBuildAssets\nCopies App_Plugins/QuickBlocks/**\nto consuming project]
CLEAN[dotnet clean] --> CLEAR[ClearPackageMsBuildAssets\nRemoves App_Plugins/QuickBlocks/**\nfrom consuming project]
The buildTransitive directory ensures these targets flow transitively through NuGet package references — the consuming project does not need to reference the targets file explicitly.
src/QuickBlocks/
│
├── App_Plugins/QuickBlocks/
│ ├── package.manifest # Dashboard registration, JS/CSS includes
│ ├── quickBlocks.html # AngularJS dashboard template
│ ├── quickBlocks.js # AngularJS controller + resource service
│ ├── quickBlocks.css # Dashboard styles
│ └── lang/
│ └── en-us.xml # Localisation strings
│
├── buildTransitive/
│ └── Umbraco.Community.QuickBlocks.targets # MSBuild copy targets
│
├── Composing/
│ ├── NotificationHandlersComposer.cs # Registers notification handlers
│ └── RegisterServicesComposer.cs # Registers IBlockCreation/Parsing services
│
├── Controllers/
│ └── QuickBlocksUmbracoApiController.cs # Main API endpoint: POST Build
│
├── Models/
│ ├── BlockConfigModel.cs # Block list configuration helper
│ ├── BlockItemModel.cs # Nested block item (data-item-name)
│ ├── BlockListModel.cs # Block list definition (data-list-name)
│ ├── ContentTypeModel.cs # Page content type (data-content-type-name)
│ ├── FolderStructure.cs # Holds IDs of created folders
│ ├── IDataTypeMapper.cs # Interface for HTML→data type mapping
│ ├── PartialViewModel.cs # Partial view definition (data-partial-name)
│ ├── PropertyModel.cs # Property (data-prop-name)
│ ├── QuickBlocksInstruction.cs # API request DTO
│ ├── RowModel.cs # Block/row definition (data-row-name)
│ └── DataTypeMappers/
│ └── DataTypeMappers.cs # Built-in mapper implementations
│
├── NotificationHandlers/
│ └── ServerVariablesParsingNotificationHandler.cs # Exposes API URL to JS
│
├── Services/
│ ├── IBlockCreationService.cs # Interface for entity creation
│ ├── BlockCreationService.cs # Creates content types, data types, partials
│ ├── IBlockParsingService.cs # Interface for HTML parsing
│ ├── BlockParsingService.cs # Parses data-* attributes into models
│ └── Resolvers/
│ ├── IDataTypeNameResolver.cs # Interface for HTML→data type resolution
│ └── DataTypeNameResolver.cs # Looks up mapper collection
│
├── DataTypeMappersCollection.cs # Umbraco collection + builder + extensions
├── QuickBlocksComposer.cs # Main composer: registers mappers + manifest
├── QuickBlocksDefaultOptions.cs # Options class (default data type name)
├── QuickBlocksManifestFilter.cs # Registers package manifest for telemetry
└── QuickBlocks.csproj # Package project file (.NET 6, NuGet config)
Documentation generated for QuickBlocks v1.0.0 — Umbraco.Community.QuickBlocks