Wednesday, May 30, 2007

Sitecore 5.3 AutoLink add-on

this post applies to Sitecore v5.3.1

So, i was thinking about the pipelines and processors available in Sitecore, and more specifically the availability of hooking into them and how it could be used on the rich text fields.. there's actually a great processor to use called uiSaveHtml that you can hook into - and so i wrote a small module/add-on that can be used to automatically convert word(s) to links, all specified in a sitecore structure and easily managed.

Here's what i ended up doing, and if you follow these steps you'll have a working AutoLink add-on:

step 1: creating the autolink item template and sample item

create a new template called 'autolinkitem'. this template we will use to define each of the substituting 'autolinks'. now, go ahead and add the following fields to it:


  • AutoLink - text

  • Target - link

  • NewWindow - checkbox

after that - save, build & publish it.

now we're going to wanna create a sample item so we'll know if it's working or not, so first create a folder somewhere called 'Autolinks' and then create a new autolinkitem (based on the above created template) in that folder (i've went with /sitecore/content/global/autolinks as the path for my folder) and set the fields to something nice..





screenshot #1: the template & sample item


step 2: create the processor

create a new C# class library (or add to an existing one) that we'll use to store our code in and reference in the processor later on. throughout this post the namespace will be 'usoniandream.autolink' but you can of course name it whatever you wish.


  • add a reference to System.Web (nice to have)

  • add a reference to Sitecore.Kernel (don't copy local).

  • add an empty class called AutoLinkProcessor.cs

now, go ahead and add the following code to your new processor:
using System;
using System.Collections.Generic;
using Sitecore;
using Sitecore.Data.Items;
using Sitecore.Data.Fields;
using
Sitecore.Pipelines;
using Sitecore.Pipelines.SaveHtml;
using
Sitecore.SecurityModel;
using System.Text;
using
System.Text.RegularExpressions;
namespace usoniandream.autolink.pipelines
{
public class AutoLinkProcessor
{
///
///
processor for handling the autolink replacement of words to links
///

/// SaveHtmlArgs
public void
Process(Sitecore.Pipelines.SaveHtml.SaveHtmlArgs args)
{
// make sure we
only do this for the items that actually has some body html to parse
if
(args.Body!="")
{
try
{
// find our root folder for the autolink
items
Item autolinkfolder =
Sitecore.Context.ContentDatabase.Items[Sitecore.Configuration.Settings.GetSetting("usoniandream.autolinks.rootfolder")];
if (autolinkfolder != null)
{
// try to get the autolink items
Item[] itms = autolinkfolder.Axes.SelectItems(autolinkfolder.Paths.Path +
"/*[@@templatekey='autolinkitem']");
if (itms != null)
{
// log it
for informational purposes
Sitecore.Diagnostics.Log.Info("found " +
itms.Length.ToString() + " autolinks to try and patch with..", "AutoLinks");
// handle it as a List instead of an array (simply easier)
List
autolinks = new List(itms);
// make sure we got some autolinks to
parse with
if (autolinks != null && autolinks.Count > 0)
{
// perform the replacement(s)
args.Body = AutoLink(args.Body,
autolinks);
}
}
else
{
// log that we didn't find any items
Sitecore.Diagnostics.Log.Info("unable to find autolink items..",
"autolink");
}
}
else
{
// log that the folder is missing
Sitecore.Diagnostics.Log.Info("unable to find autolink items root folder..",
"autolink");
}
}
catch (Exception exception)
{
// add the
message to the argument and log it for informational purposes
args.AddMessage(exception.ToString(),PipelineMessageType.Warning);
Sitecore.Diagnostics.Log.Error("autolink error:", exception, "autolink");
}
}
}
///
/// Initiates the process of iterating
through the autolink items
///

/// the body
to link within
/// Generic List of items
containing autolink information
/// the finished body html
now with linked words

private string AutoLink(string text,
List autolinks)
{
LinkField lf;
foreach (Item itm in
autolinks)
{
lf = (LinkField)itm.Fields["target"];
if (lf!=null)
{
text = ReplaceWithAutoLinks(text, itm["autolink"], lf.Url,
itm["newwindow"]);
}
}
return text;
}
///
///
Replaces all occurances of a word with the specified link text
///

/// body html to replace within
/// the word to search for
/// the url to link to
/// taken
from the checkbox field
/// System.String
private string ReplaceWithAutoLinks(string text, string search, string link,
string newWindow)
{
// make sure we don't parse any uneccessary item
data that isn't properly entered..
if (text!=string.Empty &&
search!=string.Empty && link!=string.Empty)
{
// build our
replacement link
string replacewith = " <a href="/" alt="'\">"
+ search + "</a> ";
// define our (sorry, but extremely weak)
regex pattern that'll match our words with whitespaces before and after it..
string pattern = @"\s" + search + @"\s";
// redefine the replacement
link if it's for a new window
if (newWindow == "1")
{
replacewith =
" <a href="/" target="'\" alt="'\">" + search +
"</a> ";
}
// log it so we know what's happening..
Sitecore.Diagnostics.Log.Info("autolink pattern '" + pattern + "' will be
replaced with '" + replacewith, "autolink");
// return the parsed text
text = System.Text.RegularExpressions.Regex.Replace(text, pattern,
replacewith, RegexOptions.IgnoreCase);
}
else
{
Sitecore.Diagnostics.Log.Error("autolink couldn't parse since the input data
wasn't properly supplied", "autolink");
}
return text;
}
}
}

once that's done we're gonna have to compile it and make sure it ends up in the bin folder so the system can find it when we're gonna call it.

step 3: editing the web.config file and hooking into the processor

now that we're ready to go and have our code put to use we'll need to tell sitecore that it should, so here's what we're gonna do:


  • locate the uiSaveHtml processor element in the web.config file

add the following line within it as the first processor in the list:

<processor type="usoniandream.autolink.pipelines.AutoLinkProcessor, usoniandream.autolink" mode="on">


  • add a setting so we can easily find our autolinks root folder

add the following line anywhere within the Settings section:

<setting name="usoniandream.autolinks.rootfolder" value="/sitecore/content/global/autolinks">

as you can see that setting points to a folder i mentioned earlier in the post, but as always you can name it anything you like and have it point anywhere you like.
  • save the web.config file

step 4: the finished product

now you're all set and ready to run the finished add-on! Open the content editor and go to any item that has a rich text field on it, double click it so it opens in the editor, write the autolink word(s) that you specified in your sample item (created in step 1) somewhere and click on Accept.




screenshot #2: text before it's automatically converted (just plain text in a richtext field)


Assuming you've followed the above steps correctly you should now see that the word has been replaced with a link instead.




screenshot #3: automatically converted the word 'sitecore' to a link to the sitecore website


step 5: go to bed..

well, that's it for me. time for some much needed sleep.

take care,

P.

Friday, May 25, 2007

Sitecore Small Business Starter Kit

Well, the title pretty much says it all.. Sitecore has, as of today (i think), released a version of the installer for v5.3.1 called Sitecore Small Business Starter kit and it is the kind of istaller that you'd love to have on you everywhere you go - and as soon as you get the question 'so, how great is it?', all you'll have to do from now on is to run the setup and show off the result..

weighting in at 238MB it's a big installer and could be argued that it's way to big to even consider downloading - but man it really does pull it off!

short version: there's now an installer that installs sitecore v5.3.1 just as it used to, but above that is the treat: a fully functioning, multi-purpose, highly customizable and easy to manage site that you can deploy to a customer in hours instead of days/weeks/months.

(i sure hope i can actually post the screenshots of this, because i really think it's something you all should download and try for yourselves.. and, if it turns out i'm not allowed to, well, then the screenshots will go away... )

what the heck am i talking about? i'm talking about this:





Your regular, out-of-the-box, fully functional sitecore installer..





.. that gives you your regular, out-of-the-box, fully functional sitecore cms..





but there's more! it also gives you your very own, ready-to-run, fully-functional website..






.. complete with your very own, not so standard, sample site to see how it all can come together..






.. and finally, your very own, just like you'd like to have it, fully functional help site that describes just about all there is to know and do about the Sitecore Small Business Startup Kit!

oh, don't get fooled by the resolution - all the screenshots are from my tablet-pc, and they just don't grant you the luxury of a fantastic screen size, so you'll have to live with that.

Ok, i'll admit it, i'm a 100% certified Sitecore fanatic, but man - this is really a installer that you can bring to the customer and prove the many benefits the product has - all without having to write a single line of code and it's all available there for you to grab and start using!

conclusion: an awesome job that really makes a tedious task incredibly easy & eye-catching!

and on that note i'll end this sitecore-praise post.

take care,

P.

Monday, May 21, 2007

EXIF data from MediaItem

Reading EXIF data from sitecore media items is probably gonna be -a lot- easier in the future, but as far as i can tell there's no easy way of doing it so far.. Hopefully there's a better (and easier) way of getting EXIF information, but i didn't have time to find it, and this works, so it'll do for now :) The following code will return the resolution specific EXIF information of a passed in MediaItem:
public string GetImageResolution(Sitecore.Data.Items.MediaItem mi) { // check item before we begin if (mi != null) { // inject the stream from the media item to an image (needed to get exif reader running) System.Drawing.Image img = System.Drawing.Image.FromStream(mi.GetMediaStream()); if (img!=null) { // init a new exif reader Sitecore.Drawing.Exif.Reader ExifReader = new Sitecore.Drawing.Exif.Reader(img); if (ExifReader != null) { // allocate the different types of properties we're gonna be using Sitecore.Drawing.Exif.Properties.Property prop; Sitecore.Drawing.Exif.Properties.UInt16Property u16prop; Sitecore.Drawing.Exif.Properties.RationalProperty rprop; // start fetching properties.. prop = ExifReader.GetProperty((int)Sitecore.Drawing.Exif.Properties.Tag.ResolutionUnit); if (prop != null) { // first is a UInt16 property that we'll step through and see what type it is.. u16prop = (Sitecore.Drawing.Exif.Properties.UInt16Property)prop; if (u16prop!=null) { // get the resolution unit string resUnit = "dpi"; switch (u16prop[0]) { case 1: resUnit = "n/a"; break; case 2: resUnit = "dpi"; break; case 3: resUnit = "dpcm"; break; } // now go fetch the two remaining properties (they're both rational properties) // horizontal resolution string horzRes = "1/72"; prop = ExifReader.GetProperty((int)Sitecore.Drawing.Exif.Properties.Tag.XResolution); if (prop != null) { // cast it to rational property rprop = (Sitecore.Drawing.Exif.Properties.RationalProperty)prop; if (rprop != null) { if (rprop.Values[0].ToString() != "") { horzRes = rprop.Values[0].ToString(); // try to calculate it appropriately.. (sucks, but we gotta do it unless we want mumbo jumbo text to appear..).. string[] hstrs = rprop.Values[0].ToString().Split('/'); if (hstrs.Length>0) { // format the string horzRes = string.Format("{0}", (double.Parse(hstrs[0].Trim()) / double.Parse(hstrs[1].Trim()))); } } } } // repeat for vertical string vertRes = "1/72"; prop = ExifReader.GetProperty((int)Sitecore.Drawing.Exif.Properties.Tag.YResolution); if (prop != null) { // cast it to rational property rprop = (Sitecore.Drawing.Exif.Properties.RationalProperty)prop; if (rprop != null) { if (rprop.Values[0].ToString() != "") { vertRes = rprop.Values[0].ToString(); // try to calculate it appropriately.. (sucks, but we gotta do it unless we want mumbo jumbo text to appear..).. string[] vstrs = rprop.Values[0].ToString().Split('/'); if (vstrs.Length > 0) { // format the string vertRes = string.Format("{0}", (double.Parse(vstrs[0].Trim()) / double.Parse(vstrs[1].Trim()))); } } } } // finally format the output and return it return string.Format("{0} {1} x {2} {3}", horzRes, resUnit, vertRes, resUnit); } } } } } return ""; }
The resulting output will give you a string along the lines of '300 dpi x 300 dpi' Regards, P.

Thursday, May 10, 2007

Barcelona

well, i'm off for Barcelona where i'll be enjoying the sun and some great Formula-1 action. sorry for the lack of interesting posts lately - when i get back i'm gonna start another series of posts and tutorials to make up for it. Take care, P.