Tuesday, November 24 2009
Windows 7 64 bit VPN issues and using IE 6

Having used Windows 7 for about a week now on my new PDC Acer Tablet PC, I’ve discovered that Cisco VPN doesn’t work but there is a somewhat simple workaround.  For whatever reason, mainly Cisco is a piece of Cr@p, their VPN client software doesn’t support 64 bit, even though a huge amount of PC’s ship with it everyday. (Nice job C(r)isco)

To get around this shortcoming you can either buy a $140 VPN client, troll the interwebs for VPN “freeware”, one of which crashed my machine.  Or you can download and install the free XP Mode that basically puts a XP virtual machine on windows 7 and supports  “XP Mode Applications” similar to Unity mode in VmWare on the Mac/PC.  Once you have that setup, you can then install Cisco VPN client on that virtual machine and you are good to go.

Couple pointers once you have the Cisco VPN Client installed.

  • If you are like me and use Remote Desktop, you can run it as an XP mode application by a simple registry change in the XP machine.
    • First go the registry, run->regedit, then find the following key [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtual Machine\VPCVAppExcludeList]
    • Any XP app that you want available, find it’s key, Remote Desktop is mstsc.exe, and delete its corresponding registry entry in this list.
    • Now just copy a shortcut to the application to the “C:\Documents and Settings\All Users\Start Menu” folder
    • Your app should now show up in the XP Mode Applications in Win7.
  • If you want IE 6 to show up (for all you web developers out there), just copy a shortcut to it to C:\Documents and Settings\All Users\Start Menu and you are good to go.

XPMode

Posted by Greg Roberts on Tuesday, November 24 2009 No Commentskick it on DotNetKicks.com Bookmark and Share

Thursday, November 12 2009
Testing your location with Google Loader

I’ve had some feedback wanting a working example of the Google loader location technique that I talked about in a previous blog post.  So here it is.

City Region Country Country Code Longitude Latitude

Posted by Greg Roberts on Thursday, November 12 2009 No Commentskick it on DotNetKicks.com Bookmark and Share

Thursday, October 15 2009
Quick and reliable way to get location information about your users.

[UPDATE: Nov 12, 2009]  Posted a quick demo of this working.

Just wanted to post this more for myself than anything else.  The problem is this:

You want to know where your users are coming from.  More specifically, you want their latitude and longitude, city, state, and country.  Now I know there are a lot of ways to get this info using free or paid web services, databases, and even loading map api’s.  But here’s a really dirt simple way to get at that info without much effort.

  1. Grab a reference to Google’s API loader
       1: <script type="text/javascript" src="https://www.google.com/jsapi"></script>
  2. Once the script is loaded you have access to some methods to get you the info you want.  In particular the “ClientLocation” object off of  google.loader. Here I’m assigning the location information to some inputs on a form.
       1: if (google.loader.ClientLocation) {
       2:             var loc = google.loader.ClientLocation;
       3:             if (loc.address) {
       4:                 $('#City').val(loc.address.city);
       5:                 $('#Region').val(loc.address.region);
       6:                 $('#Country').val(loc.address.country);
       7:                 $('#CountryCode').val(loc.address.country_code);                
       8:             } 
       9:  
      10:             if (loc.latitude) {
      11:                 $('#Lat').val(loc.latitude);
      12:                 $('#Lon').val(loc.longitude);
      13:             }
      14:         }
    Taken from the Google website:
    • When an application makes use of the AJAX API loader, the loader attempts to geo locate the client based on it's IP address. If this process succeeds, the client's location, scoped to the metro level, is made available in the google.loader.ClientLocation property. If the process fails to find a match, this property is set to null.
    • When populated, the google.loader.ClientLocation object is populated with the following metro-level granularity properties:

      • ClientLocation.latitude — supplies the low resolution latitude associated with the client's IP address
      • ClientLocation.longitude — supplies the low resolution longitude associated with the client's IP address
      • ClientLocation.address.city — supplies the name of the city associated with the client's IP address
      • ClientLocation.address.country — supplies the name of the country associated with the client's IP address
      • ClientLocation.address.country_code — supplies the name of the ISO 3166-1 country code associated with the client's IP address
      • ClientLocation.address.region — supplies the country specific region name associated with the client's IP address
     
  3. Sit back and relax as you solved your problem in a matter of seconds and a few short lines of code.

Posted by Greg Roberts on Thursday, October 15 2009 No Commentskick it on DotNetKicks.com Bookmark and Share

Thursday, September 24 2009
Analytical Queries using Entity Framework over Date Ranges using Linq

The other day I decided to start doing some analytics style queries purely in Linq against my existing normalized structure.  Normally these types of queries are best suited on a Star Schema, but I think for what I wanted it was simple enough to do in the transactional tables, plus most sites don’t have a true “data warehouse” anyways. 

First lets go over what I was looking to do.

  • For a given date range, get the total number of logins per day.
  • Use only Linq in my EF model ( no stored procedures).
  • Most processing should happen in the DB

I know what you are thinking, that is super simple.  I agree it is, but there are some hidden complexities that I think are worth sharing. 

First lets look at the table that we will mostly be using:

image

This is essentially my session table.  Each time a user logs in, a record is created that ties into a “Login” entity.

In plain english I want a query that counts the total amount of these records grouped by date part of the “CreatedOn” column for a given date range.

Here is how I accomplished the query in Linq, ignore the class I’m using to project the result:

   1: from l in context.LoginToken
   2: where l.CreatedOn >= start && l.CreatedOn <= finish
   3: group l by new{l.CreatedOn.Year, l.CreatedOn.Month, l.CreatedOn.Day} into groups
   4: orderby groups.Key.Year , groups.Key.Month , groups.Key.Day
   5: select new StatsDateWithCount{
   6:                               Count = groups.Count(),
   7:                               Year =  groups.Key.Year,
   8:                               Month = groups.Key.Month,
   9:                               Day = groups.Key.Day
  10:                                                  }));

The key points of this query are as follows:

  • Linq to EF won’t let you do a group by l.CreatedOn.Date, so you need to group by multiple keys on year, month, day
  • You can then order the results with these keys
  • You can’t initiate a DateTime object in your projection since it doesn’t have a parameterless constructor.  I wanted to have the result of count and Datetime, but ended up with breaking the date parts out because of this.

Now most people might say, hey great job I’m done, was that really worth a blog post?  Well maybe not until you look at the results and realize that if you had no data for a particular day (no logins) you will have a gap.  This is really important when graphing results because that 0 for a day becomes crucial to displaying an accurate line graph.

To illustrate in the following graph, the data for 9/19 – 9/20 shows the correct 0 value, but if I didn’t fix the problem then the line would draw directly from 9/18 to 9/21 which would be incorrect.

image

To fix this in code it’s as simple as doing a union of your results with another list of the same type with data for all dates and a 0 count.

Here’s a simple workaround. First create your date range list. Essentially keep looping until start equals stop.

   1: var gap = new List<StatsDateWithCount>();
   2:             DateTime currentDate = start;
   3:             if (start > stop){
   4:                 return null;
   5:             }
   6:  
   7:             while (true)
   8:             {
   9:                 var stat = new StatsDateWithCount();
  10:                 stat.Date = currentDate;
  11:                 stat.Count = 0;
  12:                 gap.Add(stat);
  13:  
  14:                 if (currentDate.IsSameDay(stop))
  15:                     break;
  16:  
  17:                 currentDate = currentDate.AddDays(1);
  18:             }

Now taking the results from your EF query, union them with this “gap” list

   1: results.Union(gap, new StatsDateComparer()).OrderBy(z => z.Date).ToList();

Since we are doing a comparison on a complex object you will need to create your own IEqualityComparer. 

   1: public class StatsDateComparer : IEqualityComparer<StatsDateWithCount>
   2:        {
   3:            public bool Equals(StatsDateWithCount x, StatsDateWithCount y){
   4:                if(x.Date.IsSameDay(y.Date)){
   5:                    return true;
   6:                }
   7:                return false;
   8:            }
   9:  
  10:            public int GetHashCode(StatsDateWithCount obj){
  11:               return obj.Date.GetHashCode();
  12:            }
  13:        }

Keep in mind that you will want to do the Hash on a particular property and not on the whole object in this case.

And that is it… We created our list with datetime and count for all days between a date range.

This ran really quick for me, but obviously if you are going to do a huge date range then this technique may not be the best, but it is good to have in your belt.

Happy coding.

Posted by Greg Roberts on Thursday, September 24 2009 No Commentskick it on DotNetKicks.com Bookmark and Share

Monday, August 10 2009
Really? Is it getting easier?

I've been working on a "green" fields project building an e-commerce system with all the latest trimmings: (win 2k8, 64 bit, Asp.net MVC, Dependency Injection, Jquery)... The list goes on, but what I've discovered is that browser consistency is even worse than its ever been.  I remember only 3-4 years ago, you made it work in IE and then you took on mozilla, but screw the rest of them.  Now IE is the bastard, Firefox is the nice guy, and now Safari, Chrome, and mobile all are valid contendors. 

Why can't this be resolved?  Really, I mean why can't we kill off IE 6?  I remember asking the team last year at PDC and they definitely understand, but said there is a MSFT pasture cycle and its not time yet.  I think this is a bit of BS, especially now that Win 7 is on the loose.  Please help kill this scurge.  Make fun of anyone using this browser.  If you own a website, make it apparent to users that their browser sucks, along with the user:)

Why do web programmers have to code like it's 1995?  Maybe one day we won't.

 

Posted by Greg Roberts on Monday, August 10 2009 No Commentskick it on DotNetKicks.com Bookmark and Share

Monday, June 29 2009
Robots gotcha down? Get reCAPTCHA in ASP.NET MVC.

So it seems that the battle between sites and bots is never ending.  On the good side we have a proven warrior, CAPTCHA, invented by Captain John Cha, a decorated war hero in the french canadian robot war of 1983. 

robots

I’m not gonna go into real details about when and where you should use CAPTCHA controls, but in general it’s probably a good idea on any form that has the potential to be misused or abused by pesky bots or pesky non-bots (usually human).  I like reCAPTCHA because its free and it makes me feel all warm inside that I’m helping a greater cause.

reCAPTCHA improves the process of digitizing books by sending words that cannot be read by computers to the Web in the form of CAPTCHAs for humans to decipher. More specifically, each word that cannot be read correctly by OCR is placed on an image and used as a CAPTCHA. This is possible because most OCR programs alert you when a word cannot be read correctly.

 

It’s pretty dead simple to get this thing going in MVC, as with most things in MVC, it’s just a different way of thinking instead of having a server (or user) control its probably best to implement it as an html helper.  Keep in mind there are a good amount of ways to do this and there are ways to customize the display of the CAPTCHA that I’m not going to get into.  Here’s a short list of things to do to get you started.

  • Sign up for the free account with reCAPTCHA.
  • Make sure you get your private and public keys.
  • Get all fancy and create a settings class to handle all of your config options.

     

/// <summary>
/// Settings for configuring reCAPTCHA
/// </summary>
public class reCAPTCHASection : ConfigurationSection
{
    public const string VerifyUrlKey = "VerifyUrl";
    public const string InsecureHostKey = "InsecureHost";
    public const string SecureHostKey = "SecureHost";
    public const string PrivateKeyKey = "PrivateKey";
    public const string PublicKeyKey = "PublicKey";
    public const string ChallengeInputNameKey = "ChallengeInputName";
    public const string ResponseInputNameKey = "ResponseInputName";
    public const string ThemeKey = "Theme";

    [ConfigurationProperty(VerifyUrlKey, DefaultValue = "http://api-verify.recaptcha.net/verify"),
    Description("The url used to verify the challenge")]
    public string VerifyUrl
    {
        get { return this[VerifyUrlKey] as string; }
        set { this[VerifyUrlKey] = value; }
    }

    [ConfigurationProperty(InsecureHostKey, DefaultValue = "http://api.recaptcha.net"),
   Description("The url used retrieve the CAPTCHA when not using ssl")]
    public string InsecureHost
    {
        get { return this[InsecureHostKey] as string; }
        set { this[InsecureHostKey] = value; }
    }


    [ConfigurationProperty(SecureHostKey, DefaultValue = "https://api-secure.recaptcha.net"),
   Description("The url used retrieve the CAPTCHA when using ssl")]
    public string SecureHost
    {
        get { return this[SecureHostKey] as string; }
        set { this[SecureHostKey] = value; }
    }

    [ConfigurationProperty(PrivateKeyKey, DefaultValue = "YourPrivateKey"),
Description("Private key used for validation")]
    public string PrivateKey
    {
        get { return this[PrivateKeyKey] as string; }
        set { this[PrivateKeyKey] = value; }
    }

    [ConfigurationProperty(PublicKeyKey, DefaultValue = "YourPublicKey"),
  Description("Public key used for validation")]
    public string PublicKey
    {
        get { return this[PublicKeyKey] as string; }
        set { this[PublicKeyKey] = value; }
    }

    [ConfigurationProperty(ResponseInputNameKey, DefaultValue = "recaptcha_response_field"),
 Description("Response field input name")]
    public string ResponseInputName
    {
        get { return this[ResponseInputNameKey] as string; }
        set { this[ResponseInputNameKey] = value; }
    }

    [ConfigurationProperty(ChallengeInputNameKey, DefaultValue = "recaptcha_challenge_field"),
Description("Challenge field input name")]
    public string ChallengeInputName
    {
        get { return this[ChallengeInputNameKey] as string; }
        set { this[ChallengeInputNameKey] = value; }
    }

    [ConfigurationProperty(ThemeKey, DefaultValue = "clean"),
Description("Theme (red, white, blackglass, clean, custom")]
    public string Theme
    {
        get { return this[ThemeKey] as string; }
        set { this[ThemeKey] = value; }
    }
}
  • Encapsulate the formatting and validation logic into its own class. Note that I’m using an HttpForm class to wrap the actual calls to the validation service. You will just need to create a WebRequest and execute a post. Most of this code is not unique and was either taken from some of the open source ASP.NET reCAPTCHA controls or other projects.
/// <summary>
/// Encapsulates the recaptcha logic
/// </summary>
public class reCAPTCHA
{

    private readonly reCAPTCHASection settings;
    private IHttpForm httpForm;

    public reCAPTCHA(reCAPTCHASection settings, IHttpForm httpForm){
        Check.Argument.IsNotNull(settings, "settings");
        Check.Argument.IsNotNull(httpForm, "httpForm");
        this.httpForm = httpForm;
        this.settings = settings;
        
    }

    /// <summary>
    /// Generates the HTML.
    /// </summary>
    /// <param name="isSecure">if set to <c>true</c> [is secure].</param>
    /// <returns></returns>
    public string GenerateHtml(bool isSecure)
    {
        string result = "";
        using (var tmpWriter = new StringWriter())
        {
            using (var writer = new HtmlTextWriter(tmpWriter))
            {

                writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
                writer.RenderBeginTag(HtmlTextWriterTag.Script);
                writer.WriteLine("var RecaptchaOptions = {");
                writer.WriteLine("theme : '{0}'".FormatWith(settings.Theme ?? string.Empty));
                writer.WriteLine("};");
                writer.RenderEndTag();

                // <script> display
                writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
                writer.AddAttribute(HtmlTextWriterAttribute.Src, GenerateChallengeUrl(isSecure,false), false);
                writer.RenderBeginTag(HtmlTextWriterTag.Script);
                writer.RenderEndTag();

                writer.RenderBeginTag(HtmlTextWriterTag.Noscript);

                writer.AddAttribute(HtmlTextWriterAttribute.Src, GenerateChallengeUrl(isSecure, true), false);
                writer.AddAttribute(HtmlTextWriterAttribute.Width, "500");
                writer.AddAttribute(HtmlTextWriterAttribute.Height, "300");
                writer.AddAttribute("frameborder", "0");
                writer.RenderBeginTag(HtmlTextWriterTag.Iframe);
                writer.RenderEndTag();

                writer.RenderBeginTag(HtmlTextWriterTag.Br);
                writer.RenderEndTag();

                writer.AddAttribute(HtmlTextWriterAttribute.Name, settings.ChallengeInputName);
                writer.AddAttribute(HtmlTextWriterAttribute.Rows, "3");
                writer.AddAttribute(HtmlTextWriterAttribute.Cols, "40");
                writer.RenderBeginTag(HtmlTextWriterTag.Textarea);
                writer.RenderEndTag();

                writer.AddAttribute(HtmlTextWriterAttribute.Name, settings.ResponseInputName);
                writer.AddAttribute(HtmlTextWriterAttribute.Value, "manual_challenge");
                writer.AddAttribute(HtmlTextWriterAttribute.Type, "hidden");
                writer.RenderBeginTag(HtmlTextWriterTag.Input);
                writer.RenderEndTag();

                writer.RenderEndTag();
                result = tmpWriter.ToString();
            }
        }
        return result;
    }

    

    /// <summary>
    /// Validates the CAPTCHA challenge.
    /// </summary>
    /// <param name="fromIpAddress">From ip address.</param>
    /// <param name="challenge">The challenge.</param>
    /// <param name="response">The response.</param>
    /// <returns></returns>
    public virtual bool Validate( string fromIpAddress, string challenge, string response){
        Check.Argument.IsNotEmpty(fromIpAddress, "fromIPAddress");
        Check.Argument.IsNotEmpty(challenge, "challenge");
        Check.Argument.IsNotEmpty(response, "response");

        try{
            var fields = new NameValueCollection{
                                                        {
                                                                "privatekey",
                                                                settings.PrivateKey.
                                                                UrlEncode()
                                                                },
                                                        {
                                                                "remoteip",
                                                                fromIpAddress
                                                                .UrlEncode()
                                                                },
                                                        {
                                                                "challenge",
                                                                challenge.
                                                                UrlEncode()
                                                                },
                                                        {
                                                                "response",
                                                                response.
                                                                UrlEncode()
                                                                }
                                                };
            string[] result = httpForm.Post(
                    new HttpFormPostRequest
                    {
                        Url = settings.VerifyUrl,
                        FormFields = fields
                    }
                    ).Response.Split();

            if (result.Length > 0){
                bool isValid;

                if (!bool.TryParse(result[0], out isValid)){
                    isValid = false;
                }

                return isValid;
            }
        }
        catch (WebException e){
            Log.Exception(e);
        }

        return true;
    }

    private string GenerateChallengeUrl(bool isSecure, bool noScript)
    {
        var urlBuilder = new StringBuilder();

        urlBuilder.Append(isSecure ? settings.SecureHost : settings.InsecureHost);

        urlBuilder.Append(noScript ? "/noscript?" : "/challenge?");
        urlBuilder.AppendFormat("k={0}", settings.PublicKey);
        return urlBuilder.ToString();
    }
}
  • You’ll notice that the generateHTML method is actually just creating the recommended html from their website which includes a noscript tag.
  • Now for the MVC part create a extension method for HTML Helper. Yes there is a little structuremap going on here, you can ignore it since you aren’t using the HTTPForm abstraction class.
/// <summary>
/// Displays the CAPTCHA.
/// </summary>
/// <param name="helper">The helper.</param>
/// <param name="settings">The settings.</param>
/// <param name="isSecure">if set to <c>true</c> [is secure].</param>
/// <returns></returns>
public static string DisplayCAPTCHA(this HtmlHelper helper, reCAPTCHASection settings, bool isSecure){

    var captcha = new reCAPTCHA(settings, ObjectFactory.GetInstance<IHttpForm>());
    return captcha.GenerateHtml(isSecure);

   
}
  • On your view page put call this code inside of your form tags.
  • Finally on your controller expect 2 form fields to be submitted and you’ll end up doing something like the following…
          string captchaChallenge = null;
               string captchaResponse = null;
               var settings = new reCAPTCHASection();
              
               model.Validate();

               captchaChallenge = HttpContext.Request.Form[settings.ChallengeInputName];
               captchaResponse = HttpContext.Request.Form[settings.ResponseInputName];
               var rval = captcha.Validate(CurrentUserIPAddress, captchaChallenge, captchaResponse); 

 

Obviously you’ll need to do something with that bool that you get back, you may decide to use the ModelState to return an error or redirect them to some other page.  I apologize for not extracting some of the injection stuff and the HTTPForm class you will either need to create or just call WebRequest directly.  I didn’t want to get into how to make a post request.  Hope this helps you to get started in the right direction. 

 

Posted by Greg Roberts on Monday, June 29 2009 No Commentskick it on DotNetKicks.com Bookmark and Share

Sunday, May 31 2009
About my blog

So while I'm setting all of this up, I might as well tell you about my ride.

  • graffiticms.com - I know that this isn't all the hipsters think this project is dead, I still really dig the interface and am hoping that they get their act together this summer.
  • disqus.com - This is pretty much a must with any amateur style blog.  They have really feature rich comment system and make it easier for randoms to comment on your stuff without having to have a login on your site.  The conversations can also live outside of this world and easily can move to twitter, facebook, or on disqus itself.  Pretty nifty actually.  Maybe this is where wave.google.com will come in when it's released.
  • Sql 2k8 - Why not.
  • Still working out the theme and how I want it to look.  I'm thinking something pretty simple, mostly white, with little noise.

The integration between disqus and graffiti took about 5 minutes, and I pretty much did step for step from this dude's blog: simpable.com/code/disqus/.

I might add more info to this post as I go.

 

Posted by Greg Roberts on Sunday, May 31 2009 No Commentskick it on DotNetKicks.com Bookmark and Share

Sunday, May 31 2009
Hello World!

I'm re-starting my tech blog.  Hopefully I'll start posting at least once a week here.  I will try to keep things a little less techy and a little more smart assy.  I'm by no means the best programmer in the world, and the world doesn't really need another programming blog.  What the world does need is a programming blog with a bit more of an attitude.  I'll try to get some of my friends to post on here as well.  I'll also post serious stuff too, mostly as historical bookmarks of what I'm into at the time.  That's all I got for now.  Maybe later this year this will be on my wave... Good night and Hello World!

Posted by Greg Roberts on Sunday, May 31 2009 No Commentskick it on DotNetKicks.com Bookmark and Share

  • Menu

  • Tags