Intro
Ektron Smartforms are one of the key content types that all
Ektron customers take advantage of in one way or another. They are reusable,
extendable, provide a great way of structuring data, and provide your content
authors with easy to input forms for content entry. But out of the box, the Ektron smartforms
aren’t customized to fit with YOUR
information architecture. Yes, we’ve
made great recent improvements especially regarding the Resource Selector which
allows you to associate content, folder, and taxonomy items to your content
item. But what if you want to associate other organizational items or custom
types not defined within the selector? The following guide will provide you
with code and instructions on extending your SmartForms to allow for custom
list types in an easy to extend class that uses our newest Framework API
functionality.
Getting Ready
To get ready there’s a few things you’ll need to make this
easier:
·
Visual Studio 2010 (Express will work)
·
Write access to: \Workarea\ContentDesigner\DataListSpec.xml
·
Write access to: \Workarea\ContentDesigner\Resources\DataListSpec[.*].resx
First Step
Alright, to get started I have created a fairly
straightforward ashx file that is really at the heart of all this. What this
file does, is provide a XML structured string to your SmartForm select field
with your list data. I have created the
following basic lists and hopefully have made it easy enough to extend for you
to further customize for your own site architecture:
·
Content List
·
Folder List
·
Taxonomy List
·
Collection List
·
Menu List
Copy the following code and paste it in a new .ASHX file in
your site root or somewhere that makes sense within your site architecture:
<%@ WebHandler Language="C#" Class="CustomList" %> using System; using System.Web; using System.Collections.Generic; using System.Text; using Ektron.Cms.Content; public class CustomList : IHttpHandler { public void ProcessRequest (HttpContext context) { string type = context.Request.QueryString["type"] ?? "ContentList"; long parentId = QueryString("parentId", 0, context); context.Response.ContentType = "text/xml"; context.Response.Write(""); } public bool IsReusable { get { return false; } } public static long QueryString(string paramName, int defaultValue, HttpContext context) { long value; if (!long.TryParse(context.Request.QueryString[paramName], out value)) return defaultValue; return value; } } public class EktLists { long _parentId; public EktLists(long parentId) { _parentId = parentId; } public string ContentList() { StringBuilder options = new StringBuilder(); Ektron.Cms.Framework.Content.ContentManager cm = new Ektron.Cms.Framework.Content.ContentManager(); ContentCriteria criteria = new ContentCriteria(Ektron.Cms.Common.ContentProperty.Id, Ektron.Cms.Common.EkEnumeration.OrderByDirection.Ascending); criteria.AddFilter(Ektron.Cms.Common.ContentProperty.FolderId, Ektron.Cms.Common.CriteriaFilterOperator.EqualTo, _parentId); Listlist = cm.GetList(criteria); foreach (Ektron.Cms.ContentData item in list) { options.AppendFormat("", item.Id, item.Title); } return options.ToString(); } public string FolderList() { StringBuilder options = new StringBuilder(); Ektron.Cms.Framework.Organization.FolderManager fm = new Ektron.Cms.Framework.Organization.FolderManager(); Ektron.Cms.FolderCriteria criteria = new Ektron.Cms.FolderCriteria(Ektron.Cms.Common.FolderProperty.Id, Ektron.Cms.Common.EkEnumeration.OrderByDirection.Ascending); criteria.AddFilter(Ektron.Cms.Common.FolderProperty.Id, Ektron.Cms.Common.CriteriaFilterOperator.EqualTo, _parentId); List list = fm.GetList(criteria); foreach (Ektron.Cms.FolderData item in list) { options.AppendFormat("", item.Id, item.Name); } return options.ToString(); } public string TaxonomyList() { StringBuilder options = new StringBuilder(); Ektron.Cms.Framework.Organization.TaxonomyManager tm = new Ektron.Cms.Framework.Organization.TaxonomyManager(); Ektron.Cms.Organization.TaxonomyCriteria criteria = new Ektron.Cms.Organization.TaxonomyCriteria(Ektron.Cms.Organization.TaxonomyProperty.Name, Ektron.Cms.Common.EkEnumeration.OrderByDirection.Ascending); criteria.AddFilter(Ektron.Cms.Organization.TaxonomyProperty.Id, Ektron.Cms.Common.CriteriaFilterOperator.EqualTo, _parentId); List list = tm.GetList(criteria); foreach (Ektron.Cms.TaxonomyData item in list) { options.AppendFormat("", item.Id, item.Name); } return options.ToString(); } public string CollectionList() { StringBuilder options = new StringBuilder(); Ektron.Cms.Framework.Organization.CollectionManager cm = new Ektron.Cms.Framework.Organization.CollectionManager(); Ektron.Cms.CollectionCriteria criteria = new Ektron.Cms.CollectionCriteria(Ektron.Cms.Common.ContentCollectionProperty.Title, Ektron.Cms.Common.EkEnumeration.OrderByDirection.Ascending); criteria.AddFilter(Ektron.Cms.Common.ContentCollectionProperty.Id, Ektron.Cms.Common.CriteriaFilterOperator.EqualTo, _parentId); List list = cm.GetList(criteria); foreach (Ektron.Cms.Organization.ContentCollectionData item in list) { options.AppendFormat("", item.Id, item.Title); } return options.ToString(); } public string MenuList() { StringBuilder options = new StringBuilder(); Ektron.Cms.Framework.Organization.MenuManager mm = new Ektron.Cms.Framework.Organization.MenuManager(); Ektron.Cms.Organization.MenuData list = mm.GetTree(_parentId); foreach (Ektron.Cms.Organization.MenuItemData item in list.Items) { options.AppendFormat("", item.Id, item.Text); } return options.ToString(); }
Now that you’ve pasted this in, let’s go over it a
little. You’ll notice the first Class,
“CustomList” is simply a generic ashx handler, which looks at two arguments,
nothing too special:
string Type: Used to define the type of list being loaded, current
acceptable values are ContentList, FolderList, TaxonomyList, CollectionList,
and MenuList.
long parentId: Used to define the containing organizational
identifier. E.g. A parent folder id.
The second class “EkLists”
however is where all the action happens. This class is instantiated right above
the switch case and given the parentId for the container your list will be
in. The proper method which fills the
list is then called. Your resulting string which is received by the editor will
look a lot like:
<select>
<option value="30">Sample
Content Block</option>
<option value="32">testing</option>
</select>
But we still need to
tell the editor about our lists!
So now that we have our new super ektlist class outputting
all the xml lists we can imagine, the editor still has no idea this file or our
cool lists exist. The good news is, all
our list information is read in from a configuration file called DataListSpec.xml located in \Workarea\ContentDesigner\.
Open this file, and right before the ending
</datalists> paste:
<datalist name="ContentList" localeRef="ContentList" src="[path]/CustomList.ashx?type=ContentList&parentId=0" cache="false" select="/select/option" captionxpath="." valuexpath="@value" validation="select-req">
<item value="" localeRef="sSel"/>
</datalist>
If you read the above statement, there are a few key parts:
- Name: Can be whatever you’d like localeRef: Name Key referenced in DataListSpec[.*].res
- src: “[path]/CustomList.ashx” Location to file you imported earlier
- type: Defaults to ContentList, can be modified to any list type defined.
- parentId: Containing element of list items
- validation: Default is select-req, which makes this field required
And that’s it; the rest of the values will normally not
change.
The only piece left is adding the reference into the resx file and
we’re ready to start viewing our list(s).
Open “\Workarea\ContentDesigner\Resources\DataListSpec[.*].resx”
which is a two column spreadsheet of a Name/Value pair. Create a new entry for
any datalists you defined, where the Name equals “localeRef” and the Value can
be whatever you’d like. For the above configuration I entered:
Name: ContentList, Value: ContentList(FolderID 0)
OK! If everything was put in the right spot, when we go into
the SmartForm data designer we should now be able to see our new list for a
“Choices Field” option.
Thanks for reading, please leave comments below for ideas or
extensions on this. Also feel free to talk with me on twitter @andrew_eddy
about this article or Ektron in general!