Dynamicweb Software

Code-first items in Dynamicweb CMS

Items in Dynamicweb CMS is a wonderful thing. It allows you to create your own specialized content types and it makes it easy to create custom functionality. Normally, I have been used to creating simple items via the Item Manager in the administration area, and this works perfectly for simple projects where you e.g. need to enable the webmaster to create custom news or calendar elements – simple stuff.

However, in a new project we needed to use the code-first approach. In this project we have a lot of front end stuff going on, and therefore the regular ItemPublisher- and Itemcreator-module in Dynamicweb just didn’t cut it. We needed more than this, and therefore we decided to build our own publisher and creator functionality. In order to do that we needed to be able to create all items via .net.

Now, when you head out in such a project you don’t always see the obstacles right in front of you. One of the main obstacles is that the items-functionality in Dynamicweb CMS is quite new and it is evolving very quickly. If only the documentation was evolving just as quickly!

This is the reason why I’m documenting my experiences here in this blog. I’ve been spending quite a lot of time investigating different approaches – both via my colleagues but also via the Developer Forum. Luckily, the developers at Dynamicweb Software are very quick to provide answers for my questions. The Developer Forum has been my savior a lot of times(!).

The first blogpost here will sum up some of the learnings that I’ve made when creating items via code. This is primarily for my own sake, but I hope that others might be able to use this knowledge as well. Please notice that I will not describe the stuff that is already documented on the developer website right here: http://developer.dynamicweb.com/documentation/for-developers/item-based-structure/getting-started.aspx

Please be aware, that if you create items code-first and you need to modify items later on in the process, you will often need to delete the XML-file representing the item, and also the database table containing the item entries. Otherwise, you might risk that the changes doesn’t appear.

Creating my base item
I first needed to create a base item that other items would inherit from. This code looks like the following:

namespace Bleau.Custom.ItemSubscriber.Items
{
    using Dynamicweb.Content.Items;
    using Dynamicweb.Content.Items.Activation;
    using Dynamicweb.Content.Items.Annotations;

    [Item("Base", "Base"), Category("Custom")]
    [AreaRule,
    StructureRule(StructureContextType.Paragraphs),
    ParentRule(ParentRestrictionRule.ParentType.RootOfWebsite, ParentRestrictionRule.ParentType.RegularPage),
    TitlePattern("{{Title}}"),
    TitleField("Title")]
    [CustomizedURLs]
    public class Base : ItemEntry
    {
        [Name("Title")]
        [Required]
        public string Title { get; set; }

        [Field("Category", typeof(Dynamicweb.Content.Items.Editors.DropDownListEditor<>))]
        [OptionItem("Category", "CategoryName", "Id", IncludeChilds = true, IncludeParagraphs = true, ItemSystemName = "Category", NameField = "CategoryName", SourceId = 32, SourceType = Dynamicweb.Content.Items.Metadata.FieldOptionItemSourceType.CurrentArea, ValueField = "Id")] 
        [Required]
        public string Category { get; set; }

        //private DateTime? activeFrom;

        //[Field("Active From", typeof(Dynamicweb.Content.Items.Editors.DateTimeEditor))]
        //[Required]
        //[DefaultValue("Now")]
        //public DateTime Active_From { get; set; }

        //[Field("Active To", typeof(Dynamicweb.Content.Items.Editors.DateTimeEditor))]
        //[Required]
        //[DefaultValue("Now")]
        //public DateTime Active_To { get; set; }

        [Field("AccessUserID", typeof(Dynamicweb.Content.Items.Editors.HiddenFieldEditor))]
        public string AccessUserID { get; set; }

        [Field("Permission", typeof(Dynamicweb.Content.Items.Editors.HiddenFieldEditor))]
        public string Permission { get; set; }

        [Field("Allow Comments", typeof(Dynamicweb.Content.Items.Editors.CheckboxEditor))]
        [DefaultValue(false)]
        public bool AllowComments { get; set; }

        [Field("Tags", typeof(Dynamicweb.Content.Items.Editors.CheckboxListEditor<>))]
        [OptionItem("Tag", "TagName", "Id", IncludeChilds = true, IncludeParagraphs = true, ItemSystemName = "Tag", NameField = "TagName", SourceId = 82, SourceType = Dynamicweb.Content.Items.Metadata.FieldOptionItemSourceType.CurrentArea, ValueField = "Id")] 
        public string Tags { get; set; }
    }
}

There are a couple of things going on here:

Decorating your item/class with relevant attributes

    [Item("Base", "Base"), Category("Custom")]
    [AreaRule,
    StructureRule(StructureContextType.Paragraphs),
    ParentRule(ParentRestrictionRule.ParentType.RootOfWebsite, ParentRestrictionRule.ParentType.RegularPage),
    TitlePattern("{{Title}}"),
    TitleField("Title")]
    [CustomizedURLs]

The code repeated here adds the necessary attributes to your item. It will create a name, and put it into a category called “Custom”. It will enable the item for paragraphs only, and it will allow the item to be created in the root of the website and underneath a regular page. It will also instruct the item to use the field called “Title” as the name of the paragraph, and finally it will create customized URLs.

Instruct a DropDownListEditor to use data from another item

        [Field("Category", typeof(Dynamicweb.Content.Items.Editors.DropDownListEditor<>))]
        [OptionItem("Category", "CategoryName", "Id", IncludeChilds = true, IncludeParagraphs = true, ItemSystemName = "Category", NameField = "CategoryName", SourceId = 32, SourceType = Dynamicweb.Content.Items.Metadata.FieldOptionItemSourceType.CurrentArea, ValueField = "Id")] 
        [Required]
        public string Category { get; set; }

Another thing that is not documented is the ability to populate a DropDownListEditor with data from another item. I had already created an item called category, and the code above allows you to use this item in your dropdowns or checkboxes. You just add the “OptionItem”-attribute, and instruct it to use the “Category”-item. You can even tell the field to use the categories placed on a particular page which is why I’m using the SourceId = 32 attribute. I should probably put that one into a setting – arh well….

Date fields
As you can see I have chosen to comment out the date fields. I’m still having trouble with this one. The problem is that Dynamicweb doesn’t create a real date field in the database when you use code-first, and hence it will crash when you try to add new dates to the item. This is probably me doing some oddball-thing, but I still haven’t figured out how to do this properly. When I create this via the Item Manager everything works perfectly, but just not from my code. I hope to have this fixed as soon as possible.

Inherit from another item
It is possible to let one item inherit fields from another item. In my case, I needed to create an article-item that would inherit from the base-item. This is done via regular C#-code like the following:

namespace Bleau.Custom.ItemSubscriber.Items
{
    using Dynamicweb.Content.Items.Activation;
    using Dynamicweb.Content.Items.Annotations;

    [Item("Article", "Article"), Category("Custom")]
    [AreaRule,
    StructureRule(StructureContextType.Paragraphs),
    ParentRule(ParentRestrictionRule.ParentType.RootOfWebsite, ParentRestrictionRule.ParentType.RegularPage),
    TitlePattern("{{Title}}"),
    TitleField("Title")]
    [CustomizedURLs]
    public class Article : Base
    {
        [Name("Teaser")]
        public string Teaser { get; set; }

        [Field("Text", typeof(Dynamicweb.Content.Items.Editors.LongTextEditor))]
        public string Text { get; set; }

        [Field("Files", typeof(Dynamicweb.Content.Items.Editors.LongTextEditor))]
        public string Files { get; set; }
    }
}

As you can see, you just inherit from the Base-class and this will be represented in Dynamicweb CMS automatically.

So this is it! I will update this blogpost when I get new information about the date fields. Hopefully, it is just me messing around….

It just got a lot easier customizing Dynamicweb CMS

One of the main differences between popular Danish CMS-platforms such as – on the one side – Umbraco and Sitecore, and – on the other – Dynamicweb CMS, is that Umbraco and Sitecore allows you to store content as flexible user-defined items while Dynamicweb is much more locked and closed in that it only allows you to create content with a predefined structure. There are historical reasons for why this is so – Dynamicweb CMS is a more *ahem* “experienced” CMS-platform that the others….

This means that if you want to create a page in Dynamicweb CMS, that page will consist of certain predefined fields, and you cannot easily change these fields. It also means that if you want to create a new news item, you will have to use the news-module – which is developed specifically for this purpose.

In my experience, it is important to note that a lot of companies using Dynamicweb CMS actually like the more locked approach. And I have seen several customers switch from Sitecore to Dynamicweb for this precise reason. It may not be “best practice” and the “prettiest” solution, but it has worked for several years, and the more locked approach also allows for a user interface that is more stable. You always know how to create a page in Dynamicweb – even though you’re working on different websites.

However, the approach that Umbraco and Sitecore uses has several advantages. The locked structure in Dynamicweb CMS means that it is way more difficult to adapt the system to each companys’ precise requirements. Instead, you would have to hire a developer that could extend the CMS-platform in order to suit the precise needs. So, a certain requirement that would be extremely simple in Sitecore and Umbraco, could take a certified Dynamicweb-developer hours to fix. So, on top of the Dynamicweb CMS license costs, you would often need to add development costs in order to address certain requirements.

Dynamicweb Software has been working on solving this issue for quite some time. First of all, the Dynamicweb Ecommerce product actually allows you to extend the data structure as much as you want, and secondly Dynamicweb Software has been working extensively on creating new methods allowing developers to extend and customize the CMS-platform as much as possible. This is the reason why the Dynamicweb Ecommerce platform has served as much more than just an ecommerce platform – in Bleau we use the Dynamicweb Ecommerce database as a container for all sorts of content. Not pretty – but it works.

Now, Dynamicweb Software has announced that the latest version of Dynamicweb – version 8.2 – will include item-based structure.

Customers already using Dynamicweb CMS shouldn’t worry too much though. There won’t be made any changes to their current workflows. Dynamicweb Software has decided to integrate the item-based structure in a very smart way – meaning that you can continue as if nothing have happened. You can still create new pages just the way you’re used to. But now you can also decide to create new item-types with your precise data structure and layout.

An item could be an event, a news item or a todo. You can create a completely customized data structure containing all necessary fields. So, a todo-item would contain a title, a description, a person and a deadline. An event would contain a headline, a description, a date and maybe a link to a booking-formula, and a news item would contain a headline, a teaser-text, a body, an author, and so on. You get the idea!

All of these items can live next to each other, and they can be created by the customer. Each item-type can be created via the Dynamicweb Management Center, but can also be created by using the following methods:

  • Database-first
    You create the database-structure, and by using a certain convention Dynamicweb CMS discovers that it is an item-type.
  • Code-first
    You create a class containing public properties adhering to a certain standard and Dynamicweb CMS will discover the new item-type on application reload
  • Schema-first
    You add a new XML-file describing the item-type and Dynamicweb CMS will discover the new item-type.

The first method – allowing the customer to add new item-types via the Management Center – is user-friendly, and you can create different kinds of field types, but the other methods allows the developer to create separate “modules” that can be reused from customer to customer and from website to website. Dynamicweb CMS now allows you to copy the Schema from one solution to another, and I’m extremely pleased with this! Developers can also extend the functionality in order to create new form-elements – e.g. creating a new select box that will grab data from an external system.

These new methods will undoubtedly change the way that content is handled in Dynamicweb CMS and how solutions are customized. On the one hand there will be much less need for customization – on the other it will be way easier to extend functionality. So all in all customers and developers should be happy about the new path that Dynamicweb Software has chosen. There’s no doubt that this step will eliminate most of the simple modules that’s being developed in projects based on Dynamicweb CMS and this means that the customer will be able to focus on more valuable functionality.

I must admit that I haven’t tried this out in a real scenario, and therefore I cannot really elaborate on all the drawbacks. I hope to use this feature as soon as possible – and will collect my experiences here. One thing that I’m extremely aware of, though, is that it is very important that we don’t get carried away and start creating the same “spaghetti” content structure that I’ve seen in several Sitecore-solutions where the company delivering the Sitecore-solution listened too much to the customer or didn’t focus enough on creating usable interfaces.

In the meantime you can view a webinar from Dynamicweb Software about this new feature:

xslt template – Dynamicweb translate

Følgende XSLT anvendes til translate-funktionalitet i XSLT-templates i Dynamicweb CMS:

  <xsl:template name="translate">
    <xsl:param name="translationkey" />
    <xsl:param name="defaulttranslation" />
    <xsl:choose>
      <xsl:when test="$translationfile/translations/
            key[@name=$translationkey]">

        <xsl:value-of select="$translationfile/
                key[@name=$translationkey]/
                translation[@culture=$currentlanguage]" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$defaulttranslation" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

Servicerelease klar fra Dynamicweb Software

Dynamicweb Software foretager en release primo marts, som har til formål at rette en lang række fejl. Faktisk bliver der rettet cirka 130 fejl i produktet. Du kan se den komplette liste her: http://engage.dynamicweb-cms.com/Releases-177.aspx#item2657

Vi har i Bleau bidraget med en hel del fejlmeldinger, som blandt andet er relateret til manglende overholdelse af W3C. Dynamicweb CMS har indeholdt en del fejl, som gjorde det mere eller mindre umuligt at overholde W3C – med mindre man lavede en del forskellige krumspring i form af diverse template extenders og hacks. Så det kunne godt lade sig gøre at overholde W3C, men det krævede tweaks.

Følgende fejlrettelser har til hensigt at eliminere en række af disse, og så må vi jo blot håbe, at de resterende fejl fikses hurtigst muligt. 🙂

5812: Bug
Area: Validation
Title: Markup validation: Statistics script causes W3C validation to fail
Description: Statistic script has been enclosed on pages so as to not interfere with W3C validation.
Workaround: None

5893: Bug
Area: Customized URLs
Title: In customized URLS of the types “Page name and ID” and “Page name and ID parameter” spaces are not replaced with an “-”
Description: The two types of customized URLs mentioned in the title will now again work with proper replacemant of spaces.
Workaround: None

5977: Bug
Area: News V2
Title: The tag News:SmallImageClean is not giving correctly encoded paths
Description: The tag News:SmallImageClean has been restored to working order, and should now conatin the properly encoded path.
Workaround: None

6044: Improvement
Area: File Manager
Title: Remove characters when uploading
Description: It is now possible to have special characters and spaces removed by the system when uploading files to the File Manager. The setting can be found in the control panel for the File Manager in the Management Center.None

6088: Improvement
Area: Customized URLs
Title: ParagraphImageClean and ParagraphImage tags now path encodes the url to the image (UrlPathEncode)
Description: ParagraphImageClean and ParagraphImage tags now path encodes the url to the image (UrlPathEncode).None

Tracking af Dynamicweb 404-sider i Google Analytics

I mit forsøg på at koble Google Analytics bedre sammen med Dynamicweb CMS er jeg nu nået til måling af 404. Her taler jeg ikke alene om måling af interne links, men også måling af broken links fra eksterne sites. Som webmaster af en hjemmeside har man naturligvis et ønske om, at alle eksterne og interne links peger korrekt, og her kan følgende lille jQuery-snippet være behjælpelig.

Koden registrerer et pageview, hvis der er tale om en 404-side. Der registreres et hit på siden 404.html, og der tilføjes oplysninger om hvilken side, man forsøgte at tilgå, og den side, som linket er placeret på. I Google Analytics kan man trække en rapport ved følgende fremgangsmåde:

  • Klik på “Indhold” i Google Analytics på den relevante konto
  • Klik på “Indhold på topniveau”
  • Indtast “404.html” i Filter sidetitel
  • Herefter vises en liste over fejlagtige links.

Brug koden, hvis du har lyst.

// The following code is used if the site is using Dynamicweb 404-pages
var url = location.pathname;
if (url.indexOf("404") != -1) {
    _gaq.push(['_trackPageview', '/404.html?page=' + document.location.pathname + document.location.search + '&from=' + document.referrer]);
} else {
    _gaq.push(['_trackPageview']);
}

Dynamicweb CMS og Google Analytics – måling af form abandoment rate

Ønsker du at vide, hvornår dine brugere giver op og nægter at spilde mere tid på at prøve at udfylde dine formularer på hjemmesiden? I Dynamicweb CMS er der ikke nogen måde at finde ud af dette på. I stedet skal du anvende jQuery og Google Analytics.

Med nedenstående metode er du i stand til at registrere alle de formularelementer, som bliver udfyldt med en værdi. De formularelementer, som ikke udfyldes, bliver ikke målt, og dermed kan du se, hvor langt brugerne kommer, når de skal udfylde en formular. Alle formularer gemmes hver for sig, så du kan skelne dem fra hinanden.

Tak til Duncan Morris (http://www.distilled.co.uk/blog/conversion-rate-optimisation/using-jquery-and-google-analytics-events-to-track-form-abandonment/). Jeg har lavet en lille ændring til hans script – jeg mente ikke, at det var nødvendigt at benytte jQuery url funktionen.

Kode:

// Handle form abandonment rate
 $(':input').blur(function () {
 if($(this).val().length > 0){
 _gaq.push(['_trackEvent', 'Form: ' + location.pathname + "/" + $(this).parents('form').attr('name'), 'input_exit', $(this).attr('name')]);
 }
 });

Dynamicweb CMS og Google Analytics – måling af udgående links

I sidste indlæg lagde jeg nogle små kodesnippets på, som giver mulighed for at måle forskellige elementer i Google Analytics på et Dynamicweb-site.

Jeg havde også brug for at kunne måle udgående links. Så her er denne kodesnippet:


// Handle external links

$("a[href^='http']").not($('a[href*="' + document.domain + '"]')).click(function () {

_gaq.push(['_trackEvent', 'outgoing', 'Links', "'" + this.href + "'"]);

});