Wednesday, October 12, 2011

Hitler Reacts to the Springbok loss at the hands of Bryce Lawrence and Australia

I normally just talk code, but I had to write something about the SA vs Australia Quarter Final Game.
I am not the biggest rugby fan, but i never miss a SA game and I bought a drifta solely for the RWC.
It was a sad day indeed for South Africa On Sunday, but at least this youtube vid cheered me up today:)
http://www.youtube.com/watch?v=P2oJlaPyU8o&feature=topvideos_mp


Wednesday, August 24, 2011

SEO Friendly Url's in ASP.NET MVC - Include a description

ASP.NET MVC Tought us many new things, it also helped us with our urls by making use of MVC Routing.

One particular example would be to view a users details: http://www.yourdomain.com/User/Detail/163. As you can see, search engines can clearly see that you are viewing a users's details. but we are missing one important thing here.

What is the person's name ?!?

Would it not make more sense to have the username as part of the url?

To have your username part of the url you have two options, possibly more, but i am going to explain to basic options:

1)http://www.yourdomain.com/User/Detail/Dusty-Roberts
To achieve this is fairly simple, as all you need to do is define your route appropriatly in your global.asax file, and you will be able to achieve the required result.


2)http://www.yourdomain.com/User/Detail/163/Dusty-Roberts
This is the route i will be focussing on, as this can be implemented after your site is already up and running, with minimal effort.

First we need to map an additional route in our global.asax file above our "default":
routes.MapRoute(
"Descriptive", // Route name
"{controller}/{action}/{id}/{description}", // URL with parameters
new { controller = "Home", action = "Dashboard", id = UrlParameter.Optional
, description = UrlParameter.Optional } // Parameter defaults
);
This will allow us to append any string after the id:

1)http://www.yourdomain.com/User/Detail/163/Dusty-Roberts
2)http://www.yourdomain.com/User/Detail/163/Some-Other-User
3)http://www.yourdomain.com/User/Detail/163/What-Ever-I-Want

You will notice that when ever i access one of those url's it will always go to /User/Detail/163, and because we are not doing anything with the string after /163/, it will be ignored.

Now all we need to do is create 3 helper functions so we can make this a little bit easer.

public static class HtmlHelperExtenstions
{
    //Action Helper
    public static string DescriptiveAction(this HtmlHelper htmlHelper, string actionName, string controllerName, string description, object routeValues)
    {
        UrlHelper urlHelper = ((Controller)htmlHelper.ViewContext.Controller).Url;
        return urlHelper.Action(actionName, controllerName, routeValues) + "/" + description.CleanForUrl();
    }
    //ActionLink Helper
    public static string DescriptiveActionLink(this HtmlHelper htmlHelper, string text, string actionName, string controllerName, string description, object routeValues)
    {
        return DescriptiveActionLink(htmlHelper, text, actionName, controllerName, description, routeValues, null);
    }
    //ActionLink Helper
    public static string DescriptiveActionLink(this HtmlHelper htmlHelper, string text, string actionName, string controllerName, string description, object routeValues, object htmlAttributes)
    {
        UrlHelper urlHelper = ((Controller)htmlHelper.ViewContext.Controller).Url;
        var tagBuilder = new TagBuilder("a");
        tagBuilder.InnerHtml = text;
        tagBuilder.MergeAttribute("href", urlHelper.Action(actionName, controllerName, routeValues) + "/" + description.CleanForUrl());
        if (htmlAttributes != null)
            tagBuilder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
        return tagBuilder.ToString();
    }
    //Some Regex to make cleanup the description
    public static string CleanForUrl(this string text)
    {
        return Regex.Replace(text, @"[^a-z^A-Z^0-9^-]", "-").Replace(" ", "-");
    }
}

How will this helpers render?
Default Url Helper
Input: Url.Action("Detail","User",new{id=193})
Output: /User/Detail/163

Extended Helper
Input: Html.DescriptiveAction("Detail","User", "Some Description Here",new{id=193})
Output: /User/Detail/163/Some-Description-Here

Default Action Link
Input: Html.ActionLink("User Details","Detail","User",new{id=193})
Output: href="/User/Detail/163"

Extended Action Link
Input: Html.DescriptiveActionLink("User Details","Detail","User", "Some Description Here",new{id=193})
Output: href="/User/Detail/163/Some-Description-Here"

All of this now makes your links just a little bit more SEO Friendly



Here is a link to the Sample Application

Tuesday, July 5, 2011

Effort Share Entrepreneur Portal

I am pleased to anounce that the first BETA version of Effort Share has been released.

Effort Share is the catalyst for housing new business ideas, products, concepts and services. Empowering entrepreneurs to take their ideas to the market, giving opportunity to subject matter experts to add value to the ideas whilst opening the gateway for investors to find ventures to sponsor.

Its been a few months coming, but we are finally at beta release.

If you have a business idea, value to add or simply want to check it out, do not hesitate.

Wednesday, June 8, 2011

jqTransform option change not firing solution

If you got to this post of mine you probably know what jqtransform is, and you are probably struggling with a dropdownlist's change event.


Reason why it is not working is due to the fact that jqtransform creates a dropdownlist using an unordered lists, then it hides your dropdownlist.

The fix is actually quite simple.

open your jquery.jqtransform.js file and find the following line:

/* Fire the onchange event */
if ($select[0].selectedIndex != $(this).attr('index') && $select[0].onchange) {
     $select[0].selectedIndex = $(this).attr('index');
     $select[0].onchange();
}
now simply add the following below that line:
/* Fire the change event */
if ($select[0].selectedIndex != $(this).attr('index')) {
     $select[0].selectedIndex = $(this).attr('index');
     $($select[0]).trigger('change');
}

Save the file and enjoy.. your change events are now firing.

Friday, May 27, 2011

XSL-FO to RTF

I am in the progress of writing a library that will convert XSL-FO to RTF. The library makes use of xslt to convert XSL-FO to RTF Tags

You can have a look at http://fonetparser.codeplex.com/, for some more details.

At this current stage it supports:
  • FO Page Parsing
  • FO Table, Header, Footer, columns, background color
  • bold
  • Italic
  • Underline
  • FO Block
  • FO Inline
  • Text Align
Here is the example files: FO, resulting RTF

Wednesday, May 25, 2011

Creating a Table with background color in RTF

There are various ways to string together a table in RTF, so i am not going to show you all the ways, i will instead focus on a default easy and simple table, and also setting the background color of a cell in the table.

To start you can create a new text document, call it "Table.txt".

Next you can add the following table code:
{\rtf1\ansi\deff0
{\colortbl;\red188\green230\blue138;}
 \trowd
 \cellx1000
 \clcbpat1\cellx2000
 \cellx3000
 cell 1\intbl\cell
 cell 2\intbl\cell
 cell 3\intbl\cell
\row}

You can string multiple colors together for example:
{\colortbl;\red188\green230\blue138;\red108\green030\blue038;}
And to use it you will call:
\clcbpat1
or
\clcbpat2

Save your text file, and then rename it to "Table.rtf", here is an example of the resulting rtf file

Tuesday, February 15, 2011

Calculate a person's age using C#

Here is a nice litter method to calculate a persons age

public static int CalculateAge(DateTime birthDate)
    {
        // cache the current time
        DateTime now = DateTime.Today; // today is fine, don't need the timestamp from now
        // get the difference in years
        int years = now.Year - birthDate.Year;
        // subtract another year if we're before the
        // birth day in the current year
        if (now.Month < birthDate.Month || (now.Month == birthDate.Month && now.Day < birthDate.Day))
            --years;

        return years;
    }
If you would now like to see if the person is younger than 18 you would simply do this:
     if (CalculateAge(personDateOfBirth) <= 18)
            {
                throw new Exception("you are younger than 18");
            }

Tuesday, January 11, 2011

Extension method to split a string on uppercase

This is a nice little regular expression that creates a space before every UPPERCASE letter.

It comes in especially handy when you have an enum and want to display it in a readable format

public static string SpaceOnCase(this string str)
        {
            return Regex.Replace(str, "[A-Z_]", " $0").Trim();
        }

Friday, January 7, 2011

Getting Information from a South African ID Document

After a good couple of days i can now say i have the most complete ID number Parser out there.

Wikipedia gives a nice explanation of the stricture of an id number. I also made use of Willem's ID Validator to validate ID Numbers.

Here are the basics of an ID Number
A South African person identification number is a 13-digit number containing only numeric characters, and no whitespace, punctuation, or alpha characters.

It is defined as YYMMDDGSSSCAZ:
  • YYMMDD represents the date of birth (DoB);
  • GSSS is a sequence number registered with the same birth date (where G is 0-4 for females, 5-9 for males);
  • C is the citizenship with 0 if the person is a SA citizen, 1 if the person is a permanent non-SA citizen;
  • A currently 8 or 9 apply, however previously this number was the used in combination with Z to provide race and status.
  • Z is a government control digit, 7 if the person is registered for National service and has completed it. 3 if the person has not completed the national service;
Using ID Number 8001015009087 as an example, it would read as follows using the old SA ID methology:

The ID indicates that a white SA citizen male was born on 1 January 1980, he was the 9th person to be registered and issued with an ID with the same birth date. He has been registered to do his national service with the defense force, and has completed the service.

As national service call up no longer exists the last digit no longer carries the same weight as in the past. And with the new IDs issued it is used as a control number to validate the ID.\

From your ID number you can determine:
  • Date Of Birth
  • Gender
  • Citizenship
  • Permanent Residency
  • *Race
  • *National Service
*= Only if you were born prior 1988

Below is a helper class that that parses a South African ID Number and returns all the required info

public static class IdNumberParser
    {
        public static IdResult Parse(this Int64 idNumber)
        {
            var idResult = new IdResult {IdNumber = idNumber};
            idResult.Valid = CheckValidity(idNumber.ToString());

            if (idResult.Valid)
            {

                #region *** GET DATE OF BIRTH ***
                var year = idNumber.ToString().Substring(0, 2);
                year = int.Parse(year) < int.Parse(DateTime.Now.Year.ToString().Substring(2,2)) ? int.Parse("20" + year).ToString() : int.Parse("19" + year).ToString();
                var month = idNumber.ToString().Substring(2, 2);
                var day = idNumber.ToString().Substring(4, 2);
                idResult.DateOfBirth = new DateTime(int.Parse(year), int.Parse(month), int.Parse(day));
                idResult.Gender = int.Parse(idNumber.ToString().Substring(6,1)) < 5 ? "Female" : "Male";
                #endregion

                #region *** CHECK CITIZENSHIP ***
                if (int.Parse(idNumber.ToString().Substring(7,1)) == 0)
                {
                    idResult.SaCitizen = true;
                    idResult.PermmanentNonSaCitizen = false;
                }
                

                if (int.Parse(idNumber.ToString().Substring(7, 1)) == 1)
                {
                    idResult.SaCitizen = false;
                    idResult.PermmanentNonSaCitizen = true;
                }

                #endregion

                #region *** CHECK NATIONAL SERVICE ***
                if (idResult.DateOfBirth.Year < 1988)
                {
                    if (int.Parse(idNumber.ToString().Substring(11, 1)) == 7)
                    {
                        idResult.NationalServiceCompleted = true;
                    }

                    if (int.Parse(idNumber.ToString().Substring(11, 1)) == 3)
                    {
                        idResult.NationalServiceCompleted = false;
                    }

                    switch(int.Parse(idNumber.ToString().Substring(10, 1)))
                    {
                        case 0: idResult.Race = "White"; break;
                        case 1: idResult.Race = "Cape Coloured"; break;
                        case 2: idResult.Race = "Malay"; break;
                        case 3: idResult.Race = "Griqua"; break;
                        case 4: idResult.Race = "Chinese"; break;
                        case 5: idResult.Race = "Indian"; break;
                        case 6: idResult.Race = "Other Asian"; break;
                        case 7: idResult.Race = "Other Coloured"; break;
                        default: idResult.Race = "Unknown"; break;
                    }
                }
                else
                {
                    //only ID's produced prior to 1988 has the race card :)
                    idResult.Race = "Unable To Determine";
                }
                #endregion
            }
            else
            {
                idResult.Valid = false;
            }

            return idResult;
        }

        private static bool CheckValidity(string idNumber)
        {
            if (idNumber.Length == 13)
            {
                int d;
                try
                {
                    var a = 0;
                    for (var i = 0; i < 6; i++)
                    {
                        a += int.Parse(idNumber[2*i].ToString());
                    }
                    var b = 0;
                    for (var i = 0; i < 6; i++)
                    {
                        b = b*10 + int.Parse(idNumber[2*i + 1].ToString());
                    }
                    b *= 2;
                    var c = 0;
                    do
                    {
                        c += b%10;
                        b = b/10;
                    } while (b > 0);
                    c += a;
                    d = 10 - (c%10);
                    if (d == 10) d = 0;
                }
                catch
                {
                    return false;
                }

                var lastDigit = int.Parse(idNumber[12].ToString());
                return lastDigit == d;
            }
            return false;
        }
    }

    public class IdResult
    {
        public Int64 IdNumber { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string Gender { get; set; }
        public bool Valid { get; set; }
        public bool SaCitizen { get; set; }
        public bool PermmanentNonSaCitizen { get; set; }
        public bool NationalServiceCompleted { get; set; }
        public string Race { get; set; }
    }
}

Example VS2008 Projects

Example console Application

Monday, September 13, 2010

MVC Confirm Username / Password Validation

Had a question the other day, where someone asked me how to do validation on two fields. a typical example would be: "Password" and "Confirm Password", or even "Email" and "Confirm Email"

All you need to do is decorate your ViewModel class with an attribute:
//Take Note of this attribute, this is where the magic happens
    [PropertiesMustMatch("Email", "ConfirmEmail", ErrorMessage = "The Email Address and confirmation Email Address do not match.")]
    public class MyModel
    {
        [DisplayName("Email:")]
        [Required(ErrorMessage = "Email is required")]
        [Email(ErrorMessage = "Email is invalid")]
        public string Email { get; set; }

        [DisplayName("Confrim Email:")]
        [Required(ErrorMessage = "Email is required")]
        [Email(ErrorMessage = "Email is invalid")]
        public string ConfirmEmail { get; set; }
    }

And here is the PropertiesMustMatch Helper Class

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public sealed class PropertiesMustMatchAttribute : ValidationAttribute
    {
        private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
        private readonly object _typeId = new object();

        public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
            : base(_defaultErrorMessage)
        {
            OriginalProperty = originalProperty;
            ConfirmProperty = confirmProperty;
        }

        public string ConfirmProperty { get; private set; }
        public string OriginalProperty { get; private set; }

        public override object TypeId
        {
            get
            {
                return _typeId;
            }
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
                OriginalProperty, ConfirmProperty);
        }

        public override bool IsValid(object value)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
            object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
            object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
            return Object.Equals(originalValue, confirmValue);
        }
    }