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, 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, 24 June 2011
Friday, 24 June 2011 11:08:03 (GMT Daylight Time, UTC+01:00) ( )

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

Today I’ve officially released CMSImport 2.0, for those of you who don’t know what CMSImport is. In short CMSImport helps you import content or members from any datasource into Umbraco on a very friendly way. Just point to a datasource and add a mapping between fields in your datasource and the Umbraco  Content or Member properties. Currently CMSImport supports the following data sources by default (you can always create your own if you like):

  • Access Database
  • BlogML
  • CSV
  • Excel File
  • RSS Feed
  • SQL Server
  • WordPress Export file
  • XML

Access database and the Excel file Data source requires you to install the Microsoft Data Connectivity Components 

What’s new

This release is all about structured import of content. For example If you want to import a Product Catalog, you want to import Product Categories and Products. CmsImport 2.0 will help you with that by defining a Parent and child definition. Simply save the parent import definition and select “Create Child definition” from the context menu as displayed in the image below.  And you need to specify a Foreign key, that’s all!  This is very flexible, you can even mix data sources if you want to

childdefinition

Because CmsImport can import whole product catalogs it’s fully optimized to import ProductCatalogs for the Teacommerce and Uwebshop Ecommerce solutions. When you import BlogML or Wordpress data and you have Blog4Umbraco or the Blog starter kit installed You’ll see the Optimize for Blog4Umbraco/ Blog starter kit option. When you select this option, comments from BlogML or Wordpress will be imported automatically. Of course you can also skip this and Import Blogs and Comments separately.

blogml

Another major improvement is the Advanced Mapping feature. For each field you can add Advanced mapping options. CmsImport will determine the type of property and will give you some advanced options based on that type. For every property it’s possible to specify a default value, for some types it’s possible to specify more options. If you want to import Media it’s possible to put the media in the root of your Umbraco install and use the Advanced mapping options for a Media picker, or Rich Text editor to specify where to store the media in the Media Library. For a Media picker it’s even possible to select a default value from the Media Library instead of using a textbox.

advancerichtextmapping

I’ve also fixed the scheduled task functionality. In the previous version it ran 95% of the time. The other 5% it didn’t run because the Umbraco scheduler wasn’t active at the time it needed to execute the task, then one minute later the scheduler is running again but the scheduled task expired. This is solved now, the handler responsible for executing a scheduled tasks will also check for scheduled tasks that didn’t run at the requested date/time. if it finds an expired task it will run the task. To give you more insight when a scheduled task did execute I’ve introduced the scheduled task dialog. This reports when the task was scheduled and when it actually did execute. This dialog is  also reporting the next execute time.

scheduledtasklog

Umbraco Deli

Umbraco recently released the Deli. The Umbraco Deli allows you to download or buy products from a single source. CmsImport can be downloaded and bought through the Deli. CmsImport Pro comes in three editions:

  • Single domain license
  • Server license
  • Unlimited license

For customers that already bought a CmsImport Pro license from my site. First of all sorry for the manual order process Winking smile Very soon you will receive an email with a new license file that you can use for CmsImport Pro 2.0.

Free edition

Of course the free edition still exists and it had some small improvements. It’s now possible to use Advanced settings when you create a mapping so you can add default values for each field. Checkout the feature comparison sheet for all differences.

Demo

Enough text, time for some video Smile

If you want to discuss if CMSImport can handle your requirements, I can always demo or discuss features via Skype.Contact me via email (richard[at]soetemansoftware[dot]nl) if you want a remote demo. Or if you just want to use it download CMSImport from the Umbraco Deli.

Comments [2] | | # 
# 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] | | #