Tutorial 4: Items

In classic Dynamicweb we use pages to build our navigation structure, paragraphs on pages to contain content, and we add apps to paragraphs when we need extended functionality.

This is still possible, but with Items we have a tool set which allows you to both build our structure, define content types, mimic module functionality and even extend built-in object types.

Basically, items allow you to define our own content types, specify which input fields should be available, where they appear and how to validate them.

In addition, we can decide how they can be used:

  • As pages or paragraphs
  • To extend website and page properties
  • To extend users and user groups
  • In item lists/item buckets to be rendered on an ad-hoc basis using an item publisher

And also where they may be used – which websites an item type is available for, and which parent- and child-items they can have.

Item types can be created in three ways.

  1. Model First – create the item type manually from the Settings > Item Types node using the administration interface. All settings are stored in an XML file in the /Files/System/Items directory of the solution, and that XML file can be deployed to other solutions.
  2. Metadata First – skip the GUI and just create an XML file and deploy that on the server. When items are updated, the XML will be registered and the relevant tables will be created.
  3. Code First – define your item type using C# code. This is done by creating a public .NET class that inherits from the ItemEntry base class and populates its properties. The class is decorated with an attribute specifying the name of the item type, which it will be identified by in the administration.

So, as you see, there is a great deal of flexibility when it comes to items – both regarding the use of items and the creation of items.

In this tutorial we will be covering the following item-related topics:

  • How to extend website- and page properties with item fields
  • How to create an item code first
  • How to interact with items at runtime

One of the most useful uses for items is the ability to extend website or page properties with item fields, then access the field values in your templates.

As there a plenty of field types available for item types, this means that you can do all sorts of interesting stuff – e.g. create color palette selectors for a website, font selectors, or any other type of switch you can think of.

To extend website or page properties with item fields:

  • Create an item type containing the fields you want to use
  • In the item type settings check the website- and page properties checkbox
  • Go to the Websites app and open the website settings of the solution you want to extend
  • Click the Item type button, then select your item type as appropriate (Figure 2.1)
  • Click OK, then Save
Figure 2.1 Extending properties with item fields

You will now have access to the item fields from the website- and page properties screens, and the saved field values in your master and layout templates through the Item.Area.* tags for website properties and the Item.Page.* tags for page properties. 

As mentioned in the introduction to this tutorial, you can create items code first. This is done by inheriting from the ItemEntry base class.

using Dynamicweb.Content.Items; namespace CustomProject { public class ExampleNewsItem : ItemEntry { //TODO Implement the item by adding properties and optionally overriding methods like Save and Delete. } }

To apply the Save() and Delete() methods, use the public override methods:

using Dynamicweb.Content.Items; namespace CustomProject { public class ExampleNewsItem : ItemEntry { public override void Delete(ItemContext context) { base.Delete(context); } public override void Save(ItemContext context) { base.Save(context); } } }

So that’s an empty code first item – now you need to add some of the fundamental item type settings to it.

To decorate the class with item type settings, add them above the class declaration:

using Dynamicweb.Content.Items; using Dynamicweb.Content.Items.Annotations; using Dynamicweb.Content.Items.Activation; namespace Dynamicweb.Examples.Items { [Item("News item", "The example of code-first item")] [AreaRule, ModuleAttachmentRule(true), StructureRule(StructureContextType.Pages, StructureContextType.Paragraphs), ParentRule(ParentRestrictionRule.ParentType.RootOfWebsite, ParentRestrictionRule.ParentType.RegularPage), ChildRule(true), ChildRule("ExampleCategory", "ExampleRegion")] [Category("Example")] [CustomizedUrls] [Icon(Dynamicweb.Core.UI.Icons.KnownIcon.Message)] [TitleField("Title")] public class ExampleNewsItem : ItemEntry { public override void Delete(ItemContext context) { base.Delete(context); } public override void Save(ItemContext context) { base.Save(context); } } }

You can see a list of all the attributes available to you in the Dynamicweb.Content.Items.Annotations namespace – but note that only some of the attributes are relevant at the class-level, while most are relevant as attributes to item type fields.

In the example above, the following attributes have been added:

  • AreaRule sets the ‘Allow in websites’ checkbox to All
  • ModuleAttachmentRule defines whether this item type can have an app attached when used as a paragraph
  • StructureRule defines what the item may be used for – in this cases as a paragraph and a page
  • ParentRule and ChildRule defines which parents and children the item type allows
  • Category defines the category of the item (categories are basically folders in the item types tree)
  • CustomizedUrls activates customized URLs for this item type
  • Icon defines an icon for the item type in the content tree and paragraph list
  • TitleField defines which of the item fields should be used as the item title

The AreaRule, ParentRule and ChildRule attributes from the example above will be presented like (Figure 4.2), when viewing the item settings in the administration:

Figure 4.2 Item settings in the interface

After defining the basic item settings, you should add fields to the item type.

An item type field consists of a datatype – like string, int or DateTime – with one of more of the properties of the FieldAttribute Class , and a number of other attributes depending on the type of item field.

A simple title field, for instance, is created in the following manner:

[Name("Title")] public string Title { get; set; }

The Title property sets the system name for the field, and the accessors get and set allow you to read and write the field value, respectively.

Fields may be added to a field group, and can have a number of properties depending on the data type:

[Group("StandardFields")] [Required] [RichText] public string Text { get; set; }

In this case, the field has been added to a field group called “StandardFields”, has been made Required, and uses a RichText editor.

For a list of the editors available to you, please see the Dynamicweb.Content.Items.Editors namespace – and for a list of other available attributes, please see the Dynamicweb.Content.Items.Annotations namespace. Keep in mind that not all data types and editors support all annotations – common sense and the Items API is advised.

In the example below, we have added many of the properties available when creating item types. 

using System; using Dynamicweb.Ecommerce; using System.Collections.Generic; using Dynamicweb.Content.Items; using Dynamicweb.Content.Items.Annotations; using Dynamicweb.Content.Items.Metadata; using Dynamicweb.Content.Items.Activation; using Dynamicweb.Content.Items.Editors; namespace Dynamicweb.Examples.Items { [Item("News item", "The example of code-first item")] [AreaRule, ModuleAttachmentRule(true), StructureRule(StructureContextType.Pages, StructureContextType.Paragraphs), ParentRule(ParentRestrictionRule.ParentType.RootOfWebsite, ParentRestrictionRule.ParentType.RegularPage), ChildRule(true), ChildRule("ExampleCategory", "ExampleRegion")] [Category("Example")] [CustomizedUrls] [Icon(Core.UI.Icons.KnownIcon.Message)] [TitleField("Title")] public class ExampleNewsItem : ItemEntry { [Group("MappingFields", LayoutGroupMetadata.GroupCollapsibleState.Collapsed)] [Field("Category", typeof(DropDownListEditor<string>))] [OptionItem("ExampleCategory", "Name", "Id", SourceType = FieldOptionItemSourceType.CurrentArea)] public string NewsCategoryId { get; set; } [Group("MappingFields")] [Field("Region", typeof(CheckboxListEditor<string>))] [OptionItem("ExampleRegion", "Name", "Id", SourceType = FieldOptionItemSourceType.CurrentArea)] public IEnumerable<string> RegionId { get; set; } [Group("StandardFields")] [Required] public string Title { get; set; } [Group("StandardFields")] [Name("Subtitle")] public string Subtitle { get; set; } [Group("StandardFields")] [Required] [RichText] public string Text { get; set; } [Group("StandardFields")] [Name("News date")] [DefaultValue("Now")] public DateTime NewsDate { get; set; } [Group("StandardFields")] [Field("Type", typeof(RadioButtonListEditor<string>))] [Option("News"), Option("Featured"), Option("Add")] [DefaultValue("News")] [Required] public string NewsType { get; set; } [Group("StandardFields")] [Color("Background color", Presets = "#FFF, #151515, #444544, #5E5E5E, #005731, #287D59, #31988D, #2D73AB, #384E9A, #703F96, #9F3F95, #A93948, #E6A04B, #DD823A, #9BBF53, #0085CA, #1C588F")] [DefaultValue("#151515")] public string BackgroundColor { get; set; } [Group("StandardFields")] [Name("Link to landing page")] [Link] public string LandingPage { get; set; } [Group("Misc", LayoutGroupMetadata.GroupCollapsibleState.None, "Title", GroupVisibilityRule.VisibilityCondition.Equals, "Great news")] [Name("Tags")] [LongText] public string Tags { get; set; } [Group("Misc")] [InputHtml5("Rank", InputType = "Number", Min = 0, Max = 10)] [Validate(typeof(RegExValidator), Parameters = "Expression=^$|^[0-9]%2b")] // Short version: [RegEx("^$|^[0-9]%2b")] // %2b - is encoded + public int Rank { get; set; } [Group("Misc")] [ItemRelationList("Related news", "ExampleNewsItem", ItemRelationListEditor.ItemListSource.Page, "Title", "SubTitle")] public int RelatedNews { get; set; } public ExampleNewsItem() { NewsCategoryId = "1"; RegionId = new List<string>(new string[] { "1" }); Title = "New news"; } public override void Save(ItemContext context) { base.Save(context); } public override void Delete(ItemContext context) { base.Delete(context); } } [Item("Product news item", "The example of code-first item with inheritance")] [AreaRule, StructureRule(StructureContextType.Pages), ParentRule(ParentRestrictionRule.ParentType.RootOfWebsite, ParentRestrictionRule.ParentType.RegularPage)] [Category("Example")] [CustomizedUrls] [Icon(Core.UI.Icons.KnownIcon.Message)] [TitlePattern("Product news {{Title}}")] public class ExampleProductNewsItem : ExampleNewsItem { //Requires Dynamicweb.Ecommerce package [Group("Product")] [Dynamicweb.Ecommerce.Content.Items.Annotations.Product("Product Link")] [Field("Product Link", typeof(Dynamicweb.Ecommerce.Content.Items.Editors.ProductEditor))] public string ProductLink { get; set; } [Group("Product")] [File("Product Image", "/Images/Products", "gif,jpg,png")] public string ProductImage { get; set; } [Group("Product")] [EditableList("Comments")] public string Comments { get; set; } [Group("Product")] [Field("Related products", typeof(CheckboxListEditor<string>))] [OptionSql("SELECT TOP 10 ProductId, ProductName FROM EcomProducts WHERE ProductPrice > 1000", "ProductName", "ProductId")] public IEnumerable<string> RelatedProducts { get; set; } [Group("Special")] [Checkbox] public bool RunExamples { get; set; } public override void Save(ItemContext context) { Examples(context); base.Save(context); } private void Examples(ItemContext context) { if (RunExamples) { var myListId = RelatedNews; ItemList.DeleteRelations(myListId); var news = new ExampleNewsItem(); news.Title = "Example of new related item"; news.NewsDate = DateTime.Now; //Default attribute not applied news.Save(context); var newsCopy = (Item)news.Copy(); newsCopy["Title"] = "Example of item copy"; newsCopy.Save(context); var genericCopy = news.Copy<ExampleNewsItem>(); genericCopy.Title = "Example of generic copy"; genericCopy.Save(context); ItemList.AddRelation(myListId, news.Id, 0); ItemList.AddRelation(myListId, newsCopy.Id, 1); ItemList.AddRelation(myListId, genericCopy.Id, 2); Title = "Run examples - completed"; } } } [AreaRule, StructureRule(StructureContextType.Pages)] [Category("Example")] public class ExampleCategory : ItemEntry { public string Name { get; set; } } [AreaRule, StructureRule(StructureContextType.Pages)] [Category("Example")] public class ExampleRegion : ItemEntry { public string Name { get; set; } } }

As a very useful feature, Items offer developers an API to interact with Items at runtime. There are several ways of doing this, and in its most basic shape it is done by querying the repository:

using Dynamicweb.Content.Items; ... var item = ItemManager.Storage.GetById("BlogPost", "123"); var headline = item["Headline"].ToString();

This returns a loosely type Item instance which is basically a key/value pair, where the keys are field names and values are field values.

If you created the item code first, Dynamicweb can construct an instance of our type and return a strongly typed object instead: 

using Dynamicweb.Content.Items; ... BlogPost p = ItemManager.Storage.GetById<BlogPost>("123"); string headline = p.Headline;

The item manager offers a range of methods for querying items, all of them offering generic overload methods making it very easy to work with code first items:

ItemManager.Storage.GetById("BlogPost", "123"); ItemManager.Storage.GetById<BlogPost>("123"); ItemManager.Storage.GetByPageId("BlogPost", "123"); ItemManager.Storage.GetByPageId<BlogPost>("123"); ItemManager.Storage.GetByParagraphId; ItemManager.Storage.GetByParagraphId<BlogPost>("123"); ItemManager.Storage.GetByParentPageId("BlogPost", "123"); ItemManager.Storage.GetByParentPageId<BlogPost>("123");

Since items can be used to define settings on pages and websites, it is very convenient to be able to read these settings at runtime if they affect the layout or behavior of the current view.

To read a setting on a website, use the following code: 

Dynamicweb.Frontend.PageView.Current().Area.Item["MyAreaSetting"].ToString();

To read a setting on a page, use the following code:

Dynamicweb.Frontend.PageView.Current().Page.Item["MyPageSetting"].ToString();

You can even manipulate item values at runtime– e.g. save a new value to an item field:

Dynamicweb.Frontend.PageView.Current().Page.Item["MyPageSetting"] = "My updated value"; Dynamicweb.Frontend.PageView.Current().Page.Item.Save();

You can also interact with items using the new ViewModel approach (Model.Area.Item & Model.Page.Item) – see more in the ViewModel documentation.

In this tutorial we’ve covered the following:

  • How to extend page and website settings with item fields – and how to interact with them at runtime
  • How to create items code-first

In the next tutorial, we will get back to extensibility – this time we will be extending the search framework we colloquially refer to as New Indexing.