Last active
December 24, 2015 18:47
-
-
Save Nicholas-Westby/7ad0934e1f68c3eeae93 to your computer and use it in GitHub Desktop.
Use Ditto to Map ArchetypeWidgets Using Custom Type Converter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace MyProject.App.Widgets | |
{ | |
/// <summary> | |
/// The interface all widgets implement. | |
/// </summary> | |
public interface IWidget | |
{ | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace MyProject.App.Widgets | |
{ | |
/// <summary> | |
/// A rich text widget. | |
/// </summary> | |
public class RichText : IWidget | |
{ | |
public string Text { get; set; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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