Category Archives: Uncategorized

Simplifying allowed content types in Editor Descriptor

Update 2: About a week I noticed that this functionality already exists according to Linus Ekström. But you can take this example on how to simplify attributes with UI hints and being able to read information directly from the Content Type.

Update: Updated to correct code

One of the new things in EPiServer 7.5 is the ability to restrict available content in ContentAreas (and similar Properties). A great way that prevents content to be dragged to a ContentArea compared to the validator option that is common in EPiServer 7 and 7.1 sites.

EPiServer developer Ben McKernan has written a blog post about this.

I’ve simplified this to make it easier for a developer to understand the purpose of the Editor Descriptor and being able to control exactly which types that are allowed from the Content Type.

Cut to the code!

This is how it will look on the accual Content Type when adding a ContentArea Property:

[AllowedContentTypes(typeof(StandardPage))]
public virtual ContentArea StandardPages { get; set; }

The code for the AllowedContentTypes attribute looks like this:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class AllowedContentTypesAttribute : UIHintAttribute
{
  public AllowedContentTypesAttribute(params Type[] types) : base("AllowedContentTypes")
  {
    this.Types = types;
  }

  public Type[] Types { get; set; }
}

And the code for the Editor Descriptor – this is a merge between Ben’s code and the original ContentArea Editor Descriptor from the EPiServer.Cms.Shell.UI assembly.

[EditorDescriptorRegistration(TargetType = typeof(ContentArea), UIHint = "AllowedContentTypes")]
public class AllowedContentTypes : EditorDescriptor
{
  public AllowedContentTypes()
  {
    this.ClientEditingClass = "epi-cms.contentediting.editors.ContentAreaEditor";
    this.OverlayConfiguration.Add("customType", "epi-cms.widget.overlay.ContentArea");
  }

  public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable attributes)
  {
    base.ModifyMetadata(metadata, attributes);

    var attribute = metadata.Attributes.OfType<AllowedContentTypesAttribute>().SingleOrDefault();
    if (attribute != null)
    {
      this.AllowedTypes = attribute.Types;
    }

    metadata.OverlayConfiguration["customType"] = (object)"epi-cms.widget.overlay.ContentArea";
    metadata.CustomEditorSettings["converter"] = (object)"epi-cms.propertycontentarea";
  }
}

Using PropertyDropDownList with EPiServer Dynamic Content

I’m creating a “widget” where the client is supposed to be able to change color to a Teaser. The color options should be editable quite easily.

The widget is added to the page by using Dynamic Content.
So I’m using the PropertyDropDownList added in EPiServer CMS 6 R2 so an administrator can edit the options through the Administrator Mode (or when PagetTypeBuilder 2 comes, a developer by creating a class deriving from IUpdateGlobalPropertySettings<MultipleOptionsListSettings>).

But since all ways to change the Property’s Settings values are locked from external binaries, I couldn’t find any other way to connect my Global Setting to the DropDownList in my Dynamic Content except creating a Custom Property that derives from PropertyDropDownList.

After that I create a Global Setting for DropDownList with some colors which I set as default for my PropertyTeaserColorDropDownList.

So by adding a Property of type PropertyTeaserColorDropDownList in my Dynamic Content class, I can let my Editor select a color from a list managed by the Administrator.

Code examples

public class PropertyTeaserColorDropDownList : PropertyDropDownList
{
}

[DynamicContentPlugIn(DisplayName = "Front Teaser", ViewUrl = "~/Widgets/FrontTeaserWidget/FrontTeaserWidget.ascx")]
public partial class FrontTeaserWidgetControl : WidgetControlBase
{
    public PropertyTeaserColorDropDownList TeaserColor { get; set; }
}

It feels a bit cumbersome to create a Custom Property for this. It would be nice if I could set the SettingsId somehow or if it could be set by some attribute.

Hello world!

Well here I go again, I think this is the third time I try bloggin.

I’m quite interested in routines and structures so I’ll try focus on that.
At least I’ll write about web development, programming, design etc.

Other things in my TODO is:

  • Writing the “about me”.
  • Finish the alfnilsson.se domain.
  • Making my own template for WordPress, I’ve done it before so it shouldn’t be that hard!
  • Complain about things that doesn’t seem to work

There, done for now.

Bye

</Alf>

Multiproperty from behind

Okay this is my first tech-blog so bare with me. Please send comments if some things I write are not properly explained.

Starting

A colleague of mine had a problem with a list of pages using Itera.MultiProperty.

He had started to make two CustomProperties as explained in the examples in the Itera.MultiProperty.Examples namespace in Itera.MultiProperty.dll:

PropertyLegacyLinks inheriting from PropertyMulitBase

[EPiServer.PlugIn.PageDefinitionTypePlugIn(DisplayName = "Legacy links", Description = "List of legacy links")]
public class PropertyLegacyLinks : PropertyMulitBase
{
  PropertyDataCollection _innerPropertyCollection;
  object lockObject = new object();
  public override PropertyDataCollection BasePropertys
  {
    get
    {
      if (_innerPropertyCollection == null)
      {
        lock (lockObject)
        {
          PropertyDataCollection _new = new PropertyDataCollection();
          _new.Add(Translate("/legacylinksproperty/legacylink"), new PropertyLegacyLink());
          _innerPropertyCollection = _new;
        }
      }
      return _innerPropertyCollection;
    }
  }
}

PropertyLegacyLink inheriting from PropertySingleBase

public class PropertyLegacyLink : PropertySingleBase
{
  PropertyDataCollection _innerPropertyCollection;
  object lockObject = new object();
  protected override PropertyDataCollection InnerPropertyCollection
  {
    get
    {
      if (_innerPropertyCollection == null)
      {
        lock (lockObject)
        {
          PropertyDataCollection _new = new PropertyDataCollection();
          _new.Add(Translate("/legacylinksproperty/formerlink"), new PropertyString(""));
          _new.Add(Translate("/legacylinksproperty/currentlink"), new PropertyPageReference());
          _innerPropertyCollection = _new;
        }

      }
      return _innerPropertyCollection;
    }
    set
    {
      lock (lockObject)
      {
        _innerPropertyCollection = value;
      }
    }
  }
}

PropertyLegacyLink contained a String (<= 255) and a PageReference which should be listed on the page.

Frontend

He had made it work and render according to Anders Hattestad’s blog post <EPiServer:Property on steroids /> where he used the <Itera:Property> iterating through the PropertyLegacyLinks Property here called “Legacy”,

Inside the Itera:Property there was another Itera:Property iterating the PropertyLegacyLink printing its values.

<Itera:Property ID="PropertyCtrl" runat="server" SuppressEmpty="false" PropertyName="Legacy">
  <ItemTemplate>
    <Itera:Property PropertyName="this.PropertyCollection" SuppressEmpty="true" runat="server" EnableViewState="false">
      <HeaderTemplate>
        <table border="1">
          <tr>
            <th colspan="2">
              Legacy Link
            </th>
          </tr>
      </HeaderTemplate>
      <ItemTemplate>
        <tr>
          <td>
            {this.Name}
          </td>
          <td>
            {this.Value}
          </td>
        </tr>
      </ItemTemplate>
      <FooterTemplate>
        </table>
      </FooterTemplate>
    </Itera:Property>
  </ItemTemplate>
</Itera:Property>

Rendering

Legacy Link
LegacyLink_d43da6749d2247fe9d038c69ae4be1ff Tidigare länk 

16

LegacyLink_0c6a5fc3e2344c74a452e63ea20c833b Äldre länk 

15

Very nice if you want to see what your MultiProperty contains and you can probably work some more with the Itera:MultiProperty to make it look good. But what if we want to fetch the data from Backend?

Backend

After some digging in Red Gate’s Reflector I found out that the MultiProperty is a PropertyDataCollection containing rows with PropertyDataCollection containing the Properties.

What we need to do is iterate through the first PropertyDataCollection by using

PropertyLegacyLinks legacies = (PropertyLegacyLinks)CurrentPage.Property["Legacy"];
PropertyDataCollection legacyLinksCollection = legacies.PropertyCollection;
PropertyDataCollection legacyLinkCollection;
foreach (PropertyLegacyLink item in legacyLinksCollection)
{
	legacyLinkCollection = item.PropertyCollection;
}

Where you can get your string through (string)legacyLinkCollection[0].Value and (PageReference)legacyLinkCollection[1].Value.

Polishing

Using PropertyDataCollections needs alot of casting. So what I would suggest is giving the PropertyLegacyLinks  a List<PropertyLegacyLink> as a Property and giving PropertyLegacyLink a string and a PageReference Property, such as this:

  [EPiServer.PlugIn.PageDefinitionTypePlugIn(DisplayName = "Legacy links", Description = "List of legacy links")]
  public class PropertyLegacyLinks : PropertyMulitBase
  {
    PropertyDataCollection _innerPropertyCollection;
    object lockObject = new object();
    private List<PropertyLegacyLink> _legacyLinks;
    public List<PropertyLegacyLink> LegacyLinks
    {
      get
      {
        if (_legacyLinks == null)
        {
          _legacyLinks = new List<PropertyLegacyLink>();
          for (int i = 0; i < BasePropertys.Count; i++)
          {
            PropertyLegacyLink item = BasePropertys[i] as PropertyLegacyLink;
              _legacyLinks.Add(item);
            }
          }
        }
        return _legacyLinks;
      }
    }
    public override PropertyDataCollection BasePropertys
    {
      get
      {
        if (_innerPropertyCollection == null)
        {
          lock (lockObject)
          {
            PropertyDataCollection _new = new PropertyDataCollection();
            _new.Add(Translate("/legacylinksproperty/legacylink"), new PropertyLegacyLink());
            _innerPropertyCollection = _new;
          }
        }
        return _innerPropertyCollection;
      }
    }
  }

and

  public class PropertyLegacyLink : PropertySingleBase
  {
    PropertyDataCollection _innerPropertyCollection;
    object lockObject = new object();
    private string _formerlink;
    public string FormerLink
    {
      get
      {
        if (_formerlink == null)
        {
          _formerlink = this.PropertyCollection[0].Value as string ?? string.Empty;
        }
        return _formerlink;
      }
      set
      {
        _formerlink = value;
      }
    }
    private PageReference _currentLink;
    public PageReference CurrentLink
    {
      get
      {
        if (_currentLink == null)
        {
          _currentLink = this.PropertyCollection[1].Value as PageReference ?? PageReference.EmptyReference;
        }
        return _currentLink;
      }
      set
      {
        _currentLink = value;
      }
    }
    protected override PropertyDataCollection InnerPropertyCollection
    {
      get
      {
        if (_innerPropertyCollection == null)
        {
          lock (lockObject)
          {
            PropertyDataCollection _new = new PropertyDataCollection();
            _new.Add(Translate("/legacylinksproperty/formerlink"), new PropertyString(""));
            _new.Add(Translate("/legacylinksproperty/currentlink"), new PropertyPageReference());
            _innerPropertyCollection = _new;
          }
        }
        return _innerPropertyCollection;
      }
      set
      {
        lock (lockObject)
        {
          _innerPropertyCollection = value;
        }
      }
    }
  }