Tuesday 17 November 2009

The solution is offline because its associated Team Foundation Server is offline

When opening a solution in Visual Studio 2008, I kept getting the following message in the Output window:

This solution is offline. [Team Foundation Server:
http://server:8080/]
The solution is offline because its associated
Team Foundation Server is offline.

I kept having to go into the "change source control" and re-bind the solution. After a while, this began to get annoying. Then I found this post: http://www.eggheadcafe.com/software/aspnet/33487526/connecting-automatically.aspx. It turned out the solution was to set some registry keys. I navigated to HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\TeamFoundation\Servers\ and changed "Offline" to 0 and "AutoReconnect" to 1. I've no idea how these got reset! (Please back-up your registry first if you want to try the same.)

Friday 13 November 2009

System.ArgumentException: Illegal characters in path

This exception caused me a headache for an hour today on a development web site. After various rebuilds failed to cure the issue, I found out that the aspnet_wp.exe process had somehow become corrupted. Re-saves of the web.config file didn't help, so I opened Task Manager, killed the process and was back up and working again.

Wednesday 28 October 2009

Get a list of all Sitecore Items tagged with specific multilist items

If you wish to return all items in the content tree that have been tagged with a specific multilist item, you might be tempted to use Sitecore.Context.Database.SelectSingleItem(), passing it an XPath query. The problem with this is that performance degrades as the content tree grows. A far more efficient way is to use the GetReferrers method of the LinkDatabase class as shown below:

Database db = Sitecore.Context.ContentDatabase;

LinkDatabase linkDb = Globals.LinkDatabase;

// lookupItem is the multilist item
ItemLink[] links = linkDb.GetReferrers( lookupItem );

foreach( ItemLink link in links )
{
Item linkedItem = db.Items[link.SourceItemID];
// Process item...
}

Monday 12 October 2009

Example Useful C# Extension Functions

Here are a few extension functions that I find useful:


public static bool IsNullOrEmpty(
this string expression )
{
bool result = string.IsNullOrEmpty(
expression );
return result;
}

public static string ToFormat(
this string expression, params object[] args )
{
string result = String.Format(
expression, args );
return result;
}

public static bool IsValidEmailAddress(
this string expression )
{
bool result = BaseRegexHelper.IsValid(
expression, emailRegex );
return result;
}

public static bool IsValidGuid(
this string expression )
{
bool result = BaseRegexHelper.IsValid(
expression, guidRegex );
return result;
}

The latter two functions call this method:

public static bool IsValid(
string input, string pattern )
{
bool isValid = false;

if( input != null )
{
string trimmedExpression =
input.Trim();

if( trimmedExpression.Length > 0 )
{
Regex regexGuid = new Regex(
pattern, RegexOptions.IgnoreCase );
isValid = regexGuid.IsMatch(
trimmedExpression );
}
}

return isValid;
}

Friday 14 August 2009

Write to Sitecore log file from XSLT

It is simple to write to the Sitecore log file from within an XSLT file without having to write any custom code. We can just hook up to the Sitecore.Diagnostics.Log class and call one of its methods.

In the web.config, define the extension under <xslextensions>:

<extension mode="on"
type="Sitecore.Diagnostics.Log, Sitecore.Kernel"
namespace="http://www.sitecore.net/log"
singleInstance="true" />

Within the XSLT file, add a prefix that matches the namespace you used in the above step:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:sc="http://www.sitecore.net/scReal"
xmlns:dot="http://www.sitecore.net/dot"
xmlns:log="http://www.sitecore.net/log"
exclude-result-prefixes="dot sc log">

The Log class contains a number of overloads for Info, Warn, Error and so on. Some of these expect an exception as a parameter, so we cannot call these direct from XSLT. We can, however match the overloads that expect a string and an object (eg. Warn( String, Object )):

<xsl:value-of select="log:Warn('Warning message', 'Another string... perhaps the name of your rendering file')" />

That's it! Now you can log whatever you like from within your renderings.

Thursday 6 August 2009

Using Linq lambda expressions to retrieve XML elements based on the value of an attribute

The following example program loads an XML file, then selects all the elements named "field" within it. It then uses a lambda expression to get the inner text of the "alternativeTitle" element.

class Program
{
static void Main( string[] args )
{
XDocument xd = XDocument.Load( "..\\..\\file.xml" );

var fields = from el in xd.Descendants()
where el.Name.ToString().StartsWith( "field" )
select el;

Console.WriteLine( GetElementValue(fields, "alternativeTitle") );
Console.ReadLine();
}

static string GetElementValue( IEnumerable<XElement> elements, string attributeName )
{
return elements.Single( p => p.Attributes().Single( q => q.Name == "name" ).Value == attributeName ).Value;
}
}

The XML file used in the example is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<add>
<doc>
<field name="id">some_id</field>
<field name="readableid">interesting_publications</field>
<field name="name">Interesting Publications</field>
<field name="alternativeTitle">Online publications</field>
</doc>
</add>

The program outputs the text:

Online publications

Tuesday 28 July 2009

Enable XSLT IntelliSense within Visual Studio

Wouldn't it be nice to have Visual Studio list the available template names when typing out a call-template statement in XSLT? Wouldn't it also be nice to have it list available parameter names when typing out a with-param statement?

To enable these autocompletions and more, you need to back up your registry and then run regedit. Within regedit, create a string value called "XsltIntellisense" under "HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\XmlEditor" key, giving it the value "True". That's it!

Ultimately, I'd like to see full IntelliSense for XSLT in Visual Studio, but this is a good start.

The type initializer for 'Sitecore.Search.SearchManager' threw an exception

If you get this (or similar) error, check that the version of the Sitecore.Kernel.dll referenced by class libraries is the same as used by the web site. It fixed a problem I had anyway!

Tuesday 14 July 2009

ASP.NET Accessibility Top Ten

Here are my top ten tips for designing accessible ASP.NET sites. These are based on my understanding and opinion. If I am radically wrong about any of these, please feel free to correct me by leaving a comment.

Always set the display of validation controls to ‘dynamic’
In this way, screen readers ignore the text of the error message when there is no error.

Do not use image buttons
They are invisible to screen readers. Use a button with a background image instead.

Do not use AutoPostBack
They are impossible to navigate using a keyboard.

Do not use separator characters between links
Characters in breadcrumbs, footers, etc. are read out by screen readers. Use a span instead with a style instead. Or better still, use a bulleted list with a background image.

Display sets of results or links in a bulleted list
In a landing page or search results page, having each result marked up as a bullet makes it easier to navigate between results.

Insert hidden header text at the start of every navigation list
Screen reader users can navigate by headers and choose to skip the navigation lists.

Surround the individual digits in telephone numbers with span tags
Otherwise, the whole number will be read out as a single value.

Use hidden images with pagination and other non-descriptive hyperlinks
If pagination links are in the format: 1 | 2 | 3, a screen reader will read the links as ‘link 1’, ‘link 2’ and so on. So, insert a hidden image that has the alt text set to something more descriptive.

Include file sizes and types in hyperlink text
Unwittingly clicking a link to a file attachment can cause confusion.

Don’t bother using access keys
They are device-dependent, they can conflict with other shortcut keys and there is no standardisation as to what each key combination should do. Better to have clear navigation.

Using a Clean Sitecore Client for Authoring

I had a situation this week where I could not get the Sitecore client to work on my local machine. Although I could browse the end web site, I got the dreaded vague null reference exception when trying to browse to /sitecore. As the web site was fairly complex, I was finding it very painful to try to re-contruct from new. In the end, it dawned on me that all I needed to do was install a clean Sitecore client that was completely separate from the web site. This gave me the ability to change content, while the end web site enabled me to view the effect of code changes. This set-up has other benefits too; the Sitecore client runs unimpeded by any custom code (unless you are using pipelines, etc.). This makes it easier to identify/eliminate custom code as a possible cause for any slowness. Similarly, you can readily identify if any errors in the log are due to your code.

Thursday 9 July 2009

Value cannot be null

When trying to open the Sitecore client (ie. by putting /sitecore in the URL), I got the following exception:

Exception: System.ArgumentNullException
Message: Value cannot be null.
Parameter name: item
Source: Sitecore.Kernel
at Sitecore.Diagnostics.Assert.ArgumentNotNull(Object argument, String argumentName)
at Sitecore.Web.UI.HtmlControls.Menu.AddFromDataSource(Item item, String target)
at Sitecore.Web.UI.HtmlControls.DataContextMenu.OnLoad(EventArgs e)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

I noticed that the URL had changed to /sitecore/shell

With so few clues as to the cause, I simply entered the full path to the log-in screen (/sitecore/login). The log-in screen loaded and I was able to log in successfully.

Out of curiosity, I logged out again and then re-tried /sitecore on its own. The log-in screen opened OK that time. I've no idea as to the cause and I don't fancy investigating. If you're having the same problem, why not try what I did? Good luck.

Wednesday 8 July 2009

Save Settings to Web.Config file

Here's a quick snippet showing how to save a setting back to the web.config file. You are best wrapping this block in a try/catch in case the ASPNET user (or equivalent) does not have write access permissions to the file.

Configuration config = WebConfigurationManager.OpenWebConfiguration( "~/" );
AppSettingsSection appSettings = config.GetSection( "appSettings" ) as AppSettingsSection;

if ( appSettings != null )
{
if ( appSettings.Settings ["mySetting"] != null )
{
appSettings.Settings.Remove( "mySetting" );
}
appSettings.Settings.Add( "mySetting", text );
}
config.Save( ConfigurationSaveMode.Modified );

Wednesday 15 April 2009

SqlParameter is Already Contained in Another SqlParameterCollection

This exception occurred between calls to a method I used to populate a SqlCommand object.

SqlCommand cmd = conn.CreateCommand();

...

foreach (SqlParameter sqlParameter in sqlParameters)
{
cmd.Parameters.Add(sqlParameter );
}


The solution was to clear the parameter collection at the end of the method (in a finally block, actually):

cmd.Parameters.Clear();

Thursday 9 April 2009

Button click event handler fires twice

I had a simple button on a form together with its click event handler. On debugging, I noticed that the event handler was firing twice. After a long investigation, I decided to check the HTTP headers. These revealed that the "Display Page Validation" option I was using in the Tools menu of the Web Dev toolbar in Firefox was the culprit.



When switched on, this runs the handler a second time, submitting the results to a validator at w3.org. This is something to bear in mind, particularly for code that must only run once, such as handling credit card payments.

Friday 16 January 2009

Accessible print-friendly web pages

There are times when you might want to render a page specifically for printing (rather than using CSS). For example you might want to include or exclude specific information on the printed page. When users view the print-friendly page, you might want to include some instructions at the top of the page, but not have these themselves print out. You might want to let users know they are viewing a print-friendly version and provide them with a link back to the on-screen view.

You can do this by first defining a “noprint” class:

<style type="text/css">
@media print
{
.noprint
{
display: none;
}
}
</style>


You can then include your instructions in a DIV having this class.

Typically, the next action a user will take on viewing the page is to open the print dialogue box. We could attach a “window.print()” JavaScript action to the body’s onload event, or within a <script> tag somewhere in the body. However, having a dialogue box appear automatically is not particularly accessible as it changes the focus. The alternative is to insert a “print this page” link if the browser supports JavaScript. For non-JavaScript browsers, simply inform the user to use his or her browser’s print menu. This is demonstrated by the following HTML:

<div class="noprint" style="background-color: #eeeeee;
border: solid 1px black; padding: 10px 10px 10px 10px;">
<p>
You are viewing a print-friendly version
of a page.</p>
<p>

<script type="text/javascript">
var str = "Print this page";
document.write(
str.link("javascript:window.print()"));
</script>

</p>
<noscript>
<p>
Please select print from your browser’s
menu to print the page</p>
</noscript>
<p>
or <a href="/path/file.htm">
return to the normal on-screen view of the page
</a>
</p>
</div>

Screen reader friendly phone numbers

When you display a telephone number on screen, screen readers read it out as a single number. The Fangs screen reader emulator (see http://sourceforge.net/projects/fangs) reads the number (01234) 567890 as “left paren six hundred sixty-eight right paren five hundred sixty-seven thousand eight hundred ninety”. The “six hundred sixty-eight” bit is particularly curious: the 01234 is being interpreted as an octal number.

By encasing each digit within a <span /> tag, the digits are read out individually. So, (01234) 567890 becomes “left paren zero one two three four right paren five six seven eight nine zero”.

I have written an XSLT template that takes a phone number and applies a <span /> to each digit:

<xsl:template name="DisplayPhoneNumber">
<xsl:param name="position" />
<xsl:param name="phonenumber" />
<xsl:variable name="digit"
select="substring($phonenumber, $position, 1)" />

<xsl:choose>
<xsl:when test="contains('012345678', $digit)">
<span>
<xsl:value-of select="$digit" />
</span>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$digit" />
</xsl:otherwise>
</xsl:choose>

<xsl:if test="$position &lt;
string-length($phonenumber)">
<xsl:call-template name="DisplayPhoneNumber">
<xsl:with-param name="position">
<xsl:value-of select="$position+1" />
</xsl:with-param>
<xsl:with-param name="phonenumber"
select="$phonenumber" />
</xsl:call-template>
</xsl:if>
</xsl:template>

To call the template, pass in the telephone number and prime the starting position using a value of 1:

<xsl:call-template name="DisplayPhoneNumber">
<xsl:with-param name="position" select="'1'" />
<xsl:with-param name="phonenumber"
select="'(01234) 567890'" />
</xsl:call-template>


If you’re using this within Sitecore, you can simply pass in a field value in the usual way:

<xsl:call-template name="DisplayPhoneNumber">
<xsl:with-param name="position" select="'1'" />
<xsl:with-param name="phonenumber"
select="sc:fld('phone', .)" />
</xsl:call-template>