Friday 19 December 2008

Loading this assembly would produce a different grant set from other instances

If you get this exception message when trying to browse an ASP.NET site you are developing, first stop the site in IIS, then clear out the ASP.NET Temporary Files folder that relate to your site (default location is under C:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files). Then start IIS and try browsing the site.

Localhost on IIS7

By default, browsing to localhost on IIS7 brings up a welcome screen. To associate localhost with a particular web site, right-click the web site in IIS7 and select "Edit Bindings" (or just "Bindings", depending on which pane you were in when you right-clicked; IIS is still as inconsistent as ever!). In the "Add Site Binding" dialogue box, add localhost as a host name. Apply the changes. You will now be able to browse to localhost.

Thursday 18 December 2008

Visual Studio External Tools Menu

Further to my post about setting up Windows Explorer as an external tool within Visual Studio (http://www.neilpullinger.co.uk/2008/03/open-visual-studio-project-folder-in.html), I have set up a number of other tools set up in the same way. Here's my list now:







TitleCommandArgumentsNotes
Run test(full path to nunit.exe)$(ItemPath)
Reflector(full path to reflector.exe)$(ItemPath)
FxCop(full path to fxcop.exe)$(TargetPath)
Explorer(full path to explorer.exe)/e, /select, $(ItemPath)
Open in Notepad(full path to notepad.exe)$(ItemPath)
Open(full path to a batch file containing just "%1")$(ItemPath)
Error Lookup(errlook.exe, in VS\Common\Tools folder)(none)Not sure where this one came from; installed by default?
Spy++(spyxx.exe, in VS\Common\Tools folder)(none)Installed by default

I've excluded a couple of the others that are there by default (eg. Create Guid and Dotfuscator).

(Edited 19/12/2008)

Tuesday 14 October 2008

Always set AutoPage to true in LinqDataSource

If you are binding a control to a LinqDataSource, always set the AutoPage property on the LinqDataSource to true (AutoPage=“true”) if you have enabled paging on the data-bound control. Without the AutoPage option set, the LinqDataSource will pull back all records in the data source every time a page is browsed. Not only is this inefficient and not scalable, but it allows the client to determine the level of resources required, which is not good practice.

<asp:LinqDataSource ID="LinqDataSource1" runat="server"
ContextTypeName="MyDataContext" TableName="MyTable" AutoPage="true"></asp:LinqDataSource>

Sadly, the AutoPage option doesn’t seem to be available in other DataSource objects, so a manual implementation is necessary for these.

Thursday 4 September 2008

CustomValidator does not fire

Recently, I needed to ensure that a user fill in one of two TextBox controls on a form I was designing. I couldn't use two RequiredFieldValidator controls as that would mandate that both fields be completed. I therefore created a CustomValidator control and added it to the page. It did not work. There was a flaw in my thinking. The control did not ever fire when needed (ie. when both fields are blank), because the control does not, by default, validate blank fields. Thankfully, there is a way to change this. You can set the ValidateEmptyText to true:

<asp:CustomValidator ID="myValidator" runat="server" ErrorMessage="Error message" OnServerValidate="myValidator_ServerValidate ValidateEmptyText="true"></asp:CustomValidator>

Wednesday 3 September 2008

Windows does not shut down

Slightly off-topic, but if your computer does not respond to an ordinary shutdown command, you can force a shutdown. To do this, select Start, Shutdown in the usual way and, as you click OK, hold down the control key. Use with care as this might terminate processes uncleanly. Always back up your data.

Thursday 21 August 2008

FindControl Causes Null Reference Exception with NMock

When using NMock on code that uses FindControl, a null reference exception is thrown:

PlaceHolder placeHolder =

(PlaceHolder) _view.MyPanel.FindControl("myplaceholder");

You could use the indexer instead:

PlaceHolder placeHolder =

(PlaceHolder) _view.MyPanel.Controls[0];

But if you insert another control in the panel, you break your code. So, the alternative is to loop through the controls and find a match by ID:

ControlCollection ctrls = _view.MyPanel.Controls;

foreach( Control ctrl in ctrls )
{
if( ctrl.ID == "myplaceholder" )
{
placeHolder = (PlaceHolder) ctrl;
break;
}
}

Saturday 2 August 2008

CruiseControl builds not working

Here's a quick checklist to assist in getting failed builds working again in CruiseControl.NET:
  • Comment out any custom tasks, such as running unit tests, in the ccnet config file (defined within <exec> elements)
  • Find error details quickly within the build log by searching for the following words/phrases:
    • could not
    • failed
    • success="false"
  • Check whether the code compiled without error by:
    • Running the project's team build as used by CCNET
    • Searching for "csc" (or "vbc") in the build log
  • Clear out temporary files on the build server to remove any sticky references

Wednesday 23 July 2008

TechEd Video

I recently discovered Animoto, so here's a quick video of my time at TechEd:

Friday 18 July 2008

Could not find property 'Name' on object of type: Sitecore.Data.SqlServer.SqlServerDataProvider

This week, I branched a solution in TFS and checked out the branch. It built successfully. I then added virtual directories in IIS to point at the locations of the app_config, sitecore, sitecore modules and data folders (I move these outside of the web site root for easier swappability and source control). On browsing to the site, I got the message:

Could not find property 'Name' on object of type: Sitecore.Data.SqlServer.SqlServerDataProvider

It took a while to figure out that this was due to the assemblies in source control being out of date. Once I'd copied the contents of the bin folder on the live web server to my local machine, I was able to browse the site.

Tuesday 8 July 2008

Creation of the virtual directory http://localhost/ failed

On attempting to check out a project in VS2008, I got the following message:

The local IIS URL http://localhost/ specified for Web project DemoWebsite has not been configured. In order to open this project the virtual directory needs to be configured. Would you like to create the virtual directory now?

On clicking yes, I got the following error:

Creation of the virtual directory http://localhost/ failed with the error: The URL 'http://localhost/' is already mapped to a different folder location.

The rest of the solution checked out all right, but the above project did not; it was marked as unavailable. I right-clicked the project node in the solution explorer and selected the option to edit the project (.csproj) file. Near the bottom of the file, I found the following:

<iisurl>http://localhost/</iisurl>

I edited this entry to add a virtual directory:

<iisurl>http://localhost/myapp</iisurl>

Having saved the change, I then right-clicked the project node in the solution explorer again and reloaded the project. When I was prompted to create the virtual directory again, the creation was successful and the project checked out. The virtual directory is created under the running web site in IIS (on Windows XP).

Thursday 12 June 2008

Songs and bands likely to appeal to IT nerds

This list was compiled with the "help" of colleagues. Feel free to add to these bad puns.
  • Motley Cruise Control
  • Status Disk Quota
  • Visio Killed the Radio Star (by De-Buggles)
  • Iron Python Maiden
  • The Google Dolls
  • USB-40
  • InXSlt
  • Fleetwood (Apple) Mac
  • Bob Marley and the E-mailers
  • Sonny and SharePoint
  • Black-eyed ASPs
  • CultureInfo Club
  • Re-bootylicious
  • System of a Downtime
  • Billy Blu-Ray Cyrus
  • Snoop Blogg
  • Shania Twain interface
  • Silverlight Lady
  • Public Image Limited Disk Space
  • Don't believe the hyperterminal
  • Token Ring of Fire (by Jonny Cache)
  • Awk This Way (by Runas /user:DMC)
  • Wired for soundcard
  • Dragonforce shutdown
  • Bat out of HTML
  • Earth, Wind and Firewire
  • MPEGgy Sue
  • Great balls of Firefox
  • Alien Ant Web Farm
And my personal favourite:

Adam and the NAnts

[Edited 8 Oct 2008]

Monday 12 May 2008

TableHeaderScope enum does not generate XHTML-compliant output

When adding the scope attribute to a column for accessibility, the following does not generate XHTML-compliant output:

TableHeaderCell th = new TableHeaderCell();
th.Scope = TableHeaderScope.Column;

It generates the following HTML:
<th scope="column">

The value of the scope attribute should be "col". A work-around is to add the scope attribute manually using the Attributes collection:

th.Attributes.Add("scope", "col");

Thursday 24 April 2008

Postback opens layout rather than the Sitecore item

This was a weird one. I created a Sitecore item with layout and a sublayout. Then I placed a Button control on the sublayout and adding a click event handler that did something simple like display "Hello World" in a label on the sublayout. However, clicking the button navigated to the layout, rather than posting back to the Sitecore item. A colleague searched the Sitecore forum and eventually sorted the problem. It was due to the following line being commented out in the Form.browser file:
<adapter controlType="System.Web.UI.HtmlControls.HtmlForm"
adapterType="Sitecore.Web.FormAdapter, Sitecore.Kernel" />

I had commented this out due to runtime exceptions I was getting that referred to this line in the file. However, it now seems perfectly happy with it left in!

The file exists in the App_Browsers folder in the web site root. If it isn’t there, copy it from a clean 5.3.1 installation.

[Edited 30/7/2008]

And thanks to my colleague Jason Linham for noting further that the folder should not be hidden.

[Updated 16/9/2008]

Wednesday 23 April 2008

System.OutOfMemoryException during Visual Studio 2008 build

Just recently, Visual Studio 2008 kept failing with a System.OutOfMemoryException when doing a build. The problem seems to have been due to inadequate Windows virtual memory. I upped the value of the paging file and, so far, have had no further exceptions.

... 1 hour later... whoops, spoke too soon. A re-installation is now taking place.

[Edit 2/5/2008: Still no luck. I have noticed that massive amounts of data are generated in the temporary ASP.NET files folder (C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files). The other day, VS created over 3Gb of data in there when building a solution that was only 187Mb in size. Might be part of the problem.]

Wednesday 16 April 2008

Sitecore item content not showing

As I’ve spent too many times trying to work out why item content (or a change to an item) is not reflected in the browser, here’s a quick note-to-self check-list:

  • If the item is in a workflow, check it has been submitted and approved

  • If you’re referencing an item using @key (eg. item[@key='fieldname']), make sure the field name is all lower case

  • Check the item has been published (do a full publish if changes have been made to templates too)

  • Do a full publish (or re-save the web.config file) if you suspect Sitecore caching is having an effect

  • Check the content of the item is in the web database by using the dbbrowser utility (in the same broswer session as your Sitecore instance, browse to /sitecore/admin/dbbrowser.aspx)

  • If you’re using local IIS, do an IIS reset to refresh the cache

  • Use the Access Viewer to check the permissions of the item for the extranet anonymous user

  • Check the item’s rendering is assigned to a valid placeholder key (be particularly careful if you are attaching renderings dynamically)

  • If applicable, view the HTML source and paste into the W3 validator to check for errors. Something like a self-closing script tag can throw the output.


Have I forgotten anything?

[Updated 7 Jan 2009; added bullet]

Friday 4 April 2008

Using ASP.NET Log-in Controls with Sitecore Extranet

It is simple to use the log-in controls to sign in to a Sitecore extranet. Having dragged a Login control to the design surface of a layout or sub-layout (ie. web form or user control), go to the control’s properties and click the lightning symbol. Double-click the field to the right of the Authenticate item to attach an event handler to the Authenticate event. In the event handler, set the Authenticated property of the AuthenticateEventArgs parameter equal to the success flag of the Sitecore Login() method.

Your Login control definition will be similar to:

<asp:Login ID="Login1" runat="server"
OnAuthenticate="Login1_Authenticate"></asp:Login>

Your event handler will be similar to:

protected void Login1_Authenticate( object sender,
AuthenticateEventArgs e )
{
e.Authenticated = Sitecore.Context.Domain.Login(
Login1.UserName, Login1.Password).Success;
}

The user instance login flag is not supported on this version of SQL Server. The connection will be closed.

I got this error this morning when moving from a SQL Express database to SQL Server 2005. The fix was simple: I just needed to remove the "User Instance=True;" in the LocalSqlServer connection string, from:

<add name="LocalSqlServer"
connectionString="Data Source=(local);
Integrated Security=True;User Instance=True;
Initial Catalog=dbname"
providerName="System.Data.SqlClient"/>

to

<add name="LocalSqlServer"
connectionString="Data Source=(local);
Integrated Security=True;Initial Catalog=dbname"
providerName="System.Data.SqlClient"/>

Saturday 29 March 2008

Open Visual Studio Project Folder in Windows Explorer

Visual Studio 2008 has this feature already built in. If you’re still using VS2005, it’s easy to add:

Select Tools, External Tools to bring up the External Tools dialogue box. Enter a title in the title field. In the command field, enter the path to the Windows Explorer executable. In the arguments field, enter $(ProjectDir) or select Project Directory from the side menu. Your dialogue should look as follows. Click OK.



You could leave it there. You can now click on a project in Solution Explorer and select Tools, External Tools, and pick the item that you added. Windows Explorer will open at the project’s location. To make things even slicker, add the external tool as an item on the tool bar. To do this, right-click on the tool bar and select Customize. In the left-hand pane of the dialogue box, select Tools. In the right-hand pane, select the item you added and drag to the tool bar. This is tricky as the tools are just shown as External Command 1, External Command 2 and so on. You will need to work out the number assigned to yours.



Once you’ve dragged the right one to the tool bar, you can give it a name and icon as you like by right-clicking on it again (still in Customization mode) and tweaking the settings.

Wednesday 26 March 2008

Cannot set a value on node type 'Element'

This error occurs if you wrongly use the Value property of XmlNode to change the value of an element.
XmlDocument xdoc = new XmlDocument();
xdoc.Load( MapPath( "XmlSample.xml" ) );
XmlNode node =
xdoc.SelectSingleNode( "//element[@id=100]" );

// The following line will lead to
// an InvalidOperationException
node.Value = "New value!!";

xdoc.Save( MapPath( "XmlSample.xml" ) );

Instead, use the InnerText property:
node.InnerText = "New value!!";

[Edited: 4 Aug 2008]

Tuesday 25 March 2008

Validate XML in IE using JavaScript

Slightly off-topic, but as there don’t seem to be many good examples on the web of how to validate an XML document in IE using JavaScript, here’s a quick snippet. In the event of an XML error, an error message is displayed in the div.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>XML Validator</title>

<script type="text/javascript">
function loadit()
{
var xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.validateOnParse = true;
xmlDoc.async="false";
xmlDoc.load("MyXmlFile.xml");

if (xmlDoc.parseError != '0')
{
document.getElementById("error").innerHTML
= xmlDoc.parseError.reason;
}

}
</script>

</head>
<body onload="loadit();">
<div id="error">
</div>
</body>
</html>

Wednesday 19 March 2008

Windows Firewall has blocked some features of a program

Should I keep blocking this program?

Unable to find script library '/aspnet_client/ system_web/1_1_4322/WebUIValidation.js'

If you get this error using .NET 1.1, try reinstalling the JavaScript file by navigating to your Framework directory (eg. C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322) and running the following command:

aspnet_regiis -c

Create a virtual directory under your web site root called ‘aspnet_client’ with all the default options and point it to the aspnet_client folder under the web root (eg. C:\Inetpub\wwwroot\aspnet_client).

Make sure you have appropriate backups before attempting these suggestions, in case you need to roll back.

Error Saving a Query String to a Cookie

Here’s one to note. I have some code that saves the requested URL to a cookie and later retrieves it. The strange thing was that when retrieving the value, anything after an ampersand in the query string was lost. The solution was to encode the Url using UrlEncode() (not HtmlEncode()).

Response.Cookies["RequestedPage"].Values.Add("Url", HttpUtility.UrlEncode(value))

Wednesday 12 March 2008

The following applications should be closed before continuing with set-up

Installing the .NET 3.5 Framework on a server this morning, I got this beauty of an error message:


Superb.

Tuesday 11 March 2008

Root element is missing

This exception arises if you store the Sitecore and Sitecore Modules folders outside of the web root and have forgotten to create virtual directories under the web root that point to them.

How many CMS developers does it take to change a light bulb?

None. It's a content issue.

(Sorry.)

Thursday 21 February 2008

Extract the Month from a DateTime Field in Sitecore

Using sc:formatdate(), it is a fairly trivial matter to format a date in a Sitecore Xslt rendering. Eg:
sc:formatdate(sc:fld('MyDateField', .),
'd MMM yyyy @ h:mm')

However, extracting just the month as a number without padding is less obvious. Using the format string 'MMM' gives three-letter month names, 'MM' gives months padded with zeros (eg. 02 for Feb), while 'M' curiously gives full month name and date padded with zeros.

A workaround is to use substring-after in conjunction with the format '/M'. Example:
substring-after(sc:formatdate(
sc:fld('MyDateField', .), '/M'), '/')

Friday 15 February 2008

Get the Path of a Sitecore File Field Given a Media Path

A file field in Sitecore uses a media path. If you want to get the actual URL to the uploaded file, you need to append the file’s media path to the media library path and use this to reference the item in the media library. You can then extract the value of the path field. Example:

<a href="{sc:fld('Path', sc:item(concat('/sitecore/media library', sc:fld('filefieldname', .)), .))}">Download file</a>

How to Determine Sitecore System Field Names

If you want to find out the name of system fields in Sitecore, you need to open the Standard Template in the Template Manager.

Here are some of the available fields (obtained from version 5.2):

Workflow:
__Workflow
__Workflow state
__Lock
__Default workflow

Help:
__Short description
__Long description
__Help link

Publishing:
__Publish
__Unpublish
__Publishing groups
__Never publish

Tasks:
__Archive date
__Reminder date
__Reminder recipients
__Reminder text

Lifetime:
__Valid from
__Valid to

Statistics:
__Created
__Created by
__Revision
__Updated
__Updated by

Might be obvious, but I couldn’t find a list on SDN.

Monday 11 February 2008

Setting Query String Parameters in Xslt

In Xslt, adding a new parameter to a query string presents a problem as, without writing your own extension function, you cannot determine whether you should prefix your parameter with ‘?’ or ‘&’. In Sitecore, the Xslt function sc:qs('parametername') can be used to obtain the value of a named parameter parametername. Unfortunately, there is no function provided that obtains the entire query string.

Here’s a workaround. Let’s say you want to add a parameter called ‘page’.

First, build up the rest of the query string. The following template checks the existence in the query string of parameter names passed to it. You need to know the names of the possible parameters. If they exist, they are written out.

<xsl:template name="SetQueryParameter">
<xsl:param name="name" />
<xsl:if test="sc:qs($name)">
<xsl:text>&amp;</xsl:text>
<xsl:value-of select="$name" />
<xsl:text>=</xsl:text>
<xsl:value-of select="sc:qs($name)" />
</xsl:if>
</xsl:template>

This template would typically be called from within a variable definition, therefore saving the created query string. In the following example, we are checking parameters type and orderby and saving them if they exist so we can add them back in later:

<xsl:variable name="RestOfQueryString">
<xsl:call-template name="SetQueryParameter">
<xsl:with-param name="name">type</xsl:with-param>
</xsl:call-template>
<xsl:call-template name="SetQueryParameter">
<xsl:with-param name="name">orderby</xsl:with-param>
</xsl:call-template>
</xsl:variable>

You are then able to append this query string to your newly-added parameter, which you add using a ‘?’:

<a>
<xsl:attribute name="href">
<xsl:text>?page=10</xsl:text>
<xsl:value-of select="$RestOfQueryString" />
</xsl:attribute>
<xsl:text>Page 10</xsl:text>
</a>

Sunday 10 February 2008

ASP.NET Custom Validator of ISBN Numbers

International Standard Book Numbers (ISBNs), like other important numbers, such as credit card numbers, lend themselves to self-validation. The last digit of an ISBN is a check digit. A value found by applying a mathematical formula to other digits of the ISBN can be compared to this check digit. If the values relate to each other in a certain way, the ISBN is taken to be of a valid format.

To use an ASP.NET custom validator to apply the validation logic, proceed as follows. (The code validates ten-digit ISBN numbers.)

First, drag a CustomValidator control to the design surface of Visual Studio. Set its ControlToValidate to the ID of the control that is to be validated (eg. a TextBox). Set the ErrorMessage and Text properties as required. Double-click the CustomValidator control and add the following code to the ServerValidate event handler (this is example starter code: you will need to check its suitability against your particular requirements):

protected void cvISBN_ServerValidate( object source,
ServerValidateEventArgs args )
{
string strInput = args.Value;

if( strInput.Length != 10 )
{
args.IsValid = false;
}
else
{
int checksum;

if( strInput[9] == 'X' )
{
checksum = 10;
}
else
{
checksum = (int)strInput[9] - 48;
}

int total = 0;

for( int i = 0; i < 9; i++ )
{
total += ( i + 1 ) * ( (int)strInput[i] - 48 );
}

args.IsValid = ( total % 11 == checksum );
}
}

To perform client-side validation too, you can write a similar function in JavaScript and attach it to the CustomValidator’s ClientValidationFunction property. The function should have the following signature:

function myvalidationfunction(source, args)

Friday 1 February 2008

Posting back to a page without aspx extension

A project I am working on has the requirement for URLs not to have .aspx extensions. Requests are handled by IIS using ISAPI_Rewrite, which effectively adds on the extension behind the scenes. Generating the URLs is a fairly simple affair in XSLT using substring-before($url, '.aspx'). However, when using a ASP.NET form, the submit button by default adds an extension. I tried specifying the action attribute explicitly of the form, but this had no effect. The solution is to add a PostBackUrl attribute to the button control. Give it the name of the current page, without the extension. Example:

<asp:Button ID="btnSubmit" PostBackUrl="mypage"
runat="server" Text="Submit" OnClick="btnSubmit_Click" />

The located assembly's manifest definition does not match the assembly reference

I got this message this morning. It occurred in a solution comprising a web site and several class library projects. Both the web site and one the class libraries had a reference to the same assembly name. To resolve the problem, I removed the reference from the web site.

Thursday 31 January 2008

Culture 'en' is a neutral culture. It cannot be used in formatting and parsing and therefore cannot be set as the thread's current culture.

The following exception may be thrown when trying to open a log file in the Sitecore Log Viewer:

“Culture ‘en’ is a neutral culture. It cannot be used in formatting and parsing and therefore cannot be set as the thread’s current culture.”

To overcome this, open the User Manager and add a value in the RegionalIsoCode field for the user affected. For GB English, enter en-GB.

Wednesday 30 January 2008

Failed to execute the request because the ASP.NET process identity does not have read permissions to the global assembly cache

If you get “Server Application Unavailable” when trying to browse to your development web site, check the Event Log on the host machine. If it contains the message “Failed to execute the request because the ASP.NET process identity does not have read permissions to the global assembly cache”, navigate to the .NET Framework directory containing the file aspnet_regiis.exe and run the command:
aspnet_regiis -ga machinename\ASPNET
(substituting machinename as appropriate).

Ensure also that the web site is an application (create an application in the IIS home directory).

If the message is simply “Service Unavailable” and you’re using IIS 6, check the application pool associated with your web site is running.

Tuesday 29 January 2008

Type conflicts with the imported type

Partial classes were introduced in .NET 2.0 enabling you to create classes spread over several source code files. What’s not immediately clear is that you only do this with source code files. You cannot add to a partial class that’s already built in a distinct assembly. If you try to do this, you’ll get something similar to the following error:

The type 'namespace.class' in 'C:\MyFolder\Class1.cs' conflicts with the imported type 'namespace.class' in 'assemblyfilename.dll'.

Generating Custom Configuration Sections in web.config (Part 2)

My earlier post about creating custom configuration sections in web.config, referred to a custom tool that can be used to do the leg work.

If you wish to implement custom sections yourself, here’s a simple example of how. This simple example uses standard key/value attributes and data that is not strongly typed. I shall later post a more advanced example, using strongly-typed data.

First, within the element of web.config, add the following element exactly as is, replacing MySectionName with the name you want to give your section:

<configSections>
<sectionGroup name="MySectionName">
<section name="Portal"
type="System.Configuration.NameValueSectionHandler,system,
Version=1.0.3300.0,Culture=neutral,
PublicKeyToken=b77a5c561934e089,Custom=null" />
</sectionGroup>
</configSections>

This uses the built-in handler. No further development is necessary.

You can then add keys to your custom section:

<MySectionName>
<MyStuff>
<add key="fruit1" value="apples" />
<add key="fruit2" value="oranges" />
</ MyStuff >
</MySectionName>

Finally, you extract the keys as follows:


NameValueCollection settings =
ConfigurationManager.GetSection(
"MySectionName/MyStuff")
as NameValueCollection;

Monday 28 January 2008

XHTML-compliant links: There is no attribute "target"

The traditional way to open links in a new window was to use target="_blank". This is non-valid XHTML. The error on the W3 validator is “there is no attribute "target".” The editor in Sitecore 5.2 generates target="_blank". I haven’t yet tried other versions.

There’s an interesting work-around on here:

http://www.sitepoint.com/article/standards-compliant-world


In short, they attach target="_blank" using JavaScript.

It seems like a fudge just to bypass the check, but the author argues it’s perfectly valid approach. The only downside is the reliance on JavaScript.

Friday 18 January 2008

Introducing MicroScrum!

Having attended various sessions at TechEd, DDD and DotNetDevNet on Scrum, I wanted to try implementing it. Trouble was, the size of the team for my current project is small (2 or 3 developers, including me) and Scrum’s intended for larger teams. So, I took the elements of Scrum that I felt still applied and, well, applied them!

Here’s what we are now doing:

We work in sprints—the tasks making up each sprint go into TFS as work items (or rather, they will be going into TFS: haven’t got around to that bit yet!). Every morning, we have a “Scrumlet”. We use it to run quickly through the tasks listed and attach estimated remaining times. It’s also a chance to re-focus efforts for the day. I keep a burn down chart up to date for my own and team’s reference. The chart is a great way to manage time and it keeps my project manager happy. Its visible nature means that the impact of moving someone onto another project is readily apparent.

I add to each sprint a couple of recurring tasks, namely the running of unit and functional tests, plus the updating of documentation. (I think of the latter as continuous integration for documentation.)

Early indications are that using “MicroScrum” is a great way to ensure project delivery remains on track, or at least that problems are seen earlier.

Tuesday 15 January 2008

Implementing the MVP Pattern in Sitecore XSLT Extension Methods

Here’s a way of implementing the MVP pattern when creating Sitecore XSLT extension methods. Ideally, you should already have some experience of creating extension methods and be familiar with MVP.

In the example I use, a “for-each” loop is used to display a list of makes of car.

First, create the view interface, defining the property or properties you want to display in the XSLT. As we are returning an array of values, the XPathNodeIterator return type is used:

public interface ICarsView
{

    XPathNodeIterator Cars { get; set; }

}


Create an interface defining the method that will get the data or information from store:

public interface ICar
{

    XPathNodeIterator GetListOfCars();

}


In your data access layer (DAL), implement the method that gets the data:

public XPathNodeIterator GetListOfCars()

{

    var cars = from c in GetMyData()

               orderby c.Make

               select c;

 

    XDocument xdoc = new XDocument();

    XElement xmlTree = new XElement("Cars");


    foreach( var car in cars )

    {

        xmlTree.Add(new XElement("Car",

            new XAttribute("Make", car.Make)));

    }


    xdoc.Add(xmlTree);


    return xdoc.CreateNavigator().Select("/Cars/Car");

}


Create the presenter class, which will store references to the view and DAL method interfaces, and define the method(s) that will set the value of the property or properties on the view:

    public class CarsPresenter

    {

        private readonly ICarsView _view;

        private readonly ICar _icar;

 

        public CarsPresenter( ICarsView view, ICar car )

        {

            _view = view;

            _icar = car;

        }

 

        public void SetCarsProperty()

        {

            _view.Cars = _icar.GetListOfCars();

        }

    }



Create the extension method that implements the view:

public class CarsView: ICarsView
{
    public XPathNodeIterator GetListOfCars()
    {
        CarsPresenter cp = new CarsPresenter(this, new CarsDal());
        cp.SetCarsProperty ();
        return Cars;
    }
 
    #region IAgreementsView Members
 
    public XPathNodeIterator Cars
    {
        get; set;
    }
 
    #endregion
}


In the web.config file, add an extension element to the xslExtensions element that references the concrete view. Use one such element for each namespace:

<extension mode="on" type="XsltViews.CarsView, XsltViews"
           namespace=" http://example.com/xsltviews/cars " />


In your XSLT file, declare the namespace prefix by adding it to the stylesheet element and including the prefix in the exclude-result-prefixes attribute:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:sc="http://www.sitecore.net/sc"
  xmlns:dot="http://www.sitecore.net/dot"
  xmlns:view="http://example.com/xsltviews/cars"
  exclude-result-prefixes="dot sc view">


Finally, you can now call the method from your XSLT:

<xsl:for-each select="view:GetListOfCars()">
  <xsl:value-of select="@Make"/>
</xsl:for-each>

Monday 14 January 2008

Creating Simple Loops in XSLT

Here's a simple way to create a loop in XSLT. As you might expect, it uses recursion.

I have defined a template that will do the work of the loop. It checks the value of a variable called 'counter' before calling itself. This is the loop condition. Before this test, you can add whatever it is you need the loop to do. In the following example, I print the letters of the alphabet, followed by "... Hello!":

<xsl:template name="DoLoop">
<xsl:param name="counter" />
<xsl:variable name="letter"
select="substring($alphabet, $counter, 1)" />

<!--Body of loop goes here-->

<xsl:value-of select="$letter" />... Hello!
<br/>

<!--End of loop body-->

<xsl:if test="$counter &lt; 26">
<xsl:call-template name="DoLoop">
<xsl:with-param name="counter">
<xsl:value-of select="$counter+1" />
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>

The loop is then initiated by calling it with the desired starting value:

<xsl:call-template name="DoLoop">
<xsl:with-param name="counter">1</xsl:with-param>
</xsl:call-template>

For info, the alphabet variable is simply defined as:

<xsl:variable name="alphabet">
<xsl:text>ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:text>
</xsl:variable>

Thursday 3 January 2008

How to Change String Case in XSLT

Here's a simple way to convert a string to lower case in XSLT. The snippet takes an input variable inputtext and saves the lower case version to a new variable called lowercasetext.

<xsl:variable name="lowercasetext" select="translate($inputtext, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')" />

To upper case a string, simply swap the position of the two alphabet literals shown.

Wednesday 2 January 2008

A valid license cannot be granted for the type CuteEditor.Editor

If you get this error message in rich text fields of the Sitecore Content Editor, check that your bin folder includes the CuteEditor licence file (CuteEditor.Lic).