Content Finders

Display global data in local web pages

Content Finders are an extremely powerful Umbraco feature that allows the serving of global data from a base page. Its only takes a few lines of code to connect these up.

For example, News Pages. 

First you would create a Document Type that will be used as a local webpage for listing all news articles, maybe it display 10 news articles at a time in paged fashion, thats up to you. One of the properties on that Document Type is a News Article Global Data MultiNode TreePicker, this allows the content editor to select from which pool of News Articles they wish to display. For this example the Content Editor uses the Document Type and creates a page called 'News', which means the Url of that page is /news/. But in reality the Content Editor is free to call it whatever they like.

Next add code that Creates a ContentFinder, this code will run whenever a front end user requests an Url that doesn't currently match an existing Webpage. The idea is this ContentFinder will match Url requests that are valid and ignore any requests that aren't. In this example, if the end user tries to surf to '/news/latest-chocolate-news' 
and one of the Global Data items that was picked by the Content Editor is named 'Latest Chocolate News', then we wish to show content from that Global Data Item using our Document Type.

Our job is to write code that takes '/news/latest-chocolate-news' and then tells Umbraco to send that request as if they had typed '/news/'

ContentFinders have Urls as their Input value and for Output allow you to say which existing Local Webpage Node you wish to handle that request. Then Umbraco will fire off the relevant Document Type Controller or Razor code applicable for that Document Type you select as Output.

  • Create Content Finders to serve global data

Powering your content via Content Finders

Example Code

Example NewsContentFinder for Umbraco v8

This creates and initailises a ContentFinder that responds to any valid Urls that match News Items. The process continues by routing to the News List Page passing the News Item Id

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Web;
using Umbraco.Web.Routing;

namespace MyCode
{
  [RuntimeLevel(MinLevel = RuntimeLevel.Run)]
  public class NewsContentFinderComposer : IUserComposer
  {
    public void Compose(Composition composition)
    {
      // Add our custom ContentFinder just after the core ContentFinderByUrl
      composition.ContentFinders().InsertAfter<ContentFinderByUrl, NewsContentFinder>();
    }
  }

  public class NewsContentFinder : IContentFinder
  {
    //  Return true if the request is a valid News Item Url, false if its not
    bool IContentFinder.TryFindContent(PublishedRequest request)
    {
      var path = request.Uri.GetComponents(UriComponents.Path, UriFormat.Unescaped);

      //  Split the Url into slugs, for example "/news/chocolate" becomes ["news", "chocolate"]
      var slugs = path.Split('/').Where(s => !string.IsNullOrWhitespace(s));
      if (slugs.Length < 2)
      {
        return false;  //  We don't have a long enough Url for it to match a News Item
      }
      
      var newsListingPageUrl = string.Join('/', slugs.Take(slugs.Length - 1));  //  Remove the last slug
      
      // See if Umbraco says this base page exists
      var newsListingPage = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetByRoute(newsListingPageUrl);
      if (newsListingPage == null)
      {
        return false;  //  The base page isn't a valid Umbraco node
      }
      if (newsListingPage.ModelTypeAlias != ModelsBuilder.NewsPage)
      {
        return false;  //  The base page isn't a News Listing page
      }

      // We have a valid News List page, get the global data that the Content Editor has selected 
      var newsPage = (ModelsBuilder.NewsPage) newsListingPage;
      var newsItems = newsPage.GlobalNewsArticles.SelectMany(e => e.DescendantsOrSelfOfType(ModelsBuilder.NewsItem.ModelTypeAlias).Cast<ModelsBuilder.NewsItem>());
      var newsItemPageUrl = slugs[slugs.Length - 1];  //  Url of News Items
      
      var match = newsItems.Where(n => strint.Compare(n.Url(), newsItemPageUrl, true) == 0);
      if (match == null || !match.Any())
      {
        return false;   //  The last slug doesn't match a News Item Url
      }
      
      //  We have a match, so tell the News Page Controller which News Item matched
      request.UmbracoContext.HttpContext.Items["NewsItemId"] = match.First().Id;
      request.PublishedContent = newsPage;
      return true;
    }
  }
}

 

Notes: ModelsBuilder.NewsPage is the Document Type of the News Listing Page which lives in Local Data and ModelsBuilder.NewsItem is the Document Type for a News Item being stored somewhere in Global Data