Skip to content

Instantly share code, notes, and snippets.

@Nicholas-Westby
Last active December 24, 2015 18:47
Show Gist options
  • Select an option

  • Save Nicholas-Westby/7ad0934e1f68c3eeae93 to your computer and use it in GitHub Desktop.

Select an option

Save Nicholas-Westby/7ad0934e1f68c3eeae93 to your computer and use it in GitHub Desktop.

Revisions

  1. Nicholas-Westby revised this gist Dec 24, 2015. 2 changed files with 41 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions Archetype Widgets with Ditto
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    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
    38 changes: 38 additions & 0 deletions WidgetCollection.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    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

    }

    }
  2. Nicholas-Westby created this gist Dec 24, 2015.
    26 changes: 26 additions & 0 deletions ArchetypeMapperAttribute.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,26 @@
    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

    }

    }
    27 changes: 27 additions & 0 deletions IArchetypeMapper.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    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

    }

    }
    11 changes: 11 additions & 0 deletions IWidget.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    namespace MyProject.App.Widgets
    {

    /// <summary>
    /// The interface all widgets implement.
    /// </summary>
    public interface IWidget
    {
    }

    }
    12 changes: 12 additions & 0 deletions RichText.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    namespace MyProject.App.Widgets
    {

    /// <summary>
    /// A rich text widget.
    /// </summary>
    public class RichText : IWidget
    {
    public string Text { get; set; }
    }

    }
    35 changes: 35 additions & 0 deletions RichTextMapper.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,35 @@
    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

    }

    }
    18 changes: 18 additions & 0 deletions Typical.cshtml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,18 @@
    @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)
    }
    }
    28 changes: 28 additions & 0 deletions TypicalPage.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,28 @@
    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

    }

    }
    198 changes: 198 additions & 0 deletions WidgetsConverter.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,198 @@
    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

    }

    }