Umbraco 5th birthday meetup in Sydney @ TheFARM

Come on down to TheFARM to share in some beers and take part in the global Umbraco 5th birthday festivities.

Your hosts will be core Umbraco team developers Shannon Deminick & Aaron Powell, both of whom work for TheFARM (http://www.thefarmdigital.com.au).

The plan is for Shan and Aaron to run a Q&A session with some demo’s of the fun stuff TheFARM has been doing with Umbraco 4.1 and the work they have been doing on this next release.

  • They'll have a look at all of the new features/fixes for 4.1 (are there are TONS)
  • They'll go into a bit more in detail on some of the new things that we've integrated into the core such as LINQ to Umbraco, Umbraco Examine, new controls, enhancements, preview, etc…
  • They’ll show you some of the sites we’ve built and talk through some of the implementation’s with things like Flash

Hopefully, with two of the core team on hand we should be able to answer most questions thrown at us – give us a go!

Once we're out of beers... TO THE PUB!

All of the details, address, etc.. is on the Our Umbraco website. Have a look and RSVP now!

http://our.umbraco.org/events/umbraco-5th-birthday-meetup-in-sydney

 

Just in case you don’t want to click through here’s the event details:

Tuesday, February 16, 2010 - 6:00 PM
Suite 101, 4 - 14 Buckingham st Surry Hills, NSW

Posted on 2/12/2010 6:46:45 PM by Shannon Deminick

Permalink | Comments (0) | Post RSSRSS comment feed |

Categories: Technology | The Farm

Tags: , ,

 

The FARM adds a nail to the coffin of IE 6

Days until TheFARM drops support for IE6



We are happy to announce that we will cease to support IE 6 development from Oct 31 2009. We will continue to support our clients’ existing websites but any new development will target Firefox 2+, IE 7+, and Web kit browsers (e.g. Safari, Chrome).

IE 6 was released in Aug 2001, it is 8 years old and 2 more browsers have since been released from Microsoft… it’s about time people stop using this horrible, horrible browser. Other larger players including YouTube have also stated that support for IE 6 will stop. We’re hoping that more agencies will adopt this idea and push for clients to finally make the upgrade or switch to a new browser… it’s 8 years old!

Comments are always welcome!

Posted on 7/20/2009 6:01:00 PM by Shannon Deminick

Permalink | Comments (209) | Post RSSRSS comment feed |

Categories: Technology | The Farm

Tags: , , ,

 

Super easy jQuery events

I've been using jQuery for a while but haven't really been using it much in the form of building custom classes or libraries. I've been working on re-developing Umbraco's JavaScript tree using jsTree (which is great by the way!) and have been writing a JavaScript library to support it. As with most code libraries, events play an important role in writing clean and effective code. In the past I've always used simple callback methods for "event handling" in JavaScript, but this isn't really an event system since it doesn't allow more than one subscriber. After some quick research, jQuery has this all built into the core, and by adding a couple very simple methods to your JavaScript classes, they will instantly support an event model!

Quick example: 

var myObject = { 
  addEventHandler: function(fnName, fn) {
    $(this).bind(fnName, fn);
  },
  removeEventHandler: function(fnName, fn) {
    $(this).unbind(fnName, fn);
  },
  doSomething: function() {
    $.event.trigger("somethingHappening", [this, "myEventArgs"]);
  }
}

The above example is an object defining 2 functions to manage it's event model and another method which raises an event called "somethingHappened" with an array of arguments that will get bubbled to the event handlers.

To subscribe to the events is easy:

function onSomethingHappening(EV, args) {
  alert("something happened!");
  alert("sender: " + args[0]);
  alert("e: " + args[1]);
}
//subscribe to the event:
myObject.addEventHandler("somethingHappening", onSomethingHappening);

You'll notice that the above event handler function has 2 arguments, one called "EV" and one called "args". The EV parameters is the jQuery event object and the second one is the custom arguments object that was created when raising the event.

Since Umbraco's admin section uses an iframe approach, i though that managing events between the iframes would be an issue since they are all using seperate jQuery instantiations, but by raising and consuming events with the above method, this is no problem.


Posted on 3/26/2009 12:08:00 AM by Shannon Deminick

Permalink | Comments (0) | Post RSSRSS comment feed |

Categories: Technology

Tags: , ,

 

Linq to Umbraco

Intro

We've been working on 3 larger Umbraco sites in which we've implemented our own Linq to Umbraco strategy. It's a fairly simple strategy, it doesn't involve custom expression trees or anything, it simply leverages Linq to Xml, IEnumerable<T> and extension methods. In simple terms, we basically convert Xml nodes into our own objects which we can then run typed Linq queries against. Since the performance of deserialization of this Xml into objects was one of our concerns, we implemented Microsoft's Policy Injection framework to handle the caching of our Umbraco objects which works really well (more on this later). Here's a quick example of what this allows you to do with Umbraco data (in this case Umbraco is storing information about events, such as festivals, etc...)

var todayEvents = (from e in GetEvents()
                               where e.FromDate.Date == DateTime.Now.Date
                               select e).ToList();

The source code is available here: LinqUmbraco.zip (566.35 kb).

Background

In order for this to work, there is a bit of setup involved. The way that we've gone about this implementation is by defining a data model for each Document Type that you want to be able to use in this framework. In this example, Umbraco will need to be setup with a few Document Types: Event, EventComment, EventOrganizer, EventsContainer. I've included the definitions of these doc types in the source code so you can easily import them. For an event model, we would create an interface:

public interface IUmbEvent : IUmbracoItem
    {
        System.Collections.Generic.List<UmbEventComment> EventComments { get; set; }
        string EventTitle { get; set; }
        DateTime FromDate { get; set; }
        string FullDescription { get; set; }
        string ShortDescription { get; set; }
        bool ShowInListing { get; set; }
        DateTime? ToDate { get; set; }
    }

You'll notice this interface extends IUmbracoItem which I've defined in our code library. It contains all of the basic properties of an Umbraco node:

 public interface IUmbracoItem
    {
        DateTime CreatedDate { get; set; }
        int CreatorId { get; set; }
        string CreatorName { get; set; }
        int Id { get; set; }
        int Level { get; set; }
        System.Collections.Generic.Dictionary<string, string> NodeData { get; }
        string NodeName { get; set; }
        int NodeType { get; set; }
        string NodeTypeAlias { get; set; }
        int ParentId { get; set; }
        string Path { get; set; }
        int SortOrder { get; set; }
        int? Template { get; set; }
        DateTime UpdateDate { get; set; }
        string UrlName { get; set; }
        string Version { get; set; }
        string WriterName { get; set; }
    }

Next I've created a class that implements this interface called UmbEvent which extends UmbracoItem(which in turn implements IUmbracoItem). The constructor function of these objects are what does the deserialization:

public UmbEvent(XElement x)
            : base(x)
        {
            //Get the from date and use the DateConverter to convert it.
            //If the returned date is MinValue, then the value in Umbraco has not actually
            //been set and since it is mandatory, we'll throw an Exception.
            FromDate = x.UmbSelectDataValue<DateTime>("FromDate", DateConverter);
            if (FromDate == DateTime.MinValue)
                throw new Exception(string.Format("The node with id {0} does not have a start date set", this.Id.ToString()));

            //Get the ToDate using a NullableDateTimeConverter
            ToDate = x.UmbSelectDataValue<DateTime?>("ToDate", NullableDateTimeConverter);

            EventTitle = x.UmbSelectDataValue("EventTitle");
            ShortDescription = x.UmbSelectDataValue("ShortDescription");
            FullDescription = x.UmbSelectDataValue("FullDescription");
           
            //Get the ShowInListing using the IntConverter since the value stored in Umbraco is
            //an integer, not a boolean.
            ShowInListing = x.UmbSelectDataValue<int>("ShowInListing", IntConverter) == 1;           
           
            //Select all child "node" nodes that have a node type alias
            //of "Event Comment" from the current element, deserialize them
            //to UmbEventComment objects, and store them in our List property.
            EventComments = x.UmbSelectNodes()
                .UmbSelectNodesWhereNodeTypeAlias("EventComment")
                .Select(n => new UmbEventComment(n))
                .ToList();           
        }

In the code for the UmbracoItem class are some helper methods for data conversion so exceptions are not thrown and that data is converted properly. 

The next step is to setup the data service layer. I generally create a different service class for each model and in this example we'll have IUmbEventService interface with an UmbEventService class defined as:

public interface IUmbEventService : IUmbracoService
    {

        int CreateEvent(IUmbEvent cqEvent);
        void UpdateEvent(int eventId, IUmbEvent e);
        List<IUmbEvent> GetEvents();
        IUmbEvent GetEvent(int eventId);
        DateTime LastEventDate { get; }

    }

Using the code

Service Layer

Most of the important work is done in the service layer. The underlying method that is called for retreiving events is the GetEvents() method which returns a List<IUmbEvent> object which we can use to query. This method will look up all events in the Umbraco xml cache, convert each one to an UmbEvent and return a list of these deserialized objects. I've created a bunch of Linq to Xml extension methods for use with Umbraco which I use to query the Umbraco xml cache. This makes querying Umbraco xml much nicer, easier and allows you to use System.Xml.Linq objects instead of the old XPath objects. Here's the definition of the GetEvents() method:

[CachingCallHandler(1, 0, 0)]
public List<IUmbEvent> GetEvents()
{

    Console.WriteLine("Looking up and caching all published events...");

    //Lookup the event container node in Umbraco
    XElement xNode = UmbXmlLinqExtensions.GetNodeByXpath(EventContainerXPath);

    var eventData = xNode
        .UmbSelectNodes() //selects all descendant "node" nodes
        .UmbSelectNodesWhereNodeTypeAlias(EventNodeTypeAlias) //selects nodes of a certain alias
        .Select(x => new UmbEvent(x)) //This does the object conversion
        .Where(x => x.FromDate != DateTime.MinValue); //ensure we don't return events with no start date

    return eventData.Cast<IUmbEvent>().ToList();
}

Now that this method is create, we can just query the result of the method to find Events by whatever criteria we've defined in the IUmbEvent interface. For example, the GetEvent method body is quite simple:

public IUmbEvent GetEvent(int eventId)
{
    var myEvent = m_This.GetEvents()
        .Where(x => x.Id == eventId);

    return myEvent.SingleOrDefault();
}

To expose my service layer classes, i have a service factory class that creates new instances of each service type when requested. This makes it easy to call services from code since there's always one point of entry.

Calling the services

I've included a class called Tests which calls the methods of the event service. With the above framework in place, using the code is extremely easy:

List<IUmbEvent> events = m_Factory.EventService.GetEvents();

or

IUmbEvent e = m_Factory.EventService.GetEvent(LookupEventId);

Also in the source code are service methods to create and update Umbraco nodes. If you're not familiar with how to do this, this will show you a fairly easy way to get the job done. The CreateEvent method actually checks to see if a "Pending" event organizer node is there and if not it creates one and then creates new unpublished events under this node.

Running the tests

I've created a simple ashx handler that you can setup in your Umbraco project to test this data layer. You'll need to build the solution and copy the DLL files over from the bin folder of the UmbracoData project into your Umbraco bin folder (don't copy over the Umbraco DLLs if you don't want to ... these are Umbraco 4 DLLs). Then add an HttpHandler to your web.config:

<add verb="*" path="datatest.ashx" type="UmbracoData.TestHandler, UmbracoData" />

You'll have to make sure Umbraco is setup for this project so you'll need to: 

  • Import all of the document types
  • Create a node of type "EventsContainer" called "Events" under the root content node

Now you can navigate to datatest.ashx which will allow you to run the tests. If you want to step through the code, just Attach to your Asp.Net process with the solution open.

Policy Injection

As I mentioned before, performance will be a factor if for every event query, we need to deserialize every event xml node into an object. This is where caching comes in handy. We use Policy Injection quite a bit to do caching and logging in our applications as it makes these tasks incredibly easy. Policy Injection is a simple form of AOP (Aspect Oriented Programming) and is part of Microsoft's Enterprise Library. You'll see that this method is attributed with the CachingCallHandler which is going to cache the output of this method for 1 hour in this case. In each subsequent call to this method Policy Injection will intercept the call and determine if it has been cached, and if so, it will simply return the cached results and cancel the execution of the method.

In order for Policy Injection to work, you need to either create a new service class or wrap and existing service class the with Policy Injection. As i mentioned before I use a service factory to expose all of my service classes and in this class, it creates each service with Policy Injection:

public class ServiceFactory
{
    public IUmbEventService EventService
    {
        get
        {
            return PolicyInjection.Create<UmbEventService, IUmbEventService>();
        }
    }       
}

There is a catch!! You'll notice in the above code snippet that defines the GetEvent method uses the syntax: m_This.GetEvents(). This is because we need a reference to the event service class wrapped in Policy Injection inside the event service class.... confusing in know. This is because the Policy Injection framework is the only thing that knows anything about the CachingCallHandler attribute and if we call GetEvents inside one of the methods of the UmbEventService, then Policy Injection is actually not playing any part of that method call. To get around this, we define an internal property in the UmbEventService class of type IUmbEventService and in the constructor function, we wrap this instance in Policy Injection and assign it to this property:

public UmbEventService()
{
    //Ensure our internal object is wrapped in PolicyInjection
    m_This = PolicyInjection.Wrap<IUmbEventService>(this);
}

/// <summary>
/// We declare this variable as a "trick" to ensure that PolicyInjection
/// is still used when we call internal members of this class.
/// For example, any method of UmbEventService that calls GetEvents() will need
/// to call m_This.GetEvents() to ensure that it returns the cached data
/// and does not re-execute the code in body of the method.
/// </summary>
IUmbEventService m_This;

Another point of interest is that i've included an Umbraco action handler to refresh the Policy Injection cache whenever a node is published.

The source code is available here: LinqUmbraco.zip (566.35 kb).

Posted on 2/24/2009 12:38:00 PM by Shannon Deminick

Permalink | Comments (0) | Post RSSRSS comment feed |

Categories: Technology

Tags: , , ,

 

Linq + Unity + AOP = Fun

I've recently had some time to look at using Dependency Injection alongside of Linq to SQL to manage the DataContext. Because of the way that Microsoft made Linq to SQL, it's not very easy to use dependency injection properly with the DataContext because many of the members in this framework are not interfaces or cannot be instantiated directly from code. Type mocking frameworks can be used to mock these objects but i wanted to see if there was a way around all of this. I've posted a project on the CodeProject website here. A few tricks had to be implemented like creating an interface for the DataContext (IDataContext) and exposing a new property called GetITable exposing a new interface called IEnumerableTable<>. This way, you can resolve IEnumerableTable's from the IDataContext instead of resolving a System.Data.Linq.Table from the GetTable<> method (which you would never be able to implement in a custom DataContext class). Because the IEnumerableTable<> interface is IEnumerable<T> itself, you can run all your normal Linq queries against it as per normal. IEnumerableTable<> also implements ITable so you can perform all of the normal Linq to SQL table methods (i.e. InsertOnSubmit, etc...) on the resolved object.

To get this framework setup and working there's quite a few things you need to build, but it's quite a fun exercise. Included in the source is a custom DataContext called XDataContext which turns the data store into XML files instead of database tables and still uses the Linq to SQL entities that are generated. You can simply just map the IDataContext to any custom data context using the Unity (Microsoft's dependency injection framework) configuration. I've also implemented PolicyInjection into this framework as it is extremely easy to do now that all of the objects are interfaced already. Now it's too easy to cache and log calls to methods.

There's a complete description of everything in the article.

Posted on 2/20/2009 1:28:00 PM by Shannon Deminick

Permalink | Comments (0) | Post RSSRSS comment feed |

Categories: Technology

Tags: , , , ,

 

Upgrading to Umbraco 4

This is a pretty detailed guide of how to upgrade a version 3 Umbraco web application project to be version Umbraco version 4. Theoretically Version 4 is 100% backwards compatible with version 3. I've run a couple of upgrades and everything seems to work great. To upgrade an Umbraco 3 installation that is not part of your own web application project, it should be nearly the same, however you wouldn't need to worry about DLL references, etc.. as mentioned below.

  1. Download umbraco 4 binary files: Umbraco 4 Download
  2. Backup Database
  3. Take backup of all files for current version 3 install
    • If using source control might be best to create a branch
  4. All of the Umbraco DLLs that your project is referencing need to be updated to the new Umbraco 4 DLLs.
    • Generally this means replacing the folder that contains your referenced DLLs with the DLLs in the bin folder of the Umbraco 4 download.
  5. Update/Replace the "umbraco" folder's files:
    • If your project has not customized any of the files located in the "umbraco" folder, then just delete your original "umbraco" folder and replace it with the new "umbraco" folder found in the Umbraco 4 download.
    • If you project does have customized files in the "umbraco" folder, then it might be worthwhile running a diff on the files compared to the new umbraco 4 files to see what has changed and then manually make the changes to the new files to suit your needs.
  6. Update/Replace the "umbraco_client" folder's files:
    • If your project has not customized any of the files located in the "umbraco_client" folder, then just delete your original "umbraco_client" folder and replace it with the new "umbraco_client" folder found in the Umbraco 4 download.
      • In most cases, files should not be modified in this folder
    • If you project does have customized files in the "umbraco_client" folder, then it might be worthwhile running a diff on the files compared to the new umbraco 4 files to see what has changed and then manually make the changes to the new files to suit your needs.
      • In most cases, files that have been modified in this folder have been done to modify the UI of the admin section (which requires a paid Umbraco license) or custom icons have been added to style tree nodes.
  7. Web.config changes:
    • There are a few changes to the Web.config in umbraco 4 which may need to be manually merged in the AppSettings:
      • Change the value of the umbracoConfigurationStatus to be an empty string... otherwise the installer will fail to run.
      • The umbracoSmtpServer value is no longer used, Umbraco 4 uses the system.net -> mailSettings -> smtp values instead
      • The umbracoDisableVersionCheck is no longer required and can be removed
      • The umbracoVersionCheckPeriod should be merged from the Web.config of the Umbraco 4 download files
      • The umbracoUseSSL should be merged from the Web.config of the Umbraco 4 download files
      • The umbracoUseMediumTrust should be merged from the Web.config of the Umbraco 4 download files
    • The browserCaps section can be removed as the /config/browserCaps.config is no longer used.
    • The Umbraco membership providers must be added to your membership providers section if you have one, otherwise create the same membership providers section as seen in the version 4 web.config
    • The Umbraco roleManager providers must be added to your roleManagers section if you have one, otherwise create the same rolderManagers section as seen in the version 4 web.config
    • The Umbraco siteMap providers must be added to your siteMap section if you have one, otherwise create the same siteMap section as seen in the version 4 web.config
    • In the pages->controls section, you need to add the umbraco reference, this is important otherwise the new master page templates will not work.
  8. All other files in the /config folder of your installation may need to be manually merged with the new versions of these files:
    • Don't overwrite your Dashboard.config, XsltExtension.config, formHandlers.config, 404handlers.config, metablogConfig.config, restExtensions.config, or your UrlRewriting.config files!!
    • If you've customized your tinyMceConfig.config then you will need to manually merge this file, otherwise, overwrite it with the new Umbraco 4 version
    • The umbraco.Settings file:
      • You'll need to merge the errors section if you want support for different error pages based on culture
      • The allowedAttributes property of the imaging tag has been updated to no longer support: title,hspace,vspace
      • The disableHtmlEmail property of the notifications section is no longer needed
      • By default the TidyEditorContent in Umbraco 4 is set to true (which uses a new version of Tidy)
      • The requestHandler section should be completely replaced by the new Umbraco 4 version (unless you've specifically made changes to this for your own reasons)
      • The templates section should be added
        • Please not if you set the useAspNetMasterPages to false, Umbraco 4 will still work (there's a couple of quirky bugs in the admin section with this but no show stoppers) but will be using the old Umbraco 3 templates. Setting this to true will automatically upgrade your templates to use master pages!
      • The logging section should be added
      • The webservices section should be replaced
      • The repostories and providers sections should be added
    • Replace the /config/splashes files with the new version 4 files.
    • Add the new App_Browsers folder
  9. Replace the default.aspx file in the root of your project with the new Umbraco 4 default.aspx file
  10. Replace the /install folder with the Umbraco 4 /install folder
  11. Copy the Umbraco 4 /data/packages folder into your /data folder
  12. Copy the master pages folder into your root directory
  13. Delete all DLLs from your project's output bin folder to ensure there are no old Umbraco DLLs hanging around
  14. Attempt to rebuild your project!
    • If your project does not build, then this is most likely due to an error in your project, not Umbraco 4 DLLs. Umbraco 4 is 100% backwards compatible with version 3 and version 3 controls/packages
  15. Run the installer: /yoursiteurl/install/
    • The installer should run seamlessly and you shouldn't need to modify anything
    • When prompted to install Runway, probably best not to do this on an existing installation
Some things to note after upgrading:
  • Though Umbraco automatically created new master page templates for me, i had to manually add the
  • I had to add a ScriptManager to my master master template as many of my controls relied on it. Umbraco 3 must have automatically inserted a ScriptManager
  • It seems as though any public boolean properties that exist in user controls that get set via macros used to either have a 1 or 0 passed in to it which Umbraco would convert. With the new umbraco:macro control, true or false needs to be passed to it instead of 1 or 0.


Happy Umbraco 4ing!

 

Posted on 2/5/2009 6:31:00 PM by Shannon Deminick

Permalink | Comments (4) | Post RSSRSS comment feed |

Categories: Technology

Tags: , ,

 

Umbraco Version 4 Tree API

It's been a long time since I've been able to work on the core of Umbraco version 4 as it's been extremely busy here. I've been meaning to post this document up for some time but just never got around it. Some umbracians were asking about trees in version 4:

http://forum.umbraco.org/yaf_postst5259_Fun-with-Trees--New-patch-released.aspx

I've attached the file that describes the version 4 tree api. This was an initial document created for the Umbraco core team. It is probably close to 100% accurate but i know there's been some minor changes to the tree api since i wrote this.

Hope this helps some of you!

Umbraco_Trees_v1.doc (246.00 kb)

Posted on 11/25/2008 10:03:00 AM by Shannon Deminick

Permalink | Comments (1) | Post RSSRSS comment feed |

Categories: Technology

Tags: , , ,

 

Barzilla

Another great Friday at the Exhibition hotel but something slightly abnormal was on the horizon... Once it was spotted, there was nothing more to do than run up to the  bar to stand next to it, simply to judge the magnitude of what we were dealing with. I may be a pretty short guy to begin with but as the evidence shows, standing next to such a giant makes me appear to be approximately 5 years old. Who the heck is that child at the bar buying beers!!? In the end, nobody was hurt, beers were had and pictures were taken. Good times! 

Posted on 10/15/2008 9:58:00 AM by Shannon Deminick

Permalink | Comments (9) | Post RSSRSS comment feed |

Categories: The Farm

Tags: ,

 

Linq and Unity Framework

We recently decided we'd try something new and look at a fresh new way to manage the Data Context object in LINQ.

I've posted an entire article on it at Code Project: http://www.codeproject.com/KB/linq/LinqAndUnity.aspx

Basically this is using Dependency Injection to manage the LINQ Data Context object between business logic classes. I think its a pretty cool idea but all feedback is much appreciated as there could be way cooler ways to use these two technologies together! 


 

Posted on 7/15/2008 7:54:00 PM by Shannon Deminick

Permalink | Comments (0) | Post RSSRSS comment feed |

Categories: Technology

Tags: , , , ,

 

An Ode to Canada

Canada Day is fast approaching so i decided to take the time and show some Canadian pride. Firstly the Hockey Night in Canada theme song debacle was a shame but at least we didn't lose it all together but it does suck that it won't be played every saturday night for the actual Hockey Night in Canada. Secondly I've made a top 10 list of the greatest Molson Canadian ads and lastly have attached one hell of a hockey fight for your enjoyment. OH CANADA! 

The most important song in history:

 

Molson Canadian Top 10 Ads: 

I AM:
http://www.youtube.com/watch?v=BRI-A3vakVg&feature=related
 
No doot aboot it!:
http://www.youtube.com/watch?v=e1RrncVgLFY&feature=related
 
Nice beaver:
http://www.youtube.com/watch?v=WZrNWEPUsH0&feature=related 
 
Pet beaver:
http://www.youtube.com/watch?v=VUqsF8vbR_Q&feature=related
 
Your sister:
http://www.youtube.com/watch?v=UBEe_4rBezw
 
The code:
http://www.youtube.com/watch?v=plCgrspcAAY 
 
Finding meat:
http://www.youtube.com/watch?v=YFNVKouaNq4 
 
Spit it out:
http://www.youtube.com/watch?v=ie06fsB52a0&feature=related 
 
500 Miles:
http://www.youtube.com/watch?v=qeWG6YG2oOA 
 
On my way:
http://www.youtube.com/watch?v=cRPe6OG_7fQ 
 
Curved sticks:
http://www.youtube.com/watch?v=KtIl06KCp_s&feature=related 
 

Greatest hockey fight!

http://www.youtube.com/watch?v=N1-25s4uwFQ&feature=related 

 

HAPPY CANADA DAY EVERYONE! 

Posted on 6/27/2008 11:56:00 AM by Shannon Deminick

Permalink | Comments (12) | Post RSSRSS comment feed |

Categories: Creativity | The Farm

Tags: ,

 
Copyright © 2010 TheFARM