Umbraco hacks for developers
May 05, 2010
A lot of the tips here are relevant for Umbraco 3.0, issues such as the debug builds have been resolved in 4.0
Sitemap XSLT
The XSL below is the source used for Sitemap on sloppycode.net. It’s taken from the Umbraco book, and modified to display the Dublin Core description alongside each page’s url.
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE xsl:stylesheet [ | |
<!ENTITY nbsp " "> | |
]> | |
<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"/> | |
<!-- update this variable on how deep your site map should be --> | |
<xsl:variable name="maxLevelForSitemap" select="4"/> | |
<xsl:template match="/"> | |
<div id="sitemap"> | |
<xsl:call-template name="drawNodes"> | |
<xsl:with-param name="parent" select="$currentPage/ancestor-or-self::node [@level=1]"/> | |
</xsl:call-template> | |
</div> | |
</xsl:template> | |
<xsl:template name="drawNodes"> | |
<xsl:param name="parent"/> | |
<ul> | |
<xsl:for-each select="$parent/node [string(./data [@alias='umbracoNaviHide']) != '1' and @level" | |
<= $maxLevelForSitemap]"> | |
<li> | |
<a href="{umbraco.library:NiceUrl(@id)}"> | |
<xsl:value-of select="@nodeName"/> | |
</a> - <xsl:value-of select="./data[@alias='dcDescription']"/> | |
<xsl:if test="count(./node [string(./data [@alias='umbracoNaviHide']) != '1' and @level" | |
<= $maxLevelForSitemap]) > 0"> | |
<xsl:call-template name="drawNodes"> | |
<xsl:with-param name="parent" select="."/> | |
</xsl:call-template> | |
</xsl:if> | |
</li> | |
</xsl:for-each> | |
</ul> | |
</xsl:template> | |
</xsl:stylesheet> |
Changing the content type of a document
I’ve had to do this quite often on sloppycode.net when I realised I’ve created something as a Textpage (a built in document type) but then need some code highlighting which comes from a different content type.
I haven’t got round to making a tool to do it, but it’s simple to do when access to SQL Enterprise Manager/Management Studio.
First of all, backup the content of the document you are going to change. This is important, as changing the content type wipes the content. I don’t know why, the developers will know the reason for that. But the bodyText and the properties dissapear. Changing the contentType is straight forward, simply run
UPDATE cmsContent SET contentType={Your ContentTypeID} WHERE nodeId={Your document ID}
in SQL Server Management studio. You can get a list of contentTypes from the contentType table. nodeId holds the ID (rather than the pk column).
Once you have done this, publish your document. It will probably show a null reference exception when you click on the node in the editor. This will go once it has been published.
Moving multiple documents
If you need to move a lot of documents at once to a new parent, it’s painful doing it inside the editor. A SQL Script for doing this is below, just change the @oldParentId and @ParentId accordingly. Make sure you backup your database first.
--drop table #temp | |
set nocount on | |
declare @OldParentId int | |
declare @ParentId int | |
declare @Id int | |
declare @Path nvarchar(150) | |
select @OldParentId = 1139 -- Set to the existing parent node | |
select @ParentId = 2457 -- Set to the new parent node | |
-- First update the parentid column | |
update umbracoNode set parentid=@ParentId where parentId=@OldParentId | |
-- Next update all the path columns for the children. | |
SELECT id,[path] into #temp FROM [umbracoNode] where parentid=@ParentId | |
while exists(select Id from #temp) | |
begin | |
select @Id=Id,@Path=[Path] from #temp | |
select @Path = REPLACE(@Path,@OldParentId,@ParentId) | |
update umbracoNode set path=@Path where id=@Id | |
print cast(@Id as varchar)+': ' +@Path | |
delete from #temp where Id=@Id | |
end |
Performance issues with Umbraco
Since installing Umbraco and running sloppycode.net on it since December 2006 I’ve noticed a few ways of making it go faster. Most of them are out of the box with Umbraco, like using the XML cache however there’s a few others worth a mention:
Truncate your umbracoLog table regularly
The table contains all exceptions and warnings plus a host of other information from the Umbraco system. Most of this you don’t need and can clog up the database. The table can grow very large quickly, particularly if you’re creating the website for the first time and testing. This image shows an example
Before this the site was 500,000 records strong before I noticed. The backups become large too. So simply run truncate on it:
TRUNCATE TABLE [dbo].umbracoLog
There’s not point saving the deletes to the transcation log with DELETE FROM, as the data is never used by Umbraco except for your information.
Make sure tracing and debug is turned off
Find the following lines in your web.config file and make sure the attributes are set to false, it makes a noticeable difference:
<trace enabled="false" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" /> ... <compilation defaultLanguage="c#" debug="false"> ... <add key="umbracoDebugMode" value="false" />
GZip your HTML output
I’ve written a small HttpModule based on some code by Bart De Smet that’s very simple to install. It GZIP’s your HTML output, reducing the bandwidth your site uses and page load time. It’s aimed at people using shared hosting or pre-IIS 5 servers.
Changing templates on multiple documents
Like moving lots of documents at once, this is also a pain in the editor. The code below (for version 3.03) does this for you. You’ll need to turn it into a webservice and make a calling application to use it.
using System; | |
using System.Web; | |
using System.Web.Services; | |
using System.Collections; | |
using System.Web.Services.Protocols; | |
using umbraco.cms.businesslogic; | |
using umbraco.cms.businesslogic.web; | |
using umbraco.BusinessLogic; | |
using System.Xml.Serialization; | |
using umbraco.cms.businesslogic.property; | |
using umbraco.cms.businesslogic.macro; | |
[WebService(Namespace = "http://tempuri.org/")] | |
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] | |
public class YourWebservice : System.Web.Services.WebService | |
{ | |
[WebMethod] | |
public void UpdateDocumentParent(int docId, int parentId, bool deep, int templateId) | |
{ | |
if (deep) | |
{ | |
ArrayList list = this.GetDocumentChildren(docId); | |
for (int i = 0; i < list.Count; i++) | |
{ | |
if (list[i] is Document) | |
{ | |
int n; | |
int.TryParse(((Document)list[i]).Id.ToString(), out n); | |
if (n > 0) | |
{ | |
try | |
{ | |
Document d = new Document(n); | |
d.Publish(new User(0)); | |
} | |
catch { } | |
} | |
} | |
} | |
} | |
else | |
{ | |
Document d = new Document(docId); | |
d.Template = templateId; | |
d.Publish(new User(0)); | |
} | |
} | |
private ArrayList GetDocumentChildren(int docId) | |
{ | |
Document doc = new Document(docId); | |
Document[] children = doc.Children; | |
ArrayList list = new ArrayList(); | |
if (children.Length > 0) | |
{ | |
for (int i = 0; i < children.Length; i++) | |
{ | |
if (children[i].HasChildren) | |
{ | |
list.AddRange(GetDocumentChildren(children[i].Id)); | |
} | |
list.Add(children[i]); | |
} | |
} | |
return list; | |
} | |
} |
Database schema
This shows the schema of the Umbraco database, imported into Visio and organised so the tables are grouped together.
Download the original Visio file
Data export
This tool came from having troubles backing up an Umbraco database on gate.com - who didn’t allow user backups of SQL databases or remote SSIS packages. The host has now been switched but for users of similar hosts like godaddy.com the tool is useful. Find it here (it needs Visual Studio to compile it, which you can get for free).
Links
These links are included to supplement the ones on the Umbraco.org site.
I'm Chris Small, a software engineer working in London. This is my tech blog. Find out more about me via Github, Stackoverflow, Resume