Navigatie

Archief

Categorieën

Blogroll

Contact

Send mail to the author(s) E-mail

View Richard Soeteman's profile on LinkedIn

RSS 2.0 | Atom 1.0 | CDF

Disclaimer
De inhoud van deze weblog betreft uitsluitend mijn persoonlijke mening, niet die van mijn werkgever. Mijn werkgever is niet verantwoordelijk voor de inhoud en sluit hierbij iedere aansprakelijkheid uit.

Sign In

Zoeken

 Tuesday, June 30, 2009
Tuesday, June 30, 2009 7:43:08 AM (W. Europe Daylight Time, UTC+02:00) (  |  )

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 7:06:31 PM (W. Europe Daylight Time, UTC+02:00) ( )

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] | | # 
 Friday, April 17, 2009
Thursday, April 16, 2009 10:02:37 PM (W. Europe Daylight Time, UTC+02:00) (  |  )

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("<table><tr><td width=\"150\">"));
  17:              _rssContentPanel.Controls.Add(_selectRssSourceLiteral);
  18:              _rssContentPanel.Controls.Add(new LiteralControl("</td><td>"));
  19:              _rssContentPanel.Controls.Add(_rssLocation);
  20:              _rssContentPanel.Controls.Add(new LiteralControl(" example http://feeds.feedburner.com/umbracoblog </td></tr></table>"));
  21:   
  22:              this.Controls.Add(_rssContentPanel);
  23:          }
  24:   
  25:          /// <summary>
  26:          /// Returns the Datasource
  27:          /// </summary>
  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:          /// <summary>
   6:          /// Alias of the import adapter
   7:          /// </summary>
   8:          public override string Alias
   9:          {
  10:              get { return "RssImport"; }
  11:          }
  12:   
  13:          /// <summary>
  14:          /// Get XML Data
  15:          /// </summary>
  16:          /// <returns></returns>
  17:          public override IDataReader GetData()
  18:          {
  19:              return XmlToDataReader(DataSource, "//item");
  20:          }
  21:   
  22:          /// <summary>
  23:          /// Validates the selected datasource
  24:          /// </summary>
  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:          /// <summary>
  43:          /// Holds a reference to the UI control of the adapter
  44:          /// </summary>
  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 8:24:15 PM (W. Europe Daylight Time, UTC+02:00) ( )

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 (W. Europe Standard Time, UTC+01:00) (  |  )

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] | | #