Nibble

Umbraco v7 trees, DefaultMenuAlias 0

Something you can do in trees created with a TreeController is define a default menu item. So instead of having the ability to choose an action it will default to the set action (like in content it will default to create).

To do this all you have to set is the DefaultMenuAlias property on your menu.

So in the example project I created I just add menu.DefaultMenuAlias = ActionDelete.Instance.Alias;

to make delete the default action on my tree items (it’s also the only option so it makes sense to default to it).

So now I don’t get the following screen anymore (the overview of available actions)

actions

but it will default to the delete action immedialty showing the delete dialog

default

If you stil wish to show the overview you can use nav.hideDialog(true) in your view

Extending the Umbraco backend Using Angularjs and WebAPI 0

After the example project I creating using MVC I thought it would be fun to show how you can now extend the Umbraco v7 backoffice with AngularJS and WepAPI. So in Umbraco v7 you have three options webforms, mvc or angularjs/webapi.

So I’ve just published a new project to github that does extactly that https://github.com/TimGeyssens/UmbracoAngularBackofficePages

image

I’l follow up this post with some more detailed ones describing the different steps

Umbraco Contour Masterclass 0

Better late then never! During the annual Umbraco conference codegarden I taught a workshop on Contour.

It contained the following things;

  • Modifying form markup (bootstrap markup)
  • Creating custom form elements (field types) 
  • Attaching extra functionality to a form (workflow types)
  • Connecting Contour to third party services (podio)
  • Hooking in custom validation rules
  • And making all these components as flexible as possible so you can deploy them across projects

So I just wanted to share the workbook and start solution for those that are interested in learning more about Contour.

Contour, adding custom validation without using code first 2

Just a quick post to share the answer to a question I got on the forum.

So as you can see in the code first examples, creating Contour forms code first also allows for an easy way to add extra (custom validation) http://www.nibble.be/?p=205

But there is also a way to add custom rules when you are designing the form in the UI.

There is an event you can hook into to add validation errors, a typical case for this would be a password and repeast password field where you need to make sure the input is the same.

image

So in the form designer these are just 2 password fields with no link

image

And to add the extra validation rules

    public class ContourValidation : ApplicationEventHandler
    {
        protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            FormRenderController.FormValidate += FormRenderController_FormValidate;
        }
 
        void FormRenderController_FormValidate(object sender, Umbraco.Forms.Mvc.FormViewEventArgs e)
        {
            if (e.Form.Name == "FormWithExtraValidation")
            {
                var s = sender as Controller;
                if (s != null)
                {
                    var pwf = e.Form.AllFields.SingleOrDefault(f => f.Caption == "Password").Id.ToString();
                    var rpwf = e.Form.AllFields.SingleOrDefault(f => f.Caption == "Repeat password").Id.ToString();
 
                    var password = e.Context.Request[pwf];
                    var repeatpassword = e.Context.Request[rpwf];
 
                    if(password != repeatpassword)
                        s.ModelState.AddModelError(rpwf, "passwords don’t match");
                    
                }
            }
        }
    }

So hook into the FormValidate event of the FormRenderController that will fire when a form validates

You can then add extra model errors to the modelstate

Have you secured your Umbraco BE festival ticket yet? 2

Join Me, colleagues from the HQ and a whole bunch of other Umbraco devs in Antwerp on April 25th for the second Umbraco BE festival!

It’s the perfect opportunity to learn all about Umbraco v7. At this point 10 out of 12 session have been confirmed http://umbracobefestival.be/sessions/ . Speakers from across Europe will share Umbraco tips and tricks. Responsive imaging, newsletters, extending the Umbraco v7 backoffice, e-commerce, automation, AngularJS and of course Mr Umbraco himself taking care of the keynote.

So check out the festival site and don’t wait to long to secure your ticket!

http://umbracobefestival.be/

Integrate Umbraco Contour with third party service, post to Podio App 1

Just a quick blog post with an example on how to attach a third party service to Contour (the Umbraco form builder).

You can attach extra functionality to a Contour form by setting up workflows, http://our.umbraco.org/projects/umbraco-pro/contour/documentation/Editor/Attaching-Workflows/

So by default a Contour form will store it’s data but if you want extra stuff (like sending an email) you can do that by adding a workflow..

There are several out of the box workflows you can use…now in my case I wanted to extend Contour to post it’s record date to a podio app

So first of all I need a podio API key, you can get that here https://developers.podio.com/

Once that is settled we can use the API, there is even a .net library available https://developers.podio.com/clients/dotnet

Next of course is to setup an app in podio, that’s pretty easy with the app builder,

So I just created a new app that has a single item and that item has a single text block (that’s the text block we’ll populate with contour record data)

image

Now I’m all set the create the workflow, here is the code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using System.Xml.XPath;
using Podio.API;
using Podio.API.Model;
using Podio.API.Utils.ItemFields;
using Umbraco.Forms.Core;
using Umbraco.Forms.Core.Enums;
using Umbraco.Forms.Data;
using Umbraco.Forms.Data.Storage;
 
namespace Contour.Addons.Podio
{
    public class AddRecordToPodioSpace : WorkflowType
    {
        [Umbraco.Forms.Core.Attributes.Setting("Client id")]
        public string ClientId { get; set; }
        [Umbraco.Forms.Core.Attributes.Setting("Client secret ")]
        public string ClientSecret { get; set; }
        [Umbraco.Forms.Core.Attributes.Setting("UserName")]
        public string UserName { get; set; }
        [Umbraco.Forms.Core.Attributes.Setting("Password")]
        public string Password { get; set; }
        [Umbraco.Forms.Core.Attributes.Setting("AppId")]
        public string AppId  { get; set; }
        [Umbraco.Forms.Core.Attributes.Setting("PodioTextField")]
        public string PodioTextFieldId { get; set; }
 
        public AddRecordToPodioSpace()
        {
            this.Id = new Guid("55517EB2-8D61-483C-A3DA-7850FCE05996");
            this.Name = "Send record to podio space";
            this.Description = "Send record to podio space";
        }
 
        public override List<Exception> ValidateSettings()
        {
            List<Exception> l = new List<Exception>();
            if (string.IsNullOrEmpty(ClientId))
                l.Add(new Exception("’ClientId’ setting has not been set"));
 
            if (string.IsNullOrEmpty(ClientSecret))
                l.Add(new Exception("’ClientSecret’ setting has not been set’"));
 
            if (string.IsNullOrEmpty(UserName))
                l.Add(new Exception("’UserName’ setting has not been set’"));
 
            if (string.IsNullOrEmpty(Password))
                l.Add(new Exception("’Password’ setting has not been set’"));
 
            return l;
        }
 
        public override WorkflowExecutionStatus Execute(Record record, RecordEventArgs e)
        {
 
            try
            {
 
                RecordsViewer viewer = new RecordsViewer();
                XmlNode xml = viewer.GetSingleXmlRecord(record, new System.Xml.XmlDocument());
 
                XPathNavigator navigator = xml.CreateNavigator();
 
                XPathExpression selectExpression = navigator.Compile("//fields/child::*");
                selectExpression.AddSort("@pageindex", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number);
                selectExpression.AddSort("@fieldsetindex", XmlSortOrder.Ascending, XmlCaseOrder.None, "",
                    XmlDataType.Number);
                selectExpression.AddSort("@sortorder", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number);
 
                XPathNodeIterator nodeIterator = navigator.Select(selectExpression);
 
                string list = "<dl>";
 
                while (nodeIterator.MoveNext())
                {
 
                    list += "<dt><strong>" +
                            Umbraco.Forms.Data.DictionaryHelper.GetText(
                                nodeIterator.Current.SelectSingleNode("caption").Value) + ": </strong><dt><dd>";
 
                    XPathNodeIterator values = nodeIterator.Current.Select(".//value");
 
                    while (values.MoveNext())
                        list +=
                            Umbraco.Forms.Data.DictionaryHelper.GetText(values.Current.Value.Trim())
                                .Replace("\n", "<br/>") + "<br/>";
 
                    list += "</dd>";
 
                }
 
                list += "</dl>";
 
                Client client = Client.ConnectAsUser(ClientId,
                    ClientSecret,
                    UserName,
                    Password);
 
                Item i = new Item();
                var textField = i.Field<TextItemField>(PodioTextFieldId);
                textField.ExternalId = PodioTextFieldId; 
                textField.Value = list;
                i.Fields.Add(textField); 
 
                client.ItemService.AddNewItem(Convert.ToInt32(AppId), i);
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex.Message);
                return WorkflowExecutionStatus.Failed;
            }
 
            return WorkflowExecutionStatus.Completed;
        }
    }
}

So all the settings I need for podio are public properties marked with the settings attribute (so I can set those up when inserting the workflow)

THen the actuall workflow code will loop through all the record data and create html list. So you see it doesn’t matter how many fields you have it will pass all of them, this code is actually borrowed from the default send email workflow code, you can get all default providers code here http://our.umbraco.org/projects/umbraco-pro/contour/ (sourcecode tab at the bottom)

THen it wil make use of the podio api to create a new item and add it …

When I know deploy this to the site, I see a new workflow type

image

When choosing that type I can setup the settings

image

So when the settings are valid and the workflow is attached the form will now post to podio when it’s submitted (if workflow is attached to submitted stage)

image

So now I have a reusable component that I can deploy whenever I need a form that needs to integrate with podio!

Advanced property editor for Umbraco v7, single file deployment 3

One of the community packages that is in progress of being migrated to v7 is uComponents, as you may know the current v4/v6 version is compiled into a single assembly so it’s easy to deploy and to update (since it’s just a single file).

As a test I wanted to see if that’s also possible with v7 prop editors (so embed the views and controllers in the assembly).

If you have seen some of the v7 prop editor examples out there you know that to define your extra components you’ll need to create a manifest file http://umbraco.github.io/Belle/#/tutorials/manifest (no more need for vs)

You can also define your prop editors in code (all the core ones are defined like that) just take a look at https://github.com/umbraco/Umbraco-CMS/tree/7.0.2/src/Umbraco.Web/PropertyEditors 

So if I take the char limit example http://www.nibble.be/?p=285 the manifest file becomes

 [PropertyEditor("Example", "Example Editor", "/App_Plugins/Example/Resource/editor.html", ValueType = "TEXT")]
    [PropertyEditorAsset(ClientDependencyType.Javascript, "/App_Plugins/Example/Resource/controller.js")]
    public class ExamplePropEditor : PropertyEditor
    {
        protected override PreValueEditor CreatePreValueEditor()
        {
            return new ExamplePreValueEditor();
        }
 
        internal class ExamplePreValueEditor : PreValueEditor
        {
            public ExamplePreValueEditor()
            {
                //create the fields
                Fields.Add(new PreValueField()
                {
                    Description = "Enter the number of chars to limit on",
                    Key = "limit",
                    View = "requiredfield",
                    Name = "Number of chars"
                });
                
            }
        }
    }

 

THe editor and controller is still the same but in vs make sure to set the build action of the js and html file to embedded resource

image

Final bit is getting the controller and view out of the assembly and this is done with a simple controller

   public class EmbeddedController : Controller
    {
        public FileStreamResult Resource(string id)
        {
            var resourceName = Assembly.GetExecutingAssembly().GetManifestResourceNames().ToList().FirstOrDefault(f => f.EndsWith(id));
 
            var a = typeof(EmbeddedController).Assembly;
 
            return new FileStreamResult(a.GetManifestResourceStream(resourceName), GetMIMEType(id));
        }
 
        private string GetMIMEType(string fileId)
        {
            if (fileId.EndsWith(".js"))
            {
                return "text/javascript";
            }
            if (fileId.EndsWith(".html"))
            {
                return "text/html";
            }
            if (fileId.EndsWith(".css"))
            {
                return "text/stylesheet";
            }
            return "text";
        }
       
    }

Of course since it’s a normal mvc controller it isn’t auto routed and I need to setup the routing

    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
 
            routes.MapRoute(
                name: "Example",
                url: "App_Plugins/Example/{action}/{id}",
                defaults: new { controller = "Embedded", action = "Resource", id = UrlParameter.Optional }
            );
        }
    }

 

And make sure that the route get’s registered when the application starts

 public class StartUpHandlers : ApplicationEventHandler
    {
        protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
    }

So now we can fetch the embedded resource by requesting

/App_Plugins/Example/Resource/resourcefilename.extension

like

/App_Plugins/Example/Resource/editor.html

And that’s it, build and just deploy the assemby to the umbraco v7 instance

Complete source of this example is available here https://github.com/TimGeyssens/EmbeddedPropEditorExample

Analytics for Umbraco 7 1

Led by Warren Buckley and in collab with Anders Bjerner we released a new package for Umbraco v7 today!

Analytics http://our.umbraco.org//projects/backoffice-extensions/analytics

The package adds a new section to your Umbraco v7 installation which allows you to view your Google Analytics statistical information directly inside Umbraco. So it’s easy for content editors, site owners to view stats without having to move to a google account and the google analytics site.

Of course your site needs to have a google analytics profile and it needs to have the necessary script includes to gather stats. The package will simply interact with the google analytics API to fetch data and display those in the Umbraco backoffice.

Install

Just get it from http://our.umbraco.org//projects/backoffice-extensions/analytics and go to developer/packages/install local package in your Umbraco backoffice

image

Then follow the installation wizard and as a final step you should see this screen

image

Then use the button to move to the new section.

Setup

Make sure to clear your cache if you are getting 404 error messages and then hit f5 (v7 needs some good cache busting)

Next you’ll see the settings screen where you need to authorize against google analytics and select the correct profile( again the package won’t gather stats that is done by google analytics so make sure that is setup for the site first).

image

So hit the Authorise button and you should see a new window popup

image

Login with google and then authorize the application (this will give it access to the stats)

Once that is done you should get a confirm message

image

Closing that will reload the settings screen and allow you to choose an account and profile

So select and then save the settings

image

Usage

Once the profile is selected you can view stats Smile

Dashboard will show some stats sfrom latest 7 days

image

Fur further stats, just select something from the tree, on those detailed stats you can also specify a date range (that will be remembered when moving between stats)

image

So that’s a quick intro, make sure to give it a try http://our.umbraco.org//projects/backoffice-extensions/analytics

Config tree On Umbraco v7 0

Another package ready for umbraco v7 (well actually thanks to the awesome work by Per and Shannon it already worked on v7 it just needed some super small UI updates, updating tree icon and updating save button)

ConfigTreeV7

Also added ctrl-s support for saving the file

http://our.umbraco.org/projects/developer-tools/config-tree

Umbraco BE Festival, 25Th April 2014 0

After the successful BE Festival in 2011, the Belgian Umbraco User Group(buug.be) is throwing another edition.

They have some great speakers lined up including Umbraco founder Niels Hartvig and other speakers from DK,UK,SE,NL,FR and BE.

For full details please go to http://www.buug.be/en/events/umbraco-festival-2014 and get your ticket today!

5536568903_15ce073856_b

Next Page »