Lee Kelleher’s Weblog

random posts on code, .NET, Umbraco and WordPress

Bit.ly Url Shortener DataType for Umbraco

leave a comment »

Over the weekend, I coded up a little treat… a Bit.ly Url Shortener data-type for Umbraco!

The new data-type lets you shorten your Umbraco page URLs using the Bit.ly API service.

For more information, go to the project page over on Our Umbraco.

Written by Lee Kelleher

January 9, 2010 at 10:43 pm

Posted in blog

Tagged with , , , ,

Updated Robots.txt Editor for Umbraco

leave a comment »

Last night I released version 2 of the Robots.txt Editor for Umbraco.

Changes & new features:

  • Restructured the package files, they all live in a folder called “robots-txt” (still under the /umbraco folder – but self-contained)
  • Errors are now displayed using the Feedback control (as opposed to the Speech Bubble in the bottom-right corner).
  • Robots.txt editor has buttons for adding new User-Agent and Disallow rules, as well as comment/uncomment functionality.
  • Changed the Robot icon… courtesy of Mozilla Firefox’s “about:robots” favicon.

Hope you all enjoy the update… if you come across any bugs or ideas for a future release, please let me know via the our.umbraco forum.

Written by Lee Kelleher

January 8, 2010 at 10:53 am

Posted in blog

Tagged with , , , , , ,

How to use umbraco.library GetMedia in XSLT

with 2 comments

From time to time I notice a reoccurring post over at the Our Umbraco forum; how to display an image (from the Media section) in XSLT?

A quick answer can be found on the Our Umbraco wiki for the umbraco.library GetMedia method.

For most uses, the last example in the wiki works great.  But I want to show you a “super safe” way of dealing with GetMedia in XSLT.

Where I find a lot of the examples go wrong is that they make the assumption that a media node (XML) is returned from the GetMedia call, e.g.

<xsl:value-of select="umbraco.library:GetMedia($currentPage/data[@alias='mediaId'], 'false')/data[@alias='umbracoFile']" />

If the ‘mediaId’ property didn’t contain either a numeric value or a valid media node id, then it would return null … meaning that the following “/data” would throw an Exception! (Displaying “Error parsing XSLT file” message on the front-end.)  Not what you or your users want to see!

In order to consider any user inputs, like media IDs not being selected, or even a referenced media node is deleted in the back-office, here is the “super safe” approach:

<xsl:template match="/">
	<xsl:variable name="mediaId" select="number($currentPage/data[@alias='mediaId'])" />
	<xsl:if test="$mediaId &gt; 0">
		<xsl:variable name="mediaNode" select="umbraco.library:GetMedia($mediaId, 0)" />
		<xsl:if test="count($mediaNode/data) &gt; 0">
			<xsl:if test="string($mediaNode/data[@alias='umbracoFile']) != ''">
				<img src="{$mediaNode/data[@alias='umbracoFile']}" alt="[image]">
					<xsl:if test="string($mediaNode/data[@alias='umbracoHeight']) != ''">
						<xsl:attribute name="height">
							<xsl:value-of select="$mediaNode/data[@alias='umbracoHeight']" />
						</xsl:attribute>
					</xsl:if>
					<xsl:if test="string($mediaNode/data[@alias='umbracoWidth']) != ''">
						<xsl:attribute name="width">
							<xsl:value-of select="$mediaNode/data[@alias='umbracoWidth']" />
						</xsl:attribute>
					</xsl:if>
				</img>
			</xsl:if>
		</xsl:if>
	</xsl:if>
</xsl:template>

Here’s what happens:

  1. The “mediaId” is pulled from a property of the “currentPage” and cast as a number.  Optionally the “mediaId” could be passed in via a macro parameter, or somewhere else?
  2. The first condition checks the the “mediaId” is numeric, and greater-than zero.
  3. The “mediaId” is passed through to “GetMedia”, along with the false flag to only pull-back the required node (not it’s children, for Folder media items).
  4. We check if the media node has any child “data” elements – which contain the data about the image/media.
  5. Then we check if the “umbracoFile” property has any data – if not, then there is no point displaying an image.
  6. There are extra conditions for the “height” and “width” properties – these are optional.

Personally, I add an “altText” property to the Image media-type … and use that in the XSLT – again this is optional, but strongly recommended!

I can see how this “super safe” approach is overkill – especially compared with a single line of XSLT … but from my experience, it’s better to be safe than sorry – especially when dealing with user data-input – your assumptions and expectations of how users will use the system aren’t always correct!

Update: OK, I agree the extra “if” statements are overkill… so here’s a condensed version – assuming that the “umbracoHeight” and “umbracoWidth” properties are always there…

<xsl:template match="/">
	<xsl:variable name="mediaId" select="number($currentPage/data[@alias='mediaId'])" />
	<xsl:if test="$mediaId &gt; 0">
		<xsl:variable name="mediaNode" select="umbraco.library:GetMedia($mediaId, 0)" />
		<xsl:if test="count($mediaNode/data) &gt; 0 and string($mediaNode/data[@alias='umbracoFile']) != ''">
			<img src="{$mediaNode/data[@alias='umbracoFile']}" alt="[image]" height="{$mediaNode/data[@alias='umbracoHeight']}" width="{$mediaNode/data[@alias='umbracoWidth']}" />
		</xsl:if>
	</xsl:if>
</xsl:template> 

Written by Lee Kelleher

November 30, 2009 at 2:33 pm

Posted in blog

Tagged with , , , ,

WordPress Plugin Development: Beginner’s Guide, by Vladimir Prelovac

with one comment

wordpress-plugin-development-book I have recently finished reading Vladimir Prelovac book on WordPress plug-in development, (WordPress Plug-in Development: Beginner’s Guide, Packt Publishing, 2009).

As an experienced WordPress plug-in developer, I was pessimistic at the start – there is already a wealth of documentation and detailed tutorials on the web, who would need a book? However with Vladimir being a well-respected WordPress plug-in developer, I was curious to see his approach on the subject, and you never know – I may learn a thing or two? (hint: I did!)

WordPress Plug-in Development (Beginner’s Guide) is a great introduction to the WordPress platform, the plug-in architecture and the ever-growing community surrounding it.

The first thing I noticed was that the book is centred around WordPress 2.6, (at the time of writing the latest version is 2.8.4, with 2.9 due out very soon), unfortunately all of the screenshots for the admin back-end are outdated, since there was a complete overhaul to the design in version 2.7. This is no fault of the author, but an example of how fast paced the WordPress developer community is. Luckily, the principles that Vladimir explains are focused on the core WordPress functionality, which are solid and stable.

Throughout the book, Vladimir encourages you to “hack” around with code, which for me is a great experience – it’s how I learnt to code! As the tagline of the book states: “Learn by doing: less theory, more results“.

There is also a great awareness of 3rd-party applications, libraries and resources, (like Digg, jQuery, Poedit, etc), which is fine for experienced web-developers, but for beginners – it will open up a world of exciting developments! Thinking back to when I first played with jQuery… or pulling my photos back from Flickr’s API… so cool!

The very nice people at Packt Publishing have a sample chapter available on-line; Chapter 2: Social Bookmarking, covers how to develop your own Digg button for your blog.

Vladimir covers all areas of WordPress development, from action and filter hooks to shortcodes and widgets to localisation (i18n & L10n). Even I didn’t know about the “wp_localize_script” function!

The mantra of the book is to leverage WordPress whenever you can, try not to re-invent the wheel. Which reminds me of Peteris Krumins‘ blog quote: “good coders code, great reuse”!

As the book progresses, each chapter’s example plug-in becomes more challenging and complex. The focus on localisation over the last few chapters was most interesting for me, as it is (probably) the least understood concept in WordPress plug-in development!

Often it is easy for WordPress users to take plug-in functionality for granted, this book exposes the amount of thought, planning, design and skill that goes into every (popular) WordPress plug-in.

In summary, WordPress Plug-in Development (Beginner’s Guide) is perfect for anyone who is interested in extending the functionality of WordPress – it goes without saying that a little PHP knowledge is required, but that isn’t a show-stopper, you can learn whilst you “hack”!

Written by Lee Kelleher

October 20, 2009 at 11:34 pm

Putting your ASP.NET Web Application in Maintenance Mode (using ISAPI_Rewrite)

with one comment

Prompted by @slace’s tweet:

Aaron Powell i wish there was a way to use app_offline but still view from certain ip’s

I replied with a suggestion that we’ve used in the past. Aaron said I should blog about it… so here I am (again)!

A while ago we needed to do an Umbraco upgrade (from v3 to v4) on a production server – in my opinion it was a pretty major upgrade on a live site, we had done a couple of test upgrades on dev and staging, all was successful.  But since there was various parts of the site that we need to regression test, I felt it best to take the entire site offline whilst we upgraded.

Usually creating an “App_Offline.htm” page in the root of your web app is enough to take it offline.  However that was no good for testing… so what to do?

This is where ISAPI_Rewrite is your best friend, (or .htaccess to be precise).  We needed to configure the site to allow access for us and redirect everyone else to a “Site under maintenance” page.  I found a few examples across the web, but to save you all that hassle, here are the .htaccess rules that we use:

# BEGIN Maintanence Mode
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !/offline.html$
RewriteCond %{REMOTE_ADDR} !^82\.13\.23\.230$
RewriteRule ^(.*)$ /offline.html [R=302,L]
</IfModule>
# END Maintanence Mode

What does it do? The first “RewriteCond” rule checks that you are not requesting the “offline.html” page (otherwise you would end up in a constant loop!) The second “RewriteCond” checks the IP address of the visitor – in my case it was “82.13.23.230″ (remember to escape the dots). If those two rules aren’t satisfied, then the “RewriteRule” is used, redirecting the visitor to the “offline.html” page.

As always, I am open to any suggestions or improvements!

Written by Lee Kelleher

September 29, 2009 at 1:22 pm

How to convert NameValueCollection to a (Query) String [Revised]

with 2 comments

Following on from a comment on my previous post about converting a NameValueCollection to a (query) string – I have finally got around to revising my code snippet.  Now the method will handle same key multiple values, (it no longer comma-separates them).

I have also added extra parameters so that you can define your own delimiter (since the HTTP specification says that you can use both ampersands & and semicolons ;) and there is an option for omitting keys with empty values.

/// <summary>
/// Constructs a NameValueCollection into a query string.
/// </summary>
/// <remarks>Consider this method to be the opposite of "System.Web.HttpUtility.ParseQueryString"</remarks>
/// <param name="parameters">The NameValueCollection</param>
/// <param name="delimiter">The String to delimit the key/value pairs</param>
/// <returns>A key/value structured query string, delimited by the specified String</returns>
public static String ConstructQueryString(NameValueCollection parameters, String delimiter, Boolean omitEmpty)
{
	if (String.IsNullOrEmpty(delimiter))
		delimiter = "&";

	Char equals = '=';
	List<String> items = new List<String>();

	for (int i = 0; i < parameters.Count; i++)
	{
		foreach (String value in parameters.GetValues(i))
		{
			Boolean addValue = (omitEmpty) ? !String.IsNullOrEmpty(value) : true;
			if (addValue)
				items.Add(String.Concat(parameters.GetKey(i), equals, HttpUtility.UrlEncode(value)));
		}
	}

	return String.Join(delimiter, items.ToArray());
}

Written by Lee Kelleher

September 19, 2009 at 11:08 pm

Posted in blog

Tagged with , , , , ,

Umbraco: Ultimate Picker XSLT Example

with 7 comments

Chatting with Dan (my partner-in-code at Bodenko) about the Ultimate Picker data-type in Umbraco, we realised that we couldn’t find any examples of how to use the data in XSLT. So obviously needing an excuse to write-up a new blog post, here we go.

If you need a quick overview about the Ultimate Picker data-type, see Tim Geyssens’ blog post.

For my example, using a default Umbraco install (with Runway), we will create a new data-type using the Ultimate Picker, (let’s call it “Runway Textpage Picker“), we select the ‘Database datatype‘ to be “Nvarchar”; the ‘Type‘ as a “List Box”; The ‘Parent nodeid‘ is “1048″ and the ‘Document Alias‘ filter is “RunwayTextpage” – we also tick the ‘Show grandchildren‘ checkbox.

Ultimate Picker Data Type settings

Ultimate Picker Data Type settings

Next, we will assign the new “Runway Textpage Picker“ data-type to a property of the Runway Textpage, we will call it “Related Content“. For more information about working with document types, please refer to the our.umbraco.org wiki page, (Working with document types).

Adding Ultimate Picker to a Document Type

Adding Ultimate Picker to a Document Type

Once the document-type is saved, we move on to the ‘Content‘ section. Select any of the Runway Textpages.  You should now see the ‘Related Content‘ property panel, containing a list of all the other Runway Textpages.  To select multiple items, hold-down the CTRL (or Command on the Mac) button.  When you have finished, click the ’Save and publish‘ button.

Selecting related content

Selecting related content

For the next part we get to the real meat of this blog post… the XSLT!

Create a new XSLT

Create a new XSLT

Create a new XSLT called “RelatedContent” (without the .xslt extension), keep it ‘Clean’ and tick the ‘Create Macro‘ checkbox.  Next a quick short-cut for you; copy-n-paste the following XSLT into the main editor window.

Copy-n-paste the XSLT

Copy-n-paste the XSLT

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp "&#xA0;"> ]>
<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"
	exclude-result-prefixes="msxml umbraco.library">
	<xsl:output method="xml" omit-xml-declaration="yes"/>

	<xsl:param name="currentPage"/>

	<xsl:template match="/">
		<xsl:variable name="preNodes">
					<xsl:variable name="relatedContent" select="$currentPage/data[@alias='RelatedContent']" />
					<xsl:variable name="nodeIds" select="umbraco.library:Split($relatedContent, ',')" />
					<xsl:for-each select="$nodeIds/value">
						<xsl:copy-of select="umbraco.library:GetXmlNodeById(.)"/>
					</xsl:for-each>
		</xsl:variable>
		<xsl:variable name="nodes" select="msxml:node-set($preNodes)/node" />
		<xsl:if test="count($nodes) > 0">
<div class="related-content">
<h3>Related Content</h3>
<ul>
					<xsl:for-each select="$nodes">
	<li>
							<a href="{umbraco.library:NiceUrl(@id)}">
								<xsl:value-of select="@nodeName" />
							</a></li>
</xsl:for-each></ul>
</div>
</xsl:if>
	</xsl:template>

</xsl:stylesheet>

Here’s a quick explanation of what is happening in the XSLT.  We are provided with a list of comma-separated nodeIds from the Ultimate Picker, which we need to parse/split – then pull back the XML node data, then we can transform it however we like!

The way we do this is 2-tiered, first we must loop through the comma-separated list, pulling back the XML node for each nodeId, adding it to an XSLT variable.  Doing this will cause the XSLT variable to be a fragmented node-tree, which means that we need to convert the node-tree fragment into a node-set.  (*Note: there are a gazillion ways to skin a cat – suggestions are welcome – this method works for me).

Once we have the complete XML node-set, we can transform into whatever HTML we  like.

Now that we are done with the XSLT, we can edit our template in the ‘Settings‘ section. Select the “Runway Textpage” template. Somewhere after the ‘bodyText‘ property item, click on the ‘Insert Macro’ button.  From the ‘Choose a macro‘ drop-down, select the ‘Related Content‘ option – this will add the macro code to the template.

Insert the macro into the template

Insert the macro into the template

Save the template and view the front-end page. We can now see the Related Content pages. Tada!

The end result! Related Content

The end result! Related Content

Written by Lee Kelleher

September 8, 2009 at 10:38 pm

Robots.txt Editor for Umbraco

leave a comment »

Following up on my recent post of using Robots.txt with Umbraco, I decided that it would be nice to be able to edit the robots.txt directly from the Umbraco back-end.  (Also I wanted to play a bit more with the BaseTree/ITree classes).

This afternoon I had a few hours to spare – actually I was procrastinating on another job, (don’t tell my client – I’ll finish it off later tonight) – so I got down to some coding.

The source-code is available on the Umbraco Extensions project (on CodePlex) and created a project page on the new Our Umbraco community website. (Don’t forget to give it some karma points! ;-p)

A direct download to the package installer (zip) is available here.

Very special thanks to Dave Kinsella for providing the Robot icon robot icon – although I know Dave, it was great proof that Twitter really does work! (Thanks to Dan too for his Johnny 5 attempt! 18480255 – Here was my attempt too!  18484040)

If you have any bugs, comments, feedback or suggestions – please feel free to get in touch with me via the Our Umbraco forums.

Written by Lee Kelleher

July 20, 2009 at 10:18 pm

Posted in blog

Tagged with , , , , , ,

Add YouTube Plug-in to Umbraco/TinyMCE

with 13 comments

Update: Following on from Dirk and Ismail’s comments, I found out that this YouTube plug-in does not work with TinyMCE v3 (which is the default richtext editor in Umbraco v4). This guide is written to works  for Umbraco v3 only, (using TinyMCE v2).

If you are looking for similar functionality in Umbraco v4, (TinyMCE v3), then all you need to do is enable the ‘Flash/media’ button in your Richtext editor data-type and embed the YouTube video like any other Flash movie (swf) – more details in my comment below.

/End of update.


Recently one of my clients wanted the ability to insert YouTube video clips directly into the TinyMCE editor within Umbraco.  My initial thought was to create a macro that would take a YouTube video URL, parse it and display it on the rendered (front-end) page.  Tim G has a blog post on how to do this on his Nibble blog, (love the Surfin’ Bird reference).

This approached worked fine, but we ran into problems trying to edit the YouTube video URL, along with that, my client had an additional step of selecting a macro, then entering the YouTube URL.

After a little researching, I eventually found a native YouTube plug-in for TinyMCE, (direct link to ZIP download).

Here is how I integrated in Umbraco:

  1. Extract the contents of the youtube.zip archive to your /umbraco_client/tinymce/plugins/youtube/
  2. In your /config/ folder, open the tinyMceConfig.config file.
  3. Insert the following lines:
    After the last <command /> entry, add…
    <command>
    	<umbracoAlias>mceYoutube</umbracoAlias>
    	<icon>../umbraco_client/tinymce/plugins/youtube/images/youtube.gif</icon>
    	<tinyMceCommand value="" userInterface="true" frontendCommand="youtube">youtube</tinyMceCommand>
    <priority>75</priority>
    </command>

    Then after the last <plugin /> entry, add…

    <plugin loadOnFrontend="false">youtube</plugin>
  4. Once the XML config entries are in place, you will need to restart the  Umbraco application – the quickest way of doing this is by modifying your Web.config, (literally open it, add a space, remove it, hit save).
  5. The YouTube button is now available in Umbraco. However, it’s not quite ready yet, there is still one more step!
  6. In your Umbraco back-end, go to the “Developer” section, expand the “DataTypes” folder and then select “Richtext editor”. In the “Buttons” section you should see a YouTube icon. Check the box next to the icon, and you’re done! If you don’t see the YouTube icon, then check that the did the config steps above, and/or check that the read permissions are set correctly on your /umbraco_client/ folder, (re-apply them if needs be).

Written by Lee Kelleher

July 16, 2009 at 3:07 pm

Posted in blog

Tagged with , , ,

Robots.txt for use with Umbraco

with 10 comments

I originally posted this over at the Our Umbraco community wiki. [Robots.txt for use with Umbraco] I am only posting it on my blog as a cross-reference. The Our Umbraco wiki version will evolve with the community’s experience and knowledge.

The Robots Exclusion Protocol has been around for many years, yet there are a lot of web-developers who are unaware of the reasons for having a robots.txt file in the root of their websites.

There have been many rumours around whether the bigger search engine crwalers (i.e. Googlebot) consider your website amateurish if you didn’t have a robots.txt – and if handled badly, could lead to your site being invisible on SERPs.

If you are happy for a crawler to crawl/index all of your website’s content, then you can use the following:

User-agent: *
Disallow:

However, when using Umbraco to power my websites, it is preferable to define which folders are accessible by the crawler. Personally, I would not like to see the contents of my /umbraco/ folder to be returned in Google’s SERPs.

Here is an example of the robots.txt that I have used on several Umbraco-powered websites.

# robots.txt for Umbraco
User-agent: *
Disallow: /aspnet_client/
Disallow: /bin/
Disallow: /config/
Disallow: /css/
Disallow: /data/
Disallow: /scripts/
Disallow: /umbraco/
Disallow: /umbraco_client/
Disallow: /usercontrols/
Disallow: /xslt/

From my perspective, there is no reason for a search engine crawler to be crawling/indexing files from any of the above folders – you may have a different perspective, to which you can amend your robots.txt accordingly.

For more information about the robots.txt standard, please refer to the official website: http://www.robotstxt.org/robotstxt.html

Written by Lee Kelleher

July 7, 2009 at 4:10 pm

Posted in blog

Tagged with , ,