diff --git a/.github/clean-blocklist-workaround.md b/.github/clean-blocklist-workaround.md deleted file mode 100644 index 8ec72c50..00000000 --- a/.github/clean-blocklist-workaround.md +++ /dev/null @@ -1,172 +0,0 @@ -# Package Fix Scripts - -This directory contains documentation for the Umbraco BlockList label workaround. - -## BlockList Label Fix (PowerShell) - -**The fix is now implemented directly in PowerShell** within `.github/workflows/powershell/CreateNuGetPackages.ps1`. No external dependencies or manual steps required! - -Automates the workaround for the Umbraco BlockList label export bug tracked at [umbraco/Umbraco-CMS#20801](https://github.com/umbraco/Umbraco-CMS/issues/20801). - -### Problem - -When creating and downloading packages from the Umbraco backoffice, the `package.xml` file doesn't include label configurations for BlockList data types, even though these labels are present in the original system. - -### Solution - -The PowerShell build script (`CreateNuGetPackages.ps1`) automatically: -1. Downloads package.zip from Umbraco API -2. Extracts the package -3. Reads label data from `template/Clean.Blog/uSync/v17/DataTypes/BlockListMainContent.config` -4. Adds missing labels to the `[BlockList] Main Content` DataType in package.xml -5. Repacks the package.zip -6. Continues with the normal build process - -### How It Works - -The fix runs automatically during: -- ✅ **PR builds** (before publishing to GitHub Packages) -- ✅ **Release builds** (before publishing to NuGet.org) - -No manual intervention needed - just run the build script as normal! - -### What Gets Fixed - -Adds labels to the `[BlockList] Main Content` DataType for: -- Rich Text blocks -- Image blocks -- Video blocks -- Code Snippet blocks -- Image Carousel blocks -- Article List blocks - -### Implementation Details - -The fix is implemented as a PowerShell function `Fix-BlockListLabels` in `CreateNuGetPackages.ps1`: -- Parses XML and JSON natively in PowerShell -- Strips markdown formatting from labels -- Unicode-escapes single quotes to match Umbraco format -- HTML-encodes for XML attributes -- No external dependencies required - -## Disabling or Removing the Fix - -### Quick Disable - -To temporarily disable the fix without removing code: - -1. Open `.github/workflows/powershell/CreateNuGetPackages.ps1` -2. Change line 13 to: `$FixBlockListLabels = $false` - - -## Why This is Temporary - -This is a workaround for a known Umbraco bug: [umbraco/Umbraco-CMS#20801](https://github.com/umbraco/Umbraco-CMS/issues/20801) - -Once Umbraco releases a fix, this entire workaround can be removed. All code sections are clearly marked with comments: -- `TEMPORARY WORKAROUND - Remove when Umbraco fixes issue #20801` - -### Complete Removal - -# How to Remove the BlockList Label Workaround - -When Umbraco fixes issue [#20801](https://github.com/umbraco/Umbraco-CMS/issues/20801), follow these steps to remove the workaround: - -## Quick Disable (for testing) - -To quickly test without the fix: - -1. Open `.github/workflows/powershell/CreateNuGetPackages.ps1` -2. Change line 13 from: - ```powershell - $FixBlockListLabels = $true - ``` - to: - ```powershell - $FixBlockListLabels = $false - ``` - -## Complete Removal - -### Step 1: Remove from PowerShell Script - -**File:** `.github/workflows/powershell/CreateNuGetPackages.ps1` - -**Delete lines 6-97** (feature flag and function): -```powershell -# ============================================================================ -# TEMPORARY WORKAROUND - Remove when Umbraco fixes issue #20801 -# https://github.com/umbraco/Umbraco-CMS/issues/20801 -# -# Set to $false to disable the BlockList label fix -# Delete this entire section when Umbraco releases a fix -# ============================================================================ -$FixBlockListLabels = $true - -function Fix-BlockListLabels { - # ... [entire function] ... -} -# ============================================================================ -``` - -**Delete the workaround block** (search for "BEGIN TEMPORARY WORKAROUND"): -```powershell -# ======================================================================== -# BEGIN TEMPORARY WORKAROUND - Umbraco issue #20801 -# Remove this entire block when Umbraco fixes BlockList label export -# ======================================================================== -if ($FixBlockListLabels) { - # ... [entire block] ... -} -# ======================================================================== -# END TEMPORARY WORKAROUND -# ======================================================================== -``` - -### Step 2: Optional - Remove Scripts Directory - -If you want to completely clean up: - -```bash -# Remove the entire scripts directory (optional) -rm -rf scripts/ -``` - -Or keep the directory for future use and just remove the workaround files: -```bash -rm scripts/fix-package-blocklist-labels.py -rm scripts/test_fix_script.py -rm scripts/REMOVAL-GUIDE.md -rm scripts/README.md -``` - -## Verification - -After removal, test that packages still build correctly: - -1. Run the package build: - ```powershell - ./.github/workflows/powershell/CreateNuGetPackages.ps1 -Version "7.0.0-test.1" - ``` - -2. Verify the package downloads successfully -3. Check that Umbraco now includes labels in the exported package.xml - -## Tracking the Umbraco Fix - -Monitor the Umbraco CMS repository for fixes: -- Issue: https://github.com/umbraco/Umbraco-CMS/issues/20801 -- Watch for releases that mention BlockList label fixes -- Check release notes for Umbraco CMS updates - -## Need Help? - -If you're unsure whether Umbraco has fixed the issue: - -1. Download a fresh package from Umbraco backoffice -2. Extract and inspect `package.xml` -3. Look for the `[BlockList] Main Content` DataType -4. Check if the `blocks` array contains `"label"` properties - -If labels are present, the bug is fixed and you can safely remove this workaround! - diff --git a/.github/clean-documentation.md b/.github/clean-documentation.md index d372c873..be56f4f9 100644 --- a/.github/clean-documentation.md +++ b/.github/clean-documentation.md @@ -98,16 +98,6 @@ Documentation related to GitHub Actions workflows and automation: --- -## Technical References - -Technical documentation for specific issues and workarounds: - -### BlockList Label Workaround - -- **[BlockList Label Workaround](clean-blocklist-workaround.md)** - Documentation for the temporary workaround for Umbraco BlockList label export issue (#20801) - ---- - ## Additional Resources ### External Resources diff --git a/.github/general-contributing.md b/.github/general-contributing.md index 32c95803..9f2854b9 100644 --- a/.github/general-contributing.md +++ b/.github/general-contributing.md @@ -88,20 +88,18 @@ This means: The uSync configuration files are located in: ``` template/Clean.Blog/uSync/ -├── v16/ # Umbraco 16 configuration -├── v17/ # Umbraco 17 configuration -│ ├── Content/ # Content nodes -│ ├── ContentTypes/ # Document Types -│ ├── DataTypes/ # Data Type definitions -│ ├── Dictionary/ # Dictionary items for translations -│ ├── Languages/ # Language configurations -│ ├── Media/ # Media items -│ ├── MediaTypes/ # Media Type definitions -│ ├── MemberTypes/ # Member Type definitions -│ ├── RelationTypes/ # Relation Type definitions -│ ├── Templates/ # Razor templates -│ └── usync.config # uSync version metadata -└── v17-backup/ # Backup configuration +└── v18/ # Umbraco 18 configuration + ├── Content/ # Content nodes + ├── ContentTypes/ # Document Types + ├── DataTypes/ # Data Type definitions + ├── Dictionary/ # Dictionary items for translations + ├── Languages/ # Language configurations + ├── Media/ # Media items + ├── MediaTypes/ # Media Type definitions + ├── MemberTypes/ # Member Type definitions + ├── RelationTypes/ # Relation Type definitions + ├── Templates/ # Razor templates + └── usync.config # uSync version metadata ``` ## Database Changes and uSync diff --git a/.github/script-create-nuget-packages.md b/.github/script-create-nuget-packages.md index 87fddf0b..aaef840b 100644 --- a/.github/script-create-nuget-packages.md +++ b/.github/script-create-nuget-packages.md @@ -2,7 +2,7 @@ ## Overview -This comprehensive script creates NuGet packages by starting Umbraco, downloading the package via API, applying BlockList label fixes (temporary workaround), updating .csproj versions, and building all packages in dependency order. +This comprehensive script creates NuGet packages by starting Umbraco, downloading the package via API, updating .csproj versions, and building all packages in dependency order. ## Script Location @@ -10,7 +10,7 @@ This comprehensive script creates NuGet packages by starting Umbraco, downloadin ## Purpose -Orchestrates the entire package creation process including running Umbraco, downloading content package, fixing BlockList labels, updating versions, and building all NuGet packages. +Orchestrates the entire package creation process including running Umbraco, downloading content package, updating versions, and building all NuGet packages. ## When It's Used @@ -30,20 +30,11 @@ Orchestrates the entire package creation process including running Umbraco, down 4. **Starts Umbraco** - Runs Clean.Blog project 5. **Waits for API** - Polls until Umbraco responds 6. **Downloads Package** - Gets package.zip via API -7. **Fixes BlockList Labels** - Applies workaround for Umbraco issue #20801 -8. **Updates Versions** - Sets version in all .csproj files -9. **Builds Packages** - In dependency order: Core → Headless → Clean → Template +7. **Updates Versions** - Sets version in all .csproj files +8. **Builds Packages** - In dependency order: Core → Headless → Clean → Template ## Key Features -### BlockList Label Fix (Temporary) - -Workaround for [Umbraco issue #20801](https://github.com/umbraco/Umbraco-CMS/issues/20801): -- Extracts package.zip -- Reads BlockList config from uSync -- Adds labels to package.xml -- Repacks package.zip - ### Dependency Order Building 1. **Clean.Core** - Base package @@ -86,12 +77,10 @@ Packages created in `.artifacts/nuget/`: ## Related Documentation - [workflow-versioning-releases.md](workflow-versioning-releases.md) - Parent workflow -- BlockList issue: https://github.com/umbraco/Umbraco-CMS/issues/20801 ## Notes -- **Most complex script** in the workflow (831 lines) -- **Temporary BlockList fix** - remove when Umbraco fixes issue +- **Most complex script** in the workflow - **Builds in dependency order** to avoid package resolution errors - **Uses local NuGet source** for intermediate packages - **Supports both PowerShell 5.x and Core 6+** diff --git a/.github/script-create-packages.md b/.github/script-create-packages.md index 9e4ee76c..ca7d51f5 100644 --- a/.github/script-create-packages.md +++ b/.github/script-create-packages.md @@ -2,7 +2,7 @@ ## Overview -The `CreateNuGetPackages.ps1` script automates the process of creating NuGet packages for the Clean Umbraco starter kit. It handles version management, package creation, and includes a temporary workaround for Umbraco BlockList label export issues. +The `CreateNuGetPackages.ps1` script automates the process of creating NuGet packages for the Clean Umbraco starter kit. It handles version management and package creation. **Location:** `.github/workflows/powershell/CreateNuGetPackages.ps1` @@ -124,7 +124,6 @@ sequenceDiagram Umbraco-->>Script: Bearer token Script->>Umbraco: Create package via API Umbraco-->>Script: package.zip - Script->>Script: Fix BlockList labels (workaround) Script->>Script: Update .csproj versions Script->>Script: Build & pack all projects Script-->>PR: 4 .nupkg files in .artifacts/nuget @@ -151,7 +150,6 @@ sequenceDiagram Umbraco-->>Script: Bearer token Script->>Umbraco: Create package via API Umbraco-->>Script: package.zip - Script->>Script: Fix BlockList labels (workaround) Script->>Script: Update .csproj versions Script->>Script: Build & pack all projects Script-->>Release: 4 .nupkg files in .artifacts/nuget @@ -206,23 +204,7 @@ The `update-packages.yml` workflow does NOT use this script. It uses `UpdateThir - Downloads the package from: `/umbraco/management/api/v1/package/created/{packageId}/download/` - Saves the package as `package.zip` in the output folder -### 5. BlockList Labels Fix (Temporary Workaround) - -**Status:** Temporary workaround for [Umbraco Issue #20801](https://github.com/umbraco/Umbraco-CMS/issues/20801) - -**What it does:** -- Extracts the downloaded `package.zip` to a temporary location -- Reads the `package.xml` file -- Reads the uSync configuration from `template/Clean.Blog/uSync/v17/DataTypes/BlockListMainContent.config` -- Extracts BlockList label mappings from the uSync configuration -- Injects the missing labels into the `[BlockList] Main Content` DataType configuration in `package.xml` -- Re-packs the package with the fixed configuration - -**To disable:** Set `$FixBlockListLabels = $false` at the top of the script - -**When to remove:** Delete the entire workaround section when Umbraco releases a fix for issue #20801 - -### 6. Version Updates in .csproj Files +### 5. Version Updates in .csproj Files **Target files:** All `.csproj` files except: - `Clean.Blog.csproj` @@ -234,12 +216,12 @@ The `update-packages.yml` workflow does NOT use this script. It uses `UpdateThir - **AssemblyVersion:** Set to base version without suffix (e.g., `7.0.0`) - **PackageReference for Clean.* packages:** Updated to match the new version -### 7. Clean Build Environment +### 6. Clean Build Environment - Empties all `bin` folders in the repository (excluding `.vs` folders) - Ensures a clean build environment for package creation -### 8. NuGet Package Building +### 7. NuGet Package Building **Build order** (to satisfy dependencies): 1. **Clean.Core** - Built and packed first @@ -254,16 +236,15 @@ The `update-packages.yml` workflow does NOT use this script. It uses `UpdateThir - Copies intermediate packages to the local NuGet source immediately after packing - Removes the temporary local source after all builds complete -### 9. Package Collection +### 8. Package Collection - Finds all `.nupkg` files in `Release` folders matching the version - Copies all packages to `.artifacts/nuget` - Displays the list of generated packages -### 10. Cleanup +### 9. Cleanup - Stops the Umbraco process -- Removes temporary extraction folders - Removes the temporary local NuGet source ## Generated Packages @@ -287,18 +268,7 @@ All packages are copied to: `.artifacts/nuget/` ## Known Issues and Workarounds -### 1. Umbraco BlockList Label Export (Issue #20801) - -**Problem:** Umbraco doesn't export BlockList labels when creating packages via the Management API. - -**Workaround:** The script includes the `Fix-BlockListLabels` function that: -- Reads labels from the uSync configuration -- Injects them into the package.xml -- Removes markdown bold markers (`**`) from labels - -**Status:** Temporary - Remove when Umbraco fixes the issue - -### 2. SSL Certificate Validation +### 1. SSL Certificate Validation **Problem:** CI/CD environments may use self-signed certificates. @@ -337,16 +307,6 @@ All packages are copied to: `.artifacts/nuget/` - Ensure the local NuGet source is added correctly - Check that intermediate packages are being copied to `.artifacts/nuget` -### BlockList labels not applied - -**Symptoms:** Package.xml doesn't contain BlockList labels - -**Solutions:** -- Verify `$FixBlockListLabels = $true` -- Check that the uSync config exists at the expected path -- Ensure the `package.xml` contains the `[BlockList] Main Content` DataType -- Review verbose output for warnings - ### Version not updated in .csproj files **Symptoms:** Some projects still have old version numbers @@ -363,7 +323,6 @@ All packages are copied to: `.artifacts/nuget/` - Umbraco startup status - API authentication status - Package download confirmation -- BlockList label fix details (before/after comparison) - List of updated .csproj files - Build and pack status for each project - List of generated NuGet packages @@ -394,19 +353,14 @@ All packages are copied to: `.artifacts/nuget/` **When to update this script:** -1. **Remove BlockList workaround** when Umbraco fixes issue #20801 - - Delete the `Fix-BlockListLabels` function (lines 15-113) - - Delete the workaround section (lines 474-523) - - Set `$FixBlockListLabels = $false` or remove the variable - -2. **Update API credentials** if the Umbraco configuration changes - - Update `client_id` and `client_secret` in the `$tokenBody` (lines 405-408) +1. **Update API credentials** if the Umbraco configuration changes + - Update `client_id` and `client_secret` in the `$tokenBody` -3. **Add new projects** to the build order - - Add project discovery (similar to lines 690-700) +2. **Add new projects** to the build order + - Add project discovery - Add build/pack step in the appropriate order -4. **Change port or URL** if the Umbraco project configuration changes +3. **Change port or URL** if the Umbraco project configuration changes - Update all references to `https://localhost:44340` ## Related Files @@ -420,7 +374,6 @@ All packages are copied to: `.artifacts/nuget/` - `README.md` - Updated with version numbers - `umbraco-marketplace-readme.md` - Umbraco marketplace README, updated with version numbers - `umbraco-marketplace-readme-clean.md` - Umbraco marketplace README for Clean package, updated with version numbers -- `template/Clean.Blog/uSync/v17/DataTypes/BlockListMainContent.config` - Source of BlockList labels ## Support diff --git a/.github/umbraco-18-upgrade-plan.md b/.github/umbraco-18-upgrade-plan.md new file mode 100644 index 00000000..69da8313 --- /dev/null +++ b/.github/umbraco-18-upgrade-plan.md @@ -0,0 +1,115 @@ +# Umbraco 18 Beta Upgrade Plan for Clean + +Investigation of Umbraco 18 beta breaking changes and the plan to make Clean +compatible. Target: Umbraco `18.0.0-beta2`, Clean package version `8.0.0-beta1`. + +## Current state + +All 5 projects (`Clean`, `Clean.Core`, `Clean.Headless`, `Clean.Blog`, +`Clean.Models`) target `net10.0` and reference Umbraco `17.1.0`. Package +version is stamped at `7.0.5`. Compatible extras: `uSync 17.0.1` and +`uSync.Command.Setup 16.1.0`. + +## Decisions + +- **Target Umbraco version**: `18.0.0-beta2` (latest beta as of 2026-05-14). +- **Clean package version**: bump to `8.0.0-beta1` (matches the existing + convention: Umbraco 17 → Clean 7, Umbraco 18 → Clean 8). +- **uSync**: keep current pins (`uSync 17.0.1`, `uSync.Command.Setup 16.1.0`) + in `Clean.Blog`. No v18-compatible release exists yet on NuGet; expect a + follow-up bump once Kevin Jump publishes one. + +## Breaking changes that affect Clean + +### 1. OpenAPI: Swashbuckle removed, replaced by `Microsoft.AspNetCore.OpenApi` + +- `template/Clean.Headless/Startup/ConfigureSwaggerGenOptions.cs` uses + `IConfigureOptions` + `SwaggerDoc(...)` — rewrite using + `builder.Services.AddOpenApi("clean-starter", options => + options.AddDocumentTransformer(...))`, setting + `Title = "Clean starter kit"`, `Version = "Latest"`, + `Description = "Contains headless endpoints for search, dictionaries and forms"`, + plus `AddOpenApiDocumentToUi("clean-starter", "Clean starter kit")`. +- `template/Clean.Headless/Startup/WorkshopComposer.cs` (line 11) — replace + the `ConfigureOptions()` call with the new + registration. The `ConfigureSwaggerGenOptions` class can collapse into the + composer or be kept as a small helper. +- The Swagger UI URL changes from `/umbraco/swagger` to `/umbraco/openapi`. + +### 2. `IPublishedContent.Parent` / `.Children` properties removed + +Both are now extension methods in `Umbraco.Extensions` (already imported via +`_ViewImports.cshtml`). Razor views need `()` added: + +- `template/Clean.Blog/Views/Author.cshtml:8` — + `Model.Parent as AuthorList` → `Model.Parent() as AuthorList` +- `template/Clean.Blog/Views/Partials/mainNavigation.cshtml:21` — + `homePage.Children.Where(...)` → `homePage.Children().Where(...)` +- `template/Clean.Blog/Views/Partials/xmlSitemap.cshtml:14,16,29` — three + `.Children` references → `.Children()` + +Existing `Children()` calls in `Partials/authors.cshtml` and +`latestArticlesRow.cshtml` are already method calls and don't need changes. + +### 3. csproj package bumps + +For every csproj, bump `Umbraco.Cms.*` from `17.1.0` to `18.0.0-beta2`: + +- `template/Clean.Models/Clean.Models.csproj` +- `template/Clean.Core/Clean.Core.csproj` +- `template/Clean.Headless/Clean.Headless.csproj` +- `template/Clean/Clean.csproj` (also bump the two `Clean.Core` / + `Clean.Headless` PackageReferences to `8.0.0-beta1`) +- `template/Clean.Blog/Clean.Blog.csproj` (bump `Umbraco.Cms` and + `Umbraco.Cms.DevelopmentMode.Backoffice`; leave uSync untouched) + +Bump ``, ``, `` from `7.0.5` +to `8.0.0-beta1` on the four packaged projects (`Clean.Models` is unversioned). + +## Confirmed NOT broken (do not change) + +- **`IEmailSender`** — interface remains; only obsolete `SendAsync` overloads + without an `expires` parameter were removed. Current call + `_emailSender.SendAsync(message, emailType: "Contact")` still binds to the + surviving overload. No change needed in `ContactSurfaceController.cs` or + `ContactV1Controller.cs`. +- **`IContentTypeBaseServiceProvider`** in `ImportPackageXmlMigration.cs` — + the `Provider` variant survived; only `IContentTypeBaseService` (without + "Provider") was removed. +- **`AsyncPackageMigrationBase`** — kept in v18; only the sync + `MigrationBase` and `PackageMigrationBase` were removed. +- **`IDictionaryItemService.GetAtRootAsync()`** in + `DictionaryApiV1Controller.cs` and `PackageController.cs` — a different + API from the removed `GetAtRoot()` (which was on `UmbracoHelper`, + `IPublishedContentCache`, `IUmbracoContext.Content`). + +## Phased execution + +1. **Phase 1 — packaged libraries**: bump `Umbraco.Cms.*` to + `18.0.0-beta2` in all 5 csproj files. Rewrite `ConfigureSwaggerGenOptions.cs` + and update `WorkshopComposer.cs`. Bump package `` to + `8.0.0-beta1`. +2. **Phase 2 — `Clean.Blog` test host**: bump `Umbraco.Cms` and + `Umbraco.Cms.DevelopmentMode.Backoffice`. Update the four Razor views to + call `Parent()` / `Children()`. +3. **Phase 3 — verification + docs**: local `dotnet build`, smoke test + (login, publish home, hit OpenAPI UI at `/umbraco/openapi`). Add an + "Umbraco 18" install section to `README.md`. + +## Risks and open items + +- **uSync 17 against Umbraco 18 may fail at runtime.** The pins are kept per + the chosen strategy; expect a follow-up bump when + [uSync](https://www.nuget.org/packages/uSync) ships a v18 build (see + [KevinJump/uSync releases](https://github.com/KevinJump/uSync/releases)). +- **Workflows** under `.github/workflows/` (e.g. + `test-umbraco-latest-nuget.yml`, `update-packages.yml`) may need a separate + look if they pin to non-prerelease versions — out of scope for this plan. + +## Sources + +- [Breaking Changes Overview — CMS 18.latest (Beta)](https://docs.umbraco.com/umbraco-cms/18.latest/get-started/upgrading-and-migrating/version-specific) +- [Umbraco-CMS v18.0.0 release notes](https://releases.umbraco.com/release/umbraco/Umbraco-CMS/18.0.0) +- [API versioning and OpenAPI — CMS 18.latest](https://docs.umbraco.com/umbraco-cms/18.latest/extend-your-project/server-side-extensions/api-versioning-and-openapi) +- [Umbraco.Cms on NuGet](https://www.nuget.org/packages/Umbraco.Cms) +- [uSync on NuGet](https://www.nuget.org/packages/uSync) diff --git a/.github/workflow-pr.md b/.github/workflow-pr.md index 652ccd2d..f89b1dad 100644 --- a/.github/workflow-pr.md +++ b/.github/workflow-pr.md @@ -488,7 +488,7 @@ flowchart TD GetVersion --> ShowVersion[5. Display Version Info
Show base version, build number, full version] - ShowVersion --> CreatePackages[6. Create NuGet Packages
Start Umbraco
Download package via API
Fix BlockList labels
Update .csproj versions
Build in dependency order] + ShowVersion --> CreatePackages[6. Create NuGet Packages
Start Umbraco
Download package via API
Update .csproj versions
Build in dependency order] CreatePackages --> UploadArtifacts[7. Upload Package Artifacts
Save .nupkg files to workflow] diff --git a/.github/workflow-versioning-releases.md b/.github/workflow-versioning-releases.md index 41817821..10154496 100644 --- a/.github/workflow-versioning-releases.md +++ b/.github/workflow-versioning-releases.md @@ -556,7 +556,7 @@ flowchart TD ShowVersion --> UpdateREADME[5. Update README Files
Map Clean version to Umbraco
Update installation commands] - UpdateREADME --> CreatePackages[6. Create NuGet Packages
Start Umbraco
Download package via API
Fix BlockList labels
Update .csproj versions
Build in dependency order] + UpdateREADME --> CreatePackages[6. Create NuGet Packages
Start Umbraco
Download package via API
Update .csproj versions
Build in dependency order] CreatePackages --> UploadArtifacts[7. Upload Package Artifacts
Save .nupkg files to workflow] diff --git a/.github/workflows/powershell/CreateNuGetPackages.ps1 b/.github/workflows/powershell/CreateNuGetPackages.ps1 index f3ec9bd0..be3627ef 100644 --- a/.github/workflows/powershell/CreateNuGetPackages.ps1 +++ b/.github/workflows/powershell/CreateNuGetPackages.ps1 @@ -3,115 +3,6 @@ param( [string]$Version ) -# ============================================================================ -# TEMPORARY WORKAROUND - Remove when Umbraco fixes issue #20801 -# https://github.com/umbraco/Umbraco-CMS/issues/20801 -# -# Set to $false to disable the BlockList label fix -# Delete this entire section when Umbraco releases a fix -# ============================================================================ -$FixBlockListLabels = $true - -function Fix-BlockListLabels { - param( - [Parameter(Mandatory = $true)] - [string]$PackageXmlPath, - [Parameter(Mandatory = $true)] - [string]$USyncConfigPath - ) - - try { - Write-Host "Fixing BlockList labels in package.xml..." -ForegroundColor Yellow - - # Read and parse uSync config to get label mappings - [xml]$usyncXml = Get-Content $USyncConfigPath -Encoding UTF8 - $configCData = $usyncXml.DataType.Config.'#cdata-section' - $usyncConfig = $configCData | ConvertFrom-Json - - # Create label mapping - $labelMap = @{} - foreach ($block in $usyncConfig.blocks) { - if ($block.contentElementTypeKey -and $block.label) { - # Preserve asterisks for markdown formatting - $labelMap[$block.contentElementTypeKey] = $block.label - } - } - - Write-Verbose "Found $($labelMap.Count) block labels in uSync config" - - # Read package.xml - $packageXmlContent = Get-Content $PackageXmlPath -Raw -Encoding UTF8 - - # Find and extract the [BlockList] Main Content DataType Configuration - $pattern = '(]*Configuration=")([^"]+)(")' - $match = [regex]::Match($packageXmlContent, $pattern) - - if (-not $match.Success) { - Write-Host "Warning: Could not find [BlockList] Main Content DataType in package.xml" -ForegroundColor Yellow - return $false - } - - # Decode HTML entities and parse JSON - $configEncoded = $match.Groups[2].Value - $configJson = [System.Web.HttpUtility]::HtmlDecode($configEncoded) - $config = $configJson | ConvertFrom-Json - - # Display BEFORE - Write-Host "`n========================================" -ForegroundColor Cyan - Write-Host "BEFORE - DataType Configuration:" -ForegroundColor Cyan - Write-Host "========================================" -ForegroundColor Cyan - Write-Host $match.Value -ForegroundColor Gray - Write-Host "`nDecoded Configuration JSON:" -ForegroundColor Cyan - Write-Host ($configJson | ConvertFrom-Json | ConvertTo-Json -Depth 10) -ForegroundColor Gray - - # Add labels to each block - $labelsAdded = 0 - foreach ($block in $config.blocks) { - if ($block.contentElementTypeKey -and $labelMap.ContainsKey($block.contentElementTypeKey)) { - # Add the label property - $block | Add-Member -MemberType NoteProperty -Name "label" -Value $labelMap[$block.contentElementTypeKey] -Force - $labelsAdded++ - } - } - - # Convert back to JSON (compact format) - $modifiedJson = $config | ConvertTo-Json -Depth 10 -Compress - - # Unicode-escape single quotes to match Umbraco format - $modifiedJson = $modifiedJson -replace "'", '\u0027' - - # HTML-encode for XML attribute (properly escape all special XML characters: <, >, &, ", ') - $modifiedEncoded = [System.Web.HttpUtility]::HtmlEncode($modifiedJson) - - # Replace in the original XML content - $prefix = $match.Groups[1].Value - $suffix = $match.Groups[3].Value - $replacement = $prefix + $modifiedEncoded + $suffix - $packageXmlContent = $packageXmlContent -replace [regex]::Escape($match.Value), $replacement - - # Display AFTER - Write-Host "`n========================================" -ForegroundColor Cyan - Write-Host "AFTER - DataType Configuration:" -ForegroundColor Cyan - Write-Host "========================================" -ForegroundColor Cyan - Write-Host $replacement -ForegroundColor Green - Write-Host "`nDecoded Configuration JSON:" -ForegroundColor Cyan - Write-Host ($modifiedJson | ConvertFrom-Json | ConvertTo-Json -Depth 10) -ForegroundColor Green - Write-Host "========================================`n" -ForegroundColor Cyan - - # Write back to file - $packageXmlContent | Set-Content $PackageXmlPath -Encoding UTF8 -NoNewline - - Write-Host "Successfully added $labelsAdded labels to [BlockList] Main Content" -ForegroundColor Green - return $true - } - catch { - Write-Host "Error fixing BlockList labels: $($_.Exception.Message)" -ForegroundColor Red - Write-Verbose $_.ScriptStackTrace - return $false - } -} -# ============================================================================ - # Enable verbose output $VerbosePreference = "Continue" @@ -417,56 +308,6 @@ try { Invoke-RestMethod @downloadParams Write-Host "✅ Package downloaded successfully to $outputFile" -ForegroundColor Green - # ======================================================================== - # BEGIN TEMPORARY WORKAROUND - Umbraco issue #20801 - # Remove this entire block when Umbraco fixes BlockList label export - # ======================================================================== - if ($FixBlockListLabels) { - Write-Host "" - - # Extract package.zip to temp location - $tempExtractPath = Join-Path $OutputFolder "temp_package_extract" - if (Test-Path $tempExtractPath) { - Remove-Item $tempExtractPath -Recurse -Force - } - New-Item -ItemType Directory -Path $tempExtractPath | Out-Null - - # Extract the package - Expand-Archive -Path $outputFile -DestinationPath $tempExtractPath -Force - - # Paths - $packageXmlPath = Join-Path $tempExtractPath "package.xml" - $usyncConfigPath = Join-Path $CurrentDir "template\Clean.Blog\uSync\v17\DataTypes\BlockListMainContent.config" - - if ((Test-Path $packageXmlPath) -and (Test-Path $usyncConfigPath)) { - # Call the PowerShell function to fix labels - $success = Fix-BlockListLabels -PackageXmlPath $packageXmlPath -USyncConfigPath $usyncConfigPath - - if ($success) { - # Repack the package.zip - if (Test-Path $outputFile) { - Remove-Item $outputFile -Force - } - Compress-Archive -Path "$tempExtractPath\*" -DestinationPath $outputFile -CompressionLevel Optimal - Write-Host "Package repacked successfully" -ForegroundColor Green - } - } else { - if (-not (Test-Path $packageXmlPath)) { - Write-Host "Warning: package.xml not found in extracted package" -ForegroundColor Yellow - } - if (-not (Test-Path $usyncConfigPath)) { - Write-Host "Warning: uSync config not found at $usyncConfigPath" -ForegroundColor Yellow - } - } - - # Clean up temp directory - if (Test-Path $tempExtractPath) { - Remove-Item $tempExtractPath -Recurse -Force - } - } - # ======================================================================== - # END TEMPORARY WORKAROUND - # ======================================================================== } catch { Write-Host "An error occurred during package download: $($_.Exception.Message)" -ForegroundColor Red diff --git a/.github/workflows/powershell/Get-UsyncKeys.ps1 b/.github/workflows/powershell/Get-UsyncKeys.ps1 index 814b6b5d..f4ac9ad1 100644 --- a/.github/workflows/powershell/Get-UsyncKeys.ps1 +++ b/.github/workflows/powershell/Get-UsyncKeys.ps1 @@ -3,7 +3,7 @@ Extracts content keys from uSync configuration files. .DESCRIPTION - This script reads uSync configuration files from the v17 directory structure + This script reads uSync configuration files from the v18 directory structure and extracts the Key (GUID) values for published content items. .PARAMETER WorkspacePath @@ -41,7 +41,7 @@ Write-Host "uSync File Type: $UsyncFileType" -ForegroundColor Yellow Write-Host "Published Only: $PublishedOnly" -ForegroundColor Yellow # Construct path to uSync directory -$usyncPath = Join-Path $WorkspacePath "template\Clean.Blog\uSync\v17\$UsyncFileType" +$usyncPath = Join-Path $WorkspacePath "template\Clean.Blog\uSync\v18\$UsyncFileType" if (-not (Test-Path $usyncPath)) { Write-Host "ERROR: uSync directory not found at: $usyncPath" -ForegroundColor Red diff --git a/.gitignore b/.gitignore index e9426ecd..5246a195 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ /template/Clean.Headless/obj .artifacts/ template/Clean.Blog/wwwroot/media/created-packages/Clean/package.zip -.claude/settings.local.json +.claude/ diff --git a/template-pack.csproj b/template-pack.csproj index 27131e74..2396cff8 100644 --- a/template-pack.csproj +++ b/template-pack.csproj @@ -1,6 +1,6 @@ - 7.0.6 + 8.0.0-rc1 Template Umbraco.Community.Templates.Clean Umbraco.Community.Templates.Clean @@ -20,8 +20,8 @@ https://github.com/prjseal/Clean git MIT - 7.0.6 - 7.0.6 + 8.0.0 + 8.0.0 diff --git a/template/Clean.Blog/Clean.Blog.csproj b/template/Clean.Blog/Clean.Blog.csproj index 8a94821d..52d06e6a 100644 --- a/template/Clean.Blog/Clean.Blog.csproj +++ b/template/Clean.Blog/Clean.Blog.csproj @@ -7,13 +7,12 @@ - - - + + + - diff --git a/template/Clean.Blog/Composing/ApiClientSetup.cs b/template/Clean.Blog/Composing/ApiClientSetup.cs new file mode 100644 index 00000000..51057fda --- /dev/null +++ b/template/Clean.Blog/Composing/ApiClientSetup.cs @@ -0,0 +1,102 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.OperationStatus; +using Umbraco.Cms.Infrastructure.Security; + +namespace Clean.Blog.Composing; + +// Provisions the backoffice API client used by CreateNuGetPackages.ps1 to export +// the package via the management API. This replaces uSync.Command.Setup, which has +// no Umbraco 18 release; content/schema sync itself is handled by uSync. +internal static class ApiClientSetup +{ + public static async Task EnsureAsync(IServiceProvider rootServices, CancellationToken ct = default) + { + using var scope = rootServices.CreateScope(); + var sp = scope.ServiceProvider; + var configuration = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + + try + { + if (configuration.GetValue("uSync:Command:AddIfMissing", false) is false) + { + return; + } + + var clientId = configuration.GetValue("uSync:Command:ClientId", string.Empty); + var clientSecret = configuration.GetValue("uSync:Command:Secret", string.Empty); + if (string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(clientSecret)) + { + logger.LogWarning("Clean.Blog API client setup is enabled but ClientId or Secret is missing from configuration"); + return; + } + + var userService = sp.GetRequiredService(); + if (await userService.FindByClientIdAsync(clientId) is not null) + { + return; + } + + var fallbackEmail = $"{Path.GetFileNameWithoutExtension(Path.GetRandomFileName())}@example.com"; + var userGroupKeys = new HashSet + { + configuration.GetValue("uSync:Command:UserGroupKey", Constants.Security.AdminGroupKey), + }; + + var createAttempt = await userService.CreateAsync( + Constants.Security.SuperUserKey, + new UserCreateModel + { + Email = configuration.GetValue("uSync:Command:Email", fallbackEmail), + UserName = configuration.GetValue("uSync:Command:Username", fallbackEmail), + Kind = UserKind.Api, + Name = configuration.GetValue("uSync:Command:Name", "Clean API User"), + UserGroupKeys = userGroupKeys, + }, + approveUser: false); + + if (createAttempt.Success is false) + { + logger.LogWarning("Could not create the API user: {status}", createAttempt.Status); + return; + } + + var userKey = createAttempt.Result.CreatedUser?.Key; + if (userKey.HasValue is false) + { + logger.LogWarning("Could not create the API user: no key returned"); + return; + } + + var addClientIdStatus = await userService.AddClientIdAsync(userKey.Value, clientId); + if (addClientIdStatus != UserClientCredentialsOperationStatus.Success) + { + logger.LogWarning("Could not attach client id to user: {status}", addClientIdStatus); + return; + } + + var applicationManager = sp.GetService(); + if (applicationManager is null) + { + logger.LogWarning("IBackOfficeApplicationManager is not available; OpenIddict client not registered"); + return; + } + + await applicationManager.EnsureBackOfficeClientCredentialsApplicationAsync(clientId, clientSecret, ct); + await userService.EnableAsync(userKey.Value, new HashSet { userKey.Value }); + + logger.LogInformation("Provisioned API client '{clientId}' for Clean.Blog", clientId); + } + catch (Exception ex) + { + logger.LogError(ex, "Error provisioning the Clean.Blog API client"); + } + } + + private sealed class ApiClientSetupMarker { } +} diff --git a/template/Clean.Blog/Program.cs b/template/Clean.Blog/Program.cs index dc749b7f..2002b7a7 100644 --- a/template/Clean.Blog/Program.cs +++ b/template/Clean.Blog/Program.cs @@ -1,3 +1,4 @@ +using Clean.Blog.Composing; using Clean.Blog.Middleware; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); @@ -12,6 +13,10 @@ await app.BootUmbracoAsync(); +// Provision the backoffice API client before the Kestrel listener accepts +// requests, so CreateNuGetPackages.ps1's first /token call doesn't race startup. +await ApiClientSetup.EnsureAsync(app.Services); + // Add security headers middleware app.UseMiddleware(); diff --git a/template/Clean.Blog/Views/Author.cshtml b/template/Clean.Blog/Views/Author.cshtml index 41b2777c..cd73db54 100644 --- a/template/Clean.Blog/Views/Author.cshtml +++ b/template/Clean.Blog/Views/Author.cshtml @@ -5,7 +5,7 @@ @{ Layout = "master.cshtml"; - var authorListPage = Model.Parent as AuthorList; + var authorListPage = Model.Parent() as AuthorList; } diff --git a/template/Clean.Blog/Views/Partials/blockgrid/items.cshtml b/template/Clean.Blog/Views/Partials/blockgrid/items.cshtml index 2703fa57..677c9aae 100644 --- a/template/Clean.Blog/Views/Partials/blockgrid/items.cshtml +++ b/template/Clean.Blog/Views/Partials/blockgrid/items.cshtml @@ -12,7 +12,7 @@ class="umb-block-grid__layout-item" data-content-element-type-alias="@item.Content.ContentType.Alias" data-content-element-type-key="@item.Content.ContentType.Key" - data-element-udi="@item.ContentUdi" + data-element-key="@item.ContentKey" data-col-span="@item.ColumnSpan" data-row-span="@item.RowSpan" style=" --umb-block-grid--item-column-span: @item.ColumnSpan; --umb-block-grid--item-row-span: @item.RowSpan; "> diff --git a/template/Clean.Blog/Views/Partials/blocklist/default.cshtml b/template/Clean.Blog/Views/Partials/blocklist/default.cshtml index 065b16b7..bbffe989 100644 --- a/template/Clean.Blog/Views/Partials/blocklist/default.cshtml +++ b/template/Clean.Blog/Views/Partials/blocklist/default.cshtml @@ -5,7 +5,7 @@ @foreach (var block in Model) { - if (block?.ContentUdi == null) { continue; } + if (block?.Content == null) { continue; } var data = block.Content; @await Html.PartialAsync("blocklist/Components/" + data.ContentType.Alias, block) diff --git a/template/Clean.Blog/Views/Partials/grid/editors/macro.cshtml b/template/Clean.Blog/Views/Partials/grid/editors/macro.cshtml index a4450d1c..a783259f 100644 --- a/template/Clean.Blog/Views/Partials/grid/editors/macro.cshtml +++ b/template/Clean.Blog/Views/Partials/grid/editors/macro.cshtml @@ -1,15 +1,5 @@ @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage - -@if (Model?.value is not null) -{ - string macroAlias = Model.value.macroAlias.ToString(); - var parameters = new Dictionary(); - foreach (var mpd in Model.value.macroParamsDictionary) - { - parameters.Add(mpd.Name, mpd.Value); - } - - - @await Umbraco.RenderMacroAsync(macroAlias, parameters) - -} +@* + Macros were removed in Umbraco 14+. The legacy grid macro editor is no longer + supported. Migrate macro grid content to the Block Grid or Block List editors. +*@ diff --git a/template/Clean.Blog/Views/Partials/mainNavigation.cshtml b/template/Clean.Blog/Views/Partials/mainNavigation.cshtml index 5b216009..8a2df05b 100644 --- a/template/Clean.Blog/Views/Partials/mainNavigation.cshtml +++ b/template/Clean.Blog/Views/Partials/mainNavigation.cshtml @@ -18,7 +18,7 @@ - @foreach (var page in homePage.Children.Where(x => !x.Value("hideFromTopNavigation"))) + @foreach (var page in homePage.Children().Where(x => !x.Value("hideFromTopNavigation"))) {