Tagging and Umbraco

I recently had to implement tagging of content in an Umbraco site that I built. It’s not a very publicised feature of Umbraco, but the functionality is all there in the APIs. There are also a couple of very good extensions that can be installed to make your job easier.

I’ve set up a blank Umbraco 4.7.2 site that I’ll be adding tagging, tag management, and then search/display of tags to.

Extensions

The extensions to install are:

  1. Sniper Systems Tags Control – a jquery-based replacement for the built-in ‘tags’ datatype. This one adds autocomplete, and is easier to use
  2. Tag Manager – a custom administration area section for the management of tags. Allows the editor to edit and remove existing tags. Handy for occasions where tags have been typo’d, or when you need to see all content associated with a particular tag.

One thing to watch for after installing Tag Manager is that you’ll need to log out/in, and also provide your users access to that section. The “admin” user has access by default.
2012-08-10_15-09-24

Setting up

Install both extensions into your site, and then do the following:

  1. Create a new DataType that uses the “Sniper Tags” control. I called mine “CWS: Tag” because I’ve installed it on a site that’s built with the Creative Website Starter Kit. Ensure that you put something into the “group” field.
    2012-08-10_14-40-26
  2. Add a new property to your Document Type that uses the DataType we just configured. I’m going to be tagging images in this example, so I added it to the “Image” Document Type:
    2012-08-10_14-54-22

You can now go in and add some tags to your content:
image

Autosuggest for existing tags:
image

Using the tag data on the front-end of the website

Some possible scenarios that you might want to pursue are:

  • Listing the tags on a particular item
  • Searching by tag name
  • Displaying related content based on tags

These are all quite simple to achieve through the use of the built-in functionality in umbraco.cms.businesslogic.Tags. Personally, I find a tool like ILSpy handy when I’m on the go and don’t have access to the Internet. With it, I can view what methods are available to be used.
image

Listing all related tags

Listing all related tags is simple, thanks to DynamicNode and Razor. Something as simple as this inline macro in your page template will display the related tags, if any exist:

<umbraco:Macro runat="server" language="cshtml">
  @if (@Model.HasProperty("tags"))
  {
    <ul>
        @foreach (var tag in @Model.tags)
        {
           <li>@tag</li>
        }
    </ul>
  }
</umbraco:Macro>

Here’s how they appear on my test site:

image

Searching by tag name

Searching by tag name is also quite simple. All that’s needed is a new page template that will hold the search results listing.

We’ll take advantage of Umbraco’s functionality that allows for template switching on the fly. I created a new template with the alias “Tag_Search”. That will allow me to access that particular template from anywhere on the site if I include the alias name in the URL:
http://umbracotest.local/tag_search.aspx?tags=structures

The template itself is quite basic. It’s only there to hold a reference to the Razor macro that we’ll create next:

<%@ Master Language="C#" MasterPageFile="~/masterpages/CWS_Master.master" AutoEventWireup="true" %>

<asp:content ContentPlaceHolderId="headerLinksContent" runat="server">  
</asp:content>

<asp:content ContentPlaceHolderId="SideBarContent" runat="server">  
</asp:content>

<asp:content ContentPlaceHolderId="ChildContent" runat="server">
    

Tag Search Results

</div> </asp:content>

The macro behind this page needs to do several things:

  1. Pick up the value of “tags” out of the querystring
  2. See if there are any content items that match those tags:
    1. If there are none, alert the user that there were no matches
    2. If some were found, display them

A simple example of this macro is shown below. The key to this is either of the following methods:

  1. umbraco.cms.businesslogic.Tags.GetNodesWithTags(string tags)
    or
  2. umbraco.cms.businesslogic.Tags.GetDocumentsWithTags(string tags)
@inherits umbraco.MacroEngines.DynamicNodeContext
@using System.Text
@using umbraco.MacroEngines
@using umbraco.cms.businesslogic.Tags

@{   
    string searchFor = Request["tags"];
  
    if(string.IsNullOrEmpty(searchFor))
    {
        @* No tags were specified *@ 
        <p>Please specify a tag to search for</p>
        return;
    }
  
    var matchingNodes = Tag.GetNodesWithTags(searchFor).ToList();

    string tagsText = searchFor.Split(',').Count() > 1 ? "tags" : "tag";
    
    if (matchingNodes.Count < 1)
    {
       @* No results were found for the specified tags *@ 
       <p>No tagged items were found that matched the @tagsText: @searchFor</p>
       return;        
    }
  
     @* Some results were found for the specified tags *@ 
     <p><strong>@matchingNodes.Count</strong> images were found that matched the @tagsText: "@searchFor"</p>
  
      <ul>
      @foreach (var node in matchingNodes)
      {
        dynamic dn = new DynamicNode(node.Id);
  
        <li><a href="@dn.Url">@dn.Name</a></li>
      }
      </ul>
}

The above macro results in this:image

You can also search for multiple tags, delimited by commas:image

Another version could look something like this:
image

Once this has been implemented, you can go back and revise the tag listing we created earlier to link to the tag search for each tag:
image

<umbraco:Macro runat="server" language="cshtml">
  @if (@Model.HasProperty("tags"))
  {
    

Tags

    @foreach (var tag in @Model.tags) {
  • @tag
  • }
} </umbraco:Macro>

Listing related images

Another use for tags would be to list images that are related to the current one, based on tags. The start of a macro that does this would be:

@inherits umbraco.MacroEngines.DynamicNodeContext
@using umbraco.MacroEngines
@using umbraco.cms.businesslogic.Tags

@{
  var allTags = string.Join(",",Model.tags.ToArray());
  var matchingNodes = Tag.GetNodesWithTags(allTags);
  
  //logic here to display all images, etc
  @matchingNodes.Count
}

This can be used to the following effect:
image

As you can see, using tags in Umbraco is very simple. Even a sysadmin like me can use them.

Umbraco: Set ContinouslyUpdateXmlDiskCache value programatically

I’m working on a site that requires the synchronisation of around 1000 content nodes to MYOB product data daily. In my quest to speed up the process I saw a post by Richard Soetman, developer of the excellent CMSImport package, where he advises to turn off the “ContinouslyUpdateXmlDiskCache” setting in umbracoSettings.config when performing bulk import operations.

I couldn’t find any code on how to do this, and being the .NET newbie that I am, it took me a little while to figure out. Here’s how I did it:

public static void SwitchContinuousUpdateOfXmlCacheState(bool newValue)
{
    if (UmbracoSettings.continouslyUpdateXmlDiskCache == newValue) return;

    var configFilePath = HttpContext.Current.Server.MapPath("~/config/umbracoSettings.config");

    if (File.Exists(configFilePath))
    {
        var umbracoConfig = new XmlDocument();
        umbracoConfig.Load(configFilePath);

        var key = umbracoConfig.SelectSingleNode("//ContinouslyUpdateXmlDiskCache");

        key.InnerText = newValue.ToString();

        umbracoConfig.Save(configFilePath);
    }
}

This way, I can toggle this setting off before performing the import, and then back on again after calling umbraco.library.RefreshContent();

I’m hoping this is the right approach. Feel free to let me know if it isn’t.

Umbraco Google Maps datatype, display with jQuery + GMaps API v3.0

Recently implemented the great Google Maps Datatype by Darren Ferguson in an Umbraco site. I first got the front-end map display working with v2 of the GMaps API and a third party jQuery addon, but I wasn’t happy with the dependency on API keys as this made it annoying to move/copy files around between production, staging, and local dev PCs.

The setup below will show the map if JS in enabled, or a placeholder image if JS is disabled. If it can’t parse the Google Maps Datatype property name, it will show the map centered on its default coordinates.

To display the map in the front-end, I did the following:

  1. Set up the following in its own JS file:
    $.fn.googleMap = function(location,options) {
        var defaults = {
            lat: -33.302959,
            long: 151.419733,
            zoom: 15,
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            mapTypeControl: false,
            controls: ['GSmallZoomControl3D']
        };
    
        options = $.extend(defaults, options || {});
      
        if (location) {
            value = $.trim(location);
            var point = location.split(',');
            options.lat = parseFloat(point[0]);
            options.long = parseFloat(point[1]);
            options.zoom = parseFloat(point[2]);
        };
          
        var center = new google.maps.LatLng(options.lat, options.long);
    
        var map = new google.maps.Map(this.get(0), $.extend(options, { center: center }));
    
        var storeMarker = new google.maps.Marker({
            position: center,
            map: map
        });
    };
  2. Included this on the page where I showed the map:
    1. JS:
      http://maps.google.com/maps/api/js?sensor=false
      
          $(document).ready(function () {
              $('#map_container img').hide();
              $('#map_container').googleMap("");
          });
      
    2. HTML:
      Could not load store map
      </div>

Displaying Google Calendar events in Umbraco

[12th October 2009 – I’ve just updated this post with a new version of the macro. It now groups events properly. Sorry to the guys/girls with multilingual sites, I haven’t put any effort into using Dictionary Items as I don’t currently require that functionality]

I’ve yet to see a good event calendar in any of the CMSes I’ve tried that properly handles recurring events and makes it easy for the layman to update calendar events. I’ve therefore recently implemented a small macro for Umbraco that displays Google Calendar events from a cached XML calendar feed. I did this using the excellent FeedCache plugin and some XSLT.

My one gripe with FeedCache is that you can’t edit the feeds.config file using ConfigTree, but rather need file-level access to it. This is a major bummer, as I wanted to test it on Umbraco Trials.

I won’t post any CSS, but the XSLT caters for styling as it uses bulleted lists and spans around key areas.

The feed will then be displayed similar to below:

Today

* Test Event (19:30-21:30)

Friday 16th October

* Meeting (19:30-21:30)

Sunday 18th October

* Seminar (10:30-12:30)
* Lunch (13:00-14:00)
* Games/Activities (14:30-15:30)

Here’s a screenshot of it working on an Umbraco Trials site. I found a random Google Calendar feed and used that for the demo. Note that I didn’t put any effort into styling the results:

image

Basically, what you need to do is:

  1. Install FeedCache
  2. Install the GCal Events List package
  3. Set up the calendar feed in FeedCache’s feeds.config. The feeds.config entry should look like this:
    [xml]&lt;feed&gt;
    &lt;url&gt;&lt;![CDATA[http://www.google.com/calendar/feeds/calendar@example.org/public/composite?orderby=starttime&amp;sortorder=ascending&amp;futureevents=true&amp;singleevents=true]]&gt; &lt;/url&gt;
    &lt;localFile&gt;GoogleCalendarFeed.xml&lt;/localFile&gt;
    &lt;/feed&gt;[/xml]
  4. Insert a macro wherever you need it, and fill out the parameters;
    1. numberOfDays – (integer) how many days worth of events you would like to display
    2. xmlFileName (string) the exact file name of the cached XML file in the umbracopluginsFergusonMoriyamafeedcache folder. eg. googleEvents.xml
  5. Call FeedCache from your browser to trigger a pull of the feed(s): http://example.org/umbraco/plugins/FergusonMoriyama/FeedCache/FeedCache.aspx
  6. View the page that contains the macro inserted in step 4.