Skip to content

Instantly share code, notes, and snippets.

@Nicholas-Westby
Last active December 24, 2015 18:47
Show Gist options
  • Save Nicholas-Westby/7ad0934e1f68c3eeae93 to your computer and use it in GitHub Desktop.
Save Nicholas-Westby/7ad0934e1f68c3eeae93 to your computer and use it in GitHub Desktop.
Use Ditto to Map ArchetypeWidgets Using Custom Type Converter
This shows how to use Ditto to map Archetype widgets to a property that is a list of an interface (i.e., each widget class implements that interface).
Note that there may be a better solution: https://github.com/micklaw/Ditto.Resolvers/issues/4
namespace MyProject.App.Attributes
{
// Namespaces.
using System;
/// <summary>
/// An attribute that should decorate all Archetype mappers.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class ArchetypeMapperAttribute : Attribute
{
#region Properties
/// <summary>
/// The alias of the Archetype fieldset the mapper can map.
/// </summary>
public string FieldsetAlias { get; set; }
#endregion
}
}
namespace MyProject.App.Widgets.Mappers
{
// Namespaces.
using Archetype.Models;
/// <summary>
/// The interface all Archetype mappers should implement.
/// </summary>
public interface IArchetypeMapper
{
#region Methods
/// <summary>
/// Converts an Archetype fieldset into a widget.
/// </summary>
/// <param name="model">The Archetype fieldset.</param>
/// <returns>The widget.</returns>
object GetWidget(ArchetypeFieldsetModel model);
#endregion
}
}
namespace MyProject.App.Widgets
{
/// <summary>
/// The interface all widgets implement.
/// </summary>
public interface IWidget
{
}
}
namespace MyProject.App.Widgets
{
/// <summary>
/// A rich text widget.
/// </summary>
public class RichText : IWidget
{
public string Text { get; set; }
}
}
namespace MyProject.App.Widgets.Mappers
{
// Namespaces.
using Archetype.Models;
using Attributes;
/// <summary>
/// Converts an Archetype fieldset to a rich text widget.
/// </summary>
[ArchetypeMapper(FieldsetAlias = "RichText")]
public class RichTextMapper : IArchetypeMapper
{
#region Methods
/// <summary>
/// Converts an Archetype fieldset to a rich text widget.
/// </summary>
/// <param name="model">The Archetype fieldset.</param>
/// <returns>The rich text widget.</returns>
public object GetWidget(ArchetypeFieldsetModel model)
{
return new RichText()
{
Text = model.GetValue<string>("richText")
};
}
#endregion
}
}
@using Our.Umbraco.Ditto
@using MyProject.App.Pages
@using MyProject.App.Widgets
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
Layout = "Wrapped.cshtml";
var page = Model.Content.As<TypicalPage>();
var widgets = page.MainContent.Widgets;
}
@foreach(var generalWidget in widgets)
{
if (generalWidget is RichText)
{
var widget = generalWidget as RichText;
@Html.Raw(widget.Text)
}
}
namespace MyProject.App.Pages
{
// Namespaces.
using Converters;
using System.ComponentModel;
using Widgets;
/// <summary>
/// The model for a typical page.
/// </summary>
public class TypicalPage
{
#region Properties
/// <summary>
/// The main content for this page.
/// </summary>
[TypeConverter(typeof(WidgetsConverter))]
public WidgetCollection MainContent { get; set; }
#endregion
}
}
namespace MyProject.App.Widgets
{
// Namespaces.
using System.Collections.Generic;
/// <summary>
/// A collection of widgets.
/// </summary>
public class WidgetCollection
{
#region Properties
/// <summary>
/// The widgets.
/// </summary>
public List<IWidget> Widgets { get; set; }
#endregion
#region Constructors
/// <summary>
/// Default constructor.
/// </summary>
public WidgetCollection()
{
Widgets = new List<IWidget>();
}
#endregion
}
}
namespace MyProject.App.Converters
{
// Namespaces.
using Archetype.Models;
using Attributes;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using Umbraco.Core.Logging;
using Widgets;
using Widgets.Mappers;
/// <summary>
/// A type converter to convert an Archetype model into a collection of widgets.
/// </summary>
public class WidgetsConverter : TypeConverter
{
#region Properties
/// <summary>
/// Stores the mappers by the Archetype fieldset alias they are able to map from.
/// </summary>
private static Dictionary<string, IArchetypeMapper> Mappers { get; set; }
/// <summary>
/// An object that can be used for thread locking when modifying the mappers.
/// </summary>
private static object MappersLock { get; set; }
/// <summary>
/// Indicates whether or not the mappers have been found.
/// </summary>
private static bool MappersFound { get; set; }
#endregion
#region Constructors
/// <summary>
/// Static constructor.
/// </summary>
static WidgetsConverter()
{
Mappers = new Dictionary<string, IArchetypeMapper>();
MappersLock = new object();
MappersFound = false;
}
#endregion
#region Conversion Methods
/// <summary>
/// Indicates whether or not this type converter can convert from the specified value.
/// </summary>
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
return sourceType == typeof(ArchetypeModel) || base.CanConvertFrom(context, sourceType);
}
/// <summary>
/// Converts from an Archetype model to a collection of widgets.
/// </summary>
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
// Only convert Archetype models.
if (value is ArchetypeModel)
{
// Variables.
var model = value as ArchetypeModel;
var content = new WidgetCollection();
// Convert each Archetype fieldset.
foreach(var item in model.Fieldsets.Where(x => !x.Disabled))
{
var mapper = GetMapper(item.Alias);
if (mapper == null)
{
LogHelper.Warn<WidgetsConverter>(
"Couldn't find an Archetype mapper for a fieldset with the alias {0}.",
() => item.Alias);
}
else
{
var widget = mapper.GetWidget(item) as IWidget;
if(widget != null)
{
content.Widgets.Add(widget);
}
}
}
// Return the widgets.
return content;
}
else
{
return base.ConvertFrom(context, culture, value);
}
}
#endregion
#region Private Methods
/// <summary>
/// Gets the mapper for the specified Archetype fieldset alias.
/// </summary>
/// <param name="fieldsetAlias">
/// The Archetype fieldset alias (e.g., "RichText").
/// </param>
/// <returns>
/// The mapper.
/// </returns>
private IArchetypeMapper GetMapper(string fieldsetAlias)
{
// Find the mappers?
if (!MappersFound)
{
lock (MappersLock)
{
if (!MappersFound)
{
// Get all mappers.
var target = typeof(IArchetypeMapper);
var mapperInstances = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => target.IsAssignableFrom(x) && !x.IsInterface)
.Select(x => Activator.CreateInstance(x) as IArchetypeMapper)
.Where(x => x != null).ToArray();
// Process mappers.
foreach(var instance in mapperInstances)
{
// Figure out which fieldsets this mapper can map based on the
// attribute on the mapper's type.
var attributes = Attribute.GetCustomAttributes(instance.GetType());
var attribute = attributes
.Select(x => x as ArchetypeMapperAttribute)
.Where(x => x != null).FirstOrDefault();
if(attribute != null)
{
// Store the mapper by the fieldset alias.
var alias = attribute.FieldsetAlias;
Mappers[alias.ToLower()] = instance;
}
}
// Done finding mappers.
MappersFound = true;
}
}
}
// Variables.
var key = fieldsetAlias.ToLower();
var mapper = default(IArchetypeMapper);
// Return the mapper
return Mappers.TryGetValue(key, out mapper) ? mapper : null;
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment