What on earth is Umbraco ServerVariables Parsing?!

What on earth is Umbraco ServerVariables Parsing?!

Hello all this is another ‘What on Earth‘ blog post detailing a specific feature or functionality of Umbraco that may be hidden away a little & this blog post is here to help describe & detail it.

Continue reading “What on earth is Umbraco ServerVariables Parsing?!”

Advertisements

Thanks!

Thanks!

Hello,
So I have been lucky enough to come back from a fun packed, long week with my second family the Umbraco community at the annual Umbraco conference, CodeGarden.

Thanks for the MVP

CodeGarden14 - MVPs
The Umbraco 2014 MVPs high fiving.        Photo: Doug Robar

I wanted to say a BIG thank you to everyone who voted for me this year to receive a community award, the Umbraco MVP award. I may not has been active as other members of the MVPs on our.umbraco.org however I like to think this blog with various code snippets & findings I have found out along the way has been useful to yourself & the community, in addition to running the weekly(ish) Umbraco  Show & Tell Google Hangout, called uHangout.

What is an Umbraco MVP?

Niels Hartvig wrote a fantastic blog post on what an Umbraco MVP is, but I will quote him:

MVPs are quite common in different projects and they’re all inspired by Microsofts “Most Valued Professional” program. In Umbraco MVP stands for “Most Valued People” as we see at least as big value in amateurs as in professionals. In fact several Umbraco MVPs started as amateurs only to find that Umbraco is now the cornerstone of their professional career.

An MVP is a person who’s not a part of the core team nor a part of the Umbraco HQ (the company), but in some other way adds extraordinary value to the project. Looking back at previous MVPs this translate into being friendly in the forum or making incredible and highly useful packages. In other words, MVPs represent the best of the community which is the last building block in what makes the foundation of the Umbraco project; the company, the core team and the community.

H5YR

I want to end my post with a big thanks & a H5YR (High Five You Rock) to the other Umbraco MVPs who help make the Umbraco community fantastic with the packages they create, to the very detailed & helpful answers they post on our.umbraco.org. I swear Jan never sleeps as he always finds the time to post on the forums. So a big thanks to my fellow MVPs & nominees you all do outstanding work & help make this a better place for all.

I never thought in a million years I would be awarded MVP, but it’s easy to get involved with the Umbraco community and you never know it could be you next year.

Umbraco MVC: Regionalisation of Error Messages

Hello, Bonjour, Hej
However you say hello in whatever language, at The CogWorks where I work we built a lot of Umbraco Sites that are multi regional for various large brands. The common problem I have noticed we have is that when building say a simple form such as a contact form the validation & error messages that are reported back are in English rather than in the current language of the site you are in, obviously if you are using Contour then this is not a problem and can regionalise labels & error messages for you, but there are some times when you need to or want to build a custom form using the typical MVC pattern and use ASP.NETs native Model validators such as [Required], [EmailAddress], [Range] etc…

Continue reading “Umbraco MVC: Regionalisation of Error Messages”

Using TeamCity for Continuous Integration with NodeJS and Grunt

Note: This is a cross post from my blog post over at Work’s blog at CogWorks, this is a cross post to keep a record of my findings.

What is TeamCity and Continuous Integration?

TeamCity is a tool developed by JetBrains to run a continuous integration environment. This allows the development team to push to a Git repository and for the TeamCity tool to monitor the Git repository and perform a series of build steps in order for the website to be built and deployed to a webserver. In our case at The Cogworks, a website in our development environment.

This allows the code to be checked ensuring the code compiles and builds, as it would inside Visual Studio. You can then be sure that a working site is deployed.

With TeamCity you can be as simple or fancy as you like, involving build steps that copy files from one place to another or running complex tasks such as utilising tools to help you migrate databases from one environment to another. There are plenty of scenarios and ideas that can be setup to fit your team’s development workflow.

What are NodeJS, Compass, Grunt and Bower?

Before I go onto explain this, I’d just like to add a disclaimer that I claim to be no expert in this field and someone with more experience with these tools may give a better explanation of their use. So as I am still new to these frontend tool sets I’ll try my best to explain them.

NodeJS

So let’s start with NodeJS. This is a Javascript application framework which allows you to build a whole range of applications ranging from a HTTP server written in Javascript, to slightly more geeky things such as controlling Parrot’s AR Drone. Some clever bods have also written Grunt and Bower in NodeJS hence the need for it in this scenario. So from what I can tell your imagination is the only limit with NodeJS and it’s worth browsing the NPM repository, to see what other people are up to.

Compass

Next onto Compass, this is seen as a common and useful extension to the popular CSS preprocessor Sass. It also gives some extra functionality to your Sass files for example providing the power to automatically create a sprite image and use it in your CSS – It’s voodoo magic!!

Grunt

Grunt is whats all the front-end devs are getting excited about at the moment. This is because Grunt is a task runner for their front-end workflows. You can compare this as an equivalent to your C# MSBuild workflows. So front-end devs can run tasks such as ‘Watch’, which will automatically monitor file changes to Sass and CoffeeScript source files which are then automatically re-compiled into CSS and JavaScript files and then the browser is reloaded. Other common workflows are to use it is a build runner, and for CSS and JS files to be minified and concatenated together, along with tasks such as running all images though an image minifier to ensure all images are optimised and compressed for the best browsing experience. Again there are a whole heap of uses for front end devs and it will be exciting to see what the future holds for Grunt.

Bower

Finally, Bower is a front end package manager which is built by the guys over at Twitter and runs on NodeJS. This fetches other front end tools you may wish to use. For example you may use Bower to install and fetch the latest version of jquery, jquery-ui and jquery.cookies along with a range of other client side dependencies. This is similar to the NodeJS NPM repository, but instead is aimed at web dependencies from jQuery to handlebars.

Why do we need all these frameworks?

All these tools basically allow us to write better front end code. It allows us to compress images, minify large JS and CSS files and hel pus improve the overall quality of the browsing experience on as many devices and connection speeds as possible automatically.

They help us take some of the hard work out of this by allowing us to automate a lot of the front end build processes. You can think of Grunt as the front end developer equivalent of a build process like we do as .NET Developers.

Installing

So I recently went through this process and made some detailed notes about how I installed and setup what was needed to get all these apps working nicely with TeamCity. So here goes:

Git

First install Git and go through the installer. The most important steps are as follows:

Git Install

From the screenshot above, you must ensure you chose the option to run Git from the Windows command prompt. This ensures that you can type Git in any console window as it has been added to your computers PATH. Without doing this you will make your life hard if you work with things like bower.io where it expects Git to be in the PATH.

The next thing to ensure your life is easier in the future is that you choose the Windows line ending from this step in the installer:

Git Install - Windows Format

Now that we have Git installed, let’s quickly check and ensure it is actually installed correctly.

From a console window type the following git -v and hit enter. You should see a version number displayed if git has been successfully installed.

NodeJS

The next part that I needed to install on the server was NodeJS which allows a whole heap of extensions to be installed via NPM (Node Package Manager). Think of this as the equivalent of NuGet in the .NET world but just specifically for NodeJS.

Again as with the Git install, there is an important step to ensure the following commands node and npm are available in your console window globally by ensuring NodeJS and NPM are installed in your PATH.

nodejs-install-4

Once the NodeJS installer has finished lets test and ensure that NodeJS has been installed and available globally in our Windows console.

Type node -v into the console and hit ‘Enter’. You should see the version number displayed if it has been successfully installed into your PATH, next try npm -v and see if that also returns a version number too.

Bower

bower-logoBower is a front end package manager from the Twitter team and uses Git to consume the packages. To install Bower we need to install it from the node package manager (npm). In the console window type the following command npm install -g bower this tells npm to install the Bower package globally. This allows us to use the command ‘Bower’ in our console window from any location. Similar to how Git and NodeJS was added to our PATH.

This is where you will see your console window go nuts as the NPM goes and downloads Bower and it’s dependencies. Do not panic you will soon get used to the number of dependencies any NPM package has.

cmd-npm-grunt-install

Again to test that Bower has been installed successfully use the bower -v command.

Bower is not a requirement but as our front-enders were using Bower in their workflow we needed to install it too on our build server as well.

Grunt

grunt-logoThe next thing we need to install and setup for our workflow is to install GruntJS which is the task runner or the equivalent as the front-enders build steps.

To install this we need to install Grunt from NPM with this following command npm install -g grunt-cli which tells node to install the Grunt command line runner globally and make it accessible from any console window.

To verify it installed all OK lets run the obligatory command of grunt -v this will list a version number and most likely will give you a warning message that it can’t find a local grunt in your folder. Don’t panic this is normal as its trying to look for a gruntfile.js

Ruby

The next part we need to install is Ruby, this allows us to use other packages that are specifically written for Ruby. We use Ruby in our setup because our front enders are using a front end tool called Compass which is an extension for the CSS preprocessor Sass.

So we need to download and install the Ruby installer from rubyinstaller.org.

ruby-install-2

As before with all installs, we need to ensure that Ruby gets installed correctly and we need to make sure it gets installed into our PATH.

Once installed let’s verify it installed correctly by typing ruby -v and also gem -v to test it out. As with NodeJS, Ruby has it’s own package manager and it’s called gem. So we can download packages such as compass from gem.

Compass

The final thing to install for our front end tools is Compass, which is a tool for the CSS preprocessor Sass, which helps give Sass some extra power with some helpers and extensions.

To install Compass we need to run the following commands in the console.
gem –update system followed by gem install sass and finally gem install compass

We can then do our usual version check and run sass -v and then compass -v
For Compass to be installed we need to ensure Sass is installed first, hence the order above for the commands is important.

Ok we have everything all setup and ready for the front end workflow that our front-enders are using, the last thing to setup is the TeamCity plugin to use NodeJS and Grunt in the build steps.

Installing the NodeJS TeamCity Plugin

The next part of this workflow is to download and install the NodeJS TeamCity plugin written by Eugene Petrenko from Germany. This plugin is open source and posted on GitHub here – https://github.com/jonnyzzz/TeamCity.Node

However rather than going over to the GitHub repo you need to head over to the JetBrains own TeamCity where it hosts this project. http://teamcity.jetbrains.com/viewType.html?buildTypeId=bt434

This url will prompt you to login, however do not panic just click the link to login as a guest under the login box. Here you will see the most recent builds for the TeamCity NodeJS plugin. To download the plugin click the ‘View artifacts‘ button next to the most recent build and download the jonnyzzz.node.zip file. At the time of this post the most current version is 1.0.46

To install the plugin you need to stop the build runner and the main web interface services on the build server. Once they are stopped you need to copy the downloaded zip file to TeamCityDataDirectory/plugins, note you do not need to unzip the file, simply copy it to the plugins folder. Once the plugin has been copied restart the two services for TeamCity backup on the build server.

If you were like me and were unsure where the TeamCityDataDirectory is, then before you stop the services, go to the Administration settings page in TeamCity to point you to the folder location path.

Now that you have the plugin installed and have restarted TeamCity, go to Settings followed by Plugin List which should show the NodeJS plugin in the list if it’s installed correctly.

teamcity-plugin-list

Adding the Build Steps

Each call/command line is its own step, so we can easily see where the build fails to help us debug the problem. Each step we add here outputs information allowing us to keep an eye at all times what is going on with our build.

Run: npm install and npm update

teamcity-npm-step

Run: bower update

teamcity-bower-step

Run: npm install grunt-cli

teamcity-grunt-cli-step

Run: grunt build

teamcity-grunt-step

Build Log

So when the build is running on TeamCity we can see the build log and the output of each of these steps to give us an insight into what is happening and if any errors are occurring. The order of these build steps is important so that they run in the specific order as defined above. This is to ensure if one step fails it stops and the other steps cannot continue to run.

Gotcha’s

There were a few gotcha’s that caught me out which I’d like to share with you which may also catch you out on your journey to front end workflow nirvana.

Bower uses Git to go off and consume packages from Git repositories nine times out of ten it would work fine, however there may be one dependency it was trying to download where it would fail with a weird error message of ‘ECMDERR Failed to execute‘. After some Googling I found the solution was best to change how Git was fetching contents of a repo. So in the console window run the following command to ensure it uses https:// protocol as opposed to git:// urls and this error went away.

git config –global url.”https://”.insteadOf git://

The second gotcha happened when I received some strange errors back from NPM. It took a while for me to figure out why I was experiencing these errors. However the solution for us was to run the Teamcity service on the server under the Administrator user specifically. After doing so the errors went away and we managed to get the Grunt workflow all sorted out for our frontenders.

The End…

Hopefully this has been useful to you and now you too can get up and running with integrating Grunt and the world of NodeJS into your TeamCity Continuous Build processes.

Now Front End Developers and .NET developers can live happily together and can stop arguing with one another! (maybe!!)

Hope this helps you,
Warren 🙂

2013 – My Umbraco Year in Review

Hello all,
As it’s the last day of 2013 I would like to look back and reflect on the year that has been.

It has been a great & fantastic year after a personal rocky year back in 2012, but as this year has progressed I have gained my confidence in myself back.

Continue reading “2013 – My Umbraco Year in Review”

Umbraco 7 – Creating a generic Settings editor with WebAPI & AngularJS

Hello all,
So my adventure for creating a new Umbraco 7 package for viewing Google Analytics information. I have created a generic way to edit settings from an XML file for this new Analytic section inside Umbraco.

So if I add any new elements to my XML config file, they will automatically appear in the Umbraco backoffice for it to be edited.

So I will run you through the parts such as the WebAPI Controller, and Angular to wire this all up nicely.

Firstly here is the XML config file I want to edit & if I add any new nodes it will automatically show in Umbraco to be edited:

<?xml version="1.0" encoding="utf-8" ?>
<Analytics>
  <ClientId label="Client ID" description="This is the Client ID key from the Google API">1234567</ClientId>
  <Username label="Username" description="The username for the Google account">warren</Username>
</Analytics>

So let’s take a look at the WebAPI controller I created to Get & Post the settings to and from the xml.config file.

using System.Collections.Generic;
using System.Linq;
using System.Web.Hosting;
using System.Xml;
using Analytics.Models;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;

namespace Analytics.Controllers
{
    /// <summary>
    /// Web API Controller for Fetching & Saving values from settings.config file
    /// 
    /// Example route URL to API
    /// http://localhost:62315/umbraco/Analytics/SettingsApi/GetSettings
    /// </summary>
    [PluginController("Analytics")]
    public class SettingsApiController : UmbracoAuthorizedApiController
    {
        /// <summary>
        /// Does what it says on the tin - the path to the settings.config file
        /// </summary>
        private const string ConfigPath = "~/App_Plugins/Analytics/settings.config";

        /// <summary>
        /// Gets Settings from the XML settings.config
        /// </summary>
        /// <returns>Serializes settings from XML file into a nice list of objects</returns>
        public List<SettingsValue> GetSettings()
        {
            //A list to store our settings
            var settings = new List<SettingsValue>();

            //Path to the settings.config file
            var configPath = HostingEnvironment.MapPath(ConfigPath);

            //Load settings.config XML file
            XmlDocument settingsXml = new XmlDocument();
            settingsXml.Load(configPath);

            //Get all child nodes of <Analytics> node
            XmlNodeList analayticsNode = settingsXml.SelectNodes("//Analytics");

            //Ensure we found the <Analytics> node in the config
            if (analayticsNode != null)
            {
                //Loop over child nodes inside <Analytics> node
                foreach (XmlNode settingNode in analayticsNode)
                {
                    foreach (XmlNode setting in settingNode.ChildNodes)
                    {
                        //Go & populate our model from each item in the XML file
                        var settingToAdd            = new SettingsValue();
                        settingToAdd.Key            = setting.Name;
                        settingToAdd.Label          = setting.Attributes["label"].Value;
                        settingToAdd.Description    = setting.Attributes["description"].Value;
                        settingToAdd.Value          = setting.InnerText;

                        //Add the item to the list
                        settings.Add(settingToAdd);
                    }
                }
            }

            //Return the list
            return settings;
        }

        public List<SettingsValue> PostSettings(List<SettingsValue> settings)
        {
            //Update the XML config file on disk

            //Path to the settings.config file
            var configPath = HostingEnvironment.MapPath(ConfigPath);

            //Load settings.config XML file
            XmlDocument settingsXml = new XmlDocument();
            settingsXml.Load(configPath);

            //Get all child nodes of <Analytics> node
            XmlNodeList analayticsNode = settingsXml.SelectNodes("//Analytics");

            //Loop over child nodes inside <Analytics> node
            foreach (XmlNode settingNode in analayticsNode)
            {
                foreach (XmlNode setting in settingNode.ChildNodes)
                {
                    //Go & populate our model from each item in the XML file
                    setting.InnerText = settings.SingleOrDefault(x => x.Key == setting.Name).Value;
                }
            }

            //Save the XML file back down to disk
            settingsXml.Save(configPath);

            //Return the same settings that passed in
            return settings;
        }
    }
}

You can see the controller is nicely commented and should be easy to follow along in what it is doing, you will notice I have a custom model/object to store the list of items so it can be serialised easily to JSON for the GET & POST requests. The model is as follows:

namespace Analytics.Models
{
    public class SettingsValue
    {
        public string Key { get; set; }

        public string Label { get; set; }

        public string Description { get; set; }

        public string Value { get; set; }
    }
}

So with these parts in place we have a way with a WebAPI to fetch & post values to our XML config file. Next we need to create an Angular View, Angular Controller & Angular Resource file to consume this WebAPI.

<div ng-controller="Analytics.SettingsController">
    <div class="umb-pane">
        <umb-control-group ng-repeat="setting in settings" label="{{ setting.Label }}" description="{{ setting.Description }}">
            <input type="text" name="{{ setting.Key }}" id="{{ setting.Key }}" value="{{ setting.Value }}" ng-model="setting.Value" />
        </umb-control-group>

        <div class="umb-tab-buttons">
            <div class="btn-group">
                <button ng-click="save(settings)" data-hotkey="ctrl+s" class="btn btn-success">
                    <localize key="buttons_save">Save</localize>
                </button>
            </div>
        </div>

    </div>
</div>

This Angular HTML view loops over the settings JSON object and repeats the markup & input field for each setting to edit within the XML file with the ng-repeat Angular data attribute.

The next part is to look at the Angular Controller file that is wired up to this view.

angular.module("umbraco").controller("Analytics.SettingsController",

function($scope, settingsResource, notificationsService) {

    //Get all settings via settingsResource - does WebAPI GET call
    settingsResource.getall().then(function(response) {
        $scope.settings = response.data;
    });

    //Save - click...
    $scope.save = function(settings) {

        //Save settings resource - does a WebAPI POST call
        settingsResource.save(settings).then(function(response) {
            $scope.settings = response.data;

            //Display Success message
            notificationsService.success("Success settings have been saved");
        });
    };

});

You can see we have created a Resource to get the settings and to save the settings, which the resource file handles the GET & POST to the WebAPI controller. When the save button is clicked it POSTs the settings to the WebAPI controller and shows a nice success message with the notification service.

angular.module("umbraco.resources")
    .factory("settingsResource", function($http) {
    return {

        getall: function() {
            return $http.get("Analytics/SettingsApi/GetSettings");
        },

        save: function(settings) {
            return $http.post("Analytics/SettingsApi/PostSettings", angular.toJson(settings));
        }
    };
}); 

With all these parts in place we now have a nice generic way of displaying & saving values to an XML config file and allowing easy update to the UI if we decide to add in more settings at a later date.

I hope someone finds this useful & can take something away to learn from it.

Cheers,
Warren 🙂

Quick Tip: Add a new custom section to Umbraco 7+

Hello all,
I have just come across a quick, simple & neat way to add a section to your Umbraco website/application be it for your own use or as a package developer.

So rather than talk about it for ages I will just jump straight into it.

    public class UmbracoStartup : ApplicationEventHandler
    {
        protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            //Get SectionService
            var sectionService = applicationContext.Services.SectionService;

            //Try & find a section with the alias of "mySection"
            var mySection = sectionService.GetSections().SingleOrDefault(x => x.Alias == "mySection");

            //If we can't find the section - doesn't exist
            if (mySection == null)
            {
                //So let's create it the section
                sectionService.MakeNew("My Section", "mySection ", "icon-users");
            }
        }
    }

So what this is doing on an application startup of Umbraco using the new Section Service checking to see if there a section with the alias of mySection already created, if not then using the section service it goes and creates it.

That’s it, it really is that simple.
Enjoy 🙂

Using Umbraco Pipeline for Member Profile URLs

Hello all,
I wanted to share with you all a new approach I have learnt after reading up on the Umbraco Pipeline.

I originally asked the question over on our.umbraco.org on how best to achieve this and first came up with URL Rewriting to solve my solution, then the thread evolved and I was able to use a custom controller & route, but now I have found a much simpler & elegant way to deal with it, which is to use an iContentFinder class.

First lets start off with creating an custom iContentFinder which is used in the pipeline for trying to find an Umbraco node that matches the URL, if it doesn’t find it it gets passed onto the next iContentFinder and so on until it gets to the bottom which is the 404 iContentFinder.

using System;
using System.Linq;
using System.Web;
using umbraco.cms.businesslogic.member;
using Umbraco.Core;
using Umbraco.Web.Routing;

namespace MySite.BusinessLogic
{
    public class MemberProfileContentFinder : IContentFinder
    {
        public bool TryFindContent(PublishedContentRequest contentRequest)
        {
            var urlParts = contentRequest.Uri.GetAbsolutePathDecoded().Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
            
            //Check if the Url Parts
            // Starts with /member/*
            if (urlParts.Length > 1 && urlParts[0].ToLower() == "member")
            {
                //Lets try & find the member
                var memberName = urlParts[1];

                //Try and find a member where the property matches the memberName
                var tryFindMember = Member.GetAll.SingleOrDefault(x => x.getProperty("profileURL").Value.ToString() == memberName);

                //Need to set the member ID or pass member object to published content
                //Will pass a null to view if not found & can display not found member message
                HttpContext.Current.Items["memberProfile"] = tryFindMember;

                //Add in string to items - for profile name user was looking for
                HttpContext.Current.Items["memberName"] = memberName;

                //Set the Published Content Node to be the /Profile node - can get properties off it & my member profile in the view
                contentRequest.PublishedContent = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetByRoute("/view-profile");

                //Return true to say found something & stop pipeline & other contentFinder's from running
                return true;
            }

            //Not found any content node to display/match - so run next ContentFinder in Pipeline
            return false;
        }
    }
}

In this code you can see it is quite straight forward. It is getting the URL from the request and splitting the string on the URL parts such as http://mysite.co.uk/member/john into a string array so the variable urlParts will have two items in the array which are member and john.

We then next check that we have at least one URL part and that the first URL part in our array is equal to members, as we don’t want to run our logic if it does not match this URL fragment.

Next part is for us to get the second item in the array which is the member profile URL we want to look up. Using the Umbraco Member API we fetch all members and using a SingleOrDefault() Linq query we check to see if we can find a single member where the property memberProfileURL on the member matches that to our variable which contains the member URL to lookup.

Moving on we add the found member (even if it returns null, aka not found a member that has that profile URL) & the member profile URL to the HTTPContext Items so that we can retrieve them from our view.

The magic part of this is the following line:


//Set the Published Content Node to be the /Profile node - can get properties off it & my member profile in the view
contentRequest.PublishedContent = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetByRoute("/view-profile");

By setting PublishedContent on the contentRequest object, we can choose what node in the Umbraco content tree we want to render. In our case I am using the routing context to help get me the node based from it’s URL, so in this case I am fetching the node that matches the URL, /view-profile

The final part is that we return true, so that we let the pipeline know that we have a found a node and to stop running any further iContentFinders from being processed. By doing so we will load the view profile node and it’s template.

The next part of this is to now register our content finder in the application starting event with this lovely code snippet so it can run in our site.

using Umbraco.Core;
using Umbraco.Core.Persistence;
using Umbraco.Web.Routing;

namespace MySite.BusinessLogic
{
    public class UmbracoStartup : ApplicationEventHandler
    {
        protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            // Insert my MemberProfileContentFinder before ContentFinderByNiceUrl
            ContentFinderResolver.Current.InsertTypeBefore<ContentFinderByNiceUrl, MemberProfileContentFinder>();
        }
    }
}

Now that we have our custom iContentFinder registered by Umbraco, we just need to ensure our template/view for our view profile node uses the values in the HTTPContext.Items collection that we set inside the iContentFinder.

@using Member = umbraco.cms.businesslogic.member.Member
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
    Layout = "Master.cshtml";

    //Get the values out of the HTTPContext that we set in the iContentFinder
    var memberProfile   = (Member)HttpContext.Current.Items["memberProfile"];
    var memberName      = (string)HttpContext.Current.Items["memberName"];

    //Bit of string replacment on the noUserFound property on the View Profile node
    //Replace it with the member name the user was trying to look for
    var notFoundMessage = Model.Content.GetProperty("noUserFound").Value.ToString();
    notFoundMessage     = notFoundMessage.Replace("##user##", memberName);
}

<h2>View Profile</h2>

@if (memberProfile == null)
{
    //Display User Not Found Message
    //Do string replacement on copy ##user## with memberName
    @Html.Raw(notFoundMessage)
}
else
{
    //We found the member - so pass the member object to our partial
    //To display the member's details etc 🙂
    @Html.Partial("MemberProfile", memberProfile)
}

In this template/view you can see I am fetching the items out of the HTTPContext and casting it to a string for the profile URL name and the other as an Umbraco Member object so we can get intellisense.

The next part is that I am doing some string replacement on an field that I am fetching off the View Profile node for a user not found message.

I then simply display the not found message if the member is null, because the iContentFinder did not set a value for it. Otherwise we pass the member into our partial view where we can display specific information about our member, like member name, email, profile image & any other custom fields set on the member type.

So the final part of the puzzle is the Partial View

@inherits Umbraco.Web.Mvc.UmbracoViewPage <umbraco.cms.businesslogic.member.Member>
<h2>Member Profile Partial</h2>

<h3>Name: @Model.Text</h3>
<h3>Email: @Model.Email</h3>
<h3>Custom Field: @Model.getProperty("customField").Value</h3>

The only important thing to know here is that the first line is that I am setting the object type to be an Umbraco Member, which lets the model know its an Umbraco Member and gives us intellisense in Visual Studio when writing our partial view to display public information about our member.

That’s it, it’s really as simple as that.

Thanks to the Umbraco community & Stephane Gay for some help with the iContentFinder Pipeline stuff.