Umbraco 4.5 & Visual Studio 2010 & .Less & jQuery vsdoc & SquishIt & masterpages
Using Umbraco with VS2010
… and masterpages, jQuery vsdoc, dotless, SquishItWelcome all to this magnificent blogpost about using Umbraco 4.5 with Visual Studio 2010 and everything you’ll need to know about some other tools!
First things first; we, at InfoCaster, use Visual Studio 2010 and .NET to develop our Umbraco web applications. We try not to use XSLT, just because .NET is much better
and everyone at InfoCaster knows .NET, unlike XSLT. I always like to say that .NET with LINQ can do like everything, but XSLT can’t or does things inefficient. It’s not even possible to assign a value to a variable for a second time. How variable is that ‘variable’ then? But that’s another discussion!
So a few months ago I started developing our own ‘Umbraco Website Starter project’. It’s like a clean Umbraco installation wrapped in a Visual Studio solution, with some extra’s, like some handy extension methods, pre-installed packages and default properties for the homepage (title prefix/suffix, Google analytics code, meta data etc.).
Now I migrated it to Umbraco 4.5 and removed all packages and installed the necessary packages again. I’d like to tell you all what I did and how.
Blueprint CSS
We’re using Blueprint CSS from now on to have a good start on creating our templates and stylesheets, this is combined with SquishIt.
.Less
We’re using .Less for our custom stylesheet. You can read everything about how to set this up in VS(2010), so I’m not going to explain that: http://www.dotlesscss.com/
I’m using SquishIt to generate the CSS files out of the .css.less files. These .css.less files can be opened with VS2010 with css highlighting / autocompletion by using this plug-in: http://visualstudiogallery.msdn.microsoft.com/en-us/dd5635b0-3c70-484f-abcb-cbdcabaa9923
<%= Bundle.Css()
.Add("~/css/blueprint/screen.css")
.Add("~/css/style.css.less")
.WithMedia("screen, projection")
.Render("~/css/output/screen_#.css")
%>
SquishIt and jQuery vsdoc
SquishIt download page: http://www.codethinked.com/post/2010/05/26/SquishIt-The-Friendly-ASPNET-JavaScript-and-CSS-Squisher.aspx
VS2010 has better intellisense features when it comes to JavaScript. So why not use jQuery with vsdoc and let SquishIt minimize and combine it for production environments?
Great idea! BUT this is how SquishIt requires it’s mark-up:
<%= Bundle.JavaScript()
.Add("~/js/jquery-1.4.2.js")
.Add("~/js/jquery-ui-1.8.1.js")
.Render("~/js/combined_#.js")
%>
Hmm… That’s not going to work with intellisense/vsdoc, because Visual Studio looks for real <script> tags…
So what I did is this:
<asp:Literal runat="server" ID="ltlScripts">
<script type="text/javascript" src="~/scripts/jquery-1.4.2.js"></script>
</asp:Literal>
Then you can just add any JavaScript reference you’d like
To enable vsdoc intellisense, you must also have the vsdoc file in the same folder as the realy jQuery file
The literal is being parsed in code-behind by using regex and it gets rewritten:
Regex scriptTagRegex = new Regex(@"<script(.*)type=""text/javascript""(.*)src=""(.*)"".*></script>");
IJavaScriptBundle jsBundle = Bundle.JavaScript();
IJavaScriptBundleBuilder jsBundleBuilder = null;
foreach (Match match in scriptTagRegex.Matches(ltlScripts.Text))
{
if (jsBundleBuilder == null)
jsBundleBuilder = jsBundle.Add(match.Groups[3].Value);
else
jsBundleBuilder.Add(match.Groups[3].Value);
}
ltlScripts.Text = jsBundleBuilder.Render("~/scripts/output/js_#.js");
Simple as that
(using SquishIt.Framework.JavaScript)
Default meta properties
(title prefix/suffix, meta tags, meta description, meta title, favicon, Google analytics)
This is the markup I have as <head>:
<head runat="server">
<title>Title will be filled from codebehind</title>
<!-- meta data -->
<meta name="author" content="InfoCaster B.V." />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="keywords" content="<%= CurrentNode.GetPropertyValueRecursive("pageKeywords") %>" />
<meta name="description" content="<%= CurrentNode.GetPropertyValueRecursive("pageDescription") %>" />
<asp:Literal runat="server" ID="ltlFavicon">
<!-- favicon -->
<link rel="shortcut icon" href="#HandledInCodeBehind" type="image/x-icon" />
</asp:Literal>
<!-- css -->
<%= Bundle.Css()
.Add("~/css/blueprint/screen.css")
.Add("~/css/style.css.less")
.WithMedia("screen, projection")
.Render("~/css/output/screen_#.css")
%>
<%= Bundle.Css()
.Add("~/css/blueprint/print.css")
.WithMedia("print")
.Render("~/css/output/print_#.css")
%>
<!--[if lt IE 8]>
<%= Bundle.Css()
.Add("~/css/blueprint/ie.css")
.WithMedia("screen, projection")
.Render("~/css/output/ie_#.css")
%>
<![endif]-->
<asp:ContentPlaceHolder runat="server" ID="cphHead" />
<!-- js @ bottom -->
<asp:Literal runat="server" ID="ltlGoogleAnalytics">
<!-- Google Analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '{0}']);
_gaq.push(['_trackPageview']);
(function () {{
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
}})();
</script>
</asp:Literal>
</head>
The title, favicon and Google analytics gets set from code-behind.
The meta tags/description are filled by using a recursive extension method I wrote.
I also added a content placeholder in the head and also at the end of the XHTML document, right before the </body>, so it’s possible to load custom CSS (head) and JS (bottom) per sub masterpage.
This is the code-behind which belongs to this mark-up:
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
#region Page stuff (google analytics, favicon, title)
// Google analytics
if (!string.IsNullOrEmpty(CurrentNode.GetPropertyValueRecursive("analyticsCode")))
ltlGoogleAnalytics.Text = string.Format(
ltlGoogleAnalytics.Text,
CurrentNode.GetPropertyValueRecursive("analyticsCode")
);
else
ltlGoogleAnalytics.Visible = false;
// Favicon
int faviconId;
if (!string.IsNullOrEmpty(CurrentNode.GetPropertyValueRecursive("favicon")) && int.TryParse(CurrentNode.GetPropertyValueRecursive("favicon"), out faviconId))
ltlFavicon.Text = string.Format(
"{0}{1}{2}",
ltlFavicon.Text.Substring(0, ltlFavicon.Text.IndexOf("href=\"") + ("href=\"").Length),
new Media(faviconId).getProperty("umbracoFile").Value,
ltlFavicon.Text.Substring(ltlFavicon.Text.IndexOf("href=\"\"") + ("href=\"").Length)
);
else
ltlFavicon.Visible = false;
// Title
Page.Title = string.Format(
"{0}{1}{2}",
CurrentNode.GetPropertyValueRecursive("titlePrefix"),
CurrentNode.GetPropertyValueOrDefault("pageTitle", CurrentNode.Name),
CurrentNode.GetPropertyValueRecursive("titleSuffix")
);
#endregion
}
Easy right?
Extension methods
I wrote some extension methods to ease some default tasks.
These extension methods are duplicated, for use in multilingual sites.
You can download the .cs file right here, it contains enough comments/documentation. Look at the end of this post
Masterpage intellisense VS2010 bug
There is a bug in VS2010 when using Umbraco’s masterpage as root master (umbraco.presentation.masterpages._default). The intellisense doesn’t work anymore. I’ve created a bug report on Microsoft Connect about this: https://connect.microsoft.com/VisualStudio/feedback/details/567771/no-asp-tag-intellisense-in-some-cases-when-using-masterpages
I’ve got a ‘dirty’ work-around. I don’t know if this work-around works with the ‘Canvas-editing’ feature in Umbraco (we and our customers don’t use it).
Just let every main masterpage (with <html> tags) inherit: umbraco.presentation.masterpages._default
#Protip: Creating new templates
When using Visual Studio, it’s nice to have code-behind files for your templates (at least, that’s how we do stuff). If you create a template in Umbraco, it creates a masterpage without code-behind…
Then you would have to add a code-behind manually, link it with the mark-up and create designer file. That sucks right?
Just do this:
- Create a nested masterpage in VS and use your ‘master’-masterpage as masterpage in the masterpages folder. Lol, there’s a lot of ‘masterpage’ in that sentence hehe.
- VS creates a masterpage for you with code-behind, sweet!
- VS opens the mark-up of this newly created masterpage, keep it opened!
- Then go to your Umbraco back-end and create a template like you would normally do. Give it the same name as you defined in step 1.
This should create an Umbraco template by using the already created masterpage as file. - Now return to Visual Studio. It should notify you that the file has changed and gives you the option to reload the file. Reload and then use CTRL+Z to undo the changes made by Umbraco (else the code-behind won’t be linked to the mark-up) and save it.
- You’re done!
Downloads
‘Master’-Masterpage with code-behind: StefanKip.InfoCaster.Umbraco.masterpage
Extension methods: UmbracoExtensions.cs (remove ending .txt)