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”

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.

Visual Studio Extensions for Umbraco

Hello all,
I haven’t blogged for a little while, as I have been busy with my new pet project uHangout. If you haven’t checked it out yet I highly recommend you do, as it s a weekly Google Hangout that I record and publish on YouTube with guests from the Umbraco community demoing implementations, snippets, packages or just general interviews like I did with founder Niels Hartvig & Per Ploug

So my next little lunchtime hacks has been involved in working with extending Visual Studio to make our life’s easier when working with Umbraco. After recently coming across the project SideWaffle from Sayed Hashimi and Mads Kristensen from Microsoft, I was inspired by how they have added new file templates such as an AngularJS Controller and others.

So with this idea I approached Sayed for some advice on how I can create Umbraco file templates in the same way. I was given some great pointers and I am now able to present to you the

Umbraco Community – Visual Studio Project Item Templates package for Visual Studio

You can download the extension directly inside Visual Studio from the extensions section or on the Visual Studio Gallery website.

If you are intrigued on how it all works then the full source code is available on GitHub for you to look through as well.

But it doesn’t stop there!

As I created another Visual Studio package that adds a new project template to Visual Studio. So with one click you can do File -> New Project and select the new Umbraco project template. This will install and setup Umbraco for you by fetching the latest version of Umbraco from NuGet.

Now you will be able to impress your boss as you install & setup Umbraco so quickly.
Like before this extension can be found when searching inside Visual Studio for extensions and the gallery website but I have also put the source code of is extension on GitHub as well.

I have a few more ideas for Visual Studio Extensions to make our lives easier as Umbraco Developers, but I would love to hear what you think.

Cheers,
Warren 🙂