Navigatie

Contact

Send mail to the author(s) E-mail

View Richard Soeteman's profile on LinkedIn

RSS 2.0 | Atom 1.0 | CDF

Archief

Categorieën

Blogroll

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Sign In

Zoeken

# Tuesday, December 20, 2011
Tuesday, December 20, 2011 1:57:00 PM (GMT Standard Time, UTC+00:00) ( MemberExport | Package | Umbraco )

I’ve just released MemberExport 2.0. Apart from another awesome logo by Arnold Visser.I’ve added some functionality as well.

memberexport100x100

Native Excel export

MemberExport 1.x could only export to a csv file. This is great for most of us but when you select a wrong separator and want to open the csv file in Excel directly it will probably looks a bit messy like the example below.

wrongformat

MemberExport 2.0 Pro let’s you select the export option csv or Excel file. When you select Excel file it will export the data to an Excel file instead of csv and all the data is formatted correctly.

image

Export provider

I don’t like closed systems, all of my packages are open for extension. When creating the Excel export option I wanted the export option to be a provider model. Not only to make this task easier for myself but also so you can use MemberExport to connect to your custom ERP system. Implementing an export provider is pretty straight forward. The example below is the implementation of the CSV Provider.

CSVProviderUI

The CSVProviderUI class is the class which is responsible for the UI part of the provider. Add a reference to the MemberExport.Library dll and derive from ExportProviderUIBase class. Implement to following methods:

  • OnInit, create the UI controls
  • Initialize, initialize the UI options based on the stored export provider settings. In this case exportProvider will hold the text delimiter and text indicator options.
  • GetOptions will return the export provider settings so we use those settings when exporting records
  • The name property will return the name of the provider stored in the Export as dropdownlist when selecting an export provider.
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using MemberExport.Resources.Helpers;
using umbraco.uicontrols;
 
namespace MemberExport.Library.ExportProviders.CSV
{
    /// <summary>
    /// Renders the Layout for the CSV Provider
    /// </summary>
    public class CSVProviderUI : ExportProviderUIBase, INamingContainer
    {
        protected DropDownList _csvTextIndicatorDropDownList = new DropDownList();
        protected DropDownList _csvSVSeperatorDropDownList = new DropDownList();
 
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
 
            _csvSVSeperatorDropDownList.Items.Add(new ListItem(";", ";"));
            _csvSVSeperatorDropDownList.Items.Add(new ListItem(",", ","));
 
            _csvTextIndicatorDropDownList.Items.Add(new ListItem(ResourceHelper.Current.GetStringResource("CSVNoTextIndicator"), ""));
            _csvTextIndicatorDropDownList.Items.Add(new ListItem("\"", "\""));
            _csvTextIndicatorDropDownList.Items.Add(new ListItem("\'", "\'"));
 
            PropertyPanel delimiterPropery = new PropertyPanel();
            delimiterPropery.Text = ResourceHelper.Current.GetStringResource("CSVSeperatorProperty");
            delimiterPropery.Controls.Add(_csvSVSeperatorDropDownList);
 
            PropertyPanel stringIndicatorPropery = new PropertyPanel();
            stringIndicatorPropery.Text = ResourceHelper.Current.GetStringResource("CSVTextIndicatorProperty");
            stringIndicatorPropery.Controls.Add(_csvTextIndicatorDropDownList);
 
            Controls.Add(delimiterPropery);
            Controls.Add(stringIndicatorPropery);
        }
 
        /// <summary>
        /// Sets the export options (delimiter and string indicator).
        /// </summary>
        /// <param name="exportProvider">The export provider.</param>
        public override void Initialize(ExportProviderBase exportProvider)
        {
            CSVProvider csvProvider = exportProvider as CSVProvider;
            if (csvProvider != null)
            {
                _csvSVSeperatorDropDownList.SelectedValue = csvProvider.Delimiter;
                _csvTextIndicatorDropDownList.SelectedValue = csvProvider.StringIndicator;
            }
        }
 
        /// <summary>
        /// Returns the selected export options (delimiter and string indicator).
        /// </summary>
        /// <returns></returns>
        public override ExportProviderBase GetOptions()
        {
            CSVProvider csvProvider = new CSVProvider();
            csvProvider.Delimiter = _csvSVSeperatorDropDownList.SelectedValue;
            csvProvider.StringIndicator = _csvTextIndicatorDropDownList.SelectedValue;
            return csvProvider;
        }
 
        /// <summary>
        /// Return the name of the provider. this will be displayed in the selectbox
        /// </summary>
        /// <value>The selected name.</value>
        public override string Name
        {
            get { return "CSV file"; }
        }
    }
}

CsvProvider

The second part we need implement is the provider itself which handles the actual export process. Create a new class and derive from ExportProviderBase.  This will give the following methods/properties:

  • Export, responsible for the export process
  • Icon, will be displayed when opening the saved exports tree

In this implementation we’ve added the StringIndicator and Delimiter options, which gets set from the UI. The export method will give a list of columns to export and all records using an IRecordReader.  The rest of the code is implementation specific. Two important things to know when exporting records.  When assigning a value always use the ValueParser factory

ValueParser.Parse(reader.GetObject(column.Alias), column));

To write the file to the browser either use the WriteResponseString (for stringvalues ) or WriteResponseBytes (for a byte array) methods. Both methods need the Contenttype (Mimetype) and fileExtension, in this case csv.

using System;
using System.Collections.Generic;
using MemberExport.Library.Csv;
using MemberExport.Library.Members;
using MemberExport.Library.Types;
 
namespace MemberExport.Library.ExportProviders.CSV
{
    /// <summary>
    /// CSV (Default) implementation of MemberExport
    /// </summary>
    [Serializable()]
    public class CSVProvider : ExportProviderBase
    {
        /// <summary>
        /// Exports the data to a csv file
        /// </summary>
        /// <param name="columns">Collection of column info</param>
        /// <param name="reader">The actual data to export</param>
        public override void Export(List<Types.MemberField> columns, umbraco.DataLayer.IRecordsReader reader)
        {
            //Use a csv writer object
            CsvWriter writer = new CsvWriter(Delimiter, StringIndicator);
 
            //Add columns to export
            foreach (MemberField field in columns)
            {
                writer.Columns.Add(field.Text);
            }
 
            //Add data to export
            while (reader.Read())
            {
                //Get values for the current row
                List<object> values = new List<object>();
                foreach (MemberField column in columns)
                {
                    values.Add(ValueParser.Parse(reader.GetObject(column.Alias), column));
                }
 
                //write values to csvwriter;
                writer.Add(values.ToArray());
            }
            //Write response to the browser
            WriteResponseString(writer.Parse(), "application/excel", "csv");
        }
 
        /// <summary>
        /// Gets or sets the string indicator.
        /// </summary>
        /// <value>The string indicator.</value>
        public string StringIndicator { get; set; }
 
        /// <summary>
        /// Gets or sets the delimiter.
        /// </summary>
        /// <value>The delimiter.</value>
        public string Delimiter { get; set; }
 
        /// <summary>
        /// Returns the icon that will be displayed in the saved tree
        /// </summary>
        /// <value>The icon.</value>
        public override string Icon
        {
            get
            {
                return "csvexport.gif";
            }
        }
    }
}

That’s all, compile and add to the bin folder of your Umbraco install and you can use the provider!

ValueParsers

By default MemberExport exports the stored value from  the database. When this value is an Id value you might want to export the text value instead. This can be achieved using a value parser. By default MemberExport comes with value parsers for the following datatypes :

  • Checkbox
  • Dropdownlist
  • MNTP
  • MuiltipleDropdownlist
  • Radiobox
  • Ultimatepicker

In the example below we will write a value parser for the True/false datatype. By default values will be exported as 1/0. We want the values to be exported as yes/no for this example.

Before you ask, this isn’t in the core because some people want 0/1, some true/false, some yes/no etc.

First we need to add a reference to the MemberExport.Library dll. Then we can create a class that implements the IValueParser interface

  public class TrueFalseParser : IValueParser
    {
        /// <summary>
        /// Returns the Datatype GUID of the true/false Render Control.
        /// </summary>
        public Guid DataTypeId
        {
            get { return new Guid("38b352c1-e9f8-4fd8-9324-9a2eab06d97a"); }
        }
 
        /// <summary>
        /// Converts the 0/1 to a normal yes/no string.
        /// </summary>
        /// <param name="memberfieldInfo">The memberfield info.</param>
        /// <param name="value">The value.</param>
        /// <returns>The yes/no string</returns>
        public object Parse(Library.Types.MemberField memberfieldInfo, object value)
        {
            return string.Format("{0}", value) == "1" ? "yes" : "no";
        }
    }

The DataTypeId property needs to return the GUID which is displayed on the datatype edit screen.

image

The Parse method will be called when a true/false property value is exported. In the above example we transform the value to yes/no.
The following information about the exported field is available in the memberFieldInfo variable:

  • DataTypeId. The GUID which is displayed on the datatype edit screen
  • DatatypeNodeId. The node id  of the Datatype.
  • Alias. The Property alias.
  • Text. The Property text.

To use the Value Parser all we need to compile the project and add the dll to the bin folder of the Umbraco install. Then it will be picked up automatically and values will be exported as yes/no. In the example below the Active column is exported as true/false value using the Value Parser.

image

Download and install

You can download the MemberExport package from the Umbraco Deli. Just install the package, also when you want to update your current version. When you are a Pro user make sure the license file is still in your bin folder to unlock the Pro features.

Happy exporting!

Comments [0] | | # 
# Tuesday, November 15, 2011
Tuesday, November 15, 2011 4:05:32 PM (GMT Standard Time, UTC+00:00) ( CMSImport | Umbraco )

A question I get a lot is how to solve date formatting issues in CMSImport. Unless you are using SQL Server all imported values are of type string. So when you try to import date values the data layer will try to convert the value to a DateTime object, if it can’t parse the string you will see an error. These errors can be solved. Let’s take the following example from a Wordpress export file that can fail.

<wp:post_date>2011-11-03 17:29:18</wp:post_date>

When mapping the value in CMSImport you can select the advanced mapping option, this will show the following mapping options if you map against a DateTime Property. As you can see you can specify a date format. Check the custom date formatting section for the exact formatting syntax. When you specify a format that exactly matches the string from your datasource(TIP use the refresh option to update the preview) CMSImport will use that format to convert the string to the DateTime object and all will import fine.

clip_image002

Hope this helps you!

Comments [0] | | # 
# Tuesday, November 08, 2011
Tuesday, November 08, 2011 10:16:29 AM (GMT Standard Time, UTC+00:00) ( Package | Umbraco )

Last week Matt Brailsford released uGoLive. uGoLive is a package with essential checks you need to do before your website goes live.Test includes checking for debug and trace mode, removing install directory etc. The great thing about uGoLive is that you can easily integrate checks into uGoLive. In fact you can already use or extend the uGoLive contrib project. But you can also integrate uGoLive into your own package. Extending uGoLive is very well documented,  I’ve just integrated uGoLive into the SEO Analyzer package I am working on.

SeoAnalyzer uGoLive

It’s all about plugin architecture

The thing I like most about Umbraco and most of the available packages is that is uses a plugin architecture. Plugin architecture means that Umbraco or a package defines an interface, or attribute and when you implement that interface, or decorate your class with the attribute in your own code it will be picked up automatically by Umbraco or the package. This also prevents you have a tight coupling with the functionality you want to implement. In other words when you add a class implementing a certain interface this will be automatically picked up when the responsible functionality in this case uGoLive exists in the Umbraco environment. If uGoLive isn’t installed it’s just a class that does nothing.

To integrate the custom checks as displayed in the above example I only added a reference to the Our.Umbraco.uGoLive.dll, decorate my class with the Check attribute and implement a class deriving from AbstractCheck for each check.

 /// <summary>
    /// Check the SEO Analyzer table for template issues
    /// </summary>
    [Check("C4B4D282-12CB-44AC-8263-389D70415895", "Template issues", "Leaving SEO Issues in templates unsolved can be bad for your ranking", "SEO Analyzer")] 
    public class SeoAnalyzerTemplateErrorsCheck : AbstractCheck
    {
        /// <summary>
        /// Checks the SEO Analyzer error log table
        /// </summary>
        public override CheckResult Check()
        {
            return TemplateIssues.GetAll().Count == 0 ? PassedResult() : FailedResult();
        }

        /// <summary>
        ///Returns the result object when the check passed
        /// </summary>
        private CheckResult PassedResult()
        {
            var result = new CheckResult();
            result.Status = CheckResultStatus.Passed;
            result.Message = "No template issues found";

            return result;
        }

        /// <summary>
        ///Returns the result object when the check failed
        /// </summary>
        private CheckResult FailedResult()
        {
            var result = new CheckResult();
            result.Status = CheckResultStatus.Failed;
            result.Message = "Template issues found, resolve them in the SEO Analyzer section";

            return result;
        }
    }

This was very straight forward to implement, a big #h5yr to the team who build uGoLive and now I better hurry up finishing SEO Analyzer to prevent you from having SEO issues in your site.

Comments [0] | | # 
# Tuesday, October 25, 2011
Tuesday, October 25, 2011 1:25:49 PM (GMT Daylight Time, UTC+01:00) ( CMSImport | Umbraco )

 

 cmsimport

A few weeks back  I’ve released CMSImport 2.1. Apart from the awesome Logo made by Arnold Visser I’ve added some functionality as well.

Import dashboard for Content Editors

CMSImport is great for developers. They can create import definitions and execute, or schedule  them. When you want to allow your content editors uploading new data and import that data you need to allow them access to the developer section, or let them upload a file to a specific location and schedule a task that imports that file very hour or so. Well not anymore. Now  you can use a dashboard control and allow users to import their data using that dashboard control. The user can select an import definition, upload a file containing the data to import and optional upload a zip file containing the media files. Before importing the data will be verified to ensure all columns still exists in the new uploaded data file. This feature is a direct request from customers who use CMSImport to update their TeaCommerce or uWebshop catalog, but can also be used for any other import requirement of course.

importdashboardcontrol

As a developer you still have full control what you want your users to import. Using the ImportDashboard settings page you can enable/disable Import definitions you want your end users to use. You can only select import definitions that allow upload of a file such as Excel, Csv, Xml etc. You will also see an xml Snippet you can customize to your needs and paste into the dashboard.config file. Using the access element you can also restrict which User types can access the Dashboard control.Check out this wiki for more information about configuring Dashboard controls.

UserImportSettingsDashboard

Finally support for Media using MNTP

With the 2.1 release it’s finally possible to upload media using the Ucomponents Multi Node Tree Picker. When you select advanced mapping options when mapping against a property based on the MNTP datatype you will see the following options screen. Set the media location where to store the media and files will be imported automatically. The benefit MNTP has over normal Media pickers is that you can use a comma separated string of media files you want to import instead of just one file. When mapping against a MNTP datatype you can only set media settings if the datatype is configured as a media picker, otherwise you will get a message that it is not possible to set the advanced options.

image

Comments [0] | | # 
# Monday, October 17, 2011
Monday, October 17, 2011 12:00:49 PM (GMT Daylight Time, UTC+01:00) ( Umbraco )

 

Last week I was triggered by a tweet from Sebastiaan Janssen mentioning the Dictionary Section Package was broken in 4.7.1

dictionarytweet

Moving the Dictionary to a separate section  is a technique that I use myself a lot when building solutions for clients, I don’t want to give content editor access to the settings section and I really don’t want to edit Dictionary items myself .I just finished a custom section where I used this, so I immediately logged in as an Editor and indeed I got a nice Exception. This hasn’t happened before so I assume this security check is introduced in 4.7.x.

userException 

It seemed that the page was throwing this error and indeed the constructor of EditDictionary sets the CurrentApp which will be used  in a later stage to validate User permissions.

 public EditDictionaryItem()
 {
     CurrentApp = BusinessLogic.DefaultApps.settings.ToString();
 }

So to fix this error we only need to set the CurrentApp property to either language or empty string (no validation at all). The easiest way is to modify the source and recompile, but I never modify the original Umbraco source to work around the Umbraco core. This is because you need to update the source code for every version update of Umbraco. So I needed a way to update the CurrentApp property using my own code. Lucky for us asp.net is an object oriented language and  so we can add our own page and only override the constructor to modify the CurrentApp property. This can be done by creating a custom aspx page where the codebehind class derives from the EditDictionaryItem class.

    /// <summary>
    /// Derive from EditDictionaryItem to use the existing Dictionary functionality
    /// </summary>
    public partial class LanguageDictionary : EditDictionaryItem
    {
        /// <summary>
        /// Use the constructor to set the current app
        /// </summary>
        public LanguageDictionary()
        {
            CurrentApp = "language";
        }
    }

Unfortunately we still some page directives and markup in our aspx page. Lucky for us the original aspx file only contains some basic markup (a placeholder), controls are rendered from codebehind so it’s not a crime to copy and paste that markup and update the Page Directives to use our new codebehind and inherit from LanguageDictionary class. Below you see all markup.

<%@ Register Namespace="umbraco" TagPrefix="umb" Assembly="umbraco" %>
<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %>
<%@ Page Language="c#" MasterPageFile="~/umbraco/masterpages/umbracoPage.Master" ValidateRequest="false"
    CodeBehind="LanguageDictionary.aspx.cs" AutoEventWireup="True" Inherits="DictionaryFix.LanguageDictionary" %>

<asp:Content ContentPlaceHolderID="body" runat="server">
    <cc1:UmbracoPanel ID="Panel1" runat="server" Width="408px" Height="264px">
    </cc1:UmbracoPanel>
    <script type="text/javascript">
        jQuery(document).ready(function () {
            UmbClientMgr.appActions().bindSaveShortCut();
        });
    </script>
</asp:Content>

So now we have a complete work around. Only thing is that this page will never be called from the tree. So we need to have a custom tree.The class responsible for rendering of Dictionary items tree  is the loadDictionary class. This class contains the Method RenderJS that renders the Javascript function to open the page. So we only need to override that method and point it to our new LanguageDictionary.aspx file, I store the aspx file in the already existing DigibizDictionarySection folder .

   public class LanguageDictionaryTree : loadDictionary
    {
        public LanguageDictionaryTree(string application) : base(application) { }

        public override void RenderJS(ref StringBuilder Javascript)
        {
            Javascript.Append(
                @"
                function openDictionaryItem(id) {
                UmbClientMgr.contentFrame('plugins/digibizdictionarysection/languagedictionary.aspx?id=' + id);
                }");
        }
    }

The last thing we need to update is the database table to use our new LanguageDictionaryTree  instead of the loadDictionary class. The database table to update is UmbracoAppTree , search for the row containing language as appAlias and update the treeHandlerAssembly and treeHandlerType values.

dbupdate

Copy the aspx page to the /umbraco/plugins/DigibizDictionarySection  folder and store the dll in the bin folder of your install. Then login as the editor again and see that you can access Dictionary items outside of the settings section again.

dictionaryfixed

Below you can find the links to the original package and the fix I’ve created. Jeroen Breuer mentioned he will implement this fix later in his package.

Comments [0] | | # 
# Tuesday, September 06, 2011
Tuesday, September 06, 2011 3:26:33 PM (GMT Daylight Time, UTC+01:00) ( mediaprotect | Package | Umbraco )

Lots of people want to run MediaProtect on a Mysql database. Currently that is not supported but this is only because of a failing installer script for the MySql database. Benjamin Howarth wrote a MySql script for generating the log table in the MySql database. Once this table exists all functionality will work just fine on a MySql database. The next version of MediaProtect will support this out of the box, for now the installation procedure will be installing MediaProtect first and then run the following script to create the tables.

CREATE TABLE mediaprotectlog ( 
LogId BIGINT(20) NOT NULL AUTO_INCREMENT, 
MediaId INT(11) NOT NULL, 
Path VARCHAR(500) NOT NULL COLLATE 'utf8_general_ci', 
Protected BIT(1) NOT NULL, 
UserName VARCHAR(150) NOT NULL COLLATE 'utf8_general_ci', 
UserIP VARCHAR(50) NOT NULL COLLATE 'utf8_general_ci', 
LogDateTime DATETIME NOT NULL, PRIMARY KEY (LogId) )

Happy Installing!

Comments [0] | | # 
# Friday, April 29, 2011
Friday, April 29, 2011 1:06:57 PM (GMT Daylight Time, UTC+01:00) ( CMSImport | Umbraco )

A question I get asked a lot is if it is possible to assign a template based on a datasource value. The answer is yes, normally the default assigned template will be assigned when importing your data and this is what 99% of the users want and expect. But using the RecordImported Event we can get the datasource value from the database and use that alias to map the template against the imported document.

Example

In this example we have some data coming from a database that we want to a standard Runway text page. Some items such as Integer and Donec are really important and we want to display these items in Pink. In Umbraco we added a new template called PinkTemplate and as you can see in the image below we added that Template alias in our Datasource that we are going to import.

Example data

As I mentioned earlier we can use the event system to manipulate the document during import . You can use the event system for CMSImport in the same way you use the normal Umbraco event system. In the example below you see that we derive from ApplicationBase and wire up the RecordImported event in the constructor. Since we only want to set the template during the initial import, we first check if the Importaction is  ImportAsNew. Then we need to check if the template column from the datasource is not null. You can access all  items from the datasource  using the e.Items collection. When the template column is not null, we get the template id from the database and assign that value to the document.

   1:  using CMSImport.Extensions.Import;
   2:  using umbraco.BusinessLogic;
   3:  using umbraco.cms.businesslogic.template;
   4:  using umbraco.cms.businesslogic.web;
   5:   
   6:  namespace AssignTemplate
   7:  {
   8:      /// <summary>
   9:      /// This class wil automatically assign a template to the document when being imported for the first time using the RecordImporting Event
  10:      /// If you want to use this class in your own project simply rename e.Items["Template"] to the template column you use
  11:      /// in your datasource
  12:      /// 
  13:      /// This code will work in both the Free and PRO edition of CMSImport and is written for version 1.2. 
  14:      /// If you have any questions please use the forum or email support@soetemansoftware.nl 
  15:      /// </summary>
  16:      public class AssignTemplateUsingDatasource : ApplicationBase
  17:      {
  18:          /// <summary>
  19:          /// Initializes a new instance of the <see cref="AssignTemplateUsingDatasource"/> class.
  20:          /// </summary>
  21:          public AssignTemplateUsingDatasource()
  22:          {
  23:              //Whenever a records gets imported 
  24:              ContentImport.RecordImported += new ContentImport.RecordImportedEventHandler(ContentImport_RecordImported);
  25:          }
  26:   
  27:          /// <summary>
  28:          /// When a record got imported this event is raised.
  29:          /// </summary>
  30:          /// <param name="sender">The imported document.</param>
  31:          /// <param name="e">The <see cref="CMSImport.Extensions.Import.RecordImportedEventArgs"/> instance containing the event data.</param>
  32:          private void ContentImport_RecordImported(object sender, RecordImportedEventArgs e)
  33:          {
  34:              //Only assign the value when its imported for the first time
  35:              if (e.ImportAction == ImportActions.ImportAsNew)
  36:              {
  37:                  Document doc = sender as Document;
  38:                  
  39:                  //Get the template value from the datasource. Datasource values are stored in the Items event arguments
  40:                  //Replace templateName with your column name in the datasource!!!
  41:                  string templateAlias = e.Items["templateName"].ToString();
  42:   
  43:                  if (doc != null && !string.IsNullOrEmpty(templateAlias))
  44:                  {
  45:                      //An alias is specified, try to get the template based on the specified alias  
  46:                      Template template = Template.GetByAlias(templateAlias);
  47:                      if (template.Id != null)
  48:                      {
  49:                          //Template is found assign it to the document
  50:                          doc.Template = template.Id;
  51:                      }
  52:                  }
  53:              }
  54:          }
  55:      }
  56:  }

When the import is finished you see that all items are imported and when you we take a look at the Integer document you’ll see that the assigned template is PinkTemplate instead of the default Runway Textpage template.

Pink template assigned

Why isn’t this supported in the UI?

As I mentioned before most people expect to assign the default value. When I include the assign template via datasource functionality in the UI it’s an option that you need to think about before setting or ignoring the option. My idea with CMSImport is that I want it to be a no brainer to use and introducing this option will make it a little bit harder to use. So if you want this I think it’s better to write 10 lines of code instead.

Downloads

Below you can download the complete source code. Change e.Items[“templateName”] to the column name you want to use in your project, build the project and add the DLL to the bin folder of your Umbraco install. That’s all!

Download the source code here

Comments [0] | | # 
# Wednesday, April 27, 2011
Wednesday, April 27, 2011 3:54:21 PM (GMT Daylight Time, UTC+01:00) ( mediaprotect | Package | security | Umbraco )

Last month  I’ve released Media protect 1.0. Mediaprotect is an Umbraco  package that helps you protect media in the same simple way as you protect documents in Umbraco. Once the package is installed and you protect certain media, or content nodes, the files will be password protected. Only authenticated members can open the files, otherwise members will be redirected to the login screen.

The problem protecting media

No solution without a problem Winking smile. When you use Umbraco, by default all media items can be accessed directly. If you have a media item that you don’t want to be accessed and someone knows the url (maybe got a link via email from a friend), or you have a link in your content  to a media item that is crawled by Google and the user uses that url the media item will be shown/downloaded, if you like it or not.

This is also the case with upload fields on a protected content node. The page is protected but the uploaded file is not protected.

How Media protect solves this problem

Once you’ve installed media protect files on protected content nodes are protected by default and you’ll get the public access menu item on the context menu of your media section.

Best of all you don’t need to modify any existing media or document type, it just works!

The public access menu item  is the same menu item you get when you want to protect a node in the content section.

Public access

When you select Public access you’ll get the same dialog as you get when protecting content. You can specify if you want to use single user or role based protection. You can also specify the login and not authorized page.

Dialog

When you are finished protecting the media item, the media item gets a No Access sign. This indicates that the media is protected and can’t be accessed directly. When you open the media item and click on the document link you will be redirected to the login page since you are not logged in. Once you are logged in as a member and the member role is assigned as allowed roles to view the media item you can download the file.

protectedmedia

Compatible with Media Pickers and default asp.net Membership provider

It’s nice that we can easily protect media items but we also want to make sure that the editor doesn’t accidently pick protected files using a Media Picker, or in the Rich Text Editor. Media Protect will also display the same No Access sign when you want to pick a media item using a media picker or via the Rich text editor. Not only the default Umbraco pickers but media protect is also compatible with some 3th party pickers, such as Multi Node Tree Picker (The awesome picker from UComponents) and the DIgibiz Advanced Media Picker. Best of all if you have a custom media picker you can add the alias of that media picker to the mediaprotect.config file and it will be picked up automatically.

DIgibiz Advanced Media Picker

Speaking about compatibility. Mediaprotect is based on the default ASP.Net Membership provider, so you can use the default asp.net controls for logging in etc. and you can also use your own  custom membership provider.  Media Protect comes with a small API you can use in a .Net usercontrol/XSLT or Razor script to determine if an item is protected, or to determine if the current logged in user has rights to access the media node.

Free alternatives

Media protect is a commercial package, it protects your media out of the box(no need to change any document or media type , it just works).It will cost you 99 euro per domain though. If you want to protect media without spending money you might want to check out these free alternatives

More info and Download

If you want more info about Media protect I suggest you check out the product site. Here you find a video demonstrating the package and documentation. You can also download a fully functional free trial from our.umbraco.org. This will work on your local machine (localhost), only thing you get for free is a nice yellow sign that says you are running in Trial mode. The trial never expires and when you want to deploy your site you can buy a license for that domain or buy an enterprise license for your whole company.

Feedback

Please tell me what you think about this package. Like it, hate it? I would love to know.
Comments [2] | | # 
# Tuesday, January 11, 2011
Tuesday, January 11, 2011 1:31:15 PM (GMT Standard Time, UTC+00:00) ( CMSImport | Umbraco )

Last week I’ve released CMSImport 1.2 (Pro). This was a minor release mainly to fix a few bugs

Change log:

  • Fixed string delimiter for csv files
  • Field adaptors on default properties didn’t get hit
  • Throw error when login is null for member import, otherwise you can’t create new members and Umbraco will throw errors on you.
  • Introduced a new FieldAdapter that can map user logins against a Member picker data type. CMSImport will auto convert this to the corresponding member id.
  • Tested against Umbraco 4.6 (beta)

Again this was a very small release. Mainly to fix some errors.

CMSImport 2.0

The most requested feature is structured import. I’m very happy to announce  that this will be included in CMSImport Pro 2.0. The video below demonstrates how this will work. I've tried to keep this as simple and flexible as possible and hope I have succeeded, if not I hope to hear from you.

CMSImport 2.0 Structured import demo from Richard Soeteman on Vimeo.

Other new functionality for 2.0

Of course structured import will not be the only new feature. Below you find a list of all other functionality that will be included

Support for more Data sources

CMSImport 2.0 will support new data sources to import from such as:

  • Rss
  • BlogMl
  • Wordpress export
  • Excel
  • MS Acces

If you have a specific product, or data format you want to import from please let me know through the comment form.

Configurable field adapters

In version 1.1 I’ve introduced field adapters. These field adapters are great to convert data from a data source to the format that is accepted by the Umbraco data type, but sometimes you need to specify some additional information. For example when you import data and want to map url’s against the Ucomponents Url picker you want to specify if the link needs to be opened in a new window or not. Currently that is only possible with a config file, not very friendly. In 2.0 it will be possible to specify some additional options like you have to do now when you import related media.

Support for third party packages

CMSImport 2.0 will come with support for third party packages such as UComponents, SuperSimpleWebshop,Teacommerce and Blog4Umbraco.

I’m sure there will be extra functionality added along the way.For now I hope you like what you’ve seen in the video and read in this post.
Comments [2] | | # 
# Wednesday, November 24, 2010
Wednesday, November 24, 2010 10:37:54 AM (GMT Standard Time, UTC+00:00) ( CMSImport | Umbraco )

Ok official it was  released in the beginning of October, but the manual wasn’t finished and I couldn’t upgrade existing clients to 1.1. From a customer satisfaction point of it was very important for me to have these things ready before blogging about all the cool new features. So what’s new?

Media Import

The main feature that is added to CMSImport PRO is the ability to import media related to content or member data. This isn’t a separate import process but integrated in content or media import. When CMSImport finds a reference to a relative path it will try to get the item and convert it to a media item, or store it in the media folder in case of an upload field. The only required thing is that the original media folder is copied to the root of your Umbraco folder.

In the example below the img folder of the original site containing two images is stored in the Umbraco root.

filesystem

In the Import wizard you can specify that you want to import media items also. Check the option “Import content related media”. If you want to map media against media pickers and/or the TinyMCE editor, you need to specify a media folder also. CMSImport will keep the imported folder structure.

mediaimportoptions

Then in the next step you can create the mapping like you would normally do.

mediamapping

  1. When a reference to an image is found in the content, CMSImport will create a media Item and update the image source to the new Media item(Currently only images are supported in Rich Text. Version 1.2 will have support for files also. Media Pickers and Upload fields will work with files also.)
  2. When an image (could also be a file) reference is mapped against an upload field. CMSImport will store the image in the Umbraco Media folder and update the reference in the Upload field
  3. When an image (could also be a file) reference is mapped against a media picker. CMSImport will create a media item and store the Id of the media item .

When the import process is finished you’ll see that the media items are imported

mediaimported

And when you open an imported item in the content section you’ll see that all the references are updated to the imported media items

contentupdated\

Again you don’t need to do anything extra apart from downloading the original media folder to you Umbraco Install.

Currently this import process will work for the following datatypes:

  • Upload Field
  • Media Picker
  • TinyMCE (Rich text editor)

In future releases CMSImport will support custom datatypes and File import for the TinyMCE datatype also.

FieldAdapters

FieldAdapters are used to convert original values from the datasource to a value the DataType expects. In previous versions when you wanted to import boolean data from a CSV file and map that against a true/false dataType, it would have failed because the True/False datatype could only handle the integer values 1/0 or a real boolean.

FieldAdapters fix those issues. During import of a field CMSImport will check based on the underlying datatype if a value can/must  be converted.

The following snippet contains the implementation of the BooleanFieldAdapter( just to show how little coding is needed for a better import experience).  It will add a relation to the True/False dataType based on the DataTypeID property . This must be the same GUID as the True/False datatype. Then in the Parse method  it will inspect if the value is either 0 or 1, if not it will convert the value to a real boolean.

  1: public class BooleanFieldAdapter : IFieldAdapter
  2: {
  3:     /// <summary>
  4:     /// Contains the GUID of the true/false datatype we want to parse using this FieldAdapter
  5:     /// </summary>
  6:     /// <value></value>
  7:     public Guid DataTypeId
  8:     {
  9:         get { return new Guid("38b352c1-e9f8-4fd8-9324-9a2eab06d97a"); }
 10:     }
 11: 
 12:     /// <summary>
 13:     /// Parse the data
 14:     /// </summary>
 15:     /// <param name="value">The value to parse</param>
 16:     /// <param name="fieldAdapterService">The field adapter service.</param>
 17:     /// <returns></returns>
 18:     public object Parse(object value, Property property, FieldAdapterOptions fieldAdapterService)
 19:     {
 20:         if (!(value.Equals("0") || value.Equals("1")))
 21:         {
 22:             // Parse boolean value and return result
 23:             bool boolValue = false;
 24:             bool.TryParse(value.ToString(), out boolValue);
 25:             
 26:             return boolValue;
 27:             
 28:         }
 29:         return value;
 30:     }
 31: }

This mechanism is very powerful. In fact the whole media import is based on this. Currently it only support a few default Umbraco Datatypes. For later versions I will make sure that FieldAdapters will fix import issues for all popular third party datatypes, like Ucomponents also. If you don’t want to wait for that use the API to create your own FieldAdapter and include it in your package.

Improved API

In 1.0 you could extend CMSImport. but extension points existed in different assemblies. With version 1.1 all extension points exist in the CMSImport.Extensions.DLL. Using this assembly you can create your own DataAdapters and FieldAdapters and you can use the event system. In chapter 8 of the manual you can read all about this. I’ve also created a sample project that you can use as a start. This contains samples from how to create a DataType to how to support Structured Import using the Event System. This was written for CMSImport PRO but you can use it for CMSIMport also, although not all samples will work. The sample code uses the AdventureWorks database a  DataSource. You can download this database from  CodePlex and you can download the Samples from the CMSImport site.

Small other changes

This following other small changes are included in this release.

New version of the CSV Parser

In the 1.0 release I was using an older version of the this Csv Parser. This one is upgraded  to the newest release an didn’t have any issues with it.

Better Error Handling for Saved Imports

When you deleted the root folder in CMSImport PRO 1.0. The only message that you would get was “Object reference not set to an instance of an object” and you could start all over again. In the current version CMSImport will report what is wrong and allows you to fix those issues.

InvalidImportOptions

Now with documentation

The whole import process and API is documented now. You can download the manual from the CMSImport site.

Delete failed import documents that were created during import

When a new document is created during the import process and the import process fails the document will be deleted instead of left in the tree.

Improved performance by not setting null values

When a value contains null it will not be mapped against the Document property. This will save a few database calls according to this blogpost. This is only for new imported  documents. When a document is being updated the null value could be the new value and therefore will be assigned to the property.

What’s next

The coming 1.2 version will include more FieldAdapters and support for third party Datatypes. This will be included in both the Free and Pro edition of CMSImport.. I hope to have this ready by the end of this year. I’m also working on version 2.0 that will support structured import out of the box. This will be released early next year. Structured Import will only be included in the PRO edition.

Get it while its hot

Download the free version from our.umbraco.org. Please vote up the package if you like it. Or buy a Pro license from the CMSImport website.

Comments [0] | | # 
# Friday, July 02, 2010
Friday, July 02, 2010 9:13:38 AM (GMT Daylight Time, UTC+01:00) ( Umbraco )

Now that version 4.5 of Umbraco is out you want to make sure that your packages work in both 4.0.x and 4.5. Or is that just me? So far I’ve had two compatibility issues

~ prefix in UmbracoPath setting

I’m  using the Umbraco property GlobalSettings.Path (which correspondents with the umbracoPath setting in the web.config file) to determine the Umbraco folder. This used to be /Umbraco, Umbraco 4.5 supports virtual folder so it’s changed to ~/umbraco. To solve this for both 4.0.x and 4.5 you can use the ToAbsolute method of the  VirtualPathUtility class.  The example below will convert the virtual path ~/umbraco to absolute.

VirtualPathUtility.ToAbsolute(umbraco.GlobalSettings.Path);

Using inline javascript in Action class breaks the complete tree

In Umbraco you can create classed that implement the IAction interface which gives you the possibility to add menu options to the context menu of a tree. In previous versions of Umbraco you could use inline JavaScript in the JSFunctionName property to have client side functionality. In Umbraco 4.5 the clientside model is changed and if you include direct JavaScript into the JSFunction  name the complete tree will break. Good thing is that Shannon AKA @Shazwazza fixed a bug in 4.5 so the JSSource property of your action class now actually gets rendered and you can include the function here, downside is that this isn’t the case in 4.0 and we now need to think of a way to determine if we are in 4.0 or 4.5. For CMSImport and MemberExport I’m using a CompatibilityHelper Class that I’ve created. On this class I have a property SupportsJQueryTree that returns true if the CurrentVersion setting is not 4.0.

    /// <summary>
    /// This class is introduced because 4.5 is a little bit different from 4.0.x 
    /// One single place to have nasty checks if the Umbraco environment supports a feature or not
    /// </summary>
    public static class CompatibilityHelper
    {
        public static bool SupportsJQueryTree
        {
            get
            {
                return !GlobalSettings.CurrentVersion.StartsWith("4.0");
            }
        }
    }

Now I can determine in my action if the JQueryTree is supported and render JavaScript for that, otherwise I will render JavaScript for the old Tree. In the snippet below you see the Implementation that I’m using in CMSImport to start an import from a node tree

        public string JsFunctionName
        {
            get
            {
                if (CompatibiltyHelper.SupportsJQueryTree)
                {
                    //Environment supports the new tree call Javascript via a function
                    //doing this with inline script this will cause the tree to crash
                    return "StartWizard()";
                }
                else
                {
                    //The old tree could not have a function in JsSource, use inline script instead.
                    return " parent.right.document.location.href = '" + umbraco.GlobalSettings.Path + "/plugins/CMSImport/Pages/wizard.aspx?id=' + nodeID; ";
                }
            }
        }

        public string JsSource
        {
            get
            {
                if (CompatibiltyHelper.SupportsJQueryTree)
                {
                    //Assign a function.
                    return "function StartWizard(){ parent.right.document.location.href = '" + VirtualPathUtility.ToAbsolute("/plugins/CMSImport/Pages/wizard.aspx") + "?id=' + 
UmbClientMgr.mainTree().getActionNode().nodeId ; }";
                }
                else
                {
                    //Doesn't work in 4.0.x
                    return string.Empty;
                }
            }
        }

This might sound like a hack and it really is a hack. But for me this works. Within a few months just a small percentage of the Umbraco users is using 4.0, the rest is on 4.5 and I might drop support for 4.0 then and only need to delete this CompatibilityHelper class and delete the old 4.0.x code 

So far these are the only compatibility issues I’ve found (I don’t use the new schema in my packages). If I find more I will create a separate blogpost for it. Hope this post helps you with your compatibility issues in your packages

Comments [1] | | # 
# Monday, June 07, 2010
Monday, June 07, 2010 12:42:40 PM (GMT Daylight Time, UTC+01:00) ( MemberExport | Package | Umbraco )

A few weeks back I’ve blogged about the new MemberExport package I was working on. Today I can tell you it’s released, both the free and the Pro (commercial) version. For those who missed the last blogpost…. MemberExport (Pro) helps you export members from your Umbraco installation to a csv file. The Free edition is limited to export only 200 records. With the PRO version it’s  possible to save the export options  steps for later use .

screenshot 

Free Edition

The free edition of MemberExport can be found on our.umbraco.org. Please vote the project up if you used and liked it.

Pricing

You can buy a single domain license of MemberExport PRO. With a single domain license you are allowed to use  MemberExport PRO  for a single domain and all subdomains, such as www.example.com, accept.example.com, and local.example.com. We also have a Enterprise license available. With an Enterprise license you are allowed to install the MemberExport PRO package on unlimited production web servers, and use it for unlimited Umbraco instances within the Enterprise. A single domain license will be available for  39 Euro, an enterprise license for 149 euro.  When you buy a license you’ll get free updates within 90 days of purchase and  free updates for all minor releases within a major release.  For example, if you purchased a  1.0 version of MemberExport PRO, you get free updates of all 1.x versions through our client area.

Special 1.0 offer. When you buy the 1.0 release you’ll get a free update to 2.x. This is a 1.0 offer only!

More Info

For more info, download, or purchase you can check out the MemberExport website

Comments [0] | | # 
# Friday, May 28, 2010
Friday, May 28, 2010 3:51:58 PM (GMT Daylight Time, UTC+01:00) ( MemberExport | Package | Umbraco )

For those who follow me on twitter probably know that I’m working on a new package for Umbraco that can export Members to a csv file. Below you see the first screenshot of the package. Basically you select the groups and fields you want to export, you specify the export options and when you click the export button a csv file is generated on the fly which you can download.

 MemberExport

Two days ago I started tweeting about this because I was very happy that exporting a few hundred members only took 2 seconds. Immediately people warned me about the fact that I used Member.GetAll to get a list of all the members which is very slow when you want to export more than 500 records. Thanks again for this warning guys (also for the brain breaking sql I had to write to improve the performance ;-)).  So I changed the Member.GetAll functionality to a custom SQL query, imported 5000 extra records  and did a performance test to see if  the modification was a success. Guess what, the export of 5000+ records only took 2142 milliseconds. I think that’s a great result to end the week with.

When will this package be available?

Next Monday I will send the package to a few people that  want to beta test MemberExport. When it’s stable enough I’ll release the package. There will be two versions of the package, a free (limited to export 200) records version and a PRO version that can also save the export options for later use. The price for the pro version will be 39 euro (ex 19% Dutch VAT, only for Dutch customers) .

I hope you find the package interesting!

Comments [1] | | # 
# Friday, April 16, 2010
Friday, April 16, 2010 2:09:04 PM (GMT Daylight Time, UTC+01:00) ( CMSImport | Umbraco )

When I released CMSImport 1.0 two months ago, I briefly described the FieldAdapters feature that  I was going to build for version 1.1. Today I want to go more into detail about the FieldAdapters feature for CMSImport.

The problem

The reason I started to think about fieldadapters was because I wanted to solve a very common problem. As you might know Umbraco only accepts three kinds of  data; string, integer and datetime. What happens when you try to map a boolean value to  a yes/no Umbraco datatype? An exception is thrown.  The following screenshot contains several products that we want to import into Umbraco.

products

When we’ve mapped the InStore column to a yes/no datatype the import will fail because it can’t map the boolean value to a 0 or 1 and we will see the following ugly screen

errors 

And a FieldAdapter can fix that?

Yes a fieldadapter inspects the value and tries to parse it, in this case it will try to pares a true/false value to 1/0, best to explain by exploring the source code. The whole feature is based on the new IFieldAdapter interface.

using System;

namespace CMSImportLibrary.Interfaces
{
    /// <summary>
    /// Implement the IFieldAdapter interface to convert an mallformed field to a correct type.
    /// </summary>
    public interface IFieldAdapter
    {
        /// <summary>
        /// Contains the GUID of the datatype we want to parse using this FieldAdapter
        /// </summary>
        Guid DataTypeId { get; }

        /// <summary>
        /// Parse the data 
        /// </summary>
        /// <param name="value">The value to parse</param>
        /// <returns></returns>
        object Parse(object value);
    }
}

As you can see in the code snippet the interface contains the property DataTypeId. The value needs to correspondent to the Id (GUID) of the datatype you are creating the fieldadapter for. During the import a factory inspects the underlying datatype of the document property, if a fieldadapter is found it will execute  the Parse method. This will work on all Umbraco datatypes (also custom datatypes or datatypes from third party packages) as long as you know the Id of the datatype. You can find the id by opening the datatype in Umbraco, there you see the RenderControl .

truefalsedatatype

When opening the source code of the rendercontrol you’ll see the Id of the datatype (a GUID). Don’t worry if you can’t open the source code of the datatype i’ll add the Id’s of all known datatypes to the documentation of CMSImport and I’ll make a free tool that shows the Id of any datatype.

Implementation of the BooleanFieldAdapter

Now enough with all the boring stuff, let’s see the fieldadapter in action. Below you’ll see the implementation of the BooleanFieldAdapter. I’ve mapped the id of the True/false datatype to the DataTypeId property so CMSImport knows that it needs to call the Parse method during the mapping of a yes/no document property. In the Parse method I simply check if the value is already in the correct format , if not it will try to convert the value to 1 or 0 and return that , otherwise it will just return the original value.

using System;
using CMSImportLibrary.Interfaces;

namespace CMSImportLibrary.FieldAdapters.DefaultFieldAdapters
{
    public class BooleanFieldAdapter : IFieldAdapter
    {
        #region IFieldAdapter Members

        public Guid DataTypeId
        {
            get { return new Guid("38b352c1-e9f8-4fd8-9324-9a2eab06d97a"); }
        }

        public object Parse(object value)
        {
            if (!(value.Equals("0") || value.Equals("1")))
            {
                bool boolValue = false;
                if (bool.TryParse(value.ToString(), out boolValue))
                {
                    return boolValue ? 1 : 0;
                }
            }
            return value;
        }

        #endregion
    }
}

Now when we run the import  again it will just import the data without any errors, just by adding a few lines of code, isn’t that powerful?

Beyond fixing problems

Now that we have this mechanism we can also use it to modify data during the import. Let’s say we import a piece of content from an old site that contains an image tag. This will just run fine but when you have a reference to an image on your old site and  you delete that old site all the references to the images are dead. FieldAdapters can help solve this issue by inspecting the text, extract the image tags, import these images into the media library and update the image tag with a reference to the media item. This will really help keep your site consistent. The same applies for Upload fields.

Will all of this be included in the free version of CMSImport also?

All FieldAdapters that fix errors (Like the BooleanFieldAdapter) will be included into the free version of CMSImport. FieldAdapters that helps you updating the content will only be included in the PRO version of CMSImport. And again you can also create your own FieldAdapters, free for both versions.

This will be the major feature of the V1.1 release of CMSImport. Please let me know what you think about this feature.

Comments [0] | | # 
# Sunday, April 04, 2010
Sunday, April 04, 2010 7:22:10 PM (GMT Daylight Time, UTC+01:00) ( Package | Umbraco )

With Taskscheduler you can schedule url's/pages to be executed on a certain date and time. It’s a simplified version of the Windows task scheduler, build on top of the Umbraco scheduler functionality. If you’ve used  CMSImport PRO, the package should look familiar because TaskScheduler is based on the scheduled import functionality of CMSImport PRO.  When you installed the package and browse to the developer section you’ll notice the new Scheduled Tasks tree. From here you can create new Scheduled tasks, or browse to existing ones.

Schedule a task

When you create a scheduled task, you’ll see the following screen. In this screen you can configure the scheduled task.

TaskScheduler

The scheduled task name contains the name of the scheduled task. This will also be shown in the tree and you can use this in the notification email template.

The Schedule Url contains the url that you want to execute. You can insert a full url (http://mydemosite.com/scheduledpage.aspx), an absolute path(/scheduledpage.aspx) or a relative path(~/scheduledpage.aspx). The last option will come handy when you start working with Umbraco 4.1 where virtual folders are supported. When you want to retrieve the HTTP output of the page and use that in your notify email you can set the url output to true.

If you want to retrieve a notification email that tells you the scheduled task has executed you can fill in your emailaddress in the notify emailaddress field.

Basically you’ll have three options

  • Execute every week on certain days and a certain time.
  • Execute every day on a certain time
  • Execute every hour

Settings

Not a lot of settings to configure. Only the settings for the notification email. You can configure the from address, the subject  and the email template. In the email template you can use two tags.

  • [#Taskname]. Will be replaced with the name of the configured task
  • [#Output] . Will be replaced with the html that got returned from the page if you had checked “Use Url output in url”.

taskschedulersettings

System Requirements

TaskScheduler is tested on Umbraco 4.0.3.1, is compatible with .NET Framework 2.0  and is compatible with SQL Server(Express) 2005, 2008.

Download

You can download the package from our.umbraco.org

As always I hope that you like the package and it’s useful for you.

Comments [1] | | # 
# Friday, March 26, 2010
Friday, March 26, 2010 10:53:02 PM (GMT Standard Time, UTC+00:00) ( SiteAnalyzer | Umbraco )

Apart from developing client websites,  writing a manual for CMSImport and  developing CMSImport V1.1 I’ve started the development of SiteAnalyzer last week.  In the coming blogposts about SiteAnalyzer I will describe the development process of this package.

What is SiteAnalyzer?

SiteAnalyzer is tool that checks published pages for broken links, broken images in both content and Templates/ XSLT/Usercontrols. In next versions of SiteAnalyzer it will check more and more. My goal is to check for every SEO rule which you can turn on/off.  It will be using a plugin architecture so you can add custom rules. SiteAnalyzer will report back pages where the rules failed to validate, like the Link failed due a 404 etc.

Now I hear you think, “Tools like these already exist why develop your own?”".  I have a few reasons for that, first I was not finished and second I think integrating such a tool into Umbraco is better so people can maintain their site through one interface instead of having a tool for everything. This is why the Google Analytics package for Umbraco is so popular.

Again I was not finished. The killer feature of SiteAnalyzer will be the auto repair functionality(only for content) . Best to explain by an example. Let’s say the content editor entered the url  “www.umbraco.org” in the Rich Text editor. The validator will fail due a 404 page not found. The auto repair function will try to fix the url and in this example it will convert “www.umbraco.org” to “http://www.umbraco.org/” So instead of just reporting that a url failed to validate, it will report that it found a link that failed and repaired it for you, which saves you a few clicks.

Will SiteAnalyzer be open source?

No just as CMSImport  SiteAnalyzer comes in two editions, standard (free) and a PRO commercial edition. The difference between standard and pro will be that the standard edition can handle only a certain amount of pages, the Pro version can be scheduled instead of manual start of the scan and the Pro version comes with advanced reporting which sends you a pdf after it executed.

When will we see a first release?

A first Preview release will be on our.umbraco.org in Q2,  before Codegarden. The first preview will focus on the Standard functionality.

The next blogpost will describe the architecture of this package.

Comments [3] | | # 
# Monday, March 08, 2010
Monday, March 08, 2010 12:48:25 PM (GMT Standard Time, UTC+00:00) ( Umbraco )

Recently I’ve developed a rather large website for a customer. To make the structure as simple as possible to understand I like to use different Icons for each document type. I was very happy with the FamFamFam Icons project from The Farm. After Installing this I got another challenge and that was to pick the right Icon.

iconspng 

Today I started working on a new project, installed the FamFamFam Icons package again and thought that it would be better to assign document types to the Image instead of assigning the image to a document type. I’ve created a small Usercontrol that lists all the icons (except for the default Umbraco sprites). Next to the Icon you find  a dropdown with the possible documenttypes which you can select.

selecticon

I did not create a package out of this. It’s working for me, but it needs some improvements before I can add it to our.umbraco.org.  Maybe that  will never happen, so attached you find a zip file. Add the IconPickerDashboard.dll file to your bin folder, the IconPickerDashboard.ascx to your usercontrol folder.

Add the following section to your  the Dashboard.config file.

    <section>
        <areas>
            <area>settings</area>
        </areas>
        <tab caption="IconPicker">
            <control>/usercontrols/IconPickerDashboard.ascx</control>
        </tab>
    </section>

Now browse to the settings section and select the icons you like.

Download the zip (Requires .net 3.5).

Or download the source:

Comments [3] | | # 
# Tuesday, February 16, 2010
Tuesday, February 16, 2010 8:57:09 AM (GMT Standard Time, UTC+00:00) ( CMSImport | Package | UmbImport | Umbraco )

I’m very pleased to announce that CMSImport 1.0.3 is released. Now I can hear you think CMSImport? Must be a fork of the great UmbImport package. No this isn’t the case. A few weeks back Niels (AKA @Umbraco) asked me to change the name since Umbraco HQ got a lot of requests about this package. So now the name is CMSImport and that’s not going to change anymore.

CMSImport PRO

Finally we’ve finished our commercial edition of CMSImport.  CMSImport PRO gives you all the options of the default package and the following extra features:

  • Update Content
  • Save Import Steps
  • Schedule imports for a certain time and day

Pricing

You can buy a single domain license of CMSImport. With a single domain license you are allowed to use  CMSImport PRO for a single domain and all subdomains, such as www.example.com, accept.example.com, and local.example.com.

We also have a Enterprise license available. With an Enterprise license you are allowed to install the CMSImport PRO on unlimited production web servers, and use it for unlimited Umbraco instances within the Enterprise. 

A single domain license will be available for  99 Euro, an enterpise license for 389 euro.

When you buy a license you’ll get free updates within 90 days of purchase and  free updates for all minor releases within a major release.  For example, if you purchased a  1.0 version of CMSImport, you get free updates of all 1.x versions through our client area.

Special 1.0 offer.When you buy the 1.0 release you’ll get a free update to 2.x. This is a 1.0 offer only!

What’s more in this release?

Several issues are solved in this release(both in the free and Pro release):

  • "item with the same key already added" error when using duplicate column names
  • Automapping column names
  • The imported document creator is not always the administrator anymore. It's using the logged in user now. When you schedule an import you can select the user that should be used as the creator of the document
  • Special characters in CSV are now supported, we’ve changed the reader from ANSI text to Unicode
  • Sometimes CSV replaced spaces with empty strings, this is solved now
  • With member import you can now merge any member property into the template. Simply surround the member property with [#(property here)]
  • Using a renamed Umbraco folder. This is possible now, although it will be better to change it after install, otherwise you have to install manually.
  • We’ve removed the limitation to allow only one DataAdapter. We are thinking to build a DataAdapter pack which contains adapters to import from wordpress, Rss, Outlook, excel etc. These adapters will be available for free in the Commercial Edition and for a small fee for the Free edition.

Roadmap

In the 1.x version we will add the following functionality:

  • FieldAdapters. Sounds boring but this is a big thing. When you import data now, sometimes the import will fail. For example if you import boolean as text (true/false) and want to store that in a True/False field in Umbraco it will fail. Umbraco expects that the value will be 0/1. FieldAdapters will solve this problem. If a insert of data fails. CMSImport will check if 1 o more FieldAdapters are available to convert the data in the right format. This will be added to version 1.1 which must be ready before CodeGarden 2010.
  • Dictionary Import. Need I say more?
  • Hierarchical imports(PRO only).

In the 2.x version we will add the following functionality:

  • Hierarchical import support in Data Adapters. Not the same as the 1.x Hierarchical import feature ;-)
  • Export/import definitions (PRO only). An easy way to deploy Import definitions

More Info

For more info, download, or purchase you’ll go to http://www.cmsimport.com/

Comments [0] | | # 
# Wednesday, February 03, 2010
Wednesday, February 03, 2010 3:45:02 PM (GMT Standard Time, UTC+00:00) ( Umbraco )

Just a small blogpost to announce that I’ve just made a new release of the Package Action Contrib project. The following actions are added to the project:

  • AddConfigurationSection
  • PermissionForApp
  • AddLanguageFileKey
  • MoveFile
  • UpdateAppTree

You can download the new release and documentation from codeplex. I’ve also updated the WIKI page on our.umbraco.org.


Many thanks to the contributors of this release, Simon Dingley, Chris Houston, Dirk de Grave and Søren Spelling Lund for submitting their code!

Comments [1] | | # 
# Monday, January 04, 2010
Monday, January 04, 2010 9:37:53 PM (GMT Standard Time, UTC+00:00) ( Umbraco )

This forum post  triggered me to share some code that I had. A while back I was building a site which  let the customers define their own custom overviews One requirement was that users could define the sort by property . To do this you can use the property picker but it wasn’t friendly enough for my case so I ended up building a custom macro parameter type.

To start you need to create a new class library and add references to the following assemblies:

  • Businesslogic
  • Cms
  • Interfaces
  • Umbraco
  • System.web

In the example below I’ve created a class that inherits from dropdownlist. This was the easiest way since I needed a dropdownlist and didn’t want to play with the control tree, Now more important is that this class inherits from the IMacroGuiRendering Interface.  This add two properties ShowCaption, to show the caption on the parameters tab and more important value which holds the selected value . 

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:  using System.Web.UI.WebControls;
   5:  using umbraco.interfaces;
   6:   
   7:  namespace MacroRenderDemo
   8:  {
   9:      public class OrderBy : DropDownList, IMacroGuiRendering
  10:      {
  11:          protected override void OnLoad(EventArgs e)
  12:          {
  13:              base.OnLoad(e);
  14:              if (this.Items.Count == 0)
  15:              {
  16:                  this.Items.Add(new ListItem("Title", "nodeName"));
  17:                  this.Items.Add(new ListItem("Date", "createDate"));
  18:              }
  19:          }
  20:   
  21:          #region IMacroGuiRendering Members
  22:   
  23:          public bool ShowCaption
  24:          {
  25:              get { return true; }
  26:          }
  27:   
  28:          public string Value
  29:          {
  30:              get
  31:              {
  32:                  return this.SelectedValue;
  33:              }
  34:              set
  35:              {
  36:                  this.SelectedValue = value;
  37:              }
  38:          }
  39:   
  40:          #endregion
  41:      }
  42:  }

When you build the project and add the binary into the bin folder of your Umbraco site you still can’t select the new type from the parameter type list. First we need to register the class  in the database. Below you’ll see the database table that holds all the Macro property types.

dbrecords 

It is important that you add the Assemblyname  including the namespace to the macroPropertyTypeRenderAssembly column and add the name of your class to the  macroPropertyTypeRenderAssembly column .

Now we can use the parameter in our macro’s

parameters

and off course we can now use the new parameter in our template when we select the macro

InsertMacro

Another example how flexible Umbraco is. If a requirement isn’t available out of the box, usuallyall it takes is to implement an interface and write a few lines of code.

Comments [4] | | # 
# Saturday, January 02, 2010
Saturday, January 02, 2010 9:34:32 AM (GMT Standard Time, UTC+00:00) ( Umbraco )

Let’s start with wishing you all the best for 2010. 2009 was an insane year for me, I had given myself one year  being successful in business running my own Umbraco  projects instead of being a .net contractor. That turned out great, I’ve got a lot of new customers, have build a few great sites for my customers  and did a few Umbraco consulting jobs. So now it’s time to make plans for 2010.  First thing is that I’m moving to a real office instead of working from home. I’m really excited about this because I’m moving to an office building with lots of small companies, I think that’s really inspiring.

When I’ve started investigating Umbraco back in 2008 we had a small amount of companies in the Netherlands (mostly freelancers) that where building websites based on Umbraco. Nowadays I see new companies that build Umbraco websites every week, not only small shops but also really big agencies. For this year my focus will be on helping companies (worldwide) being successful implementing Umbraco sites for their clients by offering consultancy services and custom package development  instead of building sites from front to end.

As you might know I’m also working for a long time on UmbImport PRO. My plan was to release this package last year. It’s the top prio on my todolist for Q1 this year. Also another package will see the light this year, UmbLinkChecker. As the name says this will be a package that checks every link in your published site, not only in content but also hard coded links in your templates etc., I will build a free and Pro version. Since Umbraco has more than 75000 active installs I think it’s profitable to build commercial packages for Umbraco and we will see more and more commercial packages from different vendors. Hope the Umbraco store will be back up in 2010 and filled with great products.

The last year I couldn’t find enough time to blog or write WIKI’s and helping the community with their problems on the forum. I’m hoping this year that will change, since I’ve got a lot of info that I’d like to share.

Have a great 2010!

Comments [0] | | # 
# Sunday, December 27, 2009
Sunday, December 27, 2009 7:37:27 AM (GMT Standard Time, UTC+00:00) ( Package | Umbraco )

Usually I develop websites that require login functionality for one or more roles, now this is all cool for my clients but it’s a nightmare for me because I simply can’t remember all the login and passwords. To make my life and other developers life’s a little bit easier I’ve developed the Memberswitcher package. This package let's you easily login members and switch between members by simply selecting a member from a pull down list instead of enter the username and password. It’s using a lower level asp.net membership method to login the member based on the username and is fully compatible with all the asp.net Membership controls. Also the methods to fill the Membergroup and Member pulldowns are using Membership methods, so it will work with the Umbraco members but also with other membership providers.

Once installed, an extra macro is added to the list. In your template select the Memberswitcher macro, optional specify a node to redirect to once logged in and that’s it.
<umbraco:Macro RedirectToNodeAfterLogin="" Alias="MemberSwitcher" runat="server"></umbraco:Macro>

When you visit the website you’ll see the control in action. First select the Membergroup and Member.

membershwitcher_1 
When you click the Login selected member button you’ll be logged in as you can see in the following screenshot that is using the asp.net LoginStatus control.

membershwitcher_2

Needless to say:
DO NOT USE THIS PACKAGE IN A PRODUCTION ENVIRONMENT!!

Download the package here

Comments [0] | | # 
# Tuesday, October 27, 2009
Tuesday, October 27, 2009 8:06:12 PM (GMT Standard Time, UTC+00:00) ( Umbraco )

A few weeks back I had this  issue when deploying a site. I needed to modify a documenttype to add a few properties and after a few minutes an error was raised and the only thing I could see when I deleted the property and saved the documenttype again, or opened a document based on that document type was the following error message (thanks Dan for the picture). 

error-delete (3)

Did I make a backup? ehh no, so I needed to fix this. Okay let’s look at  the error message again. An Object reference not set error is thrown while deleting the property. Love that Umbraco is open source and I can have a look at the source code. Below you see the method that is responsible for deleting the property from the documenttype.

   1:          public void delete()
   2:          {
   3:              // flush cache
   4:              FlushCache();
   5:   
   6:              // Delete all properties of propertytype
   7:              foreach(Content c in Content.getContentOfContentType(new ContentType(_contenttypeid)))
   8:              {
   9:                  c.getProperty(this).delete();
  10:              }
  11:              // Delete PropertyType ..
  12:              SqlHelper.ExecuteNonQuery("Delete from cmsPropertyType where id = " + this.Id);
  13:              this.InvalidateCache();
  14:          }

As you might have seen there is no check for null values on the 9 (c.getProperty(this).delete();). This is what caused the error while deleting the property. I assume it's sort of the same issue when opening a document. Now that I know this I can work on a solution. As I mentioned earlier, I needed to add properties to the document type. Below you find the code I’ve used to do that.

   1:   ContentType ct = ContentType.GetByAlias("_advertiser");
   2:          foreach (PropertyType i in ct.PropertyTypes)
   3:          {
   4:              if (i.Alias == "linkToSpecial")
   5:              {
   6:                  // Delete all properties of propertytype
   7:                  foreach (umbraco.cms.businesslogic.Content c in umbraco.cms.businesslogic.Content.getContentOfContentType(ct))
   8:                  {
   9:                      if (c.getProperty("linkToSpecial") == null)
  10:                      {
  11:                          c.addProperty(i, c.Version);
  12:                          c.Save();
  13:                      }
  14:                  }
  15:                  //Remove comment to delete the property from the doctype
  16:                  //i.delete();
  17:                  //ct.Save();
  18:              }
  19:          }

As you can see, I’ve used a  few hard coded values. _advertiser is the alias of the document type and linkToSpecial is the alias of the property that I wanted to add on the Document type. If you want to delete the property  remove  lines 9-13 and remove the comment on line 16 and 17. You can use this code in a usercontrol and use that usercontrol as a dashboard control. Needless to say, this code comes without a warranty.

I’ve added the issue to codeplex, please vote for it!. Hope this post is a nice work around for the issue.

Comments [0] | | # 
# Monday, August 10, 2009
Monday, August 10, 2009 7:18:04 PM (GMT Daylight Time, UTC+01:00) ( Umbraco )

Just a little quick tip. When you install Umbraco v4.0.2.1 (don't know about other versions) without runway and you publish your site, you will see a blank screen. This is because the data/umbraco.config file contains the runway site as you can see in the sample xml below. 

<!DOCTYPE umbraco[ &lt;!ELEMENT nodes ANY>  <!ELEMENT node ANY>  <!ATTLIST node id ID #REQUIRED> ]>
<root id="-1">
  <node id="1048" version="6f0d47e7-8cf1-43e5-a5ad-c687e0b78331" parentID="-1" level="1" writerID="0" 
creatorID="0" nodeType="1045" template="1042" sortOrder="2"
createDate="2008-05-02T09:52:36" updateDate="2009-02-18T10:19:26" nodeName="Runway Homepage"
urlName="runway-homepage" writerName="Administrator"
creatorName="Administrator" nodeTypeAlias="RunwayHomepage" path="-1,1048"> <data alias="bodyText"><![CDATA[<p>Runway gives you a bare-bones website that introduces you to a set of
well-defined conventions for building an umbraco website.</p> <p>The Runway website is very simple in form and provided without any design or functionality.
By installing Runway, you&rsquo;
ll begin with a minimal site built on best practices. You&rsquo;ll also enjoy the benefits of
speaking the same &ldquo;language&rdquo;
as the rest of the umbraco community by using common properties and naming conventions.</p> <p>Now that you know what Runway is, it is time to get started using Runway.</p>]]></data> <data alias="siteName">Runway</data> <data alias="siteDescription"><![CDATA[Off to a great start]]></data> ..................... </node> </root>

Work around for this issue is to republish the entire site first. I've added this issue to codeplex. Please vote here.

Comments [2] | | # 
# Tuesday, June 30, 2009
Tuesday, June 30, 2009 8:43:08 AM (GMT Daylight Time, UTC+01:00) ( UmbImport | Umbraco )

I'm very pleased to announce that I finally released the V1 version of UmbImport. For those of you who don't know what UmbImport is:

UmbImport helps you import content or members from any datasource into Umbraco. The following datasources are supported by default:

  • SQL Server
  • CSV file
  • XML file

You can also create your own custom DataAdapter. Check out the following links to screencasts to see the power of UmbImport. 

The package is added to our.umbraco.org the new Umbraco community site , so you can download it there or use the UmbImport site. On our.umbraco.org you will find a forum also where you can drop your questions/feature requests/ bugs etc. In August I will release a manual also. If you find any issues please report it on the forum or comment on this post.

Comments [0] | | # 
# Monday, May 04, 2009
Monday, May 04, 2009 8:06:31 PM (GMT Daylight Time, UTC+01:00) ( Umbraco )

In February I wrote this blogpost which describes the process of how to add a menu item to the context menu using the Umbraco V4 Event system. In UmbImport PRO I've used this mechanism. When I was testing this I came across a bug in my code when using a pagepicker, this was showing an empty tree in the node picker.

 EmptynodePicker

Then I got a little flashback to the level 2 course I attended last November where Niels told us to check if there was a menu attached to the node (which is not the case for the pagepicker) before adding menu items to it, otherwise an exception is thrown what will result in an empty tree. This is what happened in my case. In the example below I've only added node.Menu!= null check and now everything is working fine.

 

   1:  using System;
   2:  using umbraco.BusinessLogic;
   3:  using umbraco.cms.presentation.Trees;
   4:  using umbraco.interfaces;
   5:   
   6:  namespace UnpublishAction
   7:  {
   8:      /// <summary>
   9:      /// Add unpublish to the menu item
  10:      /// </summary>
  11:      public class AddUnpublishActionEvent :ApplicationBase
  12:      {
  13:          public AddUnpublishActionEvent()
  14:          {
  15:              BaseContentTree.BeforeNodeRender += new BaseTree.BeforeNodeRenderEventHandler(BaseTree_BeforeNodeRender);
  16:          }
  17:   
  18:          /// <summary>
  19:          /// Before a menu item gets rendered  we will add the unpublish action if the document is published
  20:          /// </summary>
  21:          private void BaseTree_BeforeNodeRender(ref XmlTree sender, ref XmlTreeNode node, EventArgs e)
  22:          {
  23:              ///Only unpublish when published
  24:              if (node.Menu!= null && !node.NotPublished.GetValueOrDefault(true))
  25:              {
  26:                  //Find the publish action and add 1 for the index
  27:                  int index = node.Menu.FindIndex(delegate(IAction a) { return a.Alias == "publish"; })+1;
  28:   
  29:                  //Insert unpublish action
  30:                  node.Menu.Insert(index, UnpublishAction.Instance);
  31:              }
  32:          }
  33:      }
  34:  }
  35:   

Download Source

Hope it didn't get you into trouble, sorry if it did.

Comments [0] | | # 
# Thursday, April 16, 2009
Thursday, April 16, 2009 11:02:37 PM (GMT Daylight Time, UTC+01:00) ( UmbImport | Umbraco )

This post is deprecated

Check out documentation on http://www.cmsimport.com/documentation.aspx instead

As I mentioned earlier it's possible to create a custom data adapter which can be plugged into umbImport. The free edition supports one custom data adapter, the pro edition will support multiple adapters.  In this multi part series I will demonstrate how you can create your own data adapter by building an RSS import adapter. In this first part we will create the basic adapter in later post we will refine the functionality. For this first part I've installed Umbraco 4.0.1 and the packages Blog4Umbraco and UmbImport beta 1.

Create the adapter

You can create a custom data adapter by deriving from two classes:

  • UmbImportLibrary.BaseTypes.ImportDataAdapter.
  • UmbImportLibrary.BaseTypes.ImportDataUI.

The ImportDataAdapter class provides the real communication to the datasource and holds a reference to the ImportDataUI class which is responsible for the user input. For our RSS import adapter we will start by creating the UI class.

 
   1:      public class RSSDataAdapterUI : ImportDataUI
   2:      {
   3:          private Panel _rssContentPanel = new Panel();
   4:          private Literal _selectRssSourceLiteral = new Literal();
   5:          private TextBox _rssLocation = new TextBox();
   6:   
   7:          protected override void OnInit(EventArgs e)
   8:          {
   9:              base.OnInit(e);
  10:   
  11:              _rssContentPanel.ID = "RssContentPanel";
  12:              _selectRssSourceLiteral.ID = "SelectRssSourceLiteral";
  13:              _selectRssSourceLiteral.Text = "Specify the RSSLocation";
  14:              _rssLocation.ID = "RssLocation";
  15:              _rssLocation.Width = 400;
  16:              _rssContentPanel.Controls.Add(new LiteralControl("
"));
  17:              _rssContentPanel.Controls.Add(_selectRssSourceLiteral);
  18:              _rssContentPanel.Controls.Add(new LiteralControl("
"));
  19:              _rssContentPanel.Controls.Add(_rssLocation);
  20:              _rssContentPanel.Controls.Add(new LiteralControl(" example http://feeds.feedburner.com/umbracoblog 
"
));
  21:   
  22:              this.Controls.Add(_rssContentPanel);
  23:          }
  24:   
  25:          /// 
  26:          /// Returns the Datasource
  27:          /// 
  28:          public override string DataSource
  29:          {
  30:              get
  31:              {
  32:                  return _rssLocation.Text;
  33:   
  34:              }
  35:          }
  36:      }

The OnInit method generates the form. The only real interesting thing in this class is the datasource property. This will be used in UmbImport to initialize the import with the selected datasource.

   1:      public class RSSDataAdapter : ImportDataAdapter
   2:      {
   3:          private ImportDataUI _xmlImportUI;
   4:   
   5:          /// 
   6:          /// Alias of the import adapter
   7:          /// 
   8:          public override string Alias
   9:          {
  10:              get { return "RssImport"; }
  11:          }
  12:   
  13:          /// 
  14:          /// Get XML Data
  15:          /// 
  16:          /// 
  17:          public override IDataReader GetData()
  18:          {
  19:              return XmlToDataReader(DataSource, "//item");
  20:          }
  21:   
  22:          /// 
  23:          /// Validates the selected datasource
  24:          /// 
  25:          public override bool Validate()
  26:          {
  27:              bool result = false;
  28:              try
  29:              {
  30:                  using (IDataReader datareader = XmlToDataReader(DataSource, "//item"))
  31:                  {
  32:                      result = true;
  33:                  }
  34:              }
  35:              catch
  36:              {
  37:                  result = false;
  38:              }
  39:              return result;
  40:          }
  41:   
  42:          /// 
  43:          /// Holds a reference to the UI control of the adapter
  44:          /// 
  45:          public override ImportDataUI UIControl
  46:          {
  47:              get
  48:              {
  49:                  if (_xmlImportUI == null)
  50:                  {
  51:                      _xmlImportUI = new RSSDataAdapterUI();
  52:                      _xmlImportUI.ID = "RssImport";
  53:                  }
  54:                  return _xmlImportUI;
  55:              }
  56:          }
  57:      }

The Alias property will return the unique alias that we can use to select our adapter during the Import process. The GetData method will return a datareader initialized with the datasource. When importing XML it needs to be converted to a datareader first. The ImportDataAdapter Base class has a method XmlToDataReader what will convert the xml file to  a datareader. The Validate method will check if the selected datasource is valid. The UIControl property holds a reference to the RSSDataAdapterUI class. The ImportDataAdapter base class has more properties/methods that you can override but for this data adapter we are done.

When you compile the project and put the DLL in the bin folder of the Umbraco install the DLL will be picked up automatically by UmbImport.

Using the RSS import adapter

When you start UmbImport you will see in step 2 that you can select the RssImport data adapter

image

In the next step we will see the form that we have created in the RSSDataAdapterUI class. Here we can specify the RSS location.

image

In the Next step we can specify the location where to store the blogposts (Note: When using the Blog package blogposts will be arranged by data automatically) and we select the blogpost as document type.  In step 5 we will create the mapping between the fields from the RSS feed and the Umbraco Document Properties. You will see a lot of other fields from the RSS Feed. Just ignore them for now. In a later post we will filter the columns.

image

The result

When you click next and next again on the confirm screen the RSS feed will be imported.

image 

In the next post I will show you how to Import the comment data with the same Adapter. I will also show you how to filter the columns for the property mapping dropdowns. When you want to play with this import adapter then  download the source here.

Comments [0] | | # 
# Wednesday, April 01, 2009
Wednesday, April 01, 2009 9:24:15 PM (GMT Daylight Time, UTC+01:00) ( Umbraco )

Last week I had a meeting with Nico Lubbers and we discussed the possibility of adding Dictionary Items on the fly. You can read in this post from Tim that it's been done before, but we want to make sure that it works in every situation (Template, Usercontrol and Xslt) and we don't want a baseclass for just adding items to the dictionary . Also we want logical keys (like savebuttonText, companynameLabel etc.) instead of generated keys. Basically we want the pain of adding  the dictionary items in the templates, usercontrols and xslt's  but we don't want the pain of adding  the dictionary items in Umbraco manually. Now before your read on I must warn you that the solution is not based on an architecture ;-)

What happens if Umbraco can't find a dictionary item?

When you add a dictionary item on a template, usercontrol or XSLT and Umbraco can't find it an exception is thrown. Umbraco will catch that exception and write it to the tracelog. For this blogpost I've used a standard Umbraco 4.0.1 instance with Runway and the modules contact form and standard navigation installed. In the example below I'be added a few non existing Dictionary items to my code.

Template

<umbraco:Item field="#Template_DetailHeader" runat="server"></umbraco:Item>

XSLT

<xsl:value-of select="umbraco.library:GetDictionaryItem('XSLT_FromDictionary')"/>

Usercontrol Markup

<%=umbraco.library.GetDictionaryItem("UserControl_Details") %>

Usercontrol Codebehind

lb_name.Text = umbraco.library.GetDictionaryItem("UserControl_LabelCaption"); 

Now when you view the page you will see empty places instead of the dictionary values. What's more interesting is to view the page with the queryparameter ?umbdebugshowtrace=true what shows you the trace info of the page like the image below.

traceinfo 

How can we get that data?

While it's nice that we can view the data, it would be great if we can parse the trace info and automatically add the missing dictionary items. This can be done using a Trace Listener, when an item is added to the trace you can configure 1 or more trace listeners that recieve the message then we can parse. In the  Class below I derive from the TraceListener class. The TraceListener methods calls the AddItemUnknown method which check the message with a regular expression. If the message can be parsed the unknow dictionary key is retrieved from the message and will be added to Umbraco. To ensure we will see the value when we refresh the page the key will also be added as the value for every language.

   1:  using System;
   2:  using System.Diagnostics;
   3:  using System.Text.RegularExpressions;
   4:  using umbraco.BusinessLogic;
   5:  using umbraco.cms.businesslogic.language;
   6:   
   7:  namespace SoetemanSoftware.Tools
   8:  {
   9:      public class AddUnknownDictionaryItemsListener : TraceListener
  10:      {
  11:          public override void Fail(string message)
  12:          {
  13:              AddItemWhenUnknown(message);
  14:          }
  15:   
  16:          public override void Fail(string message, string detailMessage)
  17:          {
  18:              AddItemWhenUnknown(message);
  19:          }
  20:   
  21:          public override void Write(string message)
  22:          {
  23:              AddItemWhenUnknown(message);
  24:          }
  25:   
  26:          public override void WriteLine(string message)
  27:          {
  28:              AddItemWhenUnknown(message);
  29:          }
  30:          public override void WriteLine(string message, string category)
  31:          {
  32:              AddItemWhenUnknown(message);
  33:          }
  34:   
  35:          /// <summary>
  36:          /// Parse at the message 
  37:          /// </summary>
  38:          /// <param name="message"></param>
  39:          private void AddItemWhenUnknown(string message)
  40:          {
  41:              try
  42:              {
  43:                  Match match = Regex.Match(message, "(Error returning dictionary item ')(.*)(' --)", RegexOptions.Multiline);
  44:                  if (match.Groups.Count > 1)
  45:                  {
  46:                      //Get key from the mach collection
  47:                      string key = match.Groups[2].Value;
  48:                      //Check if key is allready in Umbraco
  49:                      if (!umbraco.cms.businesslogic.Dictionary.DictionaryItem.hasKey(key))
  50:                      {
  51:                          //Add new key with default value to Umbraco
  52:                          int dictionaryID = umbraco.cms.businesslogic.Dictionary.DictionaryItem.addKey(key, string.Format("[{0}]", key));
  53:   
  54:                          var dictionaryItem = new umbraco.cms.businesslogic.Dictionary.DictionaryItem(dictionaryID);
  55:                          foreach (Language l in Language.getAll)
  56:                          {
  57:                              dictionaryItem.setValue(l.id, string.Format("[{0}]", key));
  58:                          }
  59:                          dictionaryItem.Save();
  60:                      }
  61:                  }
  62:              }
  63:              catch (Exception ex)
  64:              {
  65:                  //Logic may never break on this Listener
  66:                  Log.Add(LogTypes.Error, -1, string.Format("Error in Dictionary listener when adding item to dictionary {0} ", ex.Message));
  67:              }
  68:          }
  69:      }
  70:  }

Configure the TraceListener

When you want to use the TraceListener you have to configure it in the web.config. Add the following section to your web.config file.

    <system.diagnostics>
        <trace autoflush="true" indentsize="4">
            <listeners>
                <remove name="Default"/>
                <add name="AddDictionaryListener" type="SoetemanSoftware.Tools.AddUnknownDictionaryItemsListener,AddUnknownDictionaryItemsListener" />
            </listeners>
        </trace>
    </system.diagnostics>

Also modify the existing Trace element by adding the writeToDiagnosticsTrace="true". This will forward the ASP.NET trace messages to our AddUnknownDictionaryItemsListener. Simply set this attribute to false if you don't want the items to be added automatically.

<trace enabled="true" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" writeToDiagnosticsTrace="true" /> 

View the output

When you configured the trace listener and hit the page again the missing dictionary items are added to Umbraco.

dictionary items

Refresh the page again and you will see the following output

output

Sometimes you will encounter caching issues. When you want to avoid that use the umbdebugshowtrace=true querystring parameter which will prevent caching.

Download

You can download the DLL here. You can also dowload the source here.

Comments [2] | | # 
# Wednesday, March 25, 2009
Wednesday, March 25, 2009 9:17:38 PM (GMT Standard Time, UTC+00:00) ( UmbImport | Umbraco )

Exactly 1 year ago I've downloaded Umbraco for the very first time and loving it ever since. One package that I've created in September last year was UmbImport. While that was a cool package I'm happy to announce that today I've released a new beta version of UmbImport. The most requested feature was the ability to import members. I'm happy to announce that this feature is implemented now. Below you find  a screencast that demo's the member import.


Import members using umbImport from Richard Soeteman on Vimeo.

More changes

While member import is the biggest thing, it's nice to know what's also changed since the last version. I will be blogging about each feature/change the next coming weeks.

  • DataAdapters. The previous version of UmbImport only supported three datasources (Sql Server, CSV and XML). With the new version you can plugin your own datasource by creating a custom DataAdapter (in the free version this will be limited to 1).
  • Events during import. Events are added that you can handle in your custom code that notify you when a record is imported.
  • No more Dashboard control. UmbImport is moved from a dashboard control to a tree in the menu.
  • Better install experience. With Umbraco 4 it's easier to create a package and modify config files etc via PackageActions. UmbImport uses default and Custom package actions to improve the install experience.

What's next?

Within a few weeks I hope to get V1 released and have proper documentation that describe the functionality. After that I will work on UmbImport PRO, which will contain a few extra features such as save wizard steps, scheduled imports, automatic field mapping, support for more than 1 custom DataAdapter and more... The PRO version of UmbImport will not be free(prices are not available yet).

As always I hope that you like the package which you can download here

Comments [3] | | # 
# Tuesday, March 17, 2009
Tuesday, March 17, 2009 9:04:43 AM (GMT Standard Time, UTC+00:00) ( Umbraco )

Currently I'm working on an Umbraco V4 site and had an issue when rendering the meta tags.

The problem:

In my template I've added the keywords and description tag in the head to render the meta information for keywords and description.

<head runat="server">
<meta name="keywords" content="<umbraco:Item field='metaKeywords' recursive='true' runat='server'></umbraco:Item>" />
<meta name="description" content="<umbraco:Item field='metaDescription' recursive='true' runat='server'></umbraco:Item>" />
</head>

I've seen several examples that used the <Umbraco:Item> control with single quotes to set the properties so I thought this should work. However when the output was different then I would expect:

<head>
    <meta name="keywords" content="&amp;lt;umbraco:Item field='metaKeywords' recursive='true' runat='server'>&lt;/umbraco:Item>" />
    <meta name="description" content="&amp;lt;umbraco:Item field='metaDescription' recursive='true' runat='server'>&lt;/umbraco:Item>" />
</head>

The Cause:

When I took a detailed look on this issue I saw that the head tag in my template had a Runat="Server" attribute. When I removed this attribute the output was rendered fine. I'm using a few Ajax Controls on forms that throws an error when it can't find a Head tag with the Runat="Server" attribute, so simply removing it isn't the solution.

The solution,  Macro's to the rescue:

With a Macro and an XSLT file this issue can easily be solved and after thinking of it it's also a better solution because it's a better separation between HTML and ASP.Net code. To solve it you just create a Macro that excepts two parameters (keywords and description). Then create an XSLT file that outputs the values like this example:

<?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"
    exclude-result-prefixes="msxml umbraco.library">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<xsl:variable name="keywords" select="/macro/keywords"/>
<xsl:variable name="description" select="/macro/description"/>
<xsl:template match="/">
<meta name="keywords" content="{$keywords}" />
<meta name="description" content="{$description}" />
</xsl:template>
</xsl:stylesheet>

Then use the Macro in your template:

<umbraco:Macro keywords="[$metaKeywords]" description="[$metaDescription]" Alias="MetaTags" runat="server"></umbraco:Macro>

Now you have exact the same functionality and it will work in any situation. Because I'm using the $ sign before the fieldnames that are passed to the Umbraco. Umbraco will do a recursive search for the values of those properties, just like the recursive attribute does on the Umbraco:item tag.

Comments [2] | | # 
# Tuesday, March 03, 2009
Tuesday, March 03, 2009 12:39:34 PM (GMT Standard Time, UTC+00:00) ( Package | Umbraco )

Last week Warren Buckley asked for a URL Rewrite Action to use in his awesome next version of the  Umbraco Creative Website Starter site (if Warren release it make sure you download it, it's great!!). I was already thinking of creating some sort of packactions library so I started working on that a little bit sooner (and more in a hurry) than expected. The result is  a new Codeplex project called Package Actions Contrib that will hopefully be used by the community and I'm also hoping for a lot of contributors that will be part of this project.

Package Actions?

Package actions are great to include some custom functionality during the install of your package, just by implementing the IPackageAction interface and the use of Package XML in the package creator. This PDF describes all default package actions that are included in the V4 release of umbraco and it also describes how to use them.

AddUrlRewriteRule Action

Currently only one package action is included in the project. That is the AddUrlRewriteRule Action. With the AddUrlRewriteRule  you can add a new rewrite rule to the UrlRewriting.config file. The xml snippet below descibes the xml for adding the UrlRewrite rule to the config file. The action element is the normal element that you must include for each package action. The Alias is the alias that is used in the AddUrlRewriteRule class. The undo option is implemented but will not work because of this bug I recently found. And the add element is what you normally will add manually in the UrlRewriting.config file.

<Action runat="install" undo="true" alias="AddUrlRewriteRule">
    <add name="CWS_emaiAFriendID" 
    virtualUrl="^~/email-a-friend/(.[0-9]*).aspx" 
    rewriteUrlParameter="ExcludeFromClientQueryString" 
    destinationUrl="~/email-a-friend.aspx?nodeID=$1" 
    ignoreCase="true" />
</Action>

What's next

The next thing that will be added to the project is not a new action but it will be a tool where you can test your package action and package action xml without actually having to install a package. Instead you can upload the dll and enter the xml and press the testbutton to validate that the action installed or uninstalled correctly or what errors did occur. Also I like to have some documentation in the way that the normal package actions are described.

Contribute to the project

I think this project can only be a success with help from the community. So if you have some really cool custom package action now that could be useful to share please apply a patch on Codeplex or contact me by mail, also if you just have a great idea that could be included in the project.

Click here to visit the Codeplex project site. Hope to see some really nice package actions included in the contrib.

Comments [2] | | # 
# Sunday, February 22, 2009
Sunday, February 22, 2009 10:38:11 AM (GMT Standard Time, UTC+00:00) ( ASP.NET | Umbraco )

Yesterday I gave a demo about events in Umbraco V4. I did this by describing a few "Business problems" that could be solved using events. A few facts about events in Umbraco V4:

  • You find events on every Component in Umbraco.
  • To uses events, you need references to the businesslogic, cms and interfaces assemlies.
  • To use events, you have to create a class that uses umbraco.BusinessLogic.ApplicationBase as the base class.  In the constructor you can wire up the event handler. These classes will be automaticly picked up when you put the file in the App_Code folder of Umbraco or put the compiled DLL in the bin folder of Umbraco. I prefer the last one.
  • Most of the event args derive from CancelEventArgs, so we can cancel the operation.
  • Because an event executes in the background you should always document that you're using events and make sure you will write a logmessage when you're event handler gets executed.

For my demo's, I've used an Umbraco V4 installation with the Creative Website Wizard installed.

Demo 1 Auto Expire news

In the first demo.I've showed how the document.BeforePublish can be used to check if  a news item is allready expired, if so cancel the publish event, otherwise check if the expire date is set and if not, set the expire date to 14 days from now.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:  using umbraco.BusinessLogic;
   5:  using umbraco.cms.businesslogic.web;
   6:   
   7:  namespace AutoExpire
   8:  {
   9:      /// <summary>
  10:      /// 
  11:      /// </summary>
  12:      public class Expire :ApplicationBase
  13:      {
  14:          /// <summary>
  15:          /// Constructor used to wire up the event
  16:          /// </summary>
  17:          public Expire()
  18:          {
  19:              Document.BeforePublish += new Document.PublishEventHandler(Document_BeforePublish);
  20:          }
  21:   
  22:          /// <summary>
  23:          /// Check if the news item is expired, or check if we need to set the expire date
  24:          /// </summary>
  25:          void Document_BeforePublish(Document sender, umbraco.cms.businesslogic.PublishEventArgs e)
  26:          {
  27:              //Always LOG
  28:              Log.Add(LogTypes.Custom, sender.Id, "Event raised");
  29:              //Check if the doctype is news
  30:              if (sender.ContentType.Alias == "News")
  31:              {
  32:                  //Check if the expiredate is filled and the value is not allready expired
  33:                  if (sender.ExpireDate != DateTime.MinValue && sender.ExpireDate < DateTime.Now)
  34:                  {
  35:                      //Item is expired, cancel the publish
  36:                      e.Cancel = true;
  37:                  }
  38:                  else
  39:                  {
  40:                      //Date is not set set it now and save the doc. Probably better to to this in the before save event
  41:                      //since this is a demo it's allowed to do it here
  42:                      sender.ExpireDate = DateTime.Now.AddDays(14);
  43:                      sender.Save();
  44:   
  45:                  }
  46:              }
  47:          }
  48:      }
  49:  }

During this demo someone asked if it's possible to show a custom message in the speechbubble event when an event is canceled. This is not possible (tested it this morning), I've created an item on CodePlex.

Demo 2 Auto archive news

In this Demo I've created some functionality to automaticly move a news item to the archive folder when a content manager sets the archive boolean to true. First an archive boolean must be added to the news document and a new Archive folder must be created where we can store the archived News items in.

   1:  using umbraco.BusinessLogic;
   2:  using umbraco.cms.businesslogic.web;
   3:   
   4:  namespace AutoMoveToArchive
   5:  {
   6:      /// <summary>
   7:      /// Auto move demo
   8:      /// </summary>
   9:      public class AutoMoveToArchive : ApplicationBase
  10:      {
  11:          /// <summary>
  12:          /// Wire up the event handler
  13:          /// </summary>
  14:          public AutoMoveToArchive()
  15:          {
  16:              Document.BeforeSave += new Document.SaveEventHandler(Document_BeforeSave);
  17:          }
  18:   
  19:          /// <summary>
  20:          /// Before a documents get saved we wil check if the archived proerty is.
  21:          /// When it's set move it to the archive folder.
  22:          /// Again it;s demo code, normally you should also check if it's not allready IN the archived foder ;-)
  23:          /// </summary>
  24:          void Document_BeforeSave(Document sender, umbraco.cms.businesslogic.SaveEventArgs e)
  25:          {
  26:              // Set Arrchive folder id, no excuse to use hard coded values in Umbraco ;-)
  27:              int archiveId = 1123; 
  28:              //Log that we are doing something
  29:              Log.Add(LogTypes.Custom, sender.Id, "Document After save Raised");
  30:   
  31:              //Check if the item is news and must be archived
  32:              if (sender.ContentType.Alias == "News" && sender.getProperty("archived").Value.Equals(1))
  33:              {
  34:                  //Yes, move to archive
  35:                  sender.Move(archiveId);
  36:   
  37:                  //Unpublish from current Node
  38:                  umbraco.library.UnPublishSingleNode(sender.Id);
  39:                  sender.UnPublish();
  40:                  //Publish will get called if the user selected Save and publish
  41:              }
  42:          }
  43:      }
  44:  }

Demo 3 Add Unpublish menu Item to the Context menu

In this demo I've showed that we can use events to modify the context menu. I'm missing a unpublish item  in the context menu. This example is a little bit harder because we we need to create a menu item also. A menu item can be created to create a class that derives from the IAction interface. I will leave the explanation of this interface for a future blogpost.

   1:  using System;
   2:  using umbraco.BusinessLogic;
   3:  using umbraco.cms.presentation.Trees;
   4:  using umbraco.interfaces;
   5:   
   6:  namespace UnpublishAction
   7:  {
   8:      /// <summary>
   9:      /// Add unpublish to the menu item
  10:      /// </summary>
  11:      public class AddUnpublishActionEvent :ApplicationBase
  12:      {
  13:          public AddUnpublishActionEvent()
  14:          {
  15:              BaseContentTree.BeforeNodeRender += new BaseTree.BeforeNodeRenderEventHandler(BaseTree_BeforeNodeRender);
  16:          }
  17:   
  18:          /// <summary>
  19:          /// Before a menu item gets rendered  we will add the unpublish action if the document is published
  20:          /// </summary>
  21:          private void BaseTree_BeforeNodeRender(ref XmlTree sender, ref XmlTreeNode node, EventArgs e)
  22:          {
  23:              ///Only unpublish when published
  24:              if (node.Menu!= null && !node.NotPublished.GetValueOrDefault(true))
  25:              {
  26:                  //Find the publish action and add 1 for the index, so the position of the ubpublish  is direct after the publish menu item
  27:                  int index = node.Menu.FindIndex(delegate(IAction a) { return a.Alias == "publish"; })+1;
  28:   
  29:                  //Insert unpublish action
  30:                  node.Menu.Insert(index, UnpublishAction.Instance);
  31:              }
  32:          }
  33:      }
  34:  }

Download the complete solution for this demo here.

Demo 4 Invisible for Writer Usertype

In the last demo I've showed how to use the AfterNodeRender event to make protected nodes invisble for the Writer UserType

   1:  using System;
   2:  using umbraco.BusinessLogic;
   3:  using umbraco.cms.presentation.Trees;
   4:   
   5:  namespace OnlyForAdmins
   6:  {
   7:      /// <summary>
   8:      /// Makes a document invisible for writers
   9:      /// </summary>
  10:      public class MenuIsNotForWriters : ApplicationBase
  11:      {
  12:          /// <summary>
  13:          /// Wire up the event handler
  14:          /// </summary>
  15:          public MenuIsNotForWriters()
  16:          {
  17:              BaseContentTree.AfterNodeRender += new BaseTree.AfterNodeRenderEventHandler(BaseContentTree_AfterNodeRender);
  18:          }
  19:   
  20:          /// <summary>
  21:          /// Removes node from menu tree if page is protected and the user is a writer
  22:          /// </summary>
  23:          void BaseContentTree_AfterNodeRender(ref XmlTree sender, ref XmlTreeNode node, EventArgs e)
  24:          {
  25:              //check if page is protecetd and the usertype is writer
  26:              if (node.IsProtected.GetValueOrDefault(false) && umbraco.helper.GetCurrentUmbracoUser().UserType.Alias == "writer")
  27:              {
  28:                  //Writers cannot see protected pages
  29:                  sender.Remove(node);
  30:              }
  31:          }
  32:      }
  33:  }

Overview of all Events

Class Events
Access  
 

BeforeSave
AfterSave
BeforeAddProtection
AfterAddProtection
BeforeRemoveProtection
AfterRemoveProtection
BeforeAddMemberShipRoleToDocument AfterAddMemberShipRoleToDocument BeforeRemoveMemberShipRoleToDocument AfterRemoveMemberShipRoleToDocument BeforeRemoveMembershipUserFromDocument AfterRemoveMembershipUserFromDocument BeforeAddMembershipUserToDocument AfterAddMembershipUserToDocument

BaseTree  
 

BeforeNodeRender
AfterNodeRender

CMSNode  
 

BeforeSave
AfterSave
AfterNew
BeforeDelete
AfterDelete
BeforeMove
AfterMove

content  
 

BeforeUpdateDocumentCache
AfterUpdateDocumentCache
BeforeClearDocumentCache
AfterClearDocumentCache
BeforeRefreshContent
AfterRefreshContent

CreatedPackage  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete
BeforePublish
AfterPublish

DataType  
 

Saving
New
Deleting

Dictionary  
 

Saving
New
Deleting

Document  
 

BeforeSave 
AfterSave 
New 
BeforeDelete 
AfterDelete 
BeforePublish 
AfterPublish 
BeforeSendToPublish 
AfterSendToPublish 
BeforeUnPublish 
AfterUnPublish 
BeforeCopy 
AfterCopy 
BeforeRollBack 
AfterRollBack 
BeforeAddToIndex 
AfterAddToIndex

DocumentType  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete

Domain  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete

InstalledPackage  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete

Language  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete

Macro  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete

Media  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete

MediaType  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete

Member  
 

BeforeSave
AfterSave
New
BeforeAddGroup
AfterAddGroup
BeforeRemoveGroup
AfterRemoveGroup
BeforeAddToCache
AfterAddToCache
BeforeDelete
AfterDelete

MemberGroup  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete

MemberType  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete

StyleSheet  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete

StylesheetProperty  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete

Template  
 

BeforeSave
AfterSave
New
BeforeDelete
AfterDelete

User  
 

Saving
New
Disabling
Deleting
FlushingFromCache
BeforeSave
AfterSave
New
BeforeDelete
AfterDelete
BeforePublish
AfterPublish

Comments [6] | | # 
# Thursday, February 05, 2009
Thursday, February 05, 2009 7:45:52 PM (GMT Standard Time, UTC+00:00) ( Umbraco )

One of the things I like most about Umbraco is that you can extend it in a way that your custom functionality has exactly the same behaviour as the standard Umbraco functionality. The biggest compliment you can get from a user is when they say "I found a bug in Umbraco" while it's your own control (or love the way Umbraco works when it's your own control)..  One of the things to achieve that is to use the same mechanism to show messages to the user as Umbraco does. In this post we create a simple user control where the user can type a message and we will show that message using the Umbraco Speech bubble mechanism as shown in the image below.

 speechbubble

 

To build this we need to create a standard WebUserControl called SpeechBubble.ascx. In the ascx file we add the following code that creates the UI and hooks up an Event Handler for the submit button.

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="SpeechBubble.ascx.cs" Inherits="SpeechBubble" %>
Message:<asp:TextBox ID="MessageTextBox" runat="server"></asp:TextBox> <asp:Button ID="SpeechButton" runat="server" onclick="SpeechButton_Click" Text="Submit" />

In the Code Behind file we will add the following code:

   1:  using System;
   2:  using umbraco.BasePages;
   3:   
   4:  public partial class SpeechBubble : System.Web.UI.UserControl
   5:  {
   6:      protected void SpeechButton_Click(object sender, EventArgs e)
   7:      {
   8:          if (Page is BasePage)
   9:          {
  10:              //Okay we are in Umbraco
  11:              ((BasePage)Page).speechBubble(BasePage.speechBubbleIcon.info, "Message", MessageTextBox.Text);
  12:          }
  13:      }
  14:  }

We will run this code as an Dashboard Control which runs in the Umbraco context. Therefore we can cast the Page Type to a the Umbraco BasePage type and then use the speechBubble method which displays the message. If you want to play with this yourself, you can download the usercontrol here. Add the files in this zip file to the UserControl folder of Umbraco  and Modifiy the Dashboard.Config file which you can find in the Config folder:

<?xml version="1.0" encoding="utf-8" ?> 
<dashBoard>
<section>
    <areas>
        <area>developer</area>
    </areas>

    <tab caption="SpeechBubble">
        <control>/usercontrols/SpeechBubble.ascx</control>
    </tab>
</section>
</dashBoard>

Then when you login to Umbraco and go to the developer section you can send messages to yourself. Have fun.

Comments [3] | | # 
# Friday, January 23, 2009
Friday, January 23, 2009 4:19:45 PM (GMT Standard Time, UTC+00:00) ( Package | Umbraco )

Content Maintenance Dashboard is a simple package to bulk publish, unpublish and delete content items based on name,state and document type. When you install it and go to the developer section of Umbraco you can see the search screen like the image below. From the search results screen you can perform actions on a single Item or you can choose to perform the same action on all the items in the search result.

 contentmaintenance

The package is using sql to search for the documents. I'm using the new datalayer so it should be working on any type of database that is supported by Umbraco, however I only tested the package on SQL Server. I have tested this package also on the Umbraco RC3 release(this is probably the first package that is compiled against the RC3 Binaries). You can download the package here, if you want the complete source code you can download it here.

Comments [7] | | # 
# Wednesday, January 21, 2009
Wednesday, January 21, 2009 8:38:02 AM (GMT Standard Time, UTC+00:00) ( Umbraco )

Voor de echte Umbracoholics in de Benelux organiseert de Umbraco gebruikersgroep Benelux op zaterdag 21 februari haar tweede gebruikersbijeenkomst in Amsterdam. Het belooft weer een zeer informatieve dag te worden waar de nadruk uiteraard zal liggen op Umbraco V4, waarvan de tweede release candidate inmiddels uit is. In een informele sfeer zullen onder andere de volgende  onderwerpen behandeld worden.

  • Umbraco een showcase. 
  • Umbraco V4, een overzicht
  • Canvas (voorheen Live Editing)
  • DataLayer.
  • Event model.
  • Uitbreiden van Umbraco.

Uiteraard bestaat er ook de mogelijkheid je eigen implementaties, of ontwikkelde packages te tonen.

De gebruikersdag wordt gehouden bij Mirabeau in Amsterdam. Mirabeau is als full service internet bureau betrokken bij enkele grotere Umbraco implementaties, waarvan er één als showcase gepresenteerd zal worden.  Klik hier voor adres informatie en een routebeschrijving.

We zullen beginnen om 9:30 en eindigen rond 16:00. De gebruikersdag is gratis, het enige dat we vragen is je even in te schrijven via dit formulier. Doe dit snel, het aantal plaatsen is beperkt.

Tot zaterdag 21 februari.  

Comments [0] | | # 
# Thursday, January 08, 2009
Thursday, January 08, 2009 9:41:44 PM (GMT Standard Time, UTC+00:00) ( Tools | Umbraco )

Last September I've created a package called UmbImport which is capable of importing content  from various datasources into Umbraco. For a demo of this package please checkout the video below.

UmbImport from Richard Soeteman on Vimeo.

The package was based on Beta1 of Umbraco 4 and threw errors when installing on Umbraco 4 RC.  I've fixed these bugs and created a new package. Currently I'm also working on a new version that is capable of importing members and I will add support for plugging in your own datasource, so you can import data from any datasource you like.

Download umbImport here

Comments [7] | | #