Separating html and logic in Razor (WebPages or Umbraco macroscript)

Standard

Mixing logic and html can easily end up with messy, hard-to-maintain code. In MVC it’s easy to separate the parts with the controller / view-separation. But how to do it nicely in WebPages or in an Umbraco macroscript?

Ultimately I want my Razor to be free from

  • variable assignments other than for loop iterators
  • function calls other than formatting and html helpers
  • usage of data outside of the “ViewModel”

I have some WebPages and Umbraco projects with some quite advanced razor code and I’ve been having many doubts about how I mix logic in my razor code. I try to separate it, most often by placing logic at the top, but still using Razor (with helpers or RenderPage by all means). After re-thinking some about the @functions ability in Razor and found out about the overridable InitializePage function I feel I now have a better more solid structure to use.

Update: I recommend do not use this too extensively
The “functions” approach is nice, but also consider the simpler way just to have an initialization section in the top of the script – still separating C# from actual view. See this post for an example.

And if you have extensive pure C# move it to a base class which you inherit your script from.

The idea is simply this : remove all logic (but necessary iterations and some conditions) from the razor, and place it in the @functions part (in the InitializePage function) together with ViewModel properties. The ViewModel properties should contain all data that the view part needs to be able to render the page / the macro. And the view part should not access anything else than the ViewModel properties:

@* --- The logic-less view part: --- *@

<p>@SomeProperty</p>

@* --- The controller / viewmodel constructor part: --- *@

@functions{

  // the properties of the view model:
  public string SomeProperty {get; set;}

  // taking care of post data and constructing the view model:
  protected override void InitializePage()
  {
    // make up the "ViewModel" properties
    SomeProperty = "some data";
  } 
}

Advantages with this approach

  • A clear separation of view and logic (not as clear as having them in separate files tho)
  • The logic is pure C#-code, no risk of doing mistakes due to misplaced @’s (and missing code blocks)
  • The code is a big step towards MVC, and the full step will be quite easy later on if necessary
  • Minimal added whitespace in page source

Helpers are perfectly fine to add to this – but just as with the view code, I think any logic but view logic should be left out of them.

A remake of my “old skool” contact form sample

I wrote a contact form razor sample quite a while ago, guilty of mixing logic into the view. However I’m not the only one ;), a sample at asp.net.

Here’s a better (I think) remake, using the initializepage-structure:

<h2>Contact form</h2>

@if(ShowMessage)
{
   <div><strong>@Message</strong></div>
}
@if(ShowForm)
{
   <form action="#" method="post">
     <div>
       <label for="name">Name:</label>
       <input id="name" name="name" value="@PostedName"/>
     </div>
     <div>
       <label for="message">Message:</label>
       <textarea id="message" name="message">@PostedMessage</textarea>
     </div>
     <input type="submit" value="Post message"/>
 
   </form>
}


@functions{
  public bool ShowForm {get; set;}
  public bool ShowMessage {get; set;}      
  public string Message {get; set;}
  public string PostedName {get; set;}
  public string PostedMessage {get; set;}
           
  protected override void InitializePage() 
  {
    base.InitializePage();
    if (!IsPost) 
    {
       ShowForm = true;
       ShowMessage = false;
    }
    else
    {
       PostedName = Request["name"];
       PostedMessage = Request["message"];
       var IsValid = (!string.IsNullOrEmpty(PostedName) &amp;&amp; !string.IsNullOrEmpty(PostedMessage));
       if (IsValid)
       {
         var bodyText = "Message from " + PostedName + Environment.NewLine + PostedMessage;
         umbraco.library.SendMail("from@mysite.com","admin@mysite.com","New message", bodyText,false);
         Message = "Thank you " + PostedName + " for posting a message";
         ShowForm = false;
         ShowMessage = true;
       }
       else
       {
         Message = "You need to enter both name and message";
         ShowForm = true;
         ShowMessage = true;
       }
    }
  }
}

Public properties or private fields?
The public properties could be private fields without any problem (in these samples), the reason I choose public properties is that I like to resemble the MVC structure as far as possible.

Responding to an ajax-post

This structure also makes it really easy to handle and respond to ajax posts to the same form (this can only be done in a pure WebPages razor, not Umbraco macro as that does handle the Response output the same way) :

    ... in the validated post part ...
    if (IsAjax)
    {
        Json.Write(new {
          postValid = true,
          message = Message
          }, Response.Output);
    }

In Umbraco the options for ajax are to either to run razor script outside of the Umbraco context (use WebPages + add reservedpath in web.config) or to make a separate “ajaxresult-template” with a razor call:

Bonus: posting the form with ajax in Umbraco

Using this method you can return json directly from your razor script in Umbraco. The tricky part is we dont want to return the surrounding template parts, and for simplicity we want to use code in the same razor script as as we already are in.

Add a template – call it AjaxRazor – which will be responsible for rendering the razor without anything but the script result (we add an if IsAjax condition to have some kind of security against unintended run script files, please add more security checks for posts on a live site):

<%@ Master Language="C#" MasterPageFile="~/umbraco/masterpages/default.master" AutoEventWireup="true" %>

<asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server">
  <umbraco:macro language="cshtml" runat="server">
    @if (IsAjax){
      var path = Request["path"];
      if (!path.StartsWith("~")) { path = umbraco.IO.SystemDirectories.MacroScripts + "/" + path;}
      @RenderPage(path)   
    }
  </umbraco:macro>
</asp:Content>

Now it’s possible to run any razor script using the url /AjaxRazor?path={path-to-script}

Next make the form post it’s contents to the template, with the razor script path as a querystring parameter, we get that path with the help of the page property VirtualPath:

@if(showOnlyJson)
{
  Json.Write(json, Response.Output);
}
else 
{
 if(showForm)
 {
 <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.min.js"></script>
 <script type="text/javascript">
  $(document).ready(function() {
  $('form').submit(function() {
  $.post('@ajaxPostPath', 
      $(this).serialize(), 
      function(data){
         alert(data);
      });
  return false;
 });
 });
 </script>

 <form method="post" action="#">
 <input type="text" name="name"/>
 <input type="submit"/>
 </form>
 }
 if (showMessage)
 {
   <p>@message</p>
 }
}
@functions{
 private bool showOnlyJson;
 private bool showMessage;
 private bool showForm;
 private string message;
 private object json;
 private string ajaxPostPath;
 protected override void InitializePage()
 {
   if(IsPost)
   {
      var postedName = Request["name"];
      message = "Thanks for posting";
      if (IsAjax)
      {
       showOnlyJson = true;
       json = new {message=message, name = postedName};
      }
      else
      {
       showMessage = true;
       showForm = false;
      }
   }
   else
   {
       showForm = true;
       ajaxPostPath = "/AjaxRazor?path=" + VirtualPath; 
   }
 }         
}

Umbraco v5

I believe this approach still might be useful in Umbraco v5. It should likely not be recommended as an option for advanced solutions, but for simple forms (and advanced navigations) I think its suitable. And even if it’s not suitable it’s a good step towards controllers and viewmodels.

A nice deploy approach : Umbraco 5 alpha at AppHarbor [experimental]

Standard

AppHarbor is an interesting service that can deploy and host an Asp.Net application really easy. They use the slogan “Azure done right”. I’m still in an experimental mode with it. But it passed my “if I cannot get this going in less than 1 hour I’ll skip it”- test easily. Azure didn’t.

The pricing is also a pleasant detail : no cost at all for a single server instance. So for trying out stuff it’s really ideal. Oh, and deploy is as easy as a Git commit and push. *happy face*

Here’s an example as how you can use it to host an Umbraco 5 alpha:

1. Get your Umbraco website running locally

1.1 Download and extract an Umbraco prebuilt web application of your choice (nightly / alpha or whatever is available) (Alpha 3)

You should even be able use the full Umbraco project – as AppHarbor has a automatic build process. Would be cool if someone tried it. I went the easy path and only used the pre-built nightly to get going quickly.

1.2 Run the Umbraco site in WebMatrix (right click the extracted folder and choose “open as a website in WebMatrix) go through the installation and choose the SQL CE database option as it requires no extra configuration. You can use a full SQL database if you need, AppHarbor has such as an add on.

You can do the Umbraco site installation online at AppHarbor, but that is not recommended as you will miss the possibility to work with the site locally and only push to the running site when you are ready to deploy your changes.

2. Create a local Git repository

2.1 Download and install Git Extensions
2.2 Right click the folder and click Git extensions -> Browse.
2.3 In the Git Explorer click Initialize Repository. Let it initialize.
2.4 Click commit to make an “initial commit”. It looks into the folders and lists all non-committed files in a list (all of the files at this first commit).
2.5 Click Stage All (the green double arrow) to add all files to the list of files to commit.
2.6 Add a commit message for example “Initial commit”
2.7 Click Commit. Now you commited all files to your local repository.

3. First deploy to AppHarbor

3.1 Create an account at AppHarbor
3.2 Login and create an application (it’s free until you require the application to run on multiple server instances.
3.3 Go to the settings page for your application and check the checkbox “Allow write-access to file system” and click update. Umbraco needs to be able to write to the file system.
3.4 Go back to the start page for your application to see the “Getting started” instructions. We don’t need all the steps as we already taken care of some of them.

3.5 In Git Extensions browser – click “Git Bash” in the toolbar to open a command window.
3.6 In the command window enter the following commands from the AppHarbor instruction page (copy paste using the context menu is easiest) to configurate your user and a necessary buffer setting (this you only need to do once for your repo) :

git config –global user.name {yourname} (or use your full name)
git config –global user.email {your email}
git config –global http.postBuffer 52428800

3.7 Now follow the last steps in the AppHarbor instructions, enter in your command window:

git remote add appharbor {your appharbor repository path}
git push appharbor master

Git will ask you for your AppHarbor password, and then the push process will start uploading files.

After some time (there’s quite a lot of files) you will get a message saying “Queuing build of master”. That’s good news – AppHarbor will now build the application for you and automatically deploy and host it. That is often a quick process and you will then be able to access your site – check out the url on your application page:

Application URL’s

http://myumbracosite.apphb.com

Deploying changes – this is the coolest part

Now you have two copies of your Umbraco web site. One locally and one on AppHarbor. When you need to change something you’ll do that locally and then deploy the changes to AppHarbor just by using Git commit and push.

Let’s say you added some razor templates, css’es and javascripts and you like to deploy them.

4.1 Right click your local Umbraco site folder and click Git extensions > Commit. The Git will find out which files you have changed and show them in the same list as you had before your first commit.
4.2 Click Stage all
4.3 Add a commit message “New changes to my css” and click commit. Now you committed to your local repository.

4.4 Go to the Git Extensions explorer and open the Git Bash window again.
4.5 Write

git push appharbor master

enter your password when prompted.

Git will upload the changes and AppHarbor starts a new Build process. A few moments later the updated site is live!

(If you like to omit the database from the commit you will need to add it to the gitignore file.)

Commit and push from the UI – no need to open the command prompt
Open the menuitem “Remote repositorys” in Git Ext, open the “AppHarbor” remote. Add your password to the url, so it says https://myname:mypassword@appharbor.com/mysite.git. Then go to the tab “default pull behaviour” (yeah I know, it says Pull but it goes for Push as well.) Set your AppHarbor as the default “Remote repository” click Save. Then you can use the “Commit and Push” button in the UI.

A Razor application within an Umbraco site (notes from a current project)

Standard

We’re in the middle of re-developing a web application that previously was made as an asp.net Webforms app. The current requirements is to integrate the application into an Umbraco 4.7 site, to use a new design (delivered as a html, CSS & js prototype) as well as to change some of the underlying database workings.

We decided to go for an asp.net WebPages, aka Razor, aka WebMatrix, solution in this case. It’s a very straight forward, close to the metal, way of coding for the web, and we’re very happy with our experiences so far.
When we develop the application we work completely outside of Umbraco, within an Asp.Net Razor website in Visual Studio (and sometimes in WebMatrix actually). The application itself is completely independent of Umbraco context.

So, we test run and debug our application within Visual Studio, then we deploy all changes just by FTP’ing the files to the Umbraco staging site where our customer can try it within their site. It’s a very fluent and flexible developing process.

The most important parts of the application

  • A set of (~10) cshtmls that represents each different page (url) of the application.
  • A wrapper content page in Umbraco for each of the app pages, with the path to It’s corresponding cshtml as a property, RenderScriptPath.
  • A wrapper template in Umbraco, which is a copy of the standard content template for the whole site, but with one important instruction:
    <umbraco:macro runat="server" language="cshtml">
    @if(Model.RenderScriptPath!="") {
       @RenderPage(Model.RenderScriptPath)
    }
    </umbraco:macro>

    this call is rendering the actual application page within the site layout.

  • A service cshtml for serving Ajax calls
    @if (OurApp.CheckLogin() && IsAjax)
    {
      
      // routing each call to the right function / json result
    
      var action = Request["action"];
      if (action=="products") {
        var category = Request["category"];
        Json.Write(OurApp.ProductsByCategory(category),Response.Output);
      }
    }
  • A corresponding – empty – template with just this code to render the service cshtml:
    <umbraco:macro runat="server" FileLocation="/macroscripts/ourapp/ajaxservice.cshtml" />
  • An UmbracoMockLayout.cshtml to make the off-site app look nice, with references to the necessary CSS, images and JavaScript files
  • An _PageStart.cshtml to automatically use the above layout from each of our .cshtml-pages. We do not copy the _PageStart-file to the Umbraco site (and even if we did it would not be run).
    @{Layout = "_UmbracoMockLayout.cshtml";}
  • Businesslogic, in a separate project
  • PetaPoco, as the micro Orm layer for the database
  • JavaScript, CSS and image files. In the same paths in the separate project as in the Umbraco site
  • Dewd, as a way for our Umbraco editors to edit some datatables directly within Umbraco

Umbraco content and asp.Net membership

The application is not entirely disconnected from Umbraco (but almost). There are texts on a few of the pages that is editable from within Umbraco, and used with the common Model.Property syntax. Otherwise the application is just using the Umbraco site navigation and designs. Also the app is using the same membership as the site.

Conclusion

In this project we’re using techniques and products we really like and find to be very efficient and high quality. The resulting application is soon to be launced, so we cannot say much about how it ends up :-), but it’s looking good so far, and we can definitively say that the development process has been unusually friction-free and fast.

Från noll till en komplett webbplats med Umbraco CMS på mindre än en timme

Standard

Umbraco är ett gratis opensource-CMS byggt på Microsofts ASP.NET ramverk. Det är byggt för att vara lätt att förstå och använda, samt enkelt att bygga ut. Det är ett av de 3 mest populära nedladdade programvarupaketen i Microsofts Web Platform Installer och det används över hela världen för i stort sett alla typer av webbplatser.

Genom att använda gratisverktyget WebMatrix så är det väldigt smidigt att installera Umbraco på din dator – och det finns dessutom webhotell som erbjuder billig hosting för WebMatrix-sajter med mycket enkel publicering. Detta innebär att du kan ha en egen webbplats med de smidiga redigeringsmöjligheter som ett professionellt CMS erbjuder uppe och igång ute på internet på mindre än en timme.

Tror du mig inte? Häng med…

1. Ladda ned och installera WebMatrix

Gå till http://www.microsoft.com/web/webmatrix/ och installera den senaste versionen av WebMatrix.

2. Skapa en Umbraco webbplats från WebMatrix “Web Gallery”

När du startar WebMatrix får du som alternativ att starta en ny webbplats från “Web Gallery”. I galleriet hittar du Umbraco. Välj det och skriv in ett namn för din webbplats, följ sedan guiden för att ladda ned och installera Umbraco:

3. Kör Umbraco-installationen

När WebMatrix har installerat Umbraco kommer du att återgå till redigeringsdelen i WebMatrix. Klicka på länken till din webbplats (site) “URL : http://localhost:nnnn&#8221; för att öppna din lokala webbplats i din webbläsare:

Första gången du öppnar din lokala webbplats kommer Umbraco att köra sin installationsrutin. Där väljer du vilken typ av databas du vill använda, samt det användarnamn och lösenord du vill ha, dessutom får du om du vill välja “Starter kit” och “Skin” för din webbplats.

Jag rekommenderar dig att följa installationsrutinens föreslagna alternativ när sådana finns, välj således Sql CE som databas, det innebär att du inte behöver någon installerad databasserver på din dator, Sql CE använder helt enkelt en fil som kommer att sparas i din webbplats filstruktur.

När du får välja Starter kit och Skin föreslår jag att du väljer “Simple Starter kit”, och det Skin som du själv tycker bäst om.

4. Dags för det administrativa gränssnittet i Umbraco

När Umbracos installationsrutin är färdig så är det dags att titta på Umbracos användargränssnitt för första gången.

Lägg märke till att du har en trädvy med en “nod” för varje sida på din Umbracowebbplats. Klicka till att börja med på noden “Sample website” som representerar webbplatsens startsida. Ändra på några texter. När du har ändrat och vill att ändringarna ska synas så ska du klicka på Publicera (Publish). Då kommer din lokala sajt att uppdateras och du kan se webbplatsen med dina ändrade texter:

Det är enkelt att växla fram och tillbaka mellan Umbracos användargränssnitt och din webbplats med hjälp av webbläsarens flikar. Om du tappar bort din Umbraco-flik så kan du alltid öppna den igen genom att öppna adressen http://localhost:nnn/umbraco/ (när nnn är det portnummer som WebMatrix har valt åt dig, du hittar det under Site i WebMatrix).

Du har nu WebMatrix och din webbplats på din egen dator och det finns ingen möjlighet att komma åt den utifrån. Webbplatsens filer finns din mapp “Mina dokument” och undermappen “My Sites”. När du har stängt WebMatrix kan du komma åt din webbplats igen genom att öppna WebMatrix och sedan välja din webbplats under “My Sites”.

5. Ut med webbplatsen på internet

När du har redigerat din webbplats och vill att den ska synas på internet så behöver du hitta ett webbhotell (webbhost).

Du kan göra det med hjälp av en länk i WebMatrix, under Publish – Find Hosting, eller använda ett webbhotell som du känner till sedan tidigare.

Jag testade “Appliedi” när jag skrev den här artikeln. Deras installation var väldigt enkel med stöd för funktionen WebDeploy. Appliedi är dessutom gratis i tre månader (därefter $5 i månaden).

När du har beställt testpaketet ska du få ett epostmeddelande med dina inloggningsuppgifter.

Kopiera dessa uppgifter och klistra in dem i WebMatrix “Publish Settings”

6. Publicera!

När du har lagt in dessa uppgifter så kan du publicera din webbplats, klicka på “Publish”. Det kommer att ta några minuter eftersom en fullständig Umbracoinstallation innehåller ganska många filer. När publiceringen är klar kommer du – och alla andra – att komma åt din sida på Internet!

Att redigera innehållet

Både din lokala installation och den på ditt webbhotell har allting som behövs både för att visa webbplatsen och för att redigera den med Umbracos administrativa gränssnitt. Förutom publiceringsrutinen så finns det ingenting som kopplar ihop din lokala installation med den som finns på Internet. Det innebär att om du ändrar innehåll lokalt så kommer det inte att synas utåt om du inte publicerar om webbplatsen. Men det innebär också att du kan använda din lokala kopia för att testa olika idéer, och sedan lägga in dem på din publika webbplats när du är nöjd med resultatet, helt enkelt genom att jobba i det administrativa gränssnittet där också.

På samma sätt som att din lokala kopia är åtkomlig via localhost://nnn/umbraco/ så kommer du åt din webbplats på internet genom att gå till adressen http://www.mysite.com/umbraco/ . Inloggningsuppgifterna är desamma som du valde när du gjorde umbracoinstallationen.

Lycka till!

Läs mer om Umbraco på http://www.umbraco.org och om du har frågor eller kommentarer så får du gärna ställa dem här. Om det finns intresse så kommer jag återkomma med fler artiklar på svenska om Umbraco.

Jag kan också varmt rekommendera Umbraco-forumet.

Coding my own automated web site monitoring with Google Apps Script and a Spreadsheet

Standard

There are a great deal of good Web site monitoring and testing services out there. I’ve tested a few of them, but I needed two things I did not find (yet). 1) Good programmability and 2) Familiar presentation of the result in a Spreadsheet manner.

First I thought I could to create some scripts on my server and present the result in a Google Sheet. But then I found out that the Google Apps Scripts is capable of pretty much everything I need. I think they call the language “Javascript with objects”, which describes it very good. Use Javascript, but with a set of useful objects (and obviously functions).

So I created a script which can take a list of any urls and test them for avaliability, http response code, response time and look for some content. Here’s how my resulting sheet looks like after a test run. The full url’s are in the first column, then there’s a command column and an options column, currently accepting three different options (should-contain, should-not-contain and response-code):

The orange markers on the cells in column D are comments. I added the full page contents as comment texts, so that if there’s an error – I can go back and check the requested content.

I use this to test sites created with the Umbraco CMS, but it could of course run on any site. One thing that might be a bit particular for Umbraco sites is that the script is searching each content for the word “error”, which could appear on some pages if there’s an macro error or something else.

If the tester find one or more errors it will send me an email. Otherwise it’s just updating the sheet.

The script runs automatically every hour with the help of the built in scheduler in Google Apps Scripts.

Usage : 1) create a new Google sheet, add your URL’s from cell A2 and down. 2) Add the script in the script editor. 3) Run the function checkUrls(). 4) For scheduling – use the scheduler in the script editor.

function isCellEmpty(cellData) {
  return typeof(cellData) == "string" && cellData == "";
}

function checkUrls() {

  // The code below iterates over rows in a sheet and uses the value in
  // column A as an url, requests it and puts the response code in column B
  // and the request string as the comment

  var doc = SpreadsheetApp.getActiveSpreadsheet();
  var cell = doc.getRange('a1');
  var mailMessage = "";

  
  // leftmost resultcolumn
  
  var resultColumn = 3;
  
  cell.offset(0,resultColumn).setValue(new Date());
  cell.offset(0,resultColumn+1).setValue("Content type");
  cell.offset(0,resultColumn+2).setValue("Success?");
  cell.offset(0,resultColumn+3).setValue("Seconds");
  cell.offset(0,resultColumn+4).setValue("Comment");
  
  for (var i = 1; i < 20; ++i)
  {
    var cellData = cell.offset(i,0).getValue();
    if (!isCellEmpty(cellData))
    {

      var command = cell.offset(i,1).getValue();
      var optionData = cell.offset(i,2).getValue();
      if (optionData=="") optionData="{}";
      var options = Utilities.jsonParse(optionData);
      
      var hasError = false;
      var startTime = new Date();      
      
      if (command=="" | command=="GET")
      {

        var responseCode = 404;
        var requestContentText = "";
        var results = "";        
        var headers;
        var requestType ="";
        
        var expectedResponseCode = 200;
        if (options["response-code"]!=undefined)
          expectedResponseCode = options["response-code"];

        try
        {
          var response = UrlFetchApp.fetch(cellData);
          responseCode = response.getResponseCode();
          requestContentText = response.getContentText();
          headers = response.getHeaders();         
          if (headers!=undefined)
            contentType=headers["Content-Type"].toLowerCase();
        }
        catch (e)
        {
          requestContentText = e.message;
        } 
                
        cell.offset(i,resultColumn).setValue(responseCode);
        if (responseCode!=expectedResponseCode) 
        {
          hasError = true;      
          results += "Expected response code: " + expectedResponseCode + ". ";
        }
        
        if (contentType.indexOf("html")!=-1)
          cell.offset(i,resultColumn).setComment(requestContentText);
        else
          cell.offset(i,resultColumn).setComment("");
        
        cell.offset(i,resultColumn+1).setValue(contentType);
        
        // print results in column
        var colOffset = resultColumn+4;
        
        // not contain the word ERROR
        var containsError = (requestContentText.toLowerCase().indexOf("error") != -1);
        //cell.offset(i,colOffset).setValue("Error: " + containsError);
        if (containsError) 
        {
          results += "Error found. ";
          hasError = true;                      
        }
        
        if (options["should-contain"]!=undefined)
        {
          // not contain the word ERROR
          var shouldContain = options["should-contain"].toLowerCase();
          var doesContain = (requestContentText.toLowerCase().indexOf(shouldContain) != -1);
          if (!doesContain) 
          {
            results += "Not found: " + options["should-contain"] + ". ";
            hasError = true;                      
          }
          
        }

        if (options["should-not-contain"]!=undefined)
        {
          var shouldNotContain = options["should-not-contain"].toLowerCase();
          var doesContain = (requestContentText.toLowerCase().indexOf(shouldNotContain) != -1);
          if (doesContain) 
          {
            results += "Found: " + options["should-not-contain"] + ". ";
            hasError = true;                      
          }
          
        }
        cell.offset(i,colOffset).setValue(results);
        
      }
      
      // timer        
      var endTime = new Date();      
      var timeDiff = endTime-startTime;
      
      // success? (no errors)
      cell.offset(i,resultColumn+2).setValue(!hasError);
      if (hasError)
      {
        cell.offset(i,resultColumn+2).setBackgroundColor("red");  
        mailMessage += "ERROR on " + cellData + ":" + results;
      }
      else
        cell.offset(i,resultColumn+2).setBackgroundColor("green");  
      
      // time spent (in seconds)      
      cell.offset(i,resultColumn+3).setValue(timeDiff/1000);
      

    }
    else
    {
      break;
    }
  }
  if (mailMessage!="")
  {
    MailApp.sendEmail("me@myaddress.com", "ERROR on your web ", mailMessage);
  }
  
}

function getTime()
{
  var startTime = new Date();
  Browser.msgBox(startTime);
  var endTime = new Date();
  var timeDiff = endTime-startTime;
  Browser.msgBox(timeDiff);
}

Obviously there’s a lot more the ready made services can do, but the beauty of this little thing is that I can add features to it easily whenever I need. And I can share the sheets with my clients, add nice reporting stuff like charts, pivots or whatever.

One current pretty big limitation is I could not figure out how to test pages that is behind login. I hope I can solve that soon.

Sending emails with a mailing list provider (Umbraco, Razor and Mad Mimi)

Standard

I wanted to use a mailing list provider for some programmatically composed emails like account confirmations and welcome messages. My needs is pretty basic, a layout with a banner, a header and a html body text. To be sent to whoever, whenever.

Besides of some familiar provider names, I recently heard about a service called “Mad Mimi”, which I liked to try out for this. They have a pretty interface and have okay deals.

Mad Mimi Email Marketing

Said and done, I created myself an account and made a “Promotion”, which is basically a email design with images and texts. Both static and dynamic texts {in curly braces} can be used. Here’s what my test design came to look like:

Yeah, I know, I’m not a designer…

Next step was to create a function with which I could compose a mail this way:

    var promotionName = "MyPromotion";
    
    var recipientName = "Jonas";
    var recipientEmail = "whoever@site.com";
    var subject = "Question";
    
    var content = @"<h2>Question</h2>
                  <p>I will get back to you soon.</p>";
    
    var dynamicContent = new {name = recipientName, content = content};
    MadMimi.SendPromotionToRecipient(promotionName,recipientEmail,subject, dynamicContent);

As I code most my code in Razor at the moment I created myself a global Razor helper (ie a App_Code\MadMimi.cshtml).

To be able to use the code you need an Api_Key which you get from Mad Mimi when you create your account. You also need to enable Api functionality (click “Add things”). Try their support if you have questions, they were very quick to answer my questions. There’s also a Google group for dev (api) questions.

I posted the complete helper code here : https://gist.github.com/880205 (also included a function to send emails to a list, I’ll probably write anoter blog post about that.)

And heureka – using the helper and the code above I got an email looking like this:

I hope you liked the post, please feel free to send me feedback of any kind. Happy coding!

From zero to a live installation of Umbraco in 30 minutes (for beginners)

Standard

“Umbraco is a free, open-source web cms built on the Microsoft ASP.NET Framework. It is easy to use, simple to understand, and is highly extensible using industry-standard languages and patterns such as HTML, CSS, jQuery, and C#.”

Using the new, and also free, web development tool WebMatrix from Microsoft it’s now easier than ever to install Umbraco. And there are free WebMatrix hosters (at least for the first three months). Which means you can have your own web cms running with live on internet for no cost at all. And it will only take you about half an hour to go from zero to live.

Download and install Microsoft WebMatrix

Goto http://www.microsoft.com/web/webmatrix/ and install the latest version of WebMatrix.

Create an Umbraco Website from the WebMatrix Web Gallery

When you start WebMatrix you will be presented with the option to start a new site from the Web Gallery. In the gallery you find Umbraco. Select it and choose a name for your site, then follow the guide to download and install Umbraco:

Run the Umbraco Installer

When WebMatrix is finished with it’s installation of Umbraco you will get to the WebMatrix editor. For now, just click the link to your site “URL : http://localhost:nnnn&#8221; to open up your local Umbraco site in your browser:

The first time you browse to your site the Umbraco will run it’s Installer. In the installer you choose which database you like to have for yous site, as well as the login username and password you like, and an optional Starter kit and Skin for your site.

I recommend you to follow the installer defaults and choose Sql CE for the database – that option requires no database configuration at all, it’s simply a file which will be included in your site folder.

You will be prompted to select a Starter kit and a Skin. Have a look at the different options and choose the ones you like. I used “Simple Starter kit” for this demo.

Enter Umbraco

When the installer is finished it’s time to enter the Umbraco UI for the first time.

Notice you have a treeview which has a node for each of the pages of your Umbraco site. To begin just click “Sample website” and change some texts if you like. To make the changes go live you need to click Publish. At this stage “live” means running on your local computer with the WebMatrix web server IIS Express. No one else can view it at this point:

You can switch back and forth between Umbraco and your actual site with your web browser tabs. If you loose the Umbraco tab you can open it again using the address http://localhost:nnn/umbraco/ (where nnn is the port number WebMatrix decided for you).

You need to have WebMatrix and your site running on your computer to be able to view it in your browser. It’s files will be saved in the “My Sites” folder. And after closing the WebMatrix, next time you open it you will find your Umbraco site in the “My Sites” page.

Publish it for real

When you have been editing your Umbraco site and want it to be published to Internet for a live audience you will need to find a web hosting provider.

You can do that using the built in link in WebMatrix, under Publish – Find Hosting.

I used “Applied” (which I never heard of before) but their setup was very easy – and free for 3 months. And after that $5 per month. And remember you can always move your site to another host later on if you so choose.

You should get an email with your Web Deploy settings

Copy and paste those settings into WebMatrix Publish Settings

Publish to live site

Then click Publish within WebMatrix to publish your Umbraco to the live site. It will take some minutes since the full Umbraco installation has a lot of files. When the publishing is done you – and everyone else – should be able to see your site on the net!

Editing the contents

Both your local installation and the live installation of Umbraco has everything included, which means you can use the Umbraco UI on both places. But note that the two installations are completely separate, and if you edit something on one place you will need to copy that to the other installation.

If you like to edit the site content on the internet published copy of your site you just go to the address http://www.mysite.com/umbraco/ and enter your login credentials from the Umbraco installation.

Enjoy!

Read more about Umbraco at http://www.umbraco.org and if you have questions the Umbraco community is more than happy to help out.

Ajax-enabled search in Umbraco (using Examine and Razor)

Standard

I am about to implement an ajax-enabled search for an Umbraco site. Writing the prototype was quick (and fun) so I thought I’d share it. Here goes:

To make it work I have this search macro (run from my search page template):
Search.cshtml:

<form method="post">
<input type="text" id="searchstring" name="searchstring" value="@Request["searchstring"]"/>
<input type="submit"/>
</form>

<div id="searchresults">
@if (IsPost) {
  @RenderPage("SearchResults.cshtml")
}
</div>
              
<script type="text/javascript">
$(document).ready(function() {
    var content = $('#searchstring').val();
    $('#searchstring').keyup(function() {
        if ($('#searchstring').val() != content) {
            content = $('#searchstring').val();
            $.ajax({
               type: "POST",
               url: "/searchresults",
               data: "searchstring=" + content,
               success: function(msg){
                 $('#searchresults').html(msg);
               }
             });                                                                                      
        }
    });                           
});
</script>

Note that the script is using jQuery, which you will need to reference somewhere on your page.

And this macro to get search results (SearchResults.cshtml):

@using Examine
@using Examine.SearchCriteria
@using UmbracoExamine

@{
  var searchString = Request["searchstring"];
  if (!String.IsNullOrEmpty(searchString))
  {
    var searchProvider = ExamineManager.Instance.DefaultSearchProvider.Name;
    var searchResults = ExamineManager.Instance.SearchProviderCollection[searchProvider].Search(searchString, true);

    foreach (var c in searchResults)
    {
      <div>
        <h3><a href="@umbraco.library.NiceUrl(c.Id)">@c.Fields["nodeName"]</a></h3>
        @if(c.Fields.Keys.Contains("bodyText"))
        {
          var bodyText = c.Fields["bodyText"];
          if (bodyText.Length>100)
          {
            bodyText = bodyText.Substring(0,100);
          }
          <p>@Html.Raw(bodyText)</p>
        }
      </div>
    }
  }
}

And to get the search results for my Ajax post I simply have a template (searchresults):

<%@ Master Language="C#" MasterPageFile="~/umbraco/masterpages/default.master" AutoEventWireup="true" %>

<asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server">
 <umbraco:Macro Alias="SearchResults" runat="server"></umbraco:Macro>
</asp:Content>

Important about the Examine configuration

I’m using the ootb provided indexer (same as the backend) for this prototype. That’s easy to start with but not advisable for a real site, since it includes both unpublished and protected content.

Please read this article by Shannon Deminick about Examine and Razor.

Improvements

This works, but there are several areas of improvements which I am about to dive into. Suggestions are more than welcome!

* Better handling for the text change (javascript)
* Nicer display of content (examine / strings)
* Limit number of search results / paging

CoffeScript version of the js code

<script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script>
<script type="text/coffeescript">  
  content = $('#searchstring').val()
  $('#searchstring').keyup ->
   currentContent = $('#searchstring').val()
   if currentContent!=content
     content = currentContent
     $.ajax({  
               type: "POST",
               url: "/searchresults",
               data: "searchstring=" + content,
               success: (msg) ->
                 $('#searchresults').html(msg)
           })
</script>

Better structure for your Umbraco Razor scripts with RenderPage and Global helpers

Standard

Together with Razor in Umbraco 4.7 comes a bunch of useful things that’s included in the WebPages assemblies. RenderPage is one of those things. It’s simply rendering another Razor script at the call:

@RenderPage("MyOtherScript.cshtml")

The other script runs in the same page and umbraco macro context which means it has access to Model as well as the Page object. So we can do this:

@{
Page.Header = "Header";
@RenderPage("MyHeaderScript.cshtml")
}

MyHeaderScript.cshtml:

<h1>@Page.Header</h1>

Another way to get the same result is by using the RenderPage-function with a data parameter:

@RenderPage("MyHeaderScript.cshtml","My header")

The parameter is an array of Object, so when I send just one object I get the value like this:

MyHeaderScript.cshtml:

<h1>@PageData[0]</h1>

See also this page for an example of a small helper which makes this code look nicer

And remember the called script also has access to Model:

MyHeaderScript.cshtml:

<h1>@PageData[0]</h1>
On page : @Model.Name

Reference RenderPage (msdn)

Note that the Page object does not live between macros, so it’s not possible to set Page.Header=”My header”; in one macro and then use it in another macro. So the scope is within one macro + and RenderPage-calls from that macro.

A simple search functionality with some separation of logic and display

Here’s a more complete example – a very simple search functionality:

MySearch.cshtml (the macro):

@RenderPage("SearchForm.cshtml")
@if (IsPost)
{ 
 @DisplaySearchResults(Request["search-string"])
 }

@helper DisplaySearchResults(string searchString){
                                                  
  var searchRoot = Model.AncestorOrSelf();
  var values = new Dictionary<string,object>();
  values.Add("keyword", searchString.ToLower());

  var searchResults = searchRoot.DescendantsOrSelf().Where("bodyText.ToLower().Contains(keyword)",values);  

  <p>Searching for <strong>@searchString</strong> resulted in @searchResults.Count().ToString() hits</p>                                                  
  
  foreach (var item in searchResults)
  {
    @RenderPage("SearchResultItem.cshtml",item);
   }  
}

SearchForm.cshtml: (no macro)

<form method="post">
Search: 
<input type="text" name="search-string" id="search-string" value="@(Request["search-string"])"/>
<input type="submit"/>
</form>

SearchDisplayItem.cshtml: (no macro)

<p>
 <a href="@PageData[0].Url">@PageData[0].Name</a>
</p>

Saving scripts in subfolders

Did you know it’s possible to save your scripts in subfolders within the Umbraco UI?

Just add a new script and enter the name together with the folder name like this:

Run your script in the Umbraco UI with the Razor Visualizer

The Razor Visualizer is a great help when you write your Razor scripts in Umbraco UI and want an immediate test of the result they produce. Just install this package and you will get a visualize button in your Razor script editor.

You can also use it to run maintenance scripts, for example to delete all documents under a certain document.

Run a razor script directly from a template without a macro

Using this syntax you can run your scripts without a macro:

<umbraco:Macro FileLocation="~/macroScripts/any-file.cshtml" runat="server" />

A sample from a current project

We have a page with course information and registration form, like this:

Header
Information about the course
Course occasion date and place
Registrated attendees
Registration Form

Currently there’s a bunch of Xslt’s handling this for us, which was kind of hard to build, and even harder to debug and change. Now we’re moving to Razor which is much easier to handle.

We decided to use this structure for the Razor scripts:

Root folder:
  Meeting.cshtml (with a macro)

  \Meeting folder:
    Information.cshtml
    Occasion.cshtml
    Attendees.cshtml
    Form.cshtml
    FormPost.cshtml

We decided to only keep scripts that have macros connected to them in the root folder, and subfolders for all related “views” – as well as scripts that performs logic (FormPost.cshtml).

The main script (Meeting.cshtml) is very simple :

@RenderPage("Meeting/Information.cshtml")
@RenderPage("Meeting/Occasion.cshtml")
@RenderPage("Meeting/Attendees.cshtml")

if (!IsPost)
{
 @RenderPage("Meeting/Form.cshtml")
}
else
{
 @RenderPage("Meeting/FormPost.cshtml")
}

Global helpers

Another way to structure the Razorness in different files is to use global helpers, that is .cshtml (or .vbhtml) files stored in the App_Code folder. Which obviously means you need to have access to that folder, and likely use Visual Studio to edit the files. (Personally I do prefer to code my Razor scripts with the help of the Umbraco UI editor.)

App_Code/GlobalHelpers.cshtml:

@helper FancyMessage(string message){
<span class="fancy">@message</span>
}

Then you can use it from your other scripts like this:

<p>And : @GlobalHelpers.FancyMessage("Hello World!")</p>

You can of course have as many global cshtml helpers as you like, and you can even store them in subfolders – then you’ll get automatic namespaces for them, so if the abouve helper was in App_Code/Fancy/Helpers.cshtml:

<p>And : @Fancy.Helpers.FancyMessage("Hello World!")</p>

Works for functions to:
App_Code/GlobalFunctions.cshtml:

@functions{
  public static string Foo (string message) {
    return message;
  }
}

}

Update: See how you can call a helper with razor code as a parameter.

@Fancy.Helpers.DivThisThat(@<i>inner contents</i>)

An ordinary WebMatrix app in an Umbraco subfolder

Standard

I have an ordinary WebMatrix app with a bunch of .cshtml’s, a SQL CE database, some nuget dlls and helpers. And now after very little effort I seem to be able to run it beautifully together with an Umbraco 4.7 site. I still need to test it more thoroughly but initial tests have been running perfect.

Here’s how:

1) Add a reserved path for the app in web.config:

<add key="umbracoReservedPaths" value="~/umbraco,~/install/,~/myapplication/" />

2) Enable webpages in web.config:

<add key="webpages:Enabled" value="true" />

3) Add a separate web.config file in the app path (/myapplication) to set the cshtml base class to the standard WebPages page base type (System.Web.WebPages.WebPage), and not the Umbraco one (umbraco.MacroEngines.DynamicNodeContext):

<configuration>
  <system.web.webPages.razor>
    <pages pageBaseType="System.Web.WebPages.WebPage">      
      <namespaces>
        <add namespace="Microsoft.Web.Helpers" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>
</configuration>

Now I can run ordinary .cshtml files completely outside Umbraco context from my /myapplication/ folder.

Big thanks to Elijah Glover for helping me out with this.

I’m about to try the other way around aswell, i.e. run Umbraco from within a subfolder.