Archive

Archive for April, 2009

Find items which override standard values using Revolver

For a while now, the best practice for assigning presentation in Sitecore has been to apply the presentation to an item’s template standard values rather than on the item itself. This has some distinct advantages. Firstly, presentation is usually defined by the type of the item, such as a list page or a news page. It makes sense that we have a central place in which to define the presentation for all items of this type. Secondly it is much easier to update presentations site wide if all the presentations are defined in a central location. For example, we might now want to bind a mini-search component onto each page, but want to use dynamic binding and placeholders rather than statically placing the component on a layout or sublayout used by all pages.

But what about those single items that are unique in their presentation? Wouldn’t it make sense to break the rule for these? Just go ahead and define the presentation on the item itself? These items are usually things like the “contact us” item or some other form which only exists in a single place. To keep in line with the best practices, we would have to create a template for each of these unique presentation items which sounds like a lot of work, not to mention the increased number of templates we now have to manage.

So it sounds tempting to break the rule and allow presentation on the item itself. Until it comes time for maintenance on the site. And not maintenance 2 weeks after going live, try after 6 months when those individual items that “we were sure we’d never forget about” are floating around the content tree somewhere we can’t remember :( . Hind sight is 20/20.

So for these reasons, just let me clarify the answer to this dilemma before continuing. Don’t break the rule. Only assign presentation to standard values. It doesn’t matter if you have an additional 15 templates. More templates is far more desirable than trawling the content tree looking for items which might have presentation defined on them.

So what can I do if I had a moment of insanity and ignored this rule and defined presentation on the unique items directly? As usual, it’s Revolver to the rescue!

Unfortunately, if you request a field value in Sitecore, Sitecore will return to you either the field value of the item if it exists, or the standard value if the item doesn’t override the field value.

gf -f __renderings

Using the above command we cannot determine if the field value comes from the item or from the standard values. What’s more, if I list all the item’s fields:

gf

Only fields which are set on the item are returned. But the output of this command is not particularly useable. So how could we determine, through examining field values if an item has overridden the standard values of it’s template. Well we know we can retrieve an item’s field value. We know we could also grab the item’s template id:

ga -a templateid

And we can use IDs with the cd command. So we can easily, from the item, change our context to it’s template, find it’s standard values, then get the field value on the standard values. If the field value is different, then we know the item has overridden the standard values. So let’s combine all this together into a Revolver script, so we can navigate to a single item, execute the script, and get some kind of indication as to whether the item’s field value is the same as it’s template, or if it’s different, indicating an overridden standard value. For the following example, we’ll focus on comparing the __renderings field, which is where the presentation is defined.

Let’s start by storing the item’s field value in an environment variable so we can use it later to compare against.

set itemfield < (gf -f __renderings)

Now we need to navigate to the item’s template so we can get the field value from the standard values.

cd < (ga -a templateid)

About the only way we can compare a field in Revolver to some arbitrary string is using the find command with an expression filter or a field filter. For more info on expressions in Revolver, just type “help expressions” in the Revolver shell prompt. For this example we’ll use a field filter. So we’ll see if we can find the standard values item of the template, by applying the field filter. If we find the item we’ll echo the text “match!”.

find -f __renderings ($itemfield$) (echo match!)

The find command operates on the children of the current item. So in the above example, we’re relying on the fact that the standard values item will be the only item with presentation defined (as well it should be). If a child item (standard values) __renderings field matches the field value from the item, then we get the “match!” text displayed, as well as a message saying we found 1 item.

If this were in a script, we’d also want to return to the previous context item as our current context is on the item’s template. Revolver stores the previous context path whenever the cd command is run in an environment variable called “prevpath”, so we can revert our context by executing:

cd ($prevpath$)

We wrap parenthesis around the variable prevpath in case it includes spaces in it.

We could make this script a little more generic and reusable by using arguments which can be passed to the script to check any field of an item against it’s standard values. This could also be handy for fields such as “display in breadcrumb” and such where you normally use the standard value of “true” (1) but might occasionally want to see what doesn’t follow the default. The resulting script might look like this.

set itemfield < (gf -f ($1$))
cd < (ga -a templateid)
find -f ($1$) ($itemfield$) (echo match!)
cd ($prevpath$)

Let’s name this script “issv” (IS Standard Values). Now we can navigate to an item and execute the script, providing it the field we want to compare.

issv __renderings

Executing this against an item that has been overridden:

<?xml version="1.0" encoding="utf-16"?>
<r xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}" l="{14030E9F-CE92-49C6-AD87-7D49B50E42EA}">
    <r ds="" id="{67FE0428-DFDA-488E-B772-B5DFA2F89A16}" par="" ph="main" uid="{3F6D1B32-8D68-432A-976D-46C3DE95BB82}" />
    <r ds="" id="{F53BB574-1CF8-4446-A127-0EBCA1AA6D9E}" par="" ph="main" uid="{586D9E7F-8CB3-4399-A33C-C5BB90749FC7}" />
  </d>
</r>
/sitecore/templates/User Defined/Doc
Found 0 items
/sitecore/content/Home/New Doc

Executing this against an item which has not been overridden (the standard value is used):

<r xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}" l="{14030E9F-CE92-49C6-AD87-7D49B50E42EA}">
    <r ds="" id="{885B8314-7D8C-4CBB-8000-01421EA8F406}" par="" ph="main" uid="{43222D12-08C9-453B-AE96-D406EBB95126}" />
    <r ds="" id="{CE4ADCFB-7990-4980-83FB-A00C1E3673DB}" par="" ph="/main/centercolumn" uid="{CF044AD9-0332-407A-ABDE-587214A2C808}" />
    <r ds="" id="{493B3A83-0FA7-4484-8FC9-4680991CF743}" par="" ph="/main/centercolumn/content" uid="{B343725A-3A93-446E-A9C8-3A2CBD3DB489}" />
  </d>
  <d id="{46D2F427-4CE5-4E1F-BA10-EF3636F43534}" l="{14030E9F-CE92-49C6-AD87-7D49B50E42EA}">
    <r ds="" id="{493B3A83-0FA7-4484-8FC9-4680991CF743}" par="" ph="content" uid="{A08C9132-DBD1-474F-A2CA-6CA26A4AA650}" />
  </d>
</r>
/sitecore/templates/Sample/Sample Item
match!
Found 1 item
/sitecore/content/Home/Sample Item

See in the second output we get the “match!” string output. This is good if we just want to test single items at a time, but what about searching the entire content tree (content section) for items which override their standard values presentation? Combine the issv script with the find command.

cd /sitecore/content/home
find -r (issv __renderings)

...

<?xml version="1.0" encoding="utf-16"?>
<r xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}" l="{14030E9F-CE92-49C6-AD87-7D49B50E42EA}">
    <r ds="" id="{67FE0428-DFDA-488E-B772-B5DFA2F89A16}" par="" ph="main" uid="{3F6D1B32-8D68-432A-976D-46C3DE95BB82}" />
    <r ds="" id="{F53BB574-1CF8-4446-A127-0EBCA1AA6D9E}" par="" ph="main" uid="{586D9E7F-8CB3-4399-A33C-C5BB90749FC7}" />
  </d>
</r>
/sitecore/templates/User Defined/Doc
Found 0 items
/sitecore/content/Home/New Doc

<r xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}" l="{14030E9F-CE92-49C6-AD87-7D49B50E42EA}">
    <r ds="" id="{885B8314-7D8C-4CBB-8000-01421EA8F406}" par="" ph="main" uid="{43222D12-08C9-453B-AE96-D406EBB95126}" />
    <r ds="" id="{CE4ADCFB-7990-4980-83FB-A00C1E3673DB}" par="" ph="/main/centercolumn" uid="{CF044AD9-0332-407A-ABDE-587214A2C808}" />
    <r ds="" id="{493B3A83-0FA7-4484-8FC9-4680991CF743}" par="" ph="/main/centercolumn/content" uid="{B343725A-3A93-446E-A9C8-3A2CBD3DB489}" />
  </d>
  <d id="{46D2F427-4CE5-4E1F-BA10-EF3636F43534}" l="{14030E9F-CE92-49C6-AD87-7D49B50E42EA}">
    <r ds="" id="{493B3A83-0FA7-4484-8FC9-4680991CF743}" par="" ph="content" uid="{A08C9132-DBD1-474F-A2CA-6CA26A4AA650}" />
  </d>
</r>
/sitecore/templates/Sample/Sample Item
match!
Found 1 item
/sitecore/content/Home/real

...

So we can now see (once we paw through the output) which item’s override their standard values. And if we pull this into a text editor, all we have to do is search for the “Found 0 items” text to find the culprits.

Another way to check the item’s field value against it’s standard value, and have a bit nicer display of the differences, would be to export the field values and save them into files on the server’s file system, then use an external file compare utility to highlight differences for us. For this approach, we want to export the field value into a file which is stored on disk using the item’s ID as the filename for easy identification of the item during the file comparison. We would also export the standard value for each item into another folder.

Let’s start this approach by exporting the value of the field to a file which uses the item’s ID as the filename.

set p < (ga -a id)
echo -f c:\temp\comp\$p$.txt < (gf -f __renderings)

Now, export the standard value to file.

cd < (ga -a templateid)
echo -f c:\temp\compsv\$p$.txt < (gf -f __renderings (__standard values))

Of course you need to make sure the output folders exist before running the commands.

Now that the item’s field value and the standard values are on disk, we can use an external file compare tool such as WinMerge to compare which files are different. But before we do that, let’s put the above examples into a script so we can run this over all the content items again.

set p < (ga -a id)
echo -f c:\temp\comp\$p$.txt < (gf -f $1$)
cd < (ga -a templateid)
echo -f c:\temp\compsv\$p$.txt < (gf -f $1$ (__standard values))
cd ($prevpath$)

And I’ll name this script “outv”. Now let’s combine that with the recursive find command.

cd /sitecore/content/home
find -r (outv __renderings)

And this is what the folder comparison results look like in WinMerge.

field comp

So I can now see the top three items are different, and I have their IDs so I can easily navigate to them in Revolver, or even paste the ID into the search box inside the desktop to open the content editor on the item.

I hope this helps alleviate some pain for you when you have to do maintenance or updates on those old projects. And I’ve already added a feature request on the Revolver backlog to allow finding items which override standard values much easier.

Categories: Revolver, Sitecore

Multi environment config

April 17, 2009 5 comments

Managing multiple environment configurations for any project has always been a bit of a challenge. Even when you’re still in development, and different developers have different folder structures and drive assignments which would affect the location of your data folder in Sitecore, not to mention when you move to QA and production where server names will most likely also change.

There are many different ways in which to manage these configurations, but the most robust have always been incorporated into the build process, where you have a master config file from which all other environment specific versions of that file are generated.

At Next Digital, we’ve been making use of the fact that configuration in .net is mainly handled through XML files. Your web.config and App.config files are XML. So we can use XSLT to easily transform one config file into another.

The heart of this technique lies in the xsl:templates that copy over elements and attributes verbatim, so you only have to handle what needs to change. The following templates will do this for you.

<!-- Default templates to match anything else -->
<xsl:template match="@*">
  <xsl:copy/>
</xsl:template>

<xsl:template match="node()">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates/>
  </xsl:copy>
</xsl:template>

Note how the node template copies the current node and inside that node calls apply templates. This means that child elements of the current element will be run through the XSLT which will traverse the entire document.

These 2 templates must appear at the bottom of your XSLT file as templates are applied in the order they appear in the file. If these templates were at the top of the file they would match all nodes and your custom templates would never get run.

If we want to change a specific config value, then all we have to do is provide a template to match the node we want to replace, and change the value. Let’s look at how we would change the data folder in a Sitecore web.config to something else. This is an example of changing an attribute value.

<xsl:template match="/configuration/sitecore/sc.variable
  [@name='dataFolder']/@value">
  <xsl:attribute name="value">C:\inetpub\Proj\dep\
    Sitecore\Data</xsl:attribute>
</xsl:template>

Let’s change the text value of a simple node element such as changing the items cache size of a database.

<xsl:template match="databases/database
  [@id='web']/cacheSizes/items/text()">100MB</xsl:template>

And we might also want to change the contents of a complex node element that contains other nodes such as adding an event handler.

<xsl:template match="events/event
  [@name='item:copying']">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates/>
    <handler type="Class, Assembly" method="Method" />
  </xsl:copy>
</xsl:template>

Here we also have to copy the matched node which in this case is the item:copying event node, so we don’t lose any existing event handlers in the master config file.

Let’s put this all together. The following XSLT will change my dataFolder in a Sitecore web.config file and adjust the hostname to which the “website” site is bound.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/configuration/sitecore/sc.variable
    [@name='dataFolder']/@value">
    <xsl:attribute name=
      "value">C:\inetpub\MySite\dep\
  Sitecore\Data</xsl:attribute>

  </xsl:template> <xsl:templatematch="sites/site[@name='website']/@hostName">
    <xsl:attribute name="
      hostName">www.mysite.net</xsl:attribute>
  </xsl:template>

  <!-- Default templates to match anything else -->
  <xsl:template match="@*">
    <xsl:copy/>
  </xsl:template>

  <xsl:template match="node()">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

This kind of config transform is good for managing the config between different environments such as development, QA and production. When a developer adds to or changes the configuration then we can generate the environment specific versions of that config file and we don’t miss those vital configuration updates.

But we don’t want to be kicking this process off manually, let’s integrate it into the build process, so when we perform a release build the environment specific configs are generated on disk for us to move to the target environments. This will take a little bit of MSBuild tweaking.

MSBuild is the build engine used in Visual Studio and your standard project files are actually MSBuild project files. So you’ll need to edit the project file of the project that contains the master config file. You can do this from Visual Studio which means you also get IntelliSense. You’ll first need to unload the project by right clicking on the project in the solution explorer and selecting “Unload project”. Next you can right click on the unloaded project file and select “Edit <yourproject>”. This will open the text (XML) of the project file in the editor window ready for you to start editing.

In MSBuild terms, a task is an operation that needs to be performed in order such as creating a folder, compiling source files or changing the attributes on a file. Unfortunately MSBuild doesn’t come with a default XSLT task. Luckily, MSBuild is very extensible allowing you to write your own tasks. But don’t worry about writing your own XSLT task, there are several third party libraries out there that have already done this for you. In these examples I’ll be using the MSBuild Community Tasks library which you can find at http://msbuildtasks.tigris.org/.

After you’ve installed the community tasks you need to import the tasks into your project file by placing the following at the top of your MSBuild project file just under the “Project” element.

<Import Project="$(MSBuildExtensionsPath)\
  MSBuildCommunityTasks\MSBuild.Community.Tasks.targets" />

Now we can create our own target to generate our config files. A target is a group of tasks which can be invoked from another target or invoked directly by whomever is running the MSBuild file. The following target will use the XSLT task to transform the web.config file into environment specific config files for QA and production.

<Target Name="Configs">
  <Xslt RootTag="" Inputs="web.config" Output="web.QA.config"
    Xsl="web.config.QA.xslt" />
  <Xslt RootTag="" Inputs="web.config" Output="web.prod.config"
    Xsl="web.config.prod.xslt" />
</Target

We can call this target from the “AfterBuild” target, which gets called when a build is successful. We can also place a condition on the call so it will only run for a release build.

<Target Name="AfterBuild">
  <CallTarget Targets="Environment"
    Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "/>
</Target>

Now reload your project (right click on the project file) and build. Only when you select a release build will your custom config transforms run and the files will be generated on disk in the same folder as your web.config file.

Using the above technique we could generate specific config files for separate developers too. This is good for when the developer is in a different network and uses a different DB server, or their data folder is in a different location. But we don’t want to have to move those config files round by hand. We would much rather integrated this seamlessly into the build process. So that would mean copying the right config file over for this specific developer.

So how can we from inside MSBuild determine which developer is running the build? MSBuild passes all current environment variables in as properties. To access a property, you simply surround the property name with parenthesis and precede it with a dollar sign.

$(Property)

This property will be substituted before the task is run. So we have access to the COMPUTERNAME environment variable from within MSBuild. This is the perfect way to determine which developer or more specifically, which development environment I need the custom configuration for. We’ll take it a step further and only generate the environment specific development config file for this machine if one is required. The easiest way to do that is to include the computer name in the filename of your config transform file.

web.config.devpc.xslt

We can then use a conditional attribute on the XSLT task to only create the config file if the transform file for this machine exists.

<Xslt RootTag="" Inputs="web.master.config"
  Output="web.$(COMPUTERNAME).config"
  Xsl="web.config.$(COMPUTERNAME).xslt"
  Condition="Exists('web.config.$(COMPUTERNAME).xslt')" />

And we’ll couple that with a copy task to copy the output file over the config file, of course using a conditional attribute on the copy task to check if the config file for this machine has been created.

<Copy SourceFiles="web.$(COMPUTERNAME).config"
  DestinationFiles="web.config"
  Condition="Exists('web.$(COMPUTERNAME).config')" />

You’ll also want to precede those 2 tasks with a general copy of the master config file to the running config file for those cases where a machine specific transform doesn’t exist. Your resulting “Configs” MSBuild target might look something like the following. This can be called from the “AfterBuild” target.

<Target Name="Configs">
  <Copy SourceFiles="web.master.config"
    DestinationFiles="web.config" />
  <Xslt RootTag="" Inputs="web.master.config"
    Output="web.$(COMPUTERNAME).config"
    Xsl="web.config.$(COMPUTERNAME).xslt"
    Condition="Exists('web.config.$(COMPUTERNAME).xslt')" />
  <Copy SourceFiles="web.$(COMPUTERNAME).config"
    DestinationFiles="web.config"
    Condition="Exists('web.$(COMPUTERNAME).config')" />
</Target>
Categories: .net
Follow

Get every new post delivered to your Inbox.