Report 20: v8 to v17 Migration Comparison Index¶
Last Updated: 2026-02-27 Project: Progress Credit Union -- Umbraco v8 to v17 Migration
This is the master index for the comparison report series (Reports 20-29). Each report provides a detailed side-by-side analysis of a specific migration category, showing exactly how v8 patterns were transformed for v17. Developers should read this index first to understand the 12 core transformation patterns (the "Rosetta Stone"), then navigate to specific reports for full file-by-file comparisons.
Dashboard Statistics¶
| Category | v8 Count | v17 Count | Coverage | Report |
|---|---|---|---|---|
| Page templates | 48 .cshtml |
46 (2 obsolete) | 96% | Report 21 |
| Component views | 91 DTGE + 40 sub-partials | 121 blockgrid + 49 sub-partials | 93% | Report 22 |
| Layout / shared partials | 20 SiteLayout + 5 BlockList + 79 Forms | 18 SiteLayout + 7 blocklist + 13 Forms | 85% | Report 23 |
| Controllers / Services | 8 SurfaceControllers + 14 Services | 7 ViewComponents + 16 Services | 100% | Report 24 |
| Security / 2FA | 21 files (plugins + core) | 8 C# files (Security/) | 100% | Report 25 |
| Custom editors | 52 App_Plugins dirs | 32 TS editor dirs (70 .ts files) | 100% | Report 26 |
| Static assets | 44 JS + 377 CSS | 58 JS + 233 CSS | 95% | Report 27 |
| Configuration | 12 Web.config + transforms | 39 appsettings*.json | 100% | Report 28 |
| Models | 249 generated + 16 ViewModels | auto-generated + 5 ViewModels | 100% | Report 29 |
Overall migration completeness: ~95%
Twelve Transformation Patterns ("Rosetta Stone")¶
Every file in the migration uses one or more of these patterns. Master these 12 and you can read any migrated file.
P1 -- View Inheritance¶
The base class and model binding changed for every Razor view in the project.
v8 (psCreditUnion/Progress.Web/Views/Partials/Grid/Editors/DocTypeGridEditor/heroItem.cshtml):
@inherits UmbracoViewPage<ContentModels.HeroItem>
@using ContentModels = Umbraco.Web.PublishedModels;
@using Umbraco.Web.Models
@{
var heading = "";
var link = Model.Value<IEnumerable<Link>>("link");
v17 (dbl.Progress/src/www/Views/Partials/blockgrid/Components/heroItem.cshtml):
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<Umbraco.Cms.Core.Models.Blocks.BlockGridItem>
@using Umbraco.Cms.Core.Models
@using Umbraco.Cms.Core.Models.PublishedContent
@{
var content = Model.Content;
var heading = content.Value<string>("heading") ?? "";
Files affected: 170+ (all views) Referenced in: Reports 21, 22, 23
P2 -- Media Picker (IPublishedContent to MediaWithCrops)¶
v8 used IPublishedContent for media; v17 uses MediaWithCrops with .MediaUrl().
v8:
var image = "";
if (Model.Value<IPublishedContent>("firstCurve") != null)
{
firstCurve = Model.Value<IPublishedContent>("firstCurve").Url.ToString();
}
v17:
// MediaPicker3 single-pick returns MediaWithCrops (not IPublishedContent)
var firstCurveMedia = content.Value<MediaWithCrops>("firstCurve");
var firstCurve = firstCurveMedia?.MediaUrl() ?? "";
Files affected: 20+ component partials Referenced in: Reports 21, 22
P3 -- XPath to Dependency Injection¶
v8 used Umbraco.ContentSingleAtXPath() to query content nodes. v17 uses @inject with cached services.
v8 (psCreditUnion/Progress.Web/Views/master.cshtml):
@inherits Umbraco.Web.Mvc.UmbracoViewPage
@{
var settings = Umbraco.ContentSingleAtXPath("//websiteConfiguration");
var gtsettings = Umbraco.ContentSingleAtXPath("//googleTagManagerConfiguration");
var stylesSettings = Umbraco.ContentSingleAtXPath("//siteStyleConfiguration");
var menuSettings = Umbraco.ContentSingleAtXPath("//menuConfiguration");
var menuStyle = menuSettings.Value<string>("menuStyle");
v17 (dbl.Progress/src/www/Views/master.cshtml):
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
@inject ISiteSettingsService SiteSettings
@inject IDictionaryService Dictionary
@{
var website = SiteSettings.Website;
var styles = SiteSettings.Styles;
var menu = SiteSettings.Menu;
var gtm = SiteSettings.GoogleTagManager;
var menuStyle = menu?.MenuStyle?.ToLowerInvariant();
Files affected: 15+ (all templates that queried settings nodes) Referenced in: Reports 21, 24
P4 -- ClientDependency to Static Link Tags¶
v8 used the ClientDependency framework for CSS/JS bundling with priority ordering. v17 uses standard HTML <link> tags with explicit ordering.
v8 (psCreditUnion/Progress.Web/Views/master.cshtml):
@using ClientDependency.Core.Mvc
@{
Html.RequiresCss("~/vendor/bootstrap/css/bootstrap.min.css", 0);
Html.RequiresCss("~/vendor/menu4/css/style.css", 2);
Html.RequiresCss("~/vendor/slick/slick.css", 3);
v17 (dbl.Progress/src/www/Views/master.cshtml):
@* Priority 0: Bootstrap base *@
<link rel="stylesheet" href="~/vendor/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/vendor/bootstrap/css/bootstrap-select.css" />
@* Priority 2: Menu styles *@
<link rel="stylesheet" href="~/vendor/menu4/css/style.css" />
Files affected: 6 layout templates (master, MasterLogin, MasterThirdParty, etc.) Referenced in: Reports 21, 23, 27
P5 -- Grid to BlockGrid Rendering¶
v8 used Html.GetGridHtml() helper to render the entire grid. v17 uses BlockGridModel with partial rendering through a multi-section grid partial.
v8 (psCreditUnion/Progress.Web/Views/contactus.cshtml):
@inherits Umbraco.Web.Mvc.UmbracoViewPage
@using Progress.Core.Extensions
<div class="container content-page">
@Html.GetGridHtml(Model, "grid", "ContactUsGrid", true, false)
</div>
v17 (dbl.Progress/src/www/Views/contactus.cshtml):
@using Umbraco.Cms.Core.Models.Blocks
<div class="container content-page">
@{
var grid = Model.Value<BlockGridModel>("grid");
if (grid != null)
{
@await Html.PartialAsync("blockgrid/_MultiSectionGrid", grid,
new ViewDataDictionary(ViewData) {
{ "UseContainer", true },
{ "ContainerClass", "container" }
}, true)
}
}
</div>
Files affected: 45 page templates Referenced in: Reports 21, 22, 12, 13
P6 -- SurfaceController to ViewComponent¶
v8 used SurfaceController (MVC controller) with ActionResult. v17 uses ViewComponent with async InvokeAsync().
v8 (psCreditUnion/Progress.Core/SurfaceControllers/CookieSurfaceController.cs):
using Umbraco.Web.Mvc;
public class CookieSurfaceController : SurfaceController
{
public ActionResult CookiesActionResult()
{
var GlobalCookiesService = new GlobalCookiesService();
List<CookiesItem> model = GlobalCookiesService.GetCookies();
return PartialView("~/Views/Partials/cookies/_main.cshtml", model);
}
}
v17 (dbl.Progress/src/www/ViewComponents/CookiesViewComponent.cs):
using Microsoft.AspNetCore.Mvc;
public class CookiesViewComponent : ViewComponent
{
private readonly IGlobalCookiesService _globalCookiesService;
public CookiesViewComponent(IGlobalCookiesService globalCookiesService)
{
_globalCookiesService = globalCookiesService;
}
public async Task<IViewComponentResult> InvokeAsync()
{
List<CookiesItem> model = await _globalCookiesService.GetCookies();
return View("~/Views/Partials/cookies/_main.cshtml", model);
}
}
Files affected: 8 controllers migrated to 7 ViewComponents Referenced in: Reports 24
P7 -- Service Instantiation to Constructor Injection¶
v8 used new to instantiate services. v17 uses constructor injection via DI.
v8:
public ActionResult CookiesActionResult()
{
var GlobalCookiesService = new GlobalCookiesService();
model = GlobalCookiesService.GetCookies();
}
v17:
private readonly IGlobalCookiesService _globalCookiesService;
public CookiesViewComponent(IGlobalCookiesService globalCookiesService)
{
_globalCookiesService = globalCookiesService;
}
Files affected: 14 services + 8 controllers Referenced in: Reports 24
P8 -- DropDown.Flexible JSON Wrapping¶
v8 stored dropdown values as plain strings ("medium"). v17's FlexibleDropdownPropertyValueConverter expects JSON arrays (["medium"]). Migrated data may be plain strings, so every read is wrapped in try-catch.
v8:
var height = Model.Value<string>("height");
if (height != null)
{
// always a plain string like "medium"
}
v17 (dbl.Progress/src/www/Views/standardPageWow.cshtml):
// height is Umbraco.DropDown.Flexible - migrated v8 data may be a plain string
// instead of JSON array
var height = "medium";
try { height = Model.Value<string>("height") ?? "medium"; } catch { }
Files affected: 20+ files with dropdown properties (gradient, buttonStyle, imageLocation, layout, layoutStyle, flip, display, height, zoomLevel) Referenced in: Reports 22, 29
P9 -- ColorPicker Double-Hash Fix¶
v8 stored color values WITHOUT # prefix (e47d51). v8 templates used #@color in style strings. After migration, EyeDropper stores WITH # prefix, producing ##e47d51. Fix: TrimStart('#') on every color read.
v8 (psCreditUnion/Progress.Web/Views/contactus.cshtml):
var backgroundColorNew = Model.Value<String>("headerColour");
if (backgroundColorNew != "")
{
backgroundColor = backgroundColorNew;
}
style = "background-color:#" + backgroundColor;
v17 (dbl.Progress/src/www/Views/contactus.cshtml):
var backgroundColorNew = Model.Value<string>("headerColour") ?? "";
if (!string.IsNullOrEmpty(backgroundColorNew))
{
backgroundColor = backgroundColorNew.TrimStart('#');
}
style = $"background-color:#{backgroundColor}";
Files affected: 20+ files (15 page templates, 5 video partials, 3 accordion/FAQ partials) Referenced in: Reports 21, 22
P10 -- Link Model (IEnumerable to Single Link)¶
v8 Multi-URL Picker returned IEnumerable<Link>. v17 single-link properties return Umbraco.Cms.Core.Models.Link directly.
v8 (psCreditUnion/Progress.Web/Views/Partials/Grid/Editors/DocTypeGridEditor/heroItem.cshtml):
@using Umbraco.Web.Models
var link = Model.Value<IEnumerable<Link>>("link");
if (link != null && link.Any())
{
var firstLink = link.First();
linkText = firstLink.Name;
}
v17 (dbl.Progress/src/www/Views/Partials/blockgrid/Components/buttonWidgetControl.cshtml):
var link = content.Value<Umbraco.Cms.Core.Models.Link>("link");
if (link != null)
{
<a href="@link.Url" target="@link.Target">@link.Name</a>
}
Files affected: 30+ component views with link properties Referenced in: Reports 22, 23
P11 -- Bootstrap 4 to Bootstrap 5 Attributes¶
Bootstrap 5 renamed all data-* attributes to data-bs-* and changed several utility classes.
v8:
<a data-toggle="collapse" data-parent="#jag-theme-5"
href="#collapse5_@i" aria-expanded="false"
aria-controls="collapse5_@i">
<span class="sr-only">Toggle</span>
v17:
<a data-bs-toggle="collapse" data-bs-parent="#jag-theme-5"
href="#collapse5_@i" aria-expanded="false"
aria-controls="collapse5_@i">
<span class="visually-hidden">Toggle</span>
Key replacements: data-toggle to data-bs-toggle, data-target to data-bs-target, data-dismiss to data-bs-dismiss, data-parent to data-bs-parent, data-slide to data-bs-slide, sr-only to visually-hidden, ml-/mr- to ms-/me-, pl-/pr- to ps-/pe-, float-left/float-right to float-start/float-end.
Files affected: 1,142 replacements across 159 files Referenced in: Reports 15, 17, 18, 22, 23, 27
P12 -- Async Partial Rendering¶
v8 used synchronous Html.CachedPartial(). v17 uses await Html.CachedPartialAsync() with TimeSpan cache duration.
v8 (psCreditUnion/Progress.Web/Views/master.cshtml):
@Html.CachedPartial("SiteLayout/headerMenu4", Model, 3600, true)
@Html.CachedPartial("SiteLayout/headerMenu3", Model, 3600, true)
@Html.CachedPartial("SiteLayout/header", Model, 3600, true)
v17 (dbl.Progress/src/www/Views/master.cshtml):
@await Html.CachedPartialAsync("SiteLayout/headerMenu4", Model,
TimeSpan.FromHours(1), cacheByPage: true)
@await Html.CachedPartialAsync("SiteLayout/headerMenu3", Model,
TimeSpan.FromHours(1), cacheByPage: true)
@await Html.CachedPartialAsync("SiteLayout/header", Model,
TimeSpan.FromHours(1), cacheByPage: true)
Files affected: 6 layout templates with cached partials Referenced in: Reports 21, 23
Report Navigation¶
| # | Report | Description |
|---|---|---|
| 20 | This document | Master index, dashboard stats, 12 transformation patterns |
| 21 | Page Template Comparison | Side-by-side comparison of all 48 v8 page templates to their v17 equivalents. Covers header images, grid rendering, color handling, and layout changes. |
| 22 | Component View Comparison | All 91 DTGE component views mapped to 121 blockgrid components. Covers BlockGridItem model binding, MediaWithCrops, dropdown try-catch, and link model changes. |
| 23 | Layout & Shared Partial Comparison | SiteLayout partials (headers, footers, navigation), BlockList components, and Forms theme partials. Covers ClientDependency removal and async rendering. |
| 24 | Controller & Service Comparison | SurfaceController to ViewComponent migration, service interface extraction, and DI registration in Program.cs. |
| 25 | Security & 2FA Comparison | v8 AngularJS 2FA plugins (21 files) consolidated to 8 C# files using Umbraco's built-in 2FA provider architecture. |
| 26 | Custom Editor Comparison | 52 AngularJS App_Plugins directories replaced by 32 TypeScript/Lit editor directories using the Umbraco Backoffice management API. |
| 27 | Static Asset Comparison | JavaScript and CSS file inventory. Bootstrap 4.4.1 to 5.3.3 upgrade, jQuery plugin audit, and CSS consolidation. |
| 28 | Configuration Comparison | Web.config XML hierarchy flattened to appsettings.json. Connection strings, SMTP, custom app settings, and Umbraco-specific configuration sections. |
| 29 | Model Comparison | 249 ModelsBuilder-generated models (now auto-generated at build) and 16 ViewModels reduced to 5 hand-maintained ViewModels. |
Known Remaining Gaps¶
The migration is approximately 95% complete. The following items remain open and are detailed in their respective reports:
- ~20 component views still need final review for missing sub-partials (Report 22)
- Calculator slider UI needs JavaScript refactoring for Bootstrap 5 compatibility (Reports 22, 18)
- Forms theme partials reduced from 79 to 13; remaining 66 are Umbraco Forms defaults that ship with the package (Report 23)
- Hardcoded API keys have been externalized to
appsettings.jsonbut need Azure Key Vault integration for production (Report 28) - Content usage audit needed for DataType 2236 and other low-usage types to determine if views are needed (Report 22)
- Settings deduplication deferred -- per-datatype element types are confirmed correct (Reports 11, 16)
- ModelsBuilder SourceCodeManual mode planned for Progress.Baseline.Core NuGet RCL (Report 29)
For a full prioritized list, see Report 00: Master Summary.
Cross-Reference: Patterns by Report¶
| Pattern | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
|---|---|---|---|---|---|---|---|---|---|
| P1 View Inheritance | X | X | X | ||||||
| P2 Media Picker | X | X | |||||||
| P3 XPath to DI | X | X | |||||||
| P4 ClientDependency | X | X | X | ||||||
| P5 Grid to BlockGrid | X | X | |||||||
| P6 SurfaceController | X | ||||||||
| P7 Service DI | X | ||||||||
| P8 DropDown.Flexible | X | X | |||||||
| P9 ColorPicker | X | X | |||||||
| P10 Link Model | X | X | |||||||
| P11 Bootstrap 5 | X | X | X | ||||||
| P12 Async Partials | X | X |