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

# Friday, 03 October 2014
Friday, 03 October 2014 12:14:22 (GMT Daylight Time, UTC+01:00) ( SEO Checker | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

Recently Google announced that it will use HTTPS as a ranking signal in the future. With SEO Checker I don’t follow trends but this one will be in a future release since it’s coming from Google itself. But this will be a change in the configuration screen since it’s too bloated already, and that is not something for a small bug fix release. Anyway a customer already asked if there was an easy way to do this in SEO Checker. The answer is yes. Until today this wasn’t documented but if you reference the SEOChecker.Extensions project you can use the UrlRewriterProviderBase class.

When you see  the snippet below you’ll see that it’s really easy. Just modify the UrlBuilder object that is coming in. In the case of always https we set the scheme to HTTPS when the incoming request comes from http. Compile this class add it to the bin folder and it will be picked up automatically by SEO Checker and redirects to Https for all website requests (umbraco requests are not redirected automatically).

   1: /// <summary>
   2:  /// Forces Redirect to HTTPS
   3:  /// </summary>
   4:  public class HTTPSRedirect : UrlRewriteProviderBase
   5:  {
   6:      public override void RewriteUrl(UrlBuilder builder, UrlRewriteConfiguration config)
   7:      {
   8:          if (builder.Scheme.Equals("http", StringComparison.InvariantCultureIgnoreCase))
   9:          {
  10:              builder.Scheme = "https";
  11:          }
  12:      }
  13:  }

Canonical URL

If you are familiar with SEO Checker already you have probably used the Library methods  to generated all meta data. This library generates the canonical link tag also. When you rewrite your url’s using the SEOChecker UrlRewriter it will use your class also to determine the canonical url (’'). This is a benefit you don’t get when you apply your own rewrite rules in web.config.

   1: <title>Getting Started | SEO Checker</title>
   2: <meta name="description" content="You&#39;ve installed Umbraco and the &quot;Txt&quot; Starter Kit." />
   3: <meta name="robots" content="index, follow" />
   4: <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   5: <link rel="canonical" href="https://soetemansoftware.nl/getting-started"/>

Download the source for this project. Drop the SEOChecker.HTTPSRedirect.dll in the bin folder and it will start redirecting to HTTPS immediately.

Happy rewriting!

Comments [0] | | # 
# Thursday, 05 December 2013
Thursday, 05 December 2013 14:52:40 (GMT Standard Time, UTC+00:00) ( CMSImport | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

I’ve just released version 2.5 of CMSImport. This version is optimized for Umbraco V7 and should work as expected on older versions.One thing that’s changed is the use of Field adapters, the feature you only use when you need to manipulate data during import.  The Field adapters  still work but there is a better approach now, called Field providers. This change was already on the list of changes for CMSImport version 3 but got into the 2.5 release due a change Data type settings change in Umbraco V7. As you can see in the image below Id’s used to be GUIDS and are changed to alias strings in V7.

datatype-changes

What is a Field provider?

A field provider is a class that implements IFieldProvider and gets called during the import process to convert a value to the format Umbraco expects. For the above mentioned data type it will convert text values to the corresponding id values.  Below the Field adapter that is used in CMSImport for the Dropdownlist data type.

   1: /// <summary>
   2: /// Responsible to map the id of the dropdownvalue based on  the text value.
   3: /// Internally Umbraco uses this value.
   4: /// </summary>
   5: [FieldProvider(PropertyEditorAlias = "a74ea9c9-8e18-4d2a-8cf6-73c6206c5da6", Priority = FieldProviderPrio.Medium)]
   6: [FieldProvider(PropertyEditorAlias = "Umbraco.DropDown", Priority = FieldProviderPrio.Medium)]
   7: public class DropdownFieldProvider : IFieldProvider
   8: {
   9:   
  10:     /// <summary>
  11:     /// Parse the data for the dropdownlist
  12:     /// </summary>
  13:     /// <param name="value">The value to parse</param>
  14:     /// <param name="property">The property</param>
  15:     /// <param name="fieldProviderOptions">The options</param>
  16:     /// <returns></returns>
  17:     /// <exception cref="System.NotImplementedException"></exception>
  18:     public object Parse(object value, Property property, FieldProviderOptions fieldProviderOptions)
  19:     {
  20:         if (!value.IsNullOrEmpty())
  21:         {
  22:             int datatypeId = property.PropertyType.DataTypeDefinition.Id;
  23:  
  24:             string dropdownValue = PreValueHelper.GetDropdownValueByText(value, datatypeId);
  25:             if (!string.IsNullOrEmpty(dropdownValue))
  26:             {
  27:                 value = dropdownValue;
  28:             }
  29:         }
  30:         return value;  
  31:     }
  32: }

One Field provider for more than one data type

As you can see in the above example. It’s possible to use one Field provider for more than one data type because you need to decorate a Field provider with the FieldProvider attribute. In the PropertyEditorAlias parameter you specify the Alias(Umbraco V7) or GUID(Umbraco V4 and 6). You can add as many attributes as you like, its also possible to have more than one Field provider for a single data type.

The second parameter is the priority which describes the order of execution. In case you have some data that is not 100% correct you can change this before it get’s parsed by the default Field providers of CMSImport. The default priority of all Field providers used by CMSImport are Medium. Use High priority if you want to parse the data before the default Field provider parses the data. Or use Low priority when you want to parse data after the the default Field provider parsed the data.

So when you map the following data against the dropdown list:

  • Cars
  • Car’s
  • Carss

You probably want to make sure this gets all import as Cars. Really easy to do with a Field provider. As you can see in the example below I’ve implemented a Field provider gave it a high priority so it will be picked up before the default one and return Cars when the value starts with car.

   1: using System;
   2: using CMSImport.Extensions.FieldProvider;
   3: using CMSImportLibrary.TypeExtensions;
   4: using umbraco.cms.businesslogic.property;
   5:  
   6: namespace CMSImportLibrary.Providers.FieldProviders.DefaultFieldProviders
   7: {
   8:     [FieldProvider(PropertyEditorAlias = "a74ea9c9-8e18-4d2a-8cf6-73c6206c5da6", Priority = FieldProviderPrio.High)]
   9:     [FieldProvider(PropertyEditorAlias = "Umbraco.DropDown", Priority = FieldProviderPrio.High)]
  10:     public class DropdownFixFieldProvider : IFieldProvider
  11:     {
  12:         public object Parse(object value, Property property, FieldProviderOptions fieldProviderOptions)
  13:         {
  14:             return !value.IsNullOrEmpty() && value.ToString().StartsWith("car", StringComparison.InvariantCultureIgnoreCase) ? "Cars" : value;
  15:         }
  16:     }
  17: }
Comments [0] | | # 
# Monday, 02 September 2013
Monday, 02 September 2013 18:00:05 (GMT Daylight Time, UTC+01:00) ( SEO Checker | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

SEO Checker comes with a bunch of nice tools that helps you easy optimize  your site for SEO. Today I want to show you the Not Found pages functionality that is introduced in SEO Checker V1.2. I’ve always found it hard to configure not found pages for multiple sites and more than one language. You needed to tweak the configuration to get language specific not found pages working, or install uComponents and adding properties to the document types. And of course IIS and Umbraco present you different default error messages that we need to configure. I’m sure we this picture looks familiar where the .aspx version shows a different than the same url with .php as extension.

different 404 errors in Umbraco

With SEO Checker it’s now possible to specify  a not found page per site and per language. When you open the SEO Checker section and open the domain settings menu from settings. All domains that are configured as a root are listed. For each domain and language (1 in the image below)  you can then specify the not found page (2 in the image below) and the next time there is page not found on your site it will display the nice page you configured instead of an ugly one.

page not found settings seo checker

Want to check the result yourself? Install SEO Checker on your local machine, the trial version allows you to test this functionality on local host.

Comments [0] | | # 
# Wednesday, 21 August 2013
Wednesday, 21 August 2013 11:11:52 (GMT Daylight Time, UTC+01:00) ( CMSImport | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

CMSImport PRO always had support for uBlogsy. When testing the latest version of uBlogsy I found that it was completely different from the V2 version. The latest version of CMSImport PRO (2.4 and above) support version 3 also. The version 2 version of uBlogsy was easier to use from an Import point of view. Most relations to authors etc were stored as comma separated values. This is changed to MNTP now. So thanks to uBlogsy, CMSImport supports lookups for MNTP now :). But as mentioned above there are a few steps you need to do to import content in uBlogsy, so in this blog post I try to explain the process form importing all data from a Wordpress file (thanks Morten for sending me one).

Setup

First we need to install uBlogsy V3 and CMSImport. In this post I’ll be importing the comments also, therefore I need to install uCommentsy (the new commenting system for uBlogsy that is not in out of the box)also. From version 2.4 of CMSImport not all Data Adapters are included in the core package anymore, this is to avoid a large list of available adapters that most people don’t use. So to import Wordpress data we need to install the additional CMSImport.DataAdapters.Wordpress package also. Before starting CMSImport make sure you have your license file in the bin folder of your Umbraco installation also.

uBlogsy is using MNTP for assigning authors and Labels (Categories in Wordpress). The XPath configuration gave me some issues when using uBlogsy. It couldn’t find the rootnode when editing posts.  By default the xPath configuration is searching from the current node as you can see in the image below.

xpath

This might be ideal when you run multiple blogs with multiple authors and labels but for my situation selecting all authors and categories works fine and is then always available in the post. To change the xpath configuration open the following datatypes and update the xpath.

DataType XPath expression
[uBlogsy] [MNTP] Author Picker //uBlogsyContainerAuthor
[uBlogsy] [MNTP] Label Picker //uBlogsyContainerLabel

Before importing the posts we first need to import author, categories and tags data. CMSImport can use this data for lookup values and assign the values during the import of Blog posts.

Import meta information

CMSImport will automatically assign meta information such as Author, Labels (Categories in Wordpress) and  Tags. This will be based on a lookup value, when CMSImport can find the related node it will assign it to the Blog post otherwise it will be ignored. So the first thing we need to do is import the meta information. When you open CMSImport and select import data you should use the following data adapters to import the meta information:

Data adapter  
Wordpress Authors Imports authors from a Wordpress export file
Wordpress Categories Imports the labels from a Wordpress export file
Wordpress Tags Imports the tags from a Wordpress export file

When you hit next for any of the above Data Adapters you need to select a location. Below the example for authors, tags and labels work the same. You will also see that the document type is automatically assigned. This is because CMSImport knows that uBlogsy is installed. Also the mapping is automatically assigned correctly. So after assigning the location you can hit next a few times and the Meta information will be imported.

author

Importing blog posts

Ok we are coming to the real task, importing the blog posts. This the same as importing the meta information. First you select Wordpress blog posts as the datasource. Then select Blog as the root to import the data. uBlogsy will automatically create the date folders  when you are importing. Also the mapping is created automatically. When you import the data the meta information will be assigned automatically. After importing the data save the Import definition, we need it again when importing the comments. After the import definition the  post info tab of a blog post  should look similar as the image below. Author, tags and label info is assigned automatically.

postinfo

Importing comments

The last step is importing comments. This is a bit different from importing the above information. To import the comments, open the CMSImport saved imports tree for content. Right click on the saved import definition for posts and select Create child definition and select Wordpress comments as the data source. You don’t need to pick a location on the next step. CMSImport will find the location based on the imported posts. All other steps are exactly the same as importing the other data.

childimport

Once the data is imported your tree should look something like this.

tree

That’s it

That’s all you need to do to import Blog posts and comments from a Wordpress site into uBlogsy 3 for Umbraco. If your Wordpress file contains pages also you can use the Wordpress page data adapter to import the page data as well.  This article can also be used if you have BlogML data, replace Wordpress with BlogML adapters and all should work.

Happy importing!

Comments [0] | | # 
# Thursday, 14 February 2013
Thursday, 14 February 2013 10:29:23 (GMT Standard Time, UTC+00:00) ( SEO Checker | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

It took a few months of development but I’m glad to finally announce SEO checker V1! If you haven’t done already you can read previous blogposts about this package or watch the screencasts about SEO checker. I think it’s a package every site should have installed, when building sites for clients. I have followed the #umbraco tag on twitter for more than a year and have seen most sites that are released contain SEO issues.  When those sites had SEO Checker installed it would have warned you before putting the site live. So I hope you will at least download SEO checker and check you sites on localhost before putting it live.

Using SEO Checker for content editors

There is one big feature the previous blogposts didn’t cover and that is using SEO Checker for content editors.SEO Checker comes with a Data Type that can be added to document types. The SEO Checker Data Type will show a Snippet preview how the page will be shown  in the Google search results. If you are using a relevant  Title and Meta Description Google will use these in the search results. Therefore these elements are important.

seochecker-contenteditor
It’s possible to specify a focus keyword, this is the keyword you want to rank the page for, use the Keyword tool icon to get help selecting the keywords you want to rank the page for.  SEO Checker will validate if the keyword can be found in the most important elements of the page:

  • Page Title (H1)
  • Url
  • SEO Title (<title>)
  • SEO  Description (<meta name="description"…..)
  • At least once more in the text of the page

You can also specify the SEO Title, SEO Description and SEO keywords (when configured). The page will also be checked for other SEO Issues on the page.  See the documentation for a list of all validations.
The unpublished version of the page will be checked, this allows you to solve issues before publishing the page!

Configuration of the Data type

By default the SEO Checker data type will show a field for SEO title and description. If you already have those properties on your document type you can use configuration of the document type to map those .

image

1.    Map SEO title against the existing SEO title property
2.    Map SEO keywords against the existing SEO keywords property
3.    Map SEO description against the existing SEO description property
4.    Use the keywords meta tag (Google and all major search engines ignore this meta tag).

Document type meta data configuration

Using default SEO Properties you can specify default values  for SEO Title and SEO description. When the page is missing the SEO title or description info these mapped properties will be used.  The title will be copied. The description will take the first paragraph from the mapped property. This ensures you always have you meta tags filled. The validation process will validate if the tags are matching length criteria.

Title template

It’s also possible to specify a template for the SEO Title. This template will be used to render the <title> tag in snippet preview and on the page.The template can contain fixed text and placeholders, placeholders have the same syntax as you would use in Umbraco Macro’s:

  • [#Property alias ] will get the property from the current document
  • [$property alias] recursive look up the property

The following fixed properties can be used in the template:

  • @nodeName contains the name of the page
  • @seoTitle contains the value of the SEO title property

When using the above template configuration the <title> tag will be rendered as:

<title>Simple website | Simple website &#169; 2013</title>

Use Metadata inside your template

SEO Checker comes with a small library to render Meta Tags in HTML. This is only necessary when you use the SEO Checker data type. You can use the library in Razor, XSLT and usercontrol macro’s. The library can be used in both MVC and Masterpage mode. To render the metatags for the current page in razor simply use:

This will render all metatags for the page like this:

<title>Using Modules | SEO Checker</title>
<meta name="description" content="Umbraco modules encapsulate specific bits of advanced functionality that are easily added to your website." />
<meta name="robots" content="index, follow" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

Download V1 Trial

What are you waiting for. download SEO Checker and have a play on your local machine!

Comments [0] | | # 
# Monday, 04 February 2013
Monday, 04 February 2013 13:32:47 (GMT Standard Time, UTC+00:00) ( SEO Checker | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

The RC is already removed from the version info, a release is around the corner! A perfect time to talk about validating pages using SEO Checker. To validate a site, or a portion of a site for SEO issues, open SEO Checker and select the Validate Pages option from the menu. This will show the following screen. By default the root is selected, if you want to validate a portion of a site specify the start location. When the include children is checked all published children will be validated also. When you hit start the pages will be added to the validation queue.

 validate pages

Besides this manual process, pages will be added to the validation queue when they are published also. Only published pages having a template assigned will be validated!

Validation queue

Pages will not be validated immediately, instead they will be added to the validation queue. Validation occurs on the background so you will not have to wait until the validation process is finished. When all pages are validated the result will be available in the Validation issues overview.

validationqueue-small

Validation issues

This overview will show all issues found when validating the page. The documentation will contain a full list of validation when it’s released. When an issue contains the description “Template issue” it means that the issue is found in the template. A template issue is only reported once for every template instead of reported on every document that uses the template.

ValidationIssues-markedwithoptions

Options:

  1. When selected warnings will included in the overview.
  2. When selected errors will included in the overview.
  3. When a period is selected solved items since the selected period are also displayed in the overview. Solved issues are displayed in strikethrough. In the example above the metadata issues are solved.
  4. Opens the document in a popup
  5. Opens the template assigned to the document in a popup. This option will only be available for Users that have access
  6. Deletes the issue from the overview, or adds the item to the ignore list.

Delete and ignore issues

Using the delete button in the various overviews you can delete an issue from the overview. This will show the following dialog.

deleteissuedialog

When you hit “delete” it will just remove the item from the overview. Next time validation occurs the issue will be reported again. When you check the “Ignore this issue next time” checkbox the item will be added to the ignore list and the issue will not be reported again next time validation occurs. Use the Ignore list overviews to see which items are ignored.

Download

Want to try SEO Checker today? Download the release candidate.

Non production Warning:
This is a  release candidate!! SEO checker is tested on different sites but can still have some issues in specific situations. We love to get some feedback but don't use this package in production yet! If you find any issues please send us an email support@soetemansoftware.nl and we will get back to you ASAP!  Also keep an eye on our hotfix location! Here you find the most recent bugfixes.  The release candidate will expire on May 1th 2013. After May 1th it will fall back to trial mode which means it will only work on localhost and you can only validate one page at a time.

Comments [0] | | # 
# Tuesday, 22 January 2013
Tuesday, 22 January 2013 20:58:32 (GMT Standard Time, UTC+00:00) ( Package | SEO Checker | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

After releasing the beta version of SEO Checker almost two months ago, it’s time for a release candidate. An exciting time because if all goes well we have the final release available in just a few weeks(it’s all about writing the user manual ;-) ). The release candidate solves the following issues found in the beta version:

  • Fixed encoding issues in the validation process
  • Added a validator to prevent robots.txt, or meta follow/index  is blocking the whole site being crawled
  • If a robots.txt file exist on disk, use that one instead of generating a dynamic one
  • Fixed several issues regarding  url rewriting
  • Fixed several issues regarding broken inbound links
  • Changed low level error messages to user friendly ones when an exception occurs
  • Added several improvements in configuration
  • Fixed Umbraco V6 RC compatibility issues

If you already installed SEO Checker beta it’s best to upgrade since the beta expires February 1th and upgrading is easy, just use the package installer. So what are you waiting for….download the RC today

Non production Warning:
This is a  release candidate!! SEO checker is tested on different sites but can still have some issues in specific situations. We love to get some feedback but don't use this package in production! If you find any issues please send us an email support@soetemansoftware.nl and we will get back to you ASAP!  Also keep an eye on our hotfix location! Here you find the most recent bugfixes.  The release candidate will expire on May 1th 2013. After May 1th it will fall back to trial mode which means it will only work on localhost and you can only validate one page at a time.

Comments [0] | | # 
# Wednesday, 16 January 2013
Wednesday, 16 January 2013 09:51:44 (GMT Standard Time, UTC+00:00) ( Package | SEO Checker | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

A couple of weeks ago I blogged about SEO Checker. With the first release candidate and final release around the corner it’s time to highlight some features. Today I want to talk about fixing Inbound Links.We all had this experience, searching for information and found a page in Google and when we hit that link we get a 404 page, friendly message or not it’s not the best experience and Google doesn’t like this either.

In the example below I’m using the basic starter kit that comes with Umbraco and visit the page gofurther.aspx. the 404 page will be shown, but SEO Checker will also log the url.  When you open the  Inbound link error page in SEO Checker you will see that gofurther.aspx is reported as broken. Using the content picker you can select the page you want to redirect to. Next time someone is visiting the gofurther.aspx the request is redirected to the selected page.

 InboundLinkErrors  

Handling different extensions

If you migrate a site from php environment you can setup the same structure in your umbraco site. Let’s say the previous php site had this structure:

  • mytestsite.com/
    • mytestsite.com/installing-modules.php
    • mytestsite.com/go-further.php
    • mytestsite.com/getting-started.php

Our new Umbraco site is using aspx extensions (or no extensions at all), so when we try to access the installing-modules.php page, the visitor will see a 404 page.  If you have SEO Checker installed it will make sure the installing-modules.php page will be redirected to the installing-modules.aspx page.

This means search results from Google will still work (and get updated to the new page because of the 301 redirect) and all links to the php version of your site will still work.

When the page cannot be found at all the url will be logged and we can map the correct url on the Inbound Links page as described in the previous scenario.

Handling structure changes

When you rename or move a document in Umbraco the structure will change. For example when we change the Installing modules page to Installing Umbraco modules. The url changes from installing-modules.aspx to installing-umbraco-modules.aspx. The result will be that when people are visiting the old installing-modules.aspx  url, they will see a 404 instead of the Page. SEO Checker will keep track of changes in the url structure and will redirect the old installing-modules.aspx  url to the new installing-umbraco-modules.aspx url.

If you are only interested in this scenario you might want to check out the free 301 Url Tracker package.

Comments [0] | | # 
# Friday, 30 November 2012
Friday, 30 November 2012 09:24:41 (GMT Standard Time, UTC+00:00) ( Package | SEO Checker | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

 
For those of you following me on twitter probably know I’ve been working on a new package, SEO checker for the last few months(thinking about it for a few years). Today it’s time to finally release the first beta version of this package and this is the first blog post giving you an overview of most possibilities. The next blog posts will cover more detailed information about a certain feature.

SEO Checker is build to optimize your Umbraco Site SEO! It will help you find common SEO issues on the website, help you pages rank better for a certain keyword by making sure that keyword is found on the necessary HTML elements. It comes from the idea that most Umbraco websites have SEO Issues, missing meta tags, canonical url’s , broken links, test content, inline scripting, huge viewstate (my favorite ;-)) etc. When you install SEO Checker you will find a new section where you can validate pages, find inbound link errors and find configuration errors.

I’ve recorded a few screencasts that demo the functionality, check them out. I would love to have feedback so please read further, watch the screencasts,  install the package and let me know what you think.

Validate pages

SEO checker allows you to validate a complete  site, or portions of a site. Simply select validate pages from the menu, select a root node and hit start, as you can see in the image below. 
Validation happens on a background thread, you can validate 1000 pages without having to wait for the results before leaving the page. Instead pages are added to the validation queue first.

image

When you publish a page it will automatically being added to the validation queue by default.

Once the validation queue is empty you can open the validation issues page which gives you an overview of all validation issues for a given page or template. All validations are (somehow) based on the Search Engine Optimization Starter Guide document from Google , so it’s based on facts and not on opinions from SEO consultants. You can filter the overview by errors and  warnings.It’s also possible to show resolved issues since a given date. The image below shows you the url to simplesite.com is resolved and shows you a few open issues. You can edit the page, or template directly from this overview. It’s also possible to delete the item from the overview. When you delete the item you can select the option to always ignore the issue during validation.

image

 

SEO checker datatype

SEO checker allows you to preview the Google search result snippet using the SEO checker datatype when editing content  and will give feedback about keyword usage in content and meta information. The same datatype will report validation issues based on the unpublished version of the document. This allows you to fix all issues before publishing the page to your website. In the image below you’ll see that we want to rank the page for Umbraco Modules.  If the keyword is not found in a certain tag, this will be reported as a validation issue.

image

SEO checker will look for the focus keyword in the following tags:

  • Title
  • Meta description
  • H1
  • Body
  • Url of the page

SEO checker will also report any other content related issue it found on the page. In this case it found the broken link htp://www.umbraco.com. SEO checker knows this must be http://www.umbraco.com/ and suggest you will use http:// instead of htp:// in the error description.

Fix and avoid Inbound link issues

SEO checker will report any inbound link that is broken. To fix a broken link you select the page you want to redirect to and the next time a user is browsing to the broken link the user will be redirected to that selected page. In the example below the user requested installin-modulezz.aspx. This was reported as broken. and is now pointing to the installing modules page. So the next time a user is requesting the installin-modulezz.aspx the request will be redirected to installing-modules.aspx.

image

Seo checker will avoid broken links by redirecting  url’s with the wrong extension to the correct one. So if a user is requesting http://www.somesite.nl/modules.php the request will be redirected to http://www.somesite.nl/modules.aspx. When you rename or move a document inside Umbraco SEO checker will makes sure every request for the old url is redirected to the new url.

Dynamic robots.txt and sitemap.xml

SEO checker allows you to generate a dynamic Robots.txt and Sitemap.xml files. This will make sure search engines don’t index Umbraco system folders and allows you to submit an xml sitemap to Google. You can specify settings for the xml sitemap per document type, so if you want to exclude a certain document type or change the priority it’s possible with a few clicks.

image 

URL Rewriting

You want to make sure every page can be accessed by one url. If a url can be accessed with and without www. prefix for example, search engines treat those url’s as unique and it can cost you page rank. When I was building SEO checker I had some issues with URL rewriting by editing the XML configuration files. To make it easier for you SEO checker comes with a built-in URL rewriting module that you can configure with a few mouse clicks (only one if you are not using directory url’s). 

image

Requirements

SEO checker requires:

  • IIS7
  • Umbraco Version 4.8 and up.
  • One of the following database SQL Server 2005/2008 (Express), SQL CE 4 and Mysql
  • modify rights to folders: /bin, /umbraco, and /app_data folder

Commercial package

Once the beta is over SEO checker will be a commercial package.Prices will be the same as the CMSImport  package:

  • € 99,00  for a domain license
  • € 299,00 for a server license
  • € 499,00 unlimited license

Download

Update 1/22/2012 : SEO Checker Release candidate is available

Download the beta version of SEO checker today.

BETA Warning:
This is a Beta release!! SEO checker is tested on different sites but can still have some issues in specific situations. We love to get some feedback but don't use this package in production! If you find any issues please send us an email support@soetemansoftware.nl and we will get back to you ASAP!  This beta version will expire on Feb 1 2013.

Also keep an eye on our hotfix location! Here you find the most recent bugfixes.

Comments [1] | | # 
# Tuesday, 25 September 2012
Tuesday, 25 September 2012 09:36:02 (GMT Daylight Time, UTC+01:00) ( Package | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

A couple of months ago, Umbraco V4.8 was released and when I was testing CMSImport against this version I got a weird exception.

dllerror

What happened?

I’m using the HTMLAgilityPack to extract images and files from the bodytext property so we can import them into Umbraco. In previous versions, Umbraco shipped with the 1.3.0 version. The version of the HTMLAgilityPack that is shipped in 4.8+ is version 1.4.5.0. Since the dll is signed it’s throwing an error in case of a version conflict. I’ve seen a post on the forum where Lee Kelleher describes a similar issue. In the case of UComponents it’s the Lucene DLL but it’s the same problem as I was facing. To fix this issue as described in the forum thread requires an entry in the web.config file and putting the Legacy DLL into a separate folder. It’s all fine to use upgrade instructions for a client project but when you develop a package you don’t want your customers manual upgrade web.config files for a specific version. You also don’t want to have a big if statement in your installer that creates entries based on a version number and you also don’t want to drop support for older versions of Umbraco because of this minor conflict.

The solution

After searching a bit, I found this interesting article that describes the AppDomain.AssemblyResolve Event. This event gets triggered when an Assembly is required that can’t be found and allows you to return the correct assembly. In our case the DLL Version 1.3.0.0 of HTMLAgilityPack. In the example below you see that I derive my class from ApplicationBase (I know there is a new class in 4.8, but I’m still supporting 4.7 also ;-)) so this class will be picked up automatically . When the  required assembly (Args.Name on line 13) is the HTMLAgilityPack Version 1.3.0 I load that assembly from the /bin/legacy/ location and all is working again :) 

Update 2-10-2012 Modified source code. It doesn’t use HTTPContext anymore since that might not be available

   1:      public class UpdateThirdPartyDllBindings : ApplicationBase
   2:      {
   3:          public UpdateThirdPartyDllBindings()
   4:          {
   5:              AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
   6:          }
   7:   
   8:          /// <summary>
   9:          /// Assembly could not be resolved let's see if we can load it dynamically
  10:          /// </summary>
  11:          Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
  12:          {
  13:              if (args.Name == "HtmlAgilityPack, Version=1.3.0.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a")
  14:              {
  15:                  try
  16:                  {
  17:                      //Get the bin folder. Don't use HTTPContext since that can be null
  18:                      string binFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin\\legacy");
  19:                      string legacyFile = Path.Combine(binFolder, "HtmlAgilityPack.dll");
  20:                      return Assembly.LoadFile(legacyFile);
  21:   
  22:                  }
  23:                  catch (Exception ex)
  24:                  {
  25:                      Log.Add(LogTypes.Error, -1, string.Format("SEOChecker error loading HTMLAgilityPack version {0}:{1} ", args.Name, ex));
  26:                  }
  27:              }
  28:              return null;
  29:          }
  30:      }

All I need to do now is make sure the Legacy DLL is shipped in the package. When you are using an older version of Umbraco this Legacy DLL will exists in your bin folder but will be ignored and for version 4.8 + the AssemblyResolve implementation will make sure the legacy DLL is loaded.

Hope this helps you solve version conflicts of dll’s in your own packages

Comments [0] | | # 
# Tuesday, 20 December 2011
Tuesday, 20 December 2011 13:57:00 (GMT Standard Time, UTC+00:00) ( MemberExport | Package | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 15 November 2011
Tuesday, 15 November 2011 16:05:32 (GMT Standard Time, UTC+00:00) ( CMSImport | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 08 November 2011
Tuesday, 08 November 2011 10:16:29 (GMT Standard Time, UTC+00:00) ( Package | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 25 October 2011
Tuesday, 25 October 2011 13:25:49 (GMT Daylight Time, UTC+01:00) ( CMSImport | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

 

 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, 17 October 2011
Monday, 17 October 2011 12:00:49 (GMT Daylight Time, UTC+01:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

 

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, 06 September 2011
Tuesday, 06 September 2011 15:26:33 (GMT Daylight Time, UTC+01:00) ( mediaprotect | Package | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 29 April 2011
Friday, 29 April 2011 13:06:57 (GMT Daylight Time, UTC+01:00) ( CMSImport | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 27 April 2011
Wednesday, 27 April 2011 15:54:21 (GMT Daylight Time, UTC+01:00) ( mediaprotect | Package | security | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 11 January 2011
Tuesday, 11 January 2011 13:31:15 (GMT Standard Time, UTC+00:00) ( CMSImport | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 24 November 2010
Wednesday, 24 November 2010 10:37:54 (GMT Standard Time, UTC+00:00) ( CMSImport | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 02 July 2010
Friday, 02 July 2010 09:13:38 (GMT Daylight Time, UTC+01:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 07 June 2010
Monday, 07 June 2010 12:42:40 (GMT Daylight Time, UTC+01:00) ( MemberExport | Package | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 28 May 2010
Friday, 28 May 2010 15:51:58 (GMT Daylight Time, UTC+01:00) ( MemberExport | Package | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 16 April 2010
Friday, 16 April 2010 14:09:04 (GMT Daylight Time, UTC+01:00) ( CMSImport | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 04 April 2010
Sunday, 04 April 2010 19:22:10 (GMT Daylight Time, UTC+01:00) ( Package | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 26 March 2010
Friday, 26 March 2010 22:53:02 (GMT Standard Time, UTC+00:00) ( SiteAnalyzer | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 08 March 2010
Monday, 08 March 2010 12:48:25 (GMT Standard Time, UTC+00:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 16 February 2010
Tuesday, 16 February 2010 08:57:09 (GMT Standard Time, UTC+00:00) ( CMSImport | Package | UmbImport | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 03 February 2010
Wednesday, 03 February 2010 15:45:02 (GMT Standard Time, UTC+00:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 04 January 2010
Monday, 04 January 2010 21:37:53 (GMT Standard Time, UTC+00:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 02 January 2010
Saturday, 02 January 2010 09:34:32 (GMT Standard Time, UTC+00:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 27 December 2009
Sunday, 27 December 2009 07:37:27 (GMT Standard Time, UTC+00:00) ( Package | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 27 October 2009
Tuesday, 27 October 2009 20:06:12 (GMT Standard Time, UTC+00:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 10 August 2009
Monday, 10 August 2009 19:18:04 (GMT Daylight Time, UTC+01:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 30 June 2009
Tuesday, 30 June 2009 08:43:08 (GMT Daylight Time, UTC+01:00) ( UmbImport | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 04 May 2009
Monday, 04 May 2009 20:06:31 (GMT Daylight Time, UTC+01:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 16 April 2009
Thursday, 16 April 2009 23:02:37 (GMT Daylight Time, UTC+01:00) ( UmbImport | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 01 April 2009
Wednesday, 01 April 2009 21:24:15 (GMT Daylight Time, UTC+01:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 25 March 2009
Wednesday, 25 March 2009 21:17:38 (GMT Standard Time, UTC+00:00) ( UmbImport | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 17 March 2009
Tuesday, 17 March 2009 09:04:43 (GMT Standard Time, UTC+00:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 03 March 2009
Tuesday, 03 March 2009 12:39:34 (GMT Standard Time, UTC+00:00) ( Package | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 22 February 2009
Sunday, 22 February 2009 10:38:11 (GMT Standard Time, UTC+00:00) ( ASP.NET | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 05 February 2009
Thursday, 05 February 2009 19:45:52 (GMT Standard Time, UTC+00:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 23 January 2009
Friday, 23 January 2009 16:19:45 (GMT Standard Time, UTC+00:00) ( Package | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 21 January 2009
Wednesday, 21 January 2009 08:38:02 (GMT Standard Time, UTC+00:00) ( Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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, 08 January 2009
Thursday, 08 January 2009 21:41:44 (GMT Standard Time, UTC+00:00) ( Tools | Umbraco )

This Blog is not active anymore, further posts will be available on my company website. Read new posts here

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