Umbraco 4.5 & Visual Studio 2010 & .Less & jQuery vsdoc & SquishIt & masterpages

Using Umbraco with VS2010

… and masterpages, jQuery vsdoc, dotless, SquishIt

Welcome 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(&quot;~/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:

  1. 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.
  2. VS creates a masterpage for you with code-behind, sweet!
  3. VS opens the mark-up of this newly created masterpage, keep it opened!
  4. 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.
  5. 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.
  6. You’re done!

Downloads

‘Master’-Masterpage with code-behind: StefanKip.InfoCaster.Umbraco.masterpage

Extension methods: UmbracoExtensions.cs (remove ending .txt)

27 thoughts on “Umbraco 4.5 & Visual Studio 2010 & .Less & jQuery vsdoc & SquishIt & masterpages

  1. Nice article.

    I’ve been meaning to play with .LESS for ages. Will definatley be giving that a go.

    RE SquishIt, What are the benefits of using that over the built in ClientDependancy?

    Also, I’m no SEO expert, but speaking with an SEO friend of mine, he’s always told me it’s better to have no meta description/keywords than duplicates. So I can’t say I’m sold on the recursive thing. FYI though, you could also do the same using umbraco:item which then wouldn’t need your custom recursive method.

    Nice post though, and there are definatley a few things I’ll be having a play with.

    Thanks for sharing.

    Matt

  2. Thanks Nik & Matt 🙂

    @Matt
    I haven’t tried ClientDependency. I just think it’s too much, while SquishIt is nice and simple.
    There were situations I experencied that recursion in Umbraco (like the macro parameter syntax and umbraco:Item) didn’t work correctly.That’s why I chose to create my own Recursion method.

    As far as the SEO thingie: you’re trying to say that it’s better to have no description/keywords than the same description/keywords as it’s parent page?

  3. Cool, simpler is always better =)

    Hmmm, thats strange about the recursion. Have you raised an issue? Would be great if it could be fixed in the core, rather than people needing to create their own fix.

    RE SEO, that is what I’ve been told yes. I would probably recommend that it might be better instead, to try and generate a description from the content itself, rather than reusing meta data.

    Matt

  4. Cool, I was expecting you to post this so fast it extremely helpful, must have a lot of time on your hands 😛

    Are you planning on realising your Umbraco Website Starter project, for the not so DEV’y umbraco users like myself?

  5. @Ivor
    LOL! So fast?! Haha!
    I was planning to write this blogpost about 2 weeks ago 😛
    So I do not have a lot of time on my hands 😉

    I maybe will release this Website Starter Project… Have to discuss it at work. I’ll keep in touch about it. Do you by chance have twitter?

  6. NIce article! I second Matt’s comment about duplicate Meta tags, from an SEO point of view its a bad thing to have duplicate tags on the site (I’m contracting at a big SEO agency at the moment, and have had this drilled into me)!

    🙂

  7. Hi, great writeup. Was wondering how you “wrapped up” the Umbraco installation in VS? Do you keep a separate web-application project for your code and a website for Umbraco and do some post-build copying or do you roll everything together in to one website or web application?

    Thanks!

  8. Pingback: Using .LESS and the Client Dependency Framework in Umbraco « Matt Brailsford

  9. @kipusoep

    if its everything together, don’t you have .cs files in the umbraco installation then? how do you deal with that?

  10. yes but I mean, when u add new usercontrols and masterpages this way, the usercontrols and masterpages have .cs codebehind files, right? How do you deploy afterwards?

  11. Yes, it works just like a ‘normal’ ASP.NET project. We always deploy using the built-in Visual Studio Deploy option. Code-behind files will not be deployed, instead the binary file (assembly) of the project will be deployed.

  12. Ok sounds very nice. I’ll give this method a try for future projects.

    I got 2 more newbish questions if you dont mind:

    – You communicate with the DB in the masterpages. I hope this only happens upon publishing (to generate static pages without DB calls in the frontend)? I’m not that familiar with the caching/publishing mechanism and find it a bit odd that you use DB calls where xslt (that works on the cache xml) is normally used.

    – When and where do you use the SaveAndPublish extension method in your extension methods file?

    Thanks a lot for the replies

  13. Thomas

    These are indeed very basic Umbraco questions. If you have any questions in the future, please use http://our.umbraco.org, there are lot’s of helpful people over there 🙂 This blog isn’t really a forum you know 😉
    Anyway:
    1. There is no DB communication. The XSLT uses the umbraco XML cache file. .NET uses this file too when you’re using the umbraco NodeFactory or Linq2Umbraco.
    2. When I want to Save and Publish a new or altered Document 😉 Like:
    Document d = new Document(docId);
    d.GetProperty(“someProperty”).value = “some value”;
    d.SaveAndPublish();

    You must declare “using InfoCaster.Umbraco.Library;” when you want to use the extension methods.

  14. Ok thanks a lot, I thought you meant linq2sql in your original post. 🙂
    I was unaware there was something like linq2umbraco.

    Thanks for the help!

  15. “It’s not even possible to assign a value to a variable for a second time.”
    That’s because it’s a functional programming language; it’s actually by design (rather than a missing feature). Variable, here, means something like this:
    “Varying, in the context of mathematical variables, does not mean change in the course of time, but rather dependence on the context in which the variable is used.”

    It’ll make more sense to mathematicians than to programmers, but at least there’s a reasonable explanation. Other functional programming languages work the same way, tho some may have implemented a work-around (and possibly violate the functional programming paradigm in doing so).

    (See also: http://www.wisegeek.com/what-is-functional-programming.htm)

  16. Pingback: Select tool to minimize JavaScript and CSS size | Code to Preload

  17. Hey kipusoep, it is really nice article.
    Do you mind share with public your solution for Umbraco as real NET application instead (or stand-alone version) of what exists now? Umbraco is really poor copy of Ektron CMS. Difference (besides price tag) that Ektron does provide NET solution to the clients, so they do not have to suffer and can do fixes on the spot.
    Second part is a question to Rob: do you see any mathematician doing programmers work on the web? Any programing language should make sense to programmers to produce result and not to the people who may invented it but never going to actually use it…
    That is my opinion on Rob’s comment. 😉

  18. @Alex
    No I’m not going to share it. You can start with umbraco v5, which is really easy to use within Visual Studio. Also with version 5.1 it even gets better.
    I don’t know Ektron CMS, but yelling on the web that “umbraco is a poor copy of it” is really immature and unfounded.

  19. Pingback: Macroblog Base10 » Select tool to minimize JavaScript and CSS size

Leave a Reply

Your email address will not be published. Required fields are marked *

*