Skip to main content

Outline

At a glance
  • Anchor requirement: Every virtual route needs a native IContent anchor to maintain the CMS editing context and toolbar.
  • Context awareness: Routers must detect ContextMode.Edit to provide mock data or fallbacks, ensuring the UI remains functional during external outages.
  • OPE integration: Wrapping external POCOs in IContent implementations enables "Click-to-Edit" overlays and property-level feedback.
  • Navigation integrity: Manual decoration of breadcrumbs and consistent URL generation are required to prevent broken hierarchies and 404s in the CMS.

In an Optimizely CMS 13 environment, implementing partial routing for external data often creates a "user experience gap" where the boundary between native and virtual content becomes visible to editors. When segments exist only in the URL and not in the database, standard CMS features—such as On-Page Editing (OPE), preview panes, and breadcrumbs—may fail to function. Ensuring that routing patterns align with editor expectations is critical for maintaining a unified editorial workspace.

1. The Principle of the Anchor Page

For the Optimizely Editor UI to function, it requires a "Content Anchor"—a native page in the IContent repository that the UI can "hook" into for context. If a router consumes all segments without properly identifying an anchor, the editor's toolbar and edit overlays often disappear because the system cannot identify which "Edit Mode" context to load.

Maintaining Core Context

  • The RoutedContentLink Requirement: In the implementation of IPartialRouter, the RoutedContentLink property of the SegmentContext should remain pointing to the native listing page (the anchor). This signals to the CMS that while the data is virtual, the contextual container is an existing editorial page.
  • Segment Consumption Logic: Partial routers must explicitly consume only the segments they can resolve. If a segment is consumed but the router returns a null object, the CMS will return a 404, preventing the editor from even reaching the preview pane for that URL.

2. Context-Aware Routing for Edit Mode

The IPartialRouter must be aware of the ContextMode (Edit, Preview, or Default). This distinction is vital because the resolution logic for a website visitor might differ from the logic needed for a marketer inside the CMS.

Handling ContextMode.Edit

When a page is loaded in the CMS Edit UI, Optimizely appends specific query parameters (e.g., epieditmode=true). A robust routing pattern detects this state via the IContextModeResolver or by inspecting the UrlGeneratorContext.

  • Edit-Mode Fallbacks: If an external system is experiencing a temporary outage, the router should return a "Mock" or "Skeleton" object during ContextMode.Edit. This allows the editor to still see the page layout and access CMS-managed properties (like headers or footers) even if the external data is missing.
  • Versioning and Drafts: When routing to external items, the path often maps to a "Live" ID. However, if the external system supports drafting, the router implementation should handle "Stage" indicators or query segments to allow editors to preview changes before they go live on the external source.

3. Enabling On-Page Editing (OPE) for Virtual Content

One of the greatest points of friction in partial routing is the loss of the "Click-to-Edit" overlay. Standard OPE works by injecting data-attributes into the HTML that map to CMS properties. Since virtual objects (POCOs) lack these properties, the UI remains static.

Mapping to IContent Models

A more sophisticated pattern involves wrapping the external POCO in a lightweight implementation of IContent.

  1. The Wrapper Strategy: Instead of returning a raw ExternalProduct class from the router, return a class that implements IContent and maps the external fields to standard Optimizely properties (e.g., XhtmlString, ContentReference).
  2. The Property Advantage: By providing an IContent implementation, developers can use the @Html.PropertyFor() helper in the Razor view. Even though the "Save" action won't write to the external database, the "Edit Hints" will appear, and editors can interact with the page structure.

4. Sitemaps, Breadcrumbs, and Navigation

Standard CMS navigation builders (like those utilizing IContentLoader.GetChildren) are blind to virtual segments. This results in "broken" breadcrumbs where the trail stops at the native parent page.

  • Hierarchy Decoration: To maintain editor expectations, the navigation and breadcrumb components must be "decorated" or overridden. When the current content is a dynamic external item, the component logic should manually inject a "Virtual Breadcrumb" segment representing the current item name.
  • URL Rejection Handling: In GetPartialVirtualPath, the router must ensure that the generated URLs are absolute and consistent. If the router generates a URL that the UrlResolver cannot resolve back to an object, the "View on site" button in the CMS will lead to a 404, breaking the editor's verification workflow.

Implementation Example: Robust Contextual Router

public class ResilientProductRouter : IPartialRouter<ProductListingPage, ExternalProduct> { private readonly IExternalService _service; private readonly IContextModeResolver _modeResolver; public ResilientProductRouter(IExternalService service, IContextModeResolver modeResolver) { _service = service; _modeResolver = modeResolver; } public object RoutePartial(ProductListingPage content, SegmentContext context) { var nextValue = context.GetNextValue(context.RemainingSegments); var id = nextValue.Next; if (string.IsNullOrEmpty(id)) return null; // Determine if we are in the CMS Edit UI var isEditMode = _modeResolver.CurrentMode == ContextMode.Edit; try { var product = _service.GetProductById(id); if (product != null) { context.RemainingSegments = nextValue.Remaining; // Crucial: Keep the routed content link pointing to the anchor context.RoutedContentLink = content.ContentLink; return product; } } catch (Exception) { // Defensive pattern: If in Edit Mode, return a dummy object // so the editor can still see the page wrapper. if (isEditMode) { return new ExternalProduct { Name = "Data Unavailable (Edit Mode Only)", Id = id }; } } return null; } public PartialRouteData GetPartialVirtualPath(ExternalProduct content, UrlGeneratorContext context) { return new PartialRouteData { BasePathIdentifier = content.ParentLink, PartialVirtualPath = $"{content.Id}/" }; } }

5. Best Practices

  • Always identify a Content Anchor: Ensure RoutedContentLink is never null or points to a non-existent reference.
  • Register Renderers for POCOs: Use TemplateDescriptor to tell the CMS how to render external objects in the Preview pane.
  • Handle EditMode Separately: Use fallbacks or mock data during routing to ensure the CMS UI remains functional during external system downtime.
  • Decorate Navigation UI: Manually append virtual segments to breadcrumb and menu lists to prevent the appearance of a broken hierarchy.

Conclusion

The implementation of partial routing in Optimizely CMS 13 represents a powerful architectural pattern, but its success is measured by the seamlessness of the editor's workflow. By adhering to the principle of the Anchor Page and utilizing context-aware routing with IContextModeResolver, developers can bridge the gap between volatile external data and the stable environment required for CMS editorial tasks. Transitioning from raw data lookups to resilient, UI-aware patterns ensures that federated content is managed with the same precision and ease as native CMS pages.