Report 24: Controllers & Services Comparison (v8 vs v17)¶
Generated: 2026-02-27 | Scope: All controllers, services, composing, helpers, extensions, ViewModels
1. Overview: Architectural Changes¶
The v8-to-v17 migration involved several fundamental architectural shifts:
| Pattern | v8 (Umbraco 8) | v17 (Umbraco 17) |
|---|---|---|
| Controller model | SurfaceController (MVC) |
ViewComponent (ASP.NET Core) or ApiController |
| DI pattern | new Service() inline instantiation |
Constructor injection via IServiceCollection |
| Caching | System.Runtime.Caching.MemoryCache.Default via static CacheLayer |
IMemoryCache injected via DI |
| HTTP calls | WebRequest / HttpClient inline |
IHttpClientFactory injected |
| Config | WebConfigurationManager.AppSettings[] |
IConfiguration["key"] |
| Composer | IUserComposer with Composition |
IComposer with IUmbracoBuilder |
| View rendering | return PartialView("path", model) |
return View("path", model) (ViewComponent) |
| Async | Task.Factory.StartNew() + .Wait(3000ms) |
Native async/await |
| Service registration | composition.Register<I, T>(Lifetime.Request) |
builder.Services.AddScoped<I, T>() |
| Content queries | Umbraco.ContentSingleAtXPath("//...") |
ISiteSettingsService injected |
| Custom view pages | ProgressViewPage<T> : UmbracoViewPage<T> |
@inject in Razor views |
| Logging | ILogger (Umbraco v8) |
ILogger<T> (Microsoft.Extensions.Logging) |
Key consolidation¶
- 8 SurfaceControllers (Cookie, Help, Instagram, MobileTerms, Notification, Privacy, TermsConditions, Twitter) collapsed into 7 ViewComponents (Cookies, Help, LoginStatus, Notification, Privacy, TermsConditions, Twitter)
- 6 calculator SurfaceControllers consolidated into 1 API controller (
CalculatorApiController) + 1 service (CalculatorService) - 5 Headless/Heartcore services (Cookies, Help, Notifications, Privacy, TermsConditions) refactored into 2 services (
HeadlessContentService+GlobalCookiesService+GlobalNotificationsService) - v8
CacheLayerstatic class eliminated -- all services useIMemoryCacheDI
2. Side-by-Side: CookieSurfaceController vs CookiesViewComponent¶
v8: Progress.Core.SurfaceControllers.CookieSurfaceController¶
File: psCreditUnion/Progress.Core/SurfaceControllers/CookieSurfaceController.cs
public class CookieSurfaceController : SurfaceController
{
public ActionResult CookiesActionResult()
{
var GlobalCookiesService = new GlobalCookiesService(); // Manual instantiation
List<CookiesItem> model = new List<CookiesItem>() { };
model = GlobalCookiesService.GetCookies(); // Synchronous
return PartialView("~/Views/Partials/cookies/_main.cshtml", model);
}
}
v17: www.ViewComponents.CookiesViewComponent¶
File: dbl.Progress/src/www/ViewComponents/CookiesViewComponent.cs
public class CookiesViewComponent : ViewComponent
{
private readonly IGlobalCookiesService _globalCookiesService;
public CookiesViewComponent(IGlobalCookiesService globalCookiesService)
{
_globalCookiesService = globalCookiesService; // DI injection
}
public async Task<IViewComponentResult> InvokeAsync()
{
List<CookiesItem> model = await _globalCookiesService.GetCookies(); // Async
return View("~/Views/Partials/cookies/_main.cshtml", model);
}
}
Key differences¶
| Aspect | v8 | v17 |
|---|---|---|
| Base class | SurfaceController |
ViewComponent |
| DI | new GlobalCookiesService() |
Constructor injection |
| Return type | ActionResult |
Task<IViewComponentResult> |
| Async | No (sync wrapper around async calls) | Native async/await |
| View invocation | PartialView() |
View() |
| Interface | None | IGlobalCookiesService |
3. Side-by-Side: Calculator Controllers¶
v8: CalculatorLargeSurfaceController (1 of 6)¶
File: psCreditUnion/Progress.Web/surfaceControllers/calculatorLargeSurfaceController.cs
The v8 approach had 6 separate surface controllers for different calculator UI variants:
- calculatorIconSurfaceController.cs
- calculatorInputSurfaceController.cs
- calculatorInputVarRateSurfaceController.cs
- calculatorLargeSurfaceController.cs
- calculatorSmallInputSurfaceController.cs
- calculatorSmallSurfaceController.cs
Each controller was ~260 lines, containing:
- MemoryCache.Default caching with addItemToCacheMin()
- Umbraco.ContentSingleAtXPath("//calculatorsConfiguration") content queries
- BuildLoansDD() and BuildSelectedDetails() helpers
- SubmitFormCalculations() and UpdateSelectedDropdown() POST actions
- Direct ProgressLoanCalculator.LoanCalculatorEstimator usage
public class CalculatorLargeSurfaceController : SurfaceController
{
[HttpPost]
public JsonResult SubmitFormCalculations(calculatorViewModelLarge model)
{
string sRequiredLoanAmt = model.RequiredLoanAmt.Remove(0, 1); // Remove currency symbol
string sTerm = Regex.Replace(model.RequiredTerm, " months", "", RegexOptions.IgnoreCase);
decimal loan = Convert.ToDecimal(sRequiredLoanAmt);
int months = Convert.ToInt32(sTerm);
string sRate = model.SelectedRate.Split('|')[0];
double rate = Convert.ToDouble(sRate);
LoanCalculatorEstimator lc = new LoanCalculatorEstimator();
LoanCalculationResults results = lc.CalculateRepayments(loan, months, rate);
return Json(results, JsonRequestBehavior.AllowGet);
}
}
v17: CalculatorApiController (single controller for all)¶
File: dbl.Progress/src/www/Controllers/CalculatorApiController.cs
[ApiController]
[Route("api/calculator")]
public class CalculatorApiController : ControllerBase
{
private readonly ILoanCalculatorEstimator _calculator;
public CalculatorApiController(ILoanCalculatorEstimator calculator)
{
_calculator = calculator; // DI injected
}
[HttpPost("calculate")]
public IActionResult Calculate([FromForm] CalculatorRequest request)
{
if (request.RequiredLoanAmt <= 0 || request.RequiredTerm <= 0)
return BadRequest("Invalid loan amount or term");
double apr = 0;
if (!string.IsNullOrEmpty(request.SelectedRate))
{
var parts = request.SelectedRate.Split('|');
if (parts.Length > 0) double.TryParse(parts[0], out apr);
}
var results = _calculator.CalculateRepayments(
principal: request.RequiredLoanAmt,
numberOfMonths: request.RequiredTerm,
annualInterestRate: apr);
return Ok(new CalculatorResponse { /* mapped fields */ });
}
}
Key differences¶
| Aspect | v8 | v17 |
|---|---|---|
| Controllers | 6 separate SurfaceControllers | 1 ApiController |
| Routing | Umbraco surface controller routing | [Route("api/calculator")] attribute routing |
| Input parsing | Manual string parsing (currency/months) | Strongly-typed CalculatorRequest DTO |
| Calculator | new LoanCalculatorEstimator() |
ILoanCalculatorEstimator injected |
| Response | Json(results, AllowGet) |
Ok(new CalculatorResponse { }) |
| Config data | Controller fetches from CMS via XPath | CalculatorService provides config |
| Error handling | Returns null | Returns BadRequest() |
| UI data (dropdown/details) | In controller | Moved to CalculatorService + Razor views |
4. Side-by-Side: GlobalCookiesService¶
v8: Progress.Core.Services.GlobalCookiesService¶
File: psCreditUnion/Progress.Core/Services/GlobalCookiesService.cs
public class GlobalCookiesService // No interface
{
string projectAlias = "thomas-s-quick-witted-raccoon";
public void addItemToCache(object objectToCache, string key)
{
ObjectCache cache = MemoryCache.Default; // Static cache
cache.Add(key, objectToCache, DateTime.Now.AddDays(1));
}
public List<CookiesItem> GetCookies() // Synchronous return
{
List<CookiesItem> myCacheCookies = CacheLayer.Get<List<CookiesItem>>("CookieItem");
if (myCacheCookies == null)
{
Task<PagedContent> response = null;
var tCookiesConditions = Task.Factory.StartNew(() => {
response = GetCookiesAsync();
});
tCookiesConditions.Wait(TimeSpan.FromMilliseconds(3000)); // Blocking wait
// ... process response ...
}
return myCacheCookies;
}
}
v17: www.Services.GlobalCookiesService¶
File: dbl.Progress/src/www/Services/GlobalCookiesService.cs
public class GlobalCookiesService : IGlobalCookiesService // Implements interface
{
private readonly IMemoryCache _cache; // DI injected
private readonly IConfiguration _configuration;
public GlobalCookiesService(IMemoryCache memoryCache, IConfiguration configuration)
{
_cache = memoryCache;
_configuration = configuration;
projectAlias = _configuration["ProgressSettings:ProjectAlias"]
?? throw new ArgumentNullException(nameof(configuration));
}
public async Task<List<CookiesItem>> GetCookies() // Async return
{
if (_cache.TryGetValue("CookieItem", out List<CookiesItem>? myCacheCookies)
&& myCacheCookies is not null && myCacheCookies.Any())
{
return myCacheCookies;
}
PagedContent response = await GetCookiesAsync(); // Native async
// ... process response ...
return myCacheCookies;
}
}
Key differences¶
| Aspect | v8 | v17 |
|---|---|---|
| Interface | None | IGlobalCookiesService |
| Caching | MemoryCache.Default static |
IMemoryCache DI-injected |
| Config | Hardcoded project alias | IConfiguration["ProgressSettings:ProjectAlias"] |
| Async | Task.Factory.StartNew().Wait(3000ms) |
Native async/await |
| Null safety | No null checks | Null-conditional operators, pattern matching |
5. Complete File Mapping Tables¶
5.1 Surface Controllers --> ViewComponents / ApiController¶
| v8 File | v8 Path | v17 File | v17 Path | Notes |
|---|---|---|---|---|
CookieSurfaceController.cs |
Progress.Core/SurfaceControllers/ |
CookiesViewComponent.cs |
www/ViewComponents/ |
1:1 migration |
HelpSurfaceController.cs |
Progress.Core/SurfaceControllers/ |
HelpViewComponent.cs |
www/ViewComponents/ |
1:1 migration |
NotificationSurfaceController.cs |
Progress.Core/SurfaceControllers/ |
NotificationViewComponent.cs |
www/ViewComponents/ |
2 actions merged to 1 |
PrivacySurfaceController.cs |
Progress.Core/SurfaceControllers/ |
PrivacyViewComponent.cs |
www/ViewComponents/ |
2 actions merged to 1 |
TermsConditionsSurfaceController.cs |
Progress.Core/SurfaceControllers/ |
TermsConditionsViewComponent.cs |
www/ViewComponents/ |
1:1 migration |
TwitterSurfaceController.cs |
Progress.Core/SurfaceControllers/ |
TwitterViewComponent.cs |
www/ViewComponents/ |
1:1 migration |
InstagramSurfaceController.cs |
Progress.Core/SurfaceControllers/ |
-- | -- | Removed (Instagram API deprecated) |
MobileTermsConditionsSurfaceController.cs |
Progress.Core/SurfaceControllers/ |
-- | -- | Merged into HeadlessContentService |
| -- | -- | LoginStatusViewComponent.cs |
www/ViewComponents/ |
New in v17 |
5.2 Calculator Controllers¶
| v8 File | v8 Path | v17 File | v17 Path | Notes |
|---|---|---|---|---|
calculatorLargeSurfaceController.cs |
Progress.Web/surfaceControllers/ |
CalculatorApiController.cs |
www/Controllers/ |
All 6 consolidated |
calculatorInputSurfaceController.cs |
Progress.Web/surfaceControllers/ |
CalculatorApiController.cs |
www/Controllers/ |
Merged |
calculatorInputVarRateSurfaceController.cs |
Progress.Web/surfaceControllers/ |
CalculatorApiController.cs |
www/Controllers/ |
Merged |
calculatorIconSurfaceController.cs |
Progress.Web/surfaceControllers/ |
CalculatorApiController.cs |
www/Controllers/ |
Merged |
calculatorSmallSurfaceController.cs |
Progress.Web/surfaceControllers/ |
CalculatorApiController.cs |
www/Controllers/ |
Merged |
calculatorSmallInputSurfaceController.cs |
Progress.Web/surfaceControllers/ |
CalculatorApiController.cs |
www/Controllers/ |
Merged |
| -- | -- | CalculatorService.cs |
www/Services/ |
New - CMS config access |
5.3 Services¶
| v8 File | v8 Path | v17 File | v17 Path | Notes |
|---|---|---|---|---|
ArticleService.cs |
Progress.Core/Services/ |
ArticleService.cs |
www/Services/ |
Migrated; HttpRequestBase --> HttpRequest |
IArticleService.cs |
Progress.Core/Services/ |
IArticleService.cs |
www/Services/ |
Migrated |
GlobalCookiesService.cs |
Progress.Core/Services/ |
GlobalCookiesService.cs |
www/Services/ |
Migrated; added IGlobalCookiesService |
GlobalNotificationsService.cs |
Progress.Core/Services/ |
GlobalNotificationsService.cs |
www/Services/ |
Migrated; IHttpClientFactory replaces raw SDK |
TwitterService.cs |
Progress.Core/Services/ |
TwitterService.cs |
www/Services/ |
Full rewrite; IHttpClientFactory, System.Text.Json |
GlobalHelpCenter.cs |
Progress.Core/Services/ |
HeadlessContentService.cs |
www/Services/ |
Consolidated into general headless service |
GlobalPrivacyService.cs |
Progress.Core/Services/ |
HeadlessContentService.cs |
www/Services/ |
Consolidated |
GlobalTermsConditionsService.cs |
Progress.Core/Services/ |
HeadlessContentService.cs |
www/Services/ |
Consolidated |
GlobalMobileTermsConditionsService.cs |
Progress.Core/Services/ |
HeadlessContentService.cs |
www/Services/ |
Consolidated |
CommonBondValidationService.cs |
Progress.Core/Services/ |
CommonBondValidationService.cs |
Progress.CustomPropertyEditors/Forms/Services/ |
Moved to Forms project |
GalleryService.cs |
Progress.Core/Services/ |
-- | -- | Not migrated (see Section 6) |
IGalleryService.cs |
Progress.Core/Services/ |
-- | -- | Not migrated |
InstagramService.cs |
Progress.Core/Services/ |
-- | -- | Removed (API deprecated) |
MigrateNewsCategoriesComposer.cs |
Progress.Core/Services/ |
-- | -- | One-time migration, not needed |
| -- | -- | CalculatorService.cs |
www/Services/ |
New |
| -- | -- | ICalculatorService.cs |
www/Services/ |
New |
| -- | -- | DictionaryService.cs |
www/Services/ |
New - replaces Umbraco.GetDictionaryValue() |
| -- | -- | IDictionaryService.cs |
www/Services/ |
New |
| -- | -- | IHeadlessContentService.cs |
www/Services/ |
New |
| -- | -- | ITwitterService.cs |
www/Services/ |
New |
| -- | -- | IGlobalCookiesService.cs |
www/Services/ |
New |
| -- | -- | IGlobalNotificationsService.cs |
www/Services/ |
New |
| -- | -- | CacheCleanupService.cs |
www/Services/ |
New - pre-startup cache cleaning |
| -- | -- | DatabaseCleanupService.cs |
www/Services/ |
New - pre-startup DB cleaning |
5.4 Composing / Composers¶
| v8 File | v8 Path | v17 File | v17 Path | Notes |
|---|---|---|---|---|
RegisterServicesComposer.cs |
Progress.Core/Composing/ |
ServiceComposer.cs |
www/Composing/ |
Migrated; registers Twitter, Cookies |
CommonBondValidationComposer.cs |
Progress.Core/Composing/ |
CommonBondValidationHandler.cs |
CustomPropertyEditors/Forms/Validation/ |
Refactored to notification handler |
LostYourPinComposer.cs |
Progress.Core/Composing/ |
LostYourPinValidationHandler.cs + LostYourPinPrePopulateHandler.cs |
CustomPropertyEditors/Forms/Validation/ |
Split into separate handlers |
MortgageCalculationComposer.cs |
Progress.Core/Composing/ |
MortgageCalculationHandler.cs |
CustomPropertyEditors/Forms/Workflows/ |
Moved to forms project |
ReCaptchaFallbackComposer.cs |
Progress.Core/Composing/ |
ReCaptchaFallbackHandler.cs + FriendlyCaptchaValidationHandler.cs |
CustomPropertyEditors/Forms/Validation/ |
Split |
RemoveDashBoard.cs |
Progress.Core/Composing/ |
RemoveDashboards/ |
www/App_Plugins/RemoveDashboards/ |
Client-side approach in v17 |
SubscribeToContentServiceSavingComposer.cs |
Progress.Core/Composing/ |
-- | -- | ImageProcessor/WebP removed (v17 uses ImageSharp). Content saving permissions TBD |
TreeNodeRenderingComposer.cs |
Progress.Core/Composing/ |
-- | -- | Not needed (v17 backoffice handles icons natively) |
UserServiceComposer.cs |
Progress.Core/Composing/ |
-- | -- | Not migrated yet (user group protection) |
| -- | -- | McpApiUserComposer.cs |
www/Composing/ |
New - creates MCP API user on startup |
ExcludeItems.cs |
Progress.Web/umbracoFormsHideField/ |
UrlValidationNotificationHandler.cs |
CustomPropertyEditors/Forms/Validation/ |
URL validation extracted |
5.5 Extensions¶
| v8 File | v8 Path | v17 File | v17 Path | Notes |
|---|---|---|---|---|
HtmlExtensions.cs |
Progress.Core/Extensions/ |
HtmlExtensions.cs |
www/Extensions/ |
GetGridHtml removed (no Grid); Breadcrumb commented out |
EnumerableExtensions.cs |
Progress.Core/Extensions/ |
EnumerableExtensions.cs |
www/Extensions/ |
Direct port; List<T> --> collection expression [] |
IPublishedContentExtensions.cs |
Progress.Core/Extensions/ |
-- | -- | IsHomePage() inlined where needed |
HttpRequestBaseExtensions.cs |
Progress.Core/Extensions/ |
-- | -- | HttpRequestBase does not exist in ASP.NET Core |
| -- | -- | BlockGridExtensions.cs |
www/Extensions/ |
New - replaces v8 Grid rendering helpers |
5.6 Helpers¶
| v8 File | v8 Path | v17 File | v17 Path | Notes |
|---|---|---|---|---|
CacheLayer.cs |
Progress.Core/Helpers/ |
-- | -- | Removed - replaced by IMemoryCache DI |
HtmlHelpers.cs |
Progress.Core/Helpers/ |
-- | -- | Functionality moved to extensions/views |
QueryStringHelper.cs |
Progress.Core/Helpers/ |
-- | -- | Inlined into ArticleService as GetIntFromQueryString() |
ResponsiveHelper.cs |
Progress.Core/Helpers/ |
-- | -- | ImageProcessor-based; replaced by ImageSharp in v17 |
TagAttributes.cs |
Progress.Core/Helpers/ |
-- | -- | Used by ResponsiveHelper only |
| -- | -- | CssHelper.cs |
www/Helpers/ |
New - replaces ClientDependency RequiresCss() |
| -- | -- | ScriptHelper.cs |
www/Helpers/ |
New - replaces ClientDependency RequiresJs() |
5.7 ViewModels¶
| v8 File | v8 Path | v17 File | v17 Path | Notes |
|---|---|---|---|---|
CookiesList.cs |
Progress.Core/ViewModels/ |
CookiesList.cs |
www/ViewModels/ |
Migrated |
notificationList.cs |
Progress.Core/ViewModels/ |
NotificationItem.cs |
www/ViewModels/ |
Renamed; PascalCase |
ArticleResultSet.cs |
Progress.Core/ViewModels/ |
ArticleResultSet.cs |
www/ViewModels/ |
Migrated |
TweetSearchResponse.cs |
Progress.Core/ViewModels/ |
TweetSearchResponse.cs |
www/ViewModels/ |
Migrated; added errorMessage field |
HelpList.cs |
Progress.Core/ViewModels/ |
-- | -- | Merged into HeadlessContentService models |
HelpArticleIdList.cs |
Progress.Core/ViewModels/ |
-- | -- | Merged |
PrivacyList.cs |
Progress.Core/ViewModels/ |
-- | -- | Merged into HeadlessContent.cs |
TermsConditionsList.cs |
Progress.Core/ViewModels/ |
-- | -- | Merged |
MobileTermsConditionsList.cs |
Progress.Core/ViewModels/ |
-- | -- | Merged |
GalleryResultSet.cs |
Progress.Core/ViewModels/ |
-- | -- | Not migrated (GalleryService not migrated) |
Instagram.cs |
Progress.Core/ViewModels/ |
-- | -- | Removed |
| -- | -- | HeadlessContent.cs |
www/ViewModels/ |
New - consolidates Help/Privacy/T&C models |
Calculator ViewModels:
| v8 File | v8 Path | v17 File | v17 Path | Notes |
|---|---|---|---|---|
calculatorViewModelLarge.cs |
Progress.Web/ViewModels/ |
CalculatorRequest + CalculatorResponse |
www/Controllers/ (inline) |
Inlined into API controller |
calculatorViewModelInput.cs |
Progress.Web/ViewModels/ |
-- | -- | Merged into single request model |
calculatorViewModelInputVarRate.cs |
Progress.Web/ViewModels/ |
-- | -- | Merged |
calculatorViewModelIcon.cs |
Progress.Web/ViewModels/ |
-- | -- | Merged |
calculatorViewModelSmall.cs |
Progress.Web/ViewModels/ |
-- | -- | Merged |
5.8 ViewPages¶
| v8 File | v8 Path | v17 File | v17 Path | Notes |
|---|---|---|---|---|
ProgressViewPage.cs |
Progress.Core/ViewPages/ |
-- | -- | Removed - v17 uses @inject instead |
ProgressGalleryViewPage.cs |
Progress.Core/ViewPages/ |
-- | -- | Removed |
5.9 PropertyValueConverters¶
| v8 File | v8 Path | v17 File | v17 Path | Notes |
|---|---|---|---|---|
| -- | -- | OpeningSoonValueConverter.cs |
www/PropertyValueConverters/ |
New - converts OpeningSoon JSON to IEnumerable<OpeningHoursDay> |
5.10 Umbraco Forms¶
| v8 File | v8 Path | v17 File | v17 Path | Notes |
|---|---|---|---|---|
DropDownYears.cs |
Progress.Web/UmbracoFormsExtension/ |
DropdownListWithYears.cs |
CustomPropertyEditors/Forms/FieldTypes/ |
Migrated |
FriendlyCaptcha.cs |
Progress.Web/UmbracoFormsExtension/ |
FriendlyCaptchaField.cs |
CustomPropertyEditors/Forms/FieldTypes/ |
Migrated |
LostYourPinField.cs |
Progress.Web/UmbracoFormsExtension/ |
LostYourPinField.cs |
CustomPropertyEditors/Forms/FieldTypes/ |
Migrated |
MortgageCalculatorResult.cs |
Progress.Web/UmbracoFormsExtension/ |
MortgageCalculatorResult.cs |
CustomPropertyEditors/Forms/FieldTypes/ |
Migrated |
TextareaWithCount.cs |
Progress.Web/UmbracoFormsExtension/ |
TextareaWithCount.cs |
CustomPropertyEditors/Forms/FieldTypes/ |
Migrated |
TextfieldWithCount.cs |
Progress.Web/UmbracoFormsExtension/ |
TextfieldWithCount.cs |
CustomPropertyEditors/Forms/FieldTypes/ |
Migrated |
CancelUploadedFiles.cs |
Progress.Web/UmbracoFormsWorkFlows/ |
CancelUploadedFilesWorkflow.cs |
CustomPropertyEditors/Forms/Workflows/ |
Migrated |
PostAsJson.cs |
Progress.Web/UmbracoFormsWorkFlows/ |
PostAsJsonWorkflow.cs |
CustomPropertyEditors/Forms/Workflows/ |
Migrated |
New Forms files in v17 (no v8 equivalent):
| v17 File | v17 Path | Notes |
|---|---|---|
CommonBondMigrationPlan.cs |
Forms/Migrations/ |
EF Core migration for CommonBond table |
CreateCommonBondTableMigration.cs |
Forms/Migrations/ |
Table creation migration |
CommonBondRange.cs |
Forms/Models/ |
EF Core model |
LostYourPinModels.cs |
Forms/Models/ |
API request/response models |
ICommonBondValidationService.cs |
Forms/Services/ |
Interface (v8 had no interface) |
ILostYourPinService.cs |
Forms/Services/ |
Interface |
LostYourPinService.cs |
Forms/Services/ |
Extracted from composer |
CommonBondValidationHandler.cs |
Forms/Validation/ |
Notification handler |
FriendlyCaptchaValidationHandler.cs |
Forms/Validation/ |
Notification handler |
LostYourPinPrePopulateHandler.cs |
Forms/Validation/ |
Notification handler |
LostYourPinValidationHandler.cs |
Forms/Validation/ |
Notification handler |
ReCaptchaFallbackHandler.cs |
Forms/Validation/ |
Notification handler |
UrlValidationNotificationHandler.cs |
Forms/Validation/ |
Notification handler |
MortgageCalculationHandler.cs |
Forms/Workflows/ |
Workflow handler |
6. Gaps: Why Some Services Were Not Migrated¶
GalleryService + IGalleryService¶
v8 files: Progress.Core/Services/GalleryService.cs, Progress.Core/Services/IGalleryService.cs
The GalleryService provides paginated gallery content using IPublishedContent.Descendants(). It is structurally identical to ArticleService but for gallery content types. It was not migrated because:
1. The gallery feature is not actively used by Progress Credit Union
2. The galleryItems content type may not exist in the v17 destination database
3. If needed, it can be added by copying the ArticleService pattern and changing "article" to "galleryItems"
InstagramService + InstagramSurfaceController¶
v8 files: Progress.Core/Services/InstagramService.cs, Progress.Core/SurfaceControllers/InstagramSurfaceController.cs
Removed because:
1. Instagram deprecated the /?__a=1 endpoint used by the v8 service
2. The OAuth code contained hardcoded client credentials from a specific Instagram app
3. Instagram now requires Facebook Graph API with a Business account
4. The feature was rarely used and can be replaced with an embedded Instagram widget if needed
MigrateNewsCategoriesComposer¶
v8 file: Progress.Core/Services/MigrateNewsCategoriesComposer.cs
This was a one-time data migration composer that ran on v8 startup to restructure news categories. It is not needed in v17 because the migration tool handles data transformation.
SubscribeToContentServiceSavingComposer (partial)¶
v8 file: Progress.Core/Composing/SubscribeToContentServiceSavingComposer.cs
The ImageProcessor WebP conversion logic is no longer needed (v17 uses ImageSharp). The unpublish permission check and calculator cache invalidation on content save have not been migrated yet. The calculator cache invalidation is partially handled by CalculatorService's time-based cache expiry.
UserServiceComposer¶
v8 file: Progress.Core/Composing/UserServiceComposer.cs
Protects the creditUnionAdministrators and creditUnionEditor user groups from modification/deletion by non-admin users. This has not been migrated to v17 yet. In v17, this would use INotificationHandler<UserGroupSavingNotification> and INotificationHandler<UserGroupDeletingNotification>.
TreeNodeRenderingComposer¶
v8 file: Progress.Core/Composing/TreeNodeRenderingComposer.cs
Updates backoffice tree node icons to match content type icons. Not needed in v17 because the new Bellissima backoffice handles this natively.
7. Summary Statistics¶
| Category | v8 Count | v17 Count | Migrated | New | Removed/Deferred |
|---|---|---|---|---|---|
| Surface Controllers | 14 (8 core + 6 calc) | 0 | -- | -- | Superseded |
| ViewComponents | 0 | 7 | -- | 7 | -- |
| API Controllers | 0 | 1 | -- | 1 | -- |
| Services | 14 | 16 | 7 | 9 | 3 removed, 1 deferred |
| Composing | 9 + 1 ExcludeItems | 2 | 1 | 1 | 8 refactored to Forms handlers |
| Extensions | 4 | 3 | 2 | 1 | 2 removed |
| Helpers | 5 | 2 | 0 | 2 | 5 removed |
| ViewModels | 16 (11 core + 5 web) | 5 | 4 | 1 | 11 removed/merged |
| ViewPages | 2 | 0 | 0 | 0 | 2 removed |
| PropertyValueConverters | 0 | 1 | 0 | 1 | -- |
| Forms FieldTypes | 6 | 6 | 6 | 0 | -- |
| Forms Workflows | 2 | 3 | 2 | 1 | -- |
| Forms Validation | 0 (in composers) | 6 | -- | 6 | Extracted from composers |
| Forms Services | 0 (inline) | 4 | -- | 4 | Extracted |
| Forms Models | 0 (inline) | 2 | -- | 2 | Extracted |
| Forms Migrations | 0 | 2 | -- | 2 | New EF Core |
| Total | ~68 | ~60 | ~22 | ~37 new | ~18 removed |