IDataUrlMapper

Map data references to URLs

Data URL mappers define the relation between URLs and data references. For example, these mappers enable such CMS Console features as previewing data while navigating the console tree.

To use data URL mappers, you should:

  1. Create a class implementing the Composite.Core.Routing.IDataUrlMapper interface.
    namespace Composite.Core.Routing
    {
        public interface IDataUrlMapper
        {
            IDataReference GetData(PageUrlData pageUrlData);
            PageUrlData GetPageUrlData(IDataReference instance);
        }
    }
     
  2. Register an instance with one of the following methods:
    Composite.Core.Routing.DataUrls.RegisterGlobalDataUrlMapper(Type dataType,
        IDataUrlMapper dataUrlMapper)
    Composite.Core.Routing.DataUrls.RegisterStaticDataUrlMapper(Guid pageId,
        Type dataType, IDataUrlMapper dataUrlMapper)
    Composite.Core.Routing.DataUrls.RegisterDynamicDataUrlMapper(Guid pageId,
        Type dataType, IDataUrlMapper dataUrlMapper)
  • RegisterGlobalDataUrlMapper: registers a data URL mapper for a given data type
  • RegisterStaticDataUrlMapper: registers a data URL mapper for a given data type and for a specific page
  • RegisterDynamicDataUrlMapper: similar to RegisterStaticDataUrlMapper, registers a data URL mapper for a given data type and for a specific page but the instance will be cleared when the page is edited.

Both RegisterGlobalDataUrlMapper and RegisterStaticDataUrlMapper are to be called only once at the website startup while RegisterDynamicDataUrlMapper is to be called during page rendering, allowing CMS Functions to both view the data and register a mapper, so the console could be "aware" of the relation.

(The RoutedData<T> functionality is implemented using RegisterDynamicDataUrlMapper.)

While URLs for a data reference are getting built, the mappers are used in this order:

  1. The global mappers are used first.
  2. If a data item inherits IPageRelatedData (all the page data folder types do so), static mappers are then used to resolve the URL
  3. If the URL has not been resolved yet, the system will render the page (if not rendered yet) to collect and use dynamically attached data URL mappers.

Using IDataUrlMapper

In the following example, you will enable previewing data of the dynamic global data type "Demo.News" on the page "News":

The Demo.News type's fields:

  • Id: Guid
  • Title: String (64)
  • Date: Date
  • Content: String (1024), Widget: TextArea

(You can use the Demo.News package to automatically create this data type with some fictitious data.)

If you are using your own data type, change the code accordingly.

In this example, you will:

  1. Define a data URL mapper
  2. Create its instance and register it in a startup handler
  3. Create a CMS Function to display the data

Create corresponding classes (Steps 1 and 2) and copy the output DLL to /Bin on your website.

Defining a data URL mapper

using Composite.Core.Routing;
using Composite.Core.Types;
using Composite.Data;
using System;
using System.Linq;

namespace Composite.Demo
{
    public class NewsDataMapper : IDataUrlMapper
    {
        private readonly Guid _pageId;
        private readonly Type _dataType;
        private readonly Type _keyType;

        public NewsDataMapper(Type dataType, Guid pageId)
        {
            _pageId = pageId;
            _dataType = dataType;
            _keyType = _dataType.GetKeyProperties().Single().PropertyType;
        }

        public IDataReference GetData(PageUrlData pageUrlData)
        {
            if (pageUrlData.PageId != _pageId)
            {
                return null;
            }

            string pathInfo = pageUrlData.PathInfo;

            if (string.IsNullOrEmpty(pathInfo) || pathInfo.Length == 1)
            {
                return null;
            }

            string[] parts = pathInfo.Split('/');

            if (parts.Length != 2 || string.IsNullOrEmpty(parts[1]))
            {
                return null;
            }

            string keyString = parts[1];

            object keyValue = ValueTypeConverter.Convert(keyString, _keyType);

            var data = keyValue != null ? DataFacade.TryGetDataByUniqueKey(_dataType, keyValue) : null;

            return data != null ? data.ToDataReference() : null;
        }

        public PageUrlData GetPageUrlData(IDataReference instance)
        {
            if (instance.ReferencedType != _dataType)
            {
                return null;
            }

            var page = PageManager.GetPageById(_pageId);

            if (page == null)
            {
                return null;
            }

            var key = instance.KeyValue;

            return new PageUrlData(page) { PathInfo = "/" + key };
        }

    }
}

Download the code

Creating and registering an instance of the mapper

Note. In this example, a hard-coded ID of the page where the data will show is used.

using Composite.Core.Application;
using Composite.Core.Routing;
using Composite.Data;
using System;
using System.Linq;

namespace Composite.Demo
{
    [ApplicationStartup]
    public static class StartupHandler
    {
        // The ID of the page to show the data of the Demo.News type on - change accordingly
        static readonly Guid TargetPageId = new Guid("055a1076-099b-420c-a9cc-f591e0819782"); 
        
        public static void OnBeforeInitialize()
        {

        }

        public static void OnInitialized()
        {
            // Finding a dynamic data type
            var dataType = DataFacade.GetAllInterfaces().FirstOrDefault(u => u.Name == "News");

            // var dataType = typeof(....);
            if (dataType != null)
            {
                DataUrls.RegisterGlobalDataUrlMapper(dataType, new NewsDataMapper(dataType, TargetPageId));
            }
        }

    }
}

Download the code

Creating and using a CMS Function to display the data

Create a Razor function:

@using Composite.Core.Routing
@using Composite.Core.Routing.Pages

@inherits RazorFunction

@functions {
    public override string FunctionDescription
    {
        get  { return "List news on a page."; }
    }

    string GetUrl(Demo.News data)
    {
        return PageUrls.BuildUrl(DataUrls.TryGetPageUrlData(data.ToDataReference()));
    }

}

@{
    var dataReference = DataUrls.TryGetData(C1PageRoute.PageUrlData);

    Demo.News itemToShow = dataReference != null ? dataReference.Data as Demo.News : null;

    if (itemToShow != null)
    {
        C1PageRoute.RegisterPathInfoUsage();
    }
}

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://www.composite.net/ns/function/1.0">
    <head>
    </head>
    <body>
        @if (itemToShow != null)
        {
            <h1>
                @itemToShow.Title
            </h1>
            <em>
                @itemToShow.Date
            </em>
            <p>
                @itemToShow.Content
            </p>

            <br />
            <a href="@Data.SitemapNavigator.CurrentPageNode.Url"> Back to list</a>
        }
        else
        {
            <h1> List of news items: </h1>

            foreach (var item in Data.Get<Demo.News>())
            {
                <div>
                    <a href="@GetUrl(item)">
                        @item.Title
                    </a>
                </div>
            }
        }
    </body>
</html>

Download the code

Now insert the function on the page you've specified when registering the mapper in the startup handler: "News" (Id: 055a1076-099b-420c-a9cc-f591e0819782) in this example.

Examples of using the API

One can call the registered mappers by calling static methods:

DataUrls.TryGetData(PageUrlData pageUrlData)
and

DataUrls.TryGetPageUrlData(IDataReference dataReference)
Examples:

// IData ->URL: IData dataItem = ...;
var pageUrlData = DataUrls.TryGetPageUrlData(newsItem.ToDataReference());
string url = pageUrlData != null ? PageUrls.BuildUrl(pageUrlData) : null;
// URL -> IData: string url = ...;
var pageUrlData = PageUrls.ParseUrl(url);
var dataReference = pageUrlData != null ? DataUrls.TryGetData(pageUrlData) : null;
IData data = dataReference != null ? dataReference.Data : null;

Requirements:

C1 CMS version 5.0 or later