Nibble

Archive for December, 2010

UsercontrolWrapper + DataEditorSettings = super simple custom umbraco datatypes (with settings) 4

Umbraco 4.6 (Juno) introduces the data editor settings which make it a lot easier to create datatypes with custom settings (including datatypes created with the usercontrol wrapper method).

Since on this blog I have several posts about creating datatypes (using the various methods), I thought I’d also show how easy this has become in umbraco Juno, since you can combine the usercontrolwrapper with dataeditorsettings.

I’m creating the same datatype again, a textarea that has a limit on the number of characters, you can set the limit on the datatype settings

Sourcecode (including using statements it’s just 35 lines of code):

using System;
using System.Web;
using umbraco.cms.businesslogic.datatype;
 
namespace DataEditorSettings.UserControlWrapperAndDataEditorSettings
{
    public partial class CharLimitUserControl1 : System.Web.UI.UserControl,
    umbraco.editorControls.userControlGrapper.IUsercontrolDataEditor  
    {
        [DataEditorSetting("Limit")]
        public string Limit { get; set; }
 
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                CharLimit.Limit = Limit;
                if (!string.IsNullOrEmpty(_umbracoValue))
                    CharLimit.Text = _umbracoValue;
            }
        }
 
        private string _umbracoValue;
        public object value {
            get
            {
                return CharLimit.Text;
            }
            set
            {
                _umbracoValue = value.ToString();
            }
        }
    }
}

 

Datatype editor (usercontrol specific settings will load once a usercontrol is selected and save is hit):

image

Datatype in action on a content document:

image

Creating custom umbraco Data Editor Setting Types 2

Umbraco 4.6 (Juno) makes it super easy to create custom datatypes with settings (even ones created with the usercontrol wrapper) with the introduction of data editor settings. By default there are 21 types you can use out of the box (textbox, content picker, path picker, … ) and off course it’s also possible to plug in third party types let’s take a look at an example:

This will output a dropdownlist that let’s you choose between the available connectionstrings ( found in the configuration / connectionStrings part of the web.config)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using umbraco.cms.businesslogic.datatype;
using System.Web.UI.WebControls;
using System.Configuration;
 
namespace UmbracoJunoTest
{
    public class ConnectionStringPicker : DataEditorSettingType
    {
        private DropDownList ddl = new DropDownList();
 
        private string _val = string.Empty;
        public override string Value
        {
            get
            {
                return ddl.SelectedValue;
            }
            set
            {
                if (!string.IsNullOrEmpty(value))
                    _val = value;
            }
        }
 
        public override System.Web.UI.Control RenderControl(DataEditorSetting setting)
        {
            ddl.ID = setting.GetName();
 
            ddl.Items.Clear();
 
            for(int i = 0; i< ConfigurationManager.ConnectionStrings.Count; i++)
            {
                ddl.Items.Add(
                    new ListItem(
                        ConfigurationManager.ConnectionStrings[i].Name, 
                        ConfigurationManager.ConnectionStrings[i].ConnectionString));
                
            }
 
            ddl.Items.Insert(0, new ListItem(String.Empty, String.Empty));
 
            ddl.SelectedValue = _val;
 
            return ddl;
        }
    }
}

Simply inherit from DataEditorSettingType (found in the umbraco.cms.businesslogic.datatype namespace) and override the value property and the RenderControl method.

Now to use this type as a setting on your custom datatype, simply mark a property with the DataEditorSettings attribute and set to type to the created data editor setting type

[DataEditorSetting("Connectionstring",
            type= typeof(UmbracoJunoTest.ConnectionStringPicker))]
        public String ConnectionString { get; set; }

 

The result:

image

With this being in the web.config

<connectionStrings>
  
  <add name="Sample"  connectionString="server=.\sqlexpress;database=aspnetdb;user id=
DBUSER;password=DBPASSWORD" providerName="System.Data.SqlClient"/>
 
 <add name="AnotherSample"  connectionString="server=.\sqlexpress;database=aspnetdb;user id=
DBUSER;password=DBPASSWORD" providerName="System.Data.SqlClient"/>
  
 
</connectionStrings>

Xslt and RestExtensions in Umbraco 4.6 (Juno) 11

Umbraco 4.6 Beta (Juno) went out a couple of days ago, this is just a quick post highlighting one of the improvements.

As you may know if you want to register an xslt or rest extension you’ll need to do this in a config file (config/xsltextensions.config and /config/restextensions.config) but now with umbraco 4.6 you’ll be able to do this with some attributes instead.

So no more messing around with the config files(although they still work) but simply add a reference to the umbraco assembly and then you’ll be able to use the XsltExtension, RestExtension and RestExtensionMethod attributes to mark your extensions.

using System;
using umbraco;
using umbraco.presentation.umbracobase;
 
namespace UmbracoJunoTest
{
    [RestExtension("TestAlias")]
    [XsltExtension]
    public class Test
    {
        [RestExtensionMethod]
        public static string HelloWorld()
        {
            return "Hello World";
        }
    }
}

 

You can either place the .cs file in the App_Code folder of your umbraco installation or build the solution and place the assembly in the bin directory.

Want to give it a try ? Download Umbraco 4.6 Beta

umbraco page flow example 4

Instead of having a previous and next link when you want to add pagination to a list of items I wanted to try to load in the next page when the users scroll down to the latest item.

It’s actually pretty easy todo thanks to a jQuery plugin jqPageFlow, that takes care of fetching the records when the user scrolls down.

What the plugin needs is just a page where it can fetch the records (and it passes the page number as a querystring parameter)

So, all I need was a little xslt macro (very similar to the pagination one) and a alternative template (that is used in the background to fetch the items).

The xslt:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ &lt;!ENTITY nbsp "&#x00A0;"> ]>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:umbraco.contour="urn:umbraco.contour"
  exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets umbraco.contour ">
 
<xsl:output method="xml" omit-xml-declaration="yes"/>
 
<xsl:param name="currentPage"/>
 
    <xsl:variable name="records"
      select="$currentPage/* [@isDoc and string(umbracoNaviHide) != ‘1′]" />
    
    <xsl:variable name="recordsCount"
      select="count($records)" />
    
    <xsl:variable name="recordsPerPage"
      select="4"/>
    
    
    <xsl:variable name="pageNumber">
      <xsl:choose>
        <xsl:when test="umbraco.library:RequestQueryString(’p')">
           <xsl:value-of select="umbraco.library:RequestQueryString(’p')"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="0"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    
    
<xsl:template match="/">
 
<xsl:for-each select="$records">
  
  <xsl:if test="position() &gt; $recordsPerPage * (number($pageNumber)) and
                position() &lt;= number($recordsPerPage * (number($pageNumber)) + $recordsPerPage)">
                  
                  <xsl:apply-templates select="." />
  </xsl:if>
 
</xsl:for-each>
 
  <xsl:if test="$pageNumber = 0">
  <script type="text/javascript">
      $(document).ready(function(){
      
        $("body").flexiPagination({
           url: "/news/pagefeed.aspx",
           currentPage: 0,
           totalResults: <xsl:value-of select="$recordsCount"/>,
           perPage: <xsl:value-of select="$recordsPerPage"/>,
           container: "#mycontainer",
           loadcontainer: "#loadcontainer",
           pagerVar : "p",
           loaderImgPath: "images/loader.gif",
           debug : 0
        });
      });
    </script>
  </xsl:if>
  
</xsl:template>
    
<xsl:template match="*">
 
    <li>
      <a href="{umbraco.library:NiceUrl(@id)}">
        <xsl:value-of select="@nodeName"/>
      </a>
      <br/>
      <p>
        <xsl:value-of select="content"/>
      </p>
  </li>
  
</xsl:template>
  
 
</xsl:stylesheet>

How the macro is placed on the template:

 <ul id="mycontainer">
  <umbraco:Macro Alias="PaginationFlow" runat="server"></umbraco:Macro>
  </ul>
  <div id="loadcontainer">
  </div>

 

Contents of the altTemplate named pagefeed (basicly it’s just the macro):

<%@ Master Language="C#" MasterPageFile="~/umbraco/masterpages/default.master" AutoEventWireup="true" %>
 
<asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server">
 <umbraco:Macro Alias="PaginationFlow" runat="server"></umbraco:Macro>
</asp:Content>


Final result looks like this (loading happens pretty fast, so might be hard to see)

Usercontrol wrapper will also support Dataeditorsettings 2

Coming in umbraco Juno, custom settings for your datatype when using the usercontrolwrapper method

How? Also by using the DataEditorSetting attribute and marking a public property with it…

How does this look:

.ascx file:

<%@ Control Language="C#" AutoEventWireup="true" 
    CodeBehind="Demo.ascx.cs" 
    Inherits="UmbracoCreateCustomDatatypeWithWrapper.Demo" %>
 
<asp:DropDownList ID="control" runat="server">
</asp:DropDownList>

.ascx.cs file (code behind)

using System;
using System.Data;
using System.Web;
using System.Web.UI.WebControls;
using umbraco.cms.businesslogic.datatype;
 
 
namespace UmbracoCreateCustomDatatypeWithWrapper
{
    public partial class Demo : 
        System.Web.UI.UserControl,
        umbraco.editorControls.userControlGrapper.IUsercontrolDataEditor  
    {
       
        [DataEditorSetting("Connection string")]
        public string ConnectionString { get; set; }
 
        [DataEditorSetting("Select statement")]
        public string SelectStatement { get; set; }
 
        [DataEditorSetting("Text column")]
        public string TextColumn { get; set; }
 
        [DataEditorSetting("Value column")]
        public string ValueColumn { get; set; }
 
        public string umbracoValue;
 
        protected void Page_Load(object sender, EventArgs e)
        {
            if (Page.IsPostBack)
            {
                //onsave
                value = control.SelectedValue;
            }
            else
            {
                control.DataSource =
                    Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteReader(
                    ConnectionString,
                    CommandType.Text,
                    SelectStatement);
 
                control.DataTextField = TextColumn;
                control.DataValueField = ValueColumn;
                control.DataBind();
 
                control.Items.Insert(0, new ListItem(String.Empty, String.Empty));
 
                control.SelectedIndex = 0;
 
                if (value != null)
                    control.SelectedValue = value.ToString();
            }
        }
 
        public object value
        {
            get
            {
                return umbracoValue;
            }
            set
            {
                umbracoValue = value.ToString();
            }
        }
    }
}

 

The datatype editor (custom settings will be visible after selecting the usercontrol):

image

The datatype in action:

image

Contour, disabling the auto resume feature 3

If you have a multistep form in Contour and the end users doesn’t fill in all the steps, the next time he visits the form he can simply continue where he stopped…

Currently it isn’t possible to disabled this resume funtionality in the UI but it’s pretty easy to do, it’s just a matter of removing a cookie.

Here is a snipped you can use (just make sure to set the correct form guid):

<script runat="server">
  protected override void OnLoad(EventArgs e){
 
            if (!IsPostBack)
 
            {
                int pageId = umbraco.presentation.nodeFactory.Node.GetCurrent().Id;
                string formId = "204188fc-509e-43e2-b04e-dcfde2e62158";
               
                if (Request.Cookies["contour_"+pageId+"_" +formId] != null)
                {
                    var contourCookie = Request.Cookies["contour_"+pageId+"_" +formId];
                    contourCookie.Expires = DateTime.Now.AddDays(-1d);
                    Response.Cookies.Add(contourCookie);
 
                    Response.Redirect(Request.Url.ToString());
                }
            }
 
        }
</script>

Database driven dropdownlist datatype using DataEditorSettings 1

Just a quick post showing the impact of the DataEditorSettings attribute coming in umbraco Juno (making creating datatypes with custom settings super easy). If you attended the level 2 course this will look familiar, since in the course you learn how to create custom datatypes with the usercontrolwrapper, by making a dropdown that populates from a 3rd party db. Since with the usercontrolwrapper method you don’t have a way of setting up settings for your datatype you’ll basically hardcode the connectionstring, select statement, … Now in this example I also create a dropdown that can be populated with data from a db and in the setting of the datatype you simply set the connectionstring, select statement, text column (what will be shown in the dropdown) and value column (what will be saved).

Settings editor:

image

Datatype in action:

image

Complete sourcecode:

using System;
using System.Data;
using System.Web.UI.WebControls;
using umbraco.cms.businesslogic.datatype;
 
namespace DataEditorSettings.Demo
{
    public class DbDrivenDropdown: AbstractDataEditor
    {
        private DropDownList control = new DropDownList();
 
        [DataEditorSetting("Connection string")]
        public string ConnectionString { get; set; }
 
        [DataEditorSetting("Select statement")]
        public string SelectStatement { get; set; }
 
        [DataEditorSetting("Text column")]
        public string TextColumn { get; set; }
 
        [DataEditorSetting("Value column")]
        public string ValueColumn { get; set; }
 
        public override Guid Id
        {
            get{return new Guid("833D755A-0A3C-4C06-8070-CA5E4BB9F68C");}
        }
 
        public override string DataTypeName
        {
            get{return "Db driven dropdown";}
        }
 
        public DbDrivenDropdown()
        {
            base.RenderControl = control;
            control.Init += 
                new EventHandler(control_Init);
            base.DataEditorControl.OnSave += 
                new AbstractDataEditorControl.SaveEventHandler(DataEditorControl_OnSave);
        }
 
        void DataEditorControl_OnSave(EventArgs e)
        {
            base.Data.Value = control.SelectedValue;
        }
 
        void control_Init(object sender, EventArgs e)
        {
       
            control.DataSource =
                    Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteReader(
                    ConnectionString,
                    CommandType.Text,
                    SelectStatement);
            control.DataTextField = TextColumn;
            control.DataValueField = ValueColumn;
            control.DataBind();
 
            control.Items.Insert(0, new ListItem(String.Empty, String.Empty));
            control.SelectedIndex = 0;
 
            if (base.Data.Value != null)
                control.SelectedValue = base.Data.Value.ToString();
        }
    }
}