Similarity for Umbraco V7

Many moons ago I created the Similarity datatype, this was part of the awesome uComponents suite of datatypes. So as a learning excercise I have updated the datatype so that it works with Umbraco V7. You can watch a screencast here. I will be releasing it very soon so watch this space.

Advertisements

Twitter oAuth

As you may or may not be aware Twitter updated their API and deprecated the old one. So now you can only access twitter feeds using oAuth authentication. If you are using latest version of Umbraco or .net4 with Umbraco you can make use of the excellent uTwit package created by none other than the legend that is Matt Brailsford.

If like me however you have some old Umbraco sites that you cannot for whatever reason upgrade to latest version of Umbraco or .net4 you can use my package TwitterOAuthOap. I am currently in the process of rolling it out to a few legacy sites and it works a treat!

Umbraco 6 MVC and Dependency Injection

Thanks to some feedback on an Umbraco forum post I have re factored my code and made use of Dependency Injection.

I am using the awesome ninject and if you use the nuget package manager it is so easy its un real.

So navigate to https://www.nuget.org/packages/Ninject.MVC3/3.0.0.6 you can see the nuget install command Install-Package Ninject.MVC3 -Version 3.0.0.6 so install that into your web project.  This will pull down everything also in your app_start folder it will create a class NinjectWebCommon in there you have method


private static void RegisterServices(IKernel kernel)

{
kernel.Bind<ISearchService>().To<SearchService>()
}

You add your bindings there. Next update your surface controller mine looks like


private readonly ISearchService _searchService;
///
/// inject in this dependancy using ninject
///

///
public SearchFormSurfaceController(ISearchService searchService)
{
_searchService = searchService;
}

Its as easy as that. Now you too can be all hip and upto date with all the cool kids!!

Umbraco MVC localised data annotations

So in MVC you decorate your model with data annotations things like

[Display("Email Address")]
[Required]
public string EmailAddress {get; set;}

With these attributes you pass in some text or you can use a .net resource. In that case you give it assembly and key

[Required(ErrorMessage="Name required.", ErrorMessageResourceType=typeof(CustomDataAnnotationsResources)]

It would be nice if we could use the Umbraco dictionary as well. So I have created my own classes so for the display and required attributes i have


[UmbracoDisplayLocalised("Email")]
[Email("InvalidEmail")]
[UmbracoLocalisedRequired("InvalidEmail")]
public string EmailAddress {get; set;}

What I pass in is Umbraco dictionary key so now my model when rendered in view has localised labels. I also have a custom build of the excellent data annotations project which also reads from the Umbraco dictionary.

I have sent it to Lee Kelleher Sir uComponents to have a play. It may appear in there or I will release as a separate project on our. My source code is available  on github

https://github.com/ismailmayat/MvcUmbracoDataAnnotations

log4net smtp logging

In umbraco v6 logging is now performed by the excellent log4net library.  In the umbraco config directory you have the log4net.config file which is where everything is setup.  The really cool thing about log4net is you can have appenders of different types e.g file, database and smtp. You can also create your own which is what I did when I wrote LiveLogger.

So I am currently working on a project and I need to do some smtp logging of errors. Something has gone very wrong and I need to know about it as its mission critical. So I added an smtp appender to the config

<appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">
<to value="jo@site.com" />
<from value="me@site.com" />
<subject value="Info graphic error" />
<smtpHost value="localhost" />
<bufferSize value="512" />
<lossy value="true" />
<evaluator type="log4net.Core.LevelEvaluator">
<threshold value="ERROR"/>
</evaluator>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%newline%date [%thread] %-5level %logger [%property{NDC}] - %message%newline%newline%newline" />
</layout>
</appender>

This works however I get too much noise. There are other errors being generated that I am not interested in. So what you can do with log4net is to an appender add a filter so you can tell it you are only interested in certain classes only


<filter type="log4net.Filter.LoggerMatchFilter">
<loggerToMatch value="MyProject.BusinessLogic.Helpers.RazorHelper" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />

So now I will only get emails for errors that occur in class RazorHelper. In this class I have logging statements that look like


LogHelper.Error(typeof(RazorHelper), "Error writing to eloqua for email address " + record.EmailAddress, webex);

Examiness hints and tips from the trenches part 11

As I have moved roles I have decided to start blogging again on my old wordpress blog. Those of you who are aware I have been blogging about Examine see the Cogworks blog.  In this post I am going to cover how to perform range searches.

I recently did this for a client. The scenario was that they had 100 products each tagged with a feature that was a multi tree node picker.  They have 6 features 5 of which were a range e.g weight this had values

0-300 gsm
301-600 gsm
601-1000 gsm
1001-2000 gsm
2000+gsm
Don’t Know

The home page of the site had a search form that allowed you to pick one range value for each of the features. This would then perform examine search and return products matched.

To implement range search I implemented GatheringNodeData event and injected in some massaged data for each of the ranges.  So for weight I did the following

int nodeId = int.Parse(e.Fields[field]); // get the node id for the feature eg weight
//the value is mntp lookup so get the node
 var node = ApplicationContext.Current.Services.ContentService.GetById(nodeId);

string fieldRaw = node.Name; //get the value

                if (fieldRaw.Contains("+"))
                {
                    int fieldValue = 0;
                    if (isMm)
                    {
                        decimal tmp = decimal.Parse(fieldRaw.Replace("+", ""))*10;
                        fieldValue = (int) tmp;
                    }
                    else
                    {
                        //only one value inject that and a fake max
                        fieldValue = int.Parse(fieldRaw.Replace("+", ""));
                    }

                    InjectRange(fieldValue.ToString("D6"), Helpers.Constants.Examine.MaxValue, e, field);
                }
                else
                {
                    //we have min and max so put both in
                    string[] values = fieldRaw.Split('-');
                    int valueMin = 0;
                    int valueMax = 0;

                    if (isMm)
                    {
                        valueMin = (int) (float.Parse(values[0]) * 10);
                        valueMax = (int) (float.Parse(values[1]) * 10);
                    }
                    else
                    {
                         valueMin = int.Parse(values[0]);
                         valueMax = int.Parse(values[1]);
                    }

                    InjectRange(valueMin.ToString("D6"), valueMax.ToString("D6"), e, field);
                }
            }

        }

        private void InjectRange(string min, string max, IndexingNodeDataEventArgs e, string field)
        {
            e.Fields.Add(Helpers.Constants.Examine.MinPrefix + field, min);
            e.Fields.Add(Helpers.Constants.Examine.MaxPrefix + field, max);
        }

This takes the value replaces any display content that is not needed e.g gsm and then splits on – this gives us the lower and upper values for the range.  I then inject into the index into 2 new fields a lower and upper value. The values for 0-300 in the index look like

min_weight 000000

max_weight 000300

If the value is for example a single positive like 2000+ gsm then I add the 2000 value and in the index the value looks like

min_weight 002000

max_weight 100000

The last number is a fake maximum this will come in handy when doing the range search. My dropdowns on the home page display values like 0-300gsm but contain actual values 000000-000300 when performing a search the code takes those drop down values to create a range query e.g.

            string[] values = query[key].Split('-');
            if (query[key].Contains(Constants.Examine.MaxValue))
            {
                //we are doing a + max value so only need one value for the range
                examineQuery.And().Range(Constants.Examine.MaxPrefix + key, values[1], Constants.Examine.MaxValue);           
            }
            else
            {
                if (key == "MinTemp")
                {
                    /*these are negative so swap them around*/
                    examineQuery.And().Range(Constants.Examine.MinPrefix + key, values[0], Constants.Examine.MaxValue);
                    examineQuery.And().Range(Constants.Examine.MaxPrefix + key, values[1], Constants.Examine.MaxValue);
                }
                else
                {
                    examineQuery.And().Range(Constants.Examine.MinPrefix + key, Constants.Examine.MinValue, values[0]);
                    examineQuery.And().Range(Constants.Examine.MaxPrefix + key, Constants.Examine.MinValue, values[1]);
                }
            }

Codegarden 10

And that time of year is almost upon us thats right Codegarden 10 some of the things i am really looking forward to:

  1. Pre conference MVC training
  2. Canal boat evening cruise
  3. Catching up Umbraco guru and all round master coder Aarron Powell to talk about Snapshot
  4. Attending day 1 sessions on Examine / Contour / Secrets of umbaco ninjas / migrating to umbraco
  5. Attending day 2 sessions Versioning umbraco development / Prepare for umbraco 5
  6. Meeting old friends and new ones from the best and friendliest community bar none!

I just hope none of the sessions I want to attend clash.  This years codegarden is going to be the best yet and then some see you all there!

Ps I am staying at Wakeup Copenhagen