Broken Protected Pages in Umbraco 4.5?

Today, while implementing the membership functionality for a new site I'm working feverishly on to get go live next week, I came across this error when I tried to access a Protected page while not logged in:


Object reference not set to an instance of an object.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.


Stack Trace:

[NullReferenceException: Object reference not set to an instance of an object.]
   System.Web.Security.Membership.GetCurrentUserName() +68
   System.Web.Security.Membership.GetUser() +17
   umbraco.requestHandler..ctor(XmlDocument umbracoContent, String url) +8037
   umbraco.UmbracoDefault.Page_PreInit(Object sender, EventArgs e) +2534
   System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +24
   System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +41
   System.EventHandler.Invoke(Object sender, EventArgs e) +0
   System.Web.UI.Page.OnPreInit(EventArgs e) +11042957
   System.Web.UI.Page.PerformPreInit() +41
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1255

As it turns out, it seems you need to add the runAllManagedModulesForAllRequests="true" attribute to the Modules node in your web.config file:

    <modules runAllManagedModulesForAllRequests="true">

Worked like a charm :)  Thank you Lee from Blogg Fodder UK, you saved me heaps of time with this one :)

Blog Categories in Blog4Umbraco - Take Two

So my first attempt at manipulating Tag Groups by using Events didn't go so well.  Sure, it created the tags with the correct group, but it also left the original tags in the csmTags table, and if you went back and edited the blog entry, the tags would be gone because the Tag Data Type was ignoring anything that didn't belong to the default group.

So. Back to the drawing board.

Edit (10/11/2010): The first package download didn't go so well and failed dismally.  I've uploaded a new version of the package and this time double-tested it, so it should be all good.  Leave a comment if things go awry...

This time I decided to try and tackle the problem at a deeper level - what if I introduced a new Data Type that could intercept the tag group and inject the Blog Category instead?

Package Download: You can download an Umbraco Package to install the Dll that will do this for you from here.  It contains a single Dll that has a couple of Extensions in it aimed at Blog 4 Umbraco, but is not dependent on it being installed.

Enter the Blog Tag Data Type.

I didn't want to have to re-create the entire Tag Data Type, it's Editor Control, or it's Prevalue Editor, so I took advantage of the fact that almost everything can be extended in Umbraco, and just derived a new Data Type class:

 

public class DataType : umbraco.editorControls.tags.DataType
    {
        #region IDataType Members

        private IDataEditor _Editor;

        public override IDataEditor DataEditor
        {
            get
            {
                if (_Editor == null)
                {
                    var preValues = ((umbraco.editorControls.tags.PrevalueEditor)PrevalueEditor).Prevalues;
                    int? nodeId = int.Parse(umbraco.helper.Request("id"));
                    if (nodeId.HasValue)
                    {
                        string category = (string)BlogExtensionsLibrary.GetValueRecursively(nodeId.Value, "category");

                        if (!string.IsNullOrEmpty(category))
                        {
                            if (preValues["group"] == null || (string)preValues["group"] == "default")
                                preValues["group"] = category;
                        }
                    }
                    _Editor = new umbraco.editorControls.tags.DataEditor(Data, preValues);
                }
                return _Editor;
            }
        }

        public override string DataTypeName
        {
            get { return "Blog Tags"; }
        }

        public override Guid Id
        {
            get { return new Guid("BD8B240F-0DE3-47E5-A172-2DE212CC30B6"); }
            // this was the core umbraco tags datatype GUID.
            //get { return new Guid("4023e540-92f5-11dd-ad8b-0800200c9a66"); }
        }

        #endregion
    }

 

Basically, I stripped out all the stuff I didn't want to duplicate from the tags DataType class, and overrode the DataEditor property.  A few interesting things to note:

  • I could have left the Id property alone and not overridden it.  If had done so, my DataType would have taken over the core Tags DataType, and you would basically not have to do anything more.  However, I'm trying to be a good Umbraco Citizen and behave in a right neighbourly fashion.
  • The code has been designed so that if the category attribute doesn't exist in the Document Type or one of it's Ancestors, then it will not try to inject anything into the group, so it would be quite safe to replace the Core Tags Control with the new one.
  • BlogExtensionsLibrary.GetValueRecursively() is described in my previous post attempting to deal with Blog Categories, so not going to go into it here.

Ok, once you have compiled your code and copied the dll into the bin directory of your Umbraco installation, the remaining steps are as follows:

Update the Tags Data Type, or alternatively create a new one.

You will need to go and change the Tags Data Type so that it uses the new Blog Tags control instead of the Core Tags control.  Note that if you decided not to play nice, and let the new code override the Core Tags control by using the old GUID, then essentially you shouldn't need to do anything here!

Blog Tags Data Type

Update the Blog Post Document Type

You would only need to change the Blog Post Document Type if you created a new Data Type instead of modifying the Core Tags Data Type.

Altering Xslt Files for Blog Categories with Tags

This is the third post in a series looking at the issue of having Categories at the Blog level with Blog 4 Umbraco.  It builds upon the post dealing with extending the Blog Tags to take make them specific to a particular Blog by taking advantage of the Tag Group feature.  For background, see the following posts:

Blog 4 Umbraco installs a suite of xslt files that among other things aggregate Tags that the posts are associated with.  However, some of the code looks specifically for the default Tag Group, while other parts of it don't care what group the tags are in.  If you have multiple blogs on a site, this leads to tags being displayed that may actually have nothing to do with the Blog you are looking at.

In this article we're going to attempt to address this issue and clean the xslt files up a little.  Starting with the BlogCategories.xslt file, which by default looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library"
  xmlns:tagsLib="urn:tagsLib"
  exclude-result-prefixes="msxml umbraco.library tagsLib">


<xsl:output method="html" omit-xml-declaration="yes"/>

<xsl:param name="currentPage"/>

<xsl:variable name="blogRoot" select="$currentPage/ancestor-or-self::Blog/@id"/>

<xsl:template match="/">
<ul>
  <li class="cat-item"><a href="{umbraco.library:NiceUrl($blogRoot)}">All</a> <span> (<xsl:value-of select="count($currentPage/ancestor-or-self::Blog//BlogPost)"/>)</span></li>
  <xsl:for-each select="tagsLib:getAllTagsInGroup('default')/tags/tag">
        <li class="cat-link">
            <a href="{umbraco.library:NiceUrl($blogRoot)}?filterby={current()}"><xsl:value-of select="current()"/></a> (<xsl:value-of select="@nodesTagged"/>)
        </li>
  </xsl:for-each>
</ul>

</xsl:template>

</xsl:stylesheet>​


Note the line that retrieves all Tags in the default using tagsLib:getAllTagsInGroup().  This is where the problem lies:  We have a blog that has had a category attribute set to "Events" (see the first article listed above), and we now have tags that are associated with the "Events" group, not the "default" group.

In order to fix this, we need to retrieve the category from the Blog node, and use that to retrieve the correct tags.  However, if the blog hasn't implemented the category attribute, or the attribute is left blank, we need to fall back on the current functionality.  So I've made the following adjustments:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library"
  xmlns:tagsLib="urn:tagsLib"
  exclude-result-prefixes="msxml umbraco.library tagsLib">


<xsl:output method="html" omit-xml-declaration="yes"/>

<xsl:param name="currentPage"/>

<xsl:variable name="blogRoot" select="$currentPage/ancestor-or-self::Blog"/>
<xsl:variable name="blogRootId" select="$blogRoot/@id"/>
<xsl:variable name="blogCategory" select="$blogRoot/category"/>

<xsl:template match="/">
  
  <ul>
  <li class="cat-item"><a href="{umbraco.library:NiceUrl($blogRootId)}">All</a> <span> (<xsl:value-of select="count($currentPage/ancestor-or-self::Blog//BlogPost)"/>)</span></li>
  <xsl:if test="$blogCategory = ''">
    <xsl:call-template name="listCategories">
      <xsl:with-param name="category" select="'default'"/>
    </xsl:call-template>
  </xsl:if>
  <xsl:if test="$blogCategory != ''">
    <xsl:call-template name="listCategories">
      <xsl:with-param name="category" select="$blogCategory"/>
    </xsl:call-template>
</xsl:if>

</ul>

</xsl:template>

<xsl:template name="listCategories">
  <xsl:param name="category"/>
  <xsl:for-each select="tagsLib:getAllTagsInGroup($category)/tags/tag">
      <li class="cat-link">
          <a href="{umbraco.library:NiceUrl($blogRootId)}?filterby={current()}"><xsl:value-of select="current()"/></a> (<xsl:value-of select="@nodesTagged"/>)
      </li>
  </xsl:for-each>
</xsl:template>
</xsl:stylesheet>​



Notice that we've split the actual rendering of the categories list into a separate template called listCategories and have conditionally called it with either the category retrieved from the Blog document or the 'default' tag Group if the category doesn't exist.

Next we tackle the Tag Cloud in much the same way.  The original Xslt source:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
    <!ENTITY nbsp " ">
]>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:tagsLib="urn:tagsLib" xmlns:BlogLibrary="urn:BlogLibrary"
  exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib BlogLibrary ">


    <xsl:output method="xml" omit-xml-declaration="yes"/>

    <xsl:param name="currentPage"/>

    <xsl:template match="/">
        <div class="tagcloud">
            <p>

                <xsl:for-each select="tagsLib:getAllTags()/tags/tag [@nodesTagged > 0]">
                    <xsl:sort select="." order="ascending"/>
                    <a href="{umbraco.library:NiceUrl($currentPage/ancestor-or-self::Blog/@id)}?filterby={.}">
                        <xsl:attribute name="class">
                            <xsl:choose>
                                <xsl:when test="@nodesTagged > 5">
                                    <xsl:value-of select="string('tagweight5')"  />
                                </xsl:when>
                                <xsl:otherwise>
                                    <xsl:value-of select="concat('tagweight',@nodesTagged)"/>
                                </xsl:otherwise>
                            </xsl:choose>
                        </xsl:attribute>
                        <xsl:value-of select="."/>
                    </a>
                    <xsl:text> </xsl:text>
                </xsl:for-each>

            </p>
        </div>

    </xsl:template>

</xsl:stylesheet>​

And after our modifications:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
    <!ENTITY nbsp " ">
]>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:tagsLib="urn:tagsLib" xmlns:BlogLibrary="urn:BlogLibrary"
  exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib BlogLibrary ">


    <xsl:output method="xml" omit-xml-declaration="yes"/>

    <xsl:param name="currentPage"/>

    <xsl:variable name="blogRoot" select="$currentPage/ancestor-or-self::Blog"/>
    <xsl:variable name="blogRootId" select="$blogRoot/@id"/>
    <xsl:variable name="blogCategory" select="$blogRoot/category"/>
    
    <xsl:template match="/">
        <div class="tagcloud">
            <p>
              
            <xsl:if test="$blogCategory = ''">
              <xsl:call-template name="createCloud">
                <xsl:with-param name="category" select="'default'"/>
              </xsl:call-template>
            </xsl:if>
            <xsl:if test="$blogCategory != ''">
              <xsl:call-template name="createCloud">
                <xsl:with-param name="category" select="$blogCategory"/>
              </xsl:call-template>
            </xsl:if>
             
            </p>
        </div>

    </xsl:template>
    
  <xsl:template name="createCloud">
    <xsl:param name="category"/>

    <xsl:for-each select="tagsLib:getAllTagsInGroup($category)/tags/tag [@nodesTagged > 0]">
        <xsl:sort select="." order="ascending"/>
        <a href="{umbraco.library:NiceUrl($blogRootId)}?filterby={.}">
            <xsl:attribute name="class">
                <xsl:choose>
                    <xsl:when test="@nodesTagged > 5">
                        <xsl:value-of select="string('tagweight5')"  />
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="concat('tagweight',@nodesTagged)"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:attribute>
            <xsl:value-of select="."/>
        </a>
        <xsl:text> </xsl:text>
    </xsl:for-each>

  </xsl:template>
</xsl:stylesheet>​​​


And there we have it. Both the Tags list and the Tag Cloud now behave nicely when used within the context of a Blog.

Adding a Blog Category to Blog4Umbraco

Recently, I've been working on an Umbraco website for a client that required several blogs, but each blog needed to have it's own set of tags and the Tag Cloud needed to be compartmentalised.

 

NOTE: The code described below didn't quite work out as I'd hoped with further testing, so I went back to the drawing board.  Take a look at the post Blog Categories in Blog4Umbraco - Take Two for a much cleaner and more transparent solution to the problem with flexible Tag Groups.

Problem Number 1: Tag Clouds in Umbraco are non-discriminatory

If you have more than one blog in Umbraco, go take a look at your Tag Cloud.  You will notice that all tags are listed, not just those for a specific blog.  Worse, even Documents that have a Tag Data-typed attribute will contribute to the Tag Cloud, regardless of whether they are a Blog Post document or not.  As a result, clicking on one of those tags will not necessarily result in listing blog posts.

Adding a Tag Category to an blog

Adding a category is as simple as adding another attribute to the Blog Document Type.  Here's my settings:

Adding a Category attribute to the Blog Document TypeAdding a Category attribute to the Blog Document Type

To apply the category, go to the root document of your blog and update the field, save and publish.  Done.  Now what?

It's all about the Tags

First, some background:  The Umbraco Tag datatype allows the administrator to set up tags for document types, which can then be used in various ways - as a Tag Cloud, to loosely associate related documents (allowing the developer to list those documents automatically on an Article page, for example), etc.

By default, the Tags Data Type uses a Tag Group named, surprisingly enough, "Default".  However it's possible to create a new Data Type - call it say "Event Tags" - and specify a new Group - in this case, "Events":

Event Tags Data Type

Creating a new Event Tags Datatype - Notice the Tag group that can be specified once you choose the Tags control.

The advantages of doing this mean that you could have a tag cloud for a specific group of articles - essentially creating an article Category... Exactly what we want for our multiple blogs scenario.

Note: For this solution, creating a new Tag Data Type is not actually necessary.  This step is one of the paths I originally took trying to resolve this issue, and serves as background information to what we are trying to do. However I quickly realised that this wasn't going to serve my needs at all.

Using a non-standard Tag Group in Blog4Umbraco

Ok, so now we have a category associated with a blog, and we have a grouping for a set of tags.  But how do we make sure that tagging an article in a Blog Entry uses the correct Tag Group?

As it turns out, this isn't so straightforward.  We can't just change the Blog Post Document Type to use our new Event Tags Data Type, as all blog entries, on all blogs, must by necessity use the Blog Post Document Type.  If you try to create a new Document Type based on the Blog Post one, Blog 4 Umbraco simply refuses to work with it.

After some investigation, I decided that the best approach was to implement some Event Handling Code.  The beauty of this approach is that you are actually able to roll your own Library - there's no need to modify any existing source code.  So here goes:

Writing an Event Handler

We want to write an Event Handler.  In order to make sure our event handler is registered, we need to derive from the Umbraco ApplicationBase class, and register our handlers in the Constructor:

    public class SetTags : ApplicationBase
    {
        public SetTags()
        {
            Document.AfterSave += Document_AfterSave;
        }

        void Document_AfterSave(Document sender, umbraco.cms.businesslogic.SaveEventArgs e)
        {
            FixTags(sender);
        }
    }

Handling the Document.AfterSave Event

The core of this solution is to examine the Blog Post document, retrieve a list of Tags, and any tags that don't currently have a Group that matches the category attrbute we applied to the Blog earlier need to be removed and replaced with new ones associated with the desired group.  For this I have the following Method:

private void FixTags(Document sender)
        {
            if (sender.ContentType.Alias == "BlogPost")
            {
                if (sender.Parent != null)  //If top of tree, something is wrong.  Skip.
                {// Get the Blog category.
                    // We want to remove any tags on the document that aren't associated with the Blog category.

                    string category = BlogLibrary.GetValueRecursively(sender.Id, "category").ToString();
                    if (string.IsNullOrEmpty(category))
                        // No point continuing - we'll use the default tag group.
                        return;

                    StringBuilder tags = new StringBuilder();
                    foreach (var t in umbraco.editorControls.tags.library.GetTagsFromNodeAsITags(sender.Id))
                    {
                        if (t.Group != category)
                        {
                            if (tags.Length &gt; 0)
                                tags.Append(",");
                            tags.Append(t.TagCaption);

                            umbraco.editorControls.tags.library.RemoveTagFromNode(sender.Id, t.TagCaption, t.Group);
                        }
                    }
                    umbraco.editorControls.tags.library.addTagsToNode(sender.Id, tags.ToString(), category);
                }
            }
        }

Some notes on this code:

  • Line #9: The BlogLibrary.GetValueRecursively method is a refactoring of the private method in the Autoping Event Handler class in the Blog4Umbraco dll.  All I've done is extract it into a public static method in the BlogLibrary class, and switch the parameters (mainly because it makes more sense to me to have the Node Id as the first parameter).  I've also changed the return value to an object type instead of string.  For reference, I include the code for that method here:

        public static object GetValueRecursively(int nodeId, string alias)
        {
            Document n = new Document(nodeId);
            Property p = n.getProperty(alias);

            if (p != null && !string.IsNullOrEmpty(p.Value.ToString()))
                return p.Value;
            else if (n.Level &gt; 1)
                return GetValueRecursively(n.Parent.Id, alias);

            return string.Empty;
        }
  • Lines 23 & 26: The umbraco.editorControls.tags.library exposes all the tag manipulation methods we need. We retrieve the list of tags associated with this node, and any node that doesn't have a group matching the Blog Category gets removed and added to the new Tag list.  Then after going through all the tags, we add back the new Tags with the Blog category as their Tag Group.

Compile your dll, copy it to the bin directory of your Umbraco installation, and try it out.  Whenever you tag a Blog Post and save the document, all tags will re-added with the new Tag Group:

Tags Database Table

The Tags Database Table after the test Tag has been added with the Events Group.

Of course, you'll be wanting to go and tweak your Tag Cloud xslt code to take advantage of the Blog Category and Tag Groups next... I'll cover that in another Blog Entry...

Blog 4 Umbraco conflict with UComponents

Playing with Blog 4 Umbraco some more this week, I ran into this problem and decided to look deeper.

The "Author Type" datatype

The owner property on a blog relates to the "Author Type" datatype that is created when Blog 4 Umbraco is installed.  You can see this by taking a look at the Blog Document Type in Settings.  Go to the Generic Properties Tab and there it is, right at the bottom.  The problem arises because by default, Blog 4 Umbraco creats the Author Type with a databaset type of Ntext. When a new Blog is created, the code attempts to insert the Current User's Id (an integer field) into the owner property, which then blows up because the two data types are incompatible.

What Render Control is this?

As it happens, as part of the initial configuration of the site, I'd installed Blog 4 Umbraco followed by UComponents.  Both of which include a User Picker Render Control, and both of which use exactly the same Globally Unique Identifier for it.  However, the configuration interface for each datatype is different:

Blog 4 Umbraco Author PickerBlog 4 Umbraco Userpicker configured in the Author Picker - note the Database datatype.

uComponents_authPickerThe Author Picker after installing uComponents - note the Data Type GUID is identical.

It appears that the uComponents User Picker control is overriding the Blog 4 Umbraco one, regardless of which order you install the packages in.

Resolving the "Operand type clash: int is incompatible with ntext" error when creating a new Blog

So the solution that presents itself to resolve the error that occurs when you try to create a new Blog is quite simple without needing to recompile source code:

  1. Go to the Developer Section, and expand out the Data Types node.
  2. Select the "Author Picker" Data Type form the list.
  3. If you don't have uComponents installed: Change the Database datatype field to nvarchar
  4. Save the Data Type.

Go and create a test blog, and all should be well.

As always, any comments, thoughts, or feedback are always welcome.

Using refactored.email

Overview

Refactored.Email is a dotNet 2.0 Library that provides functionality for sending templated emails with mail merge functionality in both HTML and Plain Text formats from within dotNet websites.

Features:

  • Any images embedded in the HTML format are converted to attachments and referenced correctly so that message format is not compromised.
  • Mail Merge capabilities - with customisable field placeholders and fields
  • Full HTML Email and Plain Text alternative support
  • Support for BCC email addresses allows masking of recipients list in the final email.

This library takes advantage of the System.Net.Mail framework, and is essentially a wrapper.

Downloads

Download it from here.

Sending Mail

After including the Refactored.Email dll, you can send an email in Html format using the following code:

// Set the Mail Merge Field Pattern:
Email.FieldPattern = "[{0}]";
// Set up our HTML and Plain Text templates:
string html = @"<html>
<head>
<title>Simple Html Email</title>
</head>
<body>
<p>Hello World!</p>
<p>This email contains a link to the <a href="""">Refactored Website</a></p>
<p>It also contains a mail-merge field delimited by [ and ]: [date]</p>
</body>
</html>";

string text = @"Hello World!
This email contains a link to the Refactored Website:
It also contains a mail-merge field delimited by [ and ]: [date]";

// Create a new parameters collection to hold our mail-merge fields:
NameValueCollection parameters = new NameValueCollection();
parameters.Add("date", DateTime.Today.ToString());
string subject;

// We now want to parse the message templates, inserting the mail merge data and extracting the subject:
string htmlContent = Email.ParseMessageTemplateContent(html, parameters, out subject);
string textContent = Email.ParseMessageTemplateContent(text, parameters);

Email.SendEmail("no-reply@refoster.com.au", "info@refoster.com.au", subject, htmlContent, textContent);

This is a basic example that will send html formatted email with it's plain text equivalent.  It contains a field delimited by '[' and ']' and the html will link back to the website.  The default field pattern however uses '{' and '}', so we need to tell the library to use our chosen delimiters.  To do that, we use the line :

Email.FieldPattern = "[{0}]";

The {0} part must be present for it is the placeholder in the field pattern for the field names.  Having the ability to change the field pattern means that the user can write email templates that are compatible with the templates used by Microsoft's ASP.Net MailDefinition class such as the CreateUserWizard control.  These controls use the delimiters '<%' and '%>'.

Note that calling Email.ParseMessageTemplateContent and including the subject parameter will try to extract the subject from within any <title></title> tags in the content.

Using Email Template files

The ability to use files containing the html and plain text templates is a powerful way of managing the content of the emails.

Refactored.Email has the ability to parse templates by file name or by AppSettings key name if the template filename has been set in the AppSettings section of the web.config configuration file.

// Set the Mail Merge Field Pattern:
Email.FieldPattern = "[{0}]";

// Set the Base URL for hyperlinks found in the message templates
Email.WebBaseUrl = "";

// Set the directory containing the message templates
Email.MailTemplateDirectory = @"C:\Mail Templates";

// Create a new parameters collection to hold our mail-merge fields:
NameValueCollection parameters = new NameValueCollection();
parameters.Add("date", DateTime.Today.ToString());
string subject;

// We now want to parse the message templates, inserting the mail merge data and extracting the subject:
string htmlContent = Email.ParseMessageTemplate("htmlTemplate.htm", parameters, out subject);
string textContent = Email.ParseMessageTemplate("textTemplate.txt", parameters);

// Send the email with both html and plain text content.
Email.SendEmail("no-reply@refoster.com.au", "info@refoster.com.au", subject, htmlContent, textContent);

Note we have introduced 2 new Properties: MailTemplateDirectory and WebBaseUrl.  MailTemplateDirectory is set to allow us to just name the templates without having to set the full path.  WebBaseUrl allows us to have relative urls embedded in the HTML mail.  More on that later.

The string passed into Email.ParseMessagTemplate may be a direct file name, or it could be a Web.Config AppSettings key name, where the value of that key is the actual file containing the template.  This means that we can have the AppSettings key specified in the code, and if we decide to use a different file later on, we can just go and change the configuration instead of recompiling.

That's about it really, take a look at the API documentation found on the download page, and feel free to post any questions and suggestions, and I'll try to answer them as quickly as I can.

Enjoy!

Syntax Highlighter TinyMCE plugin for Umbraco

I decided that selecting "Preformatted" in Umbraco's Richtext control and then editing the HTML to include the brush I wanted every time I inserted some code into my blog was getting a little tedious.

I'd already downloaded and configured my templates for using SyntaxHighlighter, but I wanted to go the next step and actually have a nice interface to be able to paste the code into, choose a couple of options and let it be done.

So here it is.  You can download the tinymce plugin from here.  You'll also want to download the SyntaxHighlighter as well and set that up as you like it.  Instructions for setting up SyntaxHighlighter can be found on Alexis' website here.

Credits

I found the basis for this on Nawaf's Blog.  As such I can't in anyway claim this as my own.  All I've done is updated it so that it uses the latest SyntaxHighlighter and made sure it works with a current version of TinyMCE.  And integrated it with Umbraco.  I've also updated the language list and the list of display options.

Installing the Plugin into Tinymce

Unzip the package into the tinymce plugins directory.  You should see the following layout:

tinymce_plugin_dir

then it's simply a matter of customising your tinymce initialization to inlude codehighlighting in your advanced theme.  Something like this would do, customise if how you like:

<!--
tinyMCE.init({
 theme : "advanced",
 theme_advanced_toolbar_location : "top",


    plugins : 'preview,codehighlighting',

    theme_advanced_toolbar_align : "right",
    theme_advanced_buttons1_add : " fontselect,fontsizeselect,zoom",
    theme_advanced_buttons2_add : "preview,separator,forecolor,backcolor",
    theme_advanced_buttons3_add_before : "tablecontrols, codehighlighting"
   
});
// -->


The important lines to take note of here are the ones containing codehighlighting.  That is, the plugins line and the theme_advanced_buttons3_add_before.

Additional Umbraco Specific Steps

Adding any additional functionality to the Richtext Editor in Umbraco is a matter of jumping through hoops, and has been written up many a time, so I'll just put a specific example here.

Firstly, open up the tinyMceConfig.config file found in your umbraco installation.  If you haven't already, install the excellent Config Tree Umbraco Package, as it is an excellent tool and makes this step a breeze.

    
    
    <command>
      <umbracoAlias>mceCodehighlighting</umbracoAlias>
      <icon>../umbraco_client/tinymce3/plugins/codehighlighting/img/codehighlight.gif</icon>
      <tinyMceCommand value="" userInterface="true" frontendCommand="codehighlighting">codehighlighting</tinyMceCommand>
      <priority>16</priority>
    </command>

..

  <plugins>
    <plugin loadOnFrontend="true">paste</plugin>
    <plugin loadOnFrontend="true">inlinepopups</plugin>

..

    <plugin loadOnFrontend="false">codehighlighting</plugin>
  </plugins>

Notice the priority - it's best to keep it unique, and determines the placement of the button in the toolbar.  Save your changes, and then go to the web.config file and save it without making any changes to force Umbraco to load the new configuration.

Notice also that I'm adding the codehighlighting plugin to the end of the list of plugins further down the config file.

Once you have done that, head over to the RichText editor datatype and select your new button:

tinymce_plugin_richtext

You should now be able to use it when editing your content.  Choosing the new Code button will bring up a popup allowing you to paste in your code and set the language and a few options to alter the final output.

tinymce_plugin_popupThat's pretty much all there is to it.  If you have any comments or suggestions, please feel free to leave me a note and I'll try to get back to you as soon as possible.

Enjoy!

Getting Umbraco Gallery to work...

Issue # 1 - Trying to render Display folders containing Non-Image items

If you've used the Gallery addon for Umbraco from Designit, and tried to display a Media folder that contains other folders, no doubt you've come across this message before:

Object reference not set to an instance of an object.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.


The reason for this is that the code assumes that all Media items in the selected folder are Images, and doesn't do anything to filter out other items.

The solution is to add the following code snippet in the  the repImages_ItemDataBound event handler method:

            if (currentImage.ContentType.Alias != "Image")
            {
                e.Item.Visible = false;
                return;
            }

right after

            var currentImage = (Media)e.Item.DataItem;

Issue # 2 - Javascript include files are not automatically included in the page

The most common mistake that results in the Gallery not working as expected (ie, clicking on an image brings up the Lightbox) is to do with missing server-side form tags from your Master page.

Make sure you include the form element on as it's required by ASP.Net to register client side scripts properly:

  <form runat="server" id="form1">
    ..
  </form>

More information about this problem can be found here:

"The client-side script is emitted just after the opening tag of the Page object's <form runat= server> element. The script block is emitted as the object that renders the output is defined, so you must include both tags of the <script> element."

While you are at it, do yourself a favour and make sure your head tag includes the runat="server" attribute as well.  The Gallery package looks for the head tag and creates a new css link to include the lighbox css file.

Hope this helps you out, the second issue in particular was causing me grief before I finally turned to Microsoft's documentation, - I had some sites that would work, but others wouldn't.  It finally clicked that those that worked without me adding the necessary javascript links were using the form tag as mentioned above!