Friday, May 6, 2016

Theatre on the River - reskin


My sister runs a very successful drama teaching company in London, and her current site is nice but it uses device detection ( if you're interested) for mobile/tablet which is no longer the cool way to handle mobile traffic (as now enforced by our Google overlords).

Instead, sites must be responsive which is hardly a term new to anyone reading this drivel but for those of you who've been living in a cave eating beetle dung for the last few years, "responsive" is an HTML 5-based concept where the site's presentation layer (the HTML and CSS) must adapt to the device the site is rendering into.

This means no cop-out of sniffing the device server-side and redirecting to a /mobile set of layouts/masterpages/etc but instead - there is just one site to rule them all. Which is done using CSS media queries.

This isn't the first responsive site I've worked on, ohnono. My brother runs a very successful glass machinery oligarchy in Rugby (home of the game, Rugby fans!) and his site was the first independent site I rebuilt in a responsive style.

Glass Machinery Solutions Ltd

As you can see, it looks good on

a) desktop,
b) tablet, and of course
c) mobile

All served from just the one URL and this pleases our Google overlords, who will continue to rank your site in accordance with their holy scriptures. And not penalise you for redirecting to effectively another site (or worse, your site is not mobile-friendly at all and therefore you fail at everything).

I'm no front-end guru; never have been, don't want to be either. It'll push other, more useful stuff out of my head such as being able to order a beer, or pulling a wheelie between lanes of traffic. What I am good at doing with front-end stuff is glueing someone else's efforts together into an ASP.NET application.

I do understand media queries, they're not hard. I do not however have the necessary skill/patience to make sure the resulting CSS & HTML functions across all browsers, devices and small hairy marsupials.

So ! HTML5Up to the rescue. These guys offer a bunch of free, responsive skins (a bunch of CSS, JavaScript and sample HTML pages) for you to use, as long as you acknowledge their awesome efforts (no probs there guys!).

They also have even more elaborate skins you have to pay for, not a lot, but whether you choose to go the free or paid route, I can highly recommend their work.

The new site needs some image work and polishing (so don't mail me about images being different heights or whatever COS YEAH I KNOW) but I'm sure you'll agree, it looks better. For minimal outlay.

People dedicating their lives to making awesome HTML 5 skins - you have my utmost respect and infinite gratitude.

BTW please don't spam my sister's email using the Contact Us form.

BTTW I'll know who did it and I will find you

BTTTW Look at me. I'm the captain now.


Thursday, June 4, 2015

Be careful what you delete for II - Judgment Day

A while back I posted this regarding deleting data from Sitecore's Analytics database.

TL;DR - Sitecore 6.4 (and possibly other versions using SQL-based analytics) do not want you to truncate the IPOwner table. Analytics stops working.

Lo and behold, we discover today that one of our Sitecore applications hasn't been collecting analytics for a while. Investigation of the database revealed - gasp - no data in the IPOwner table !!!

The Sitecore logs revealed a multitude of Foreign Key constraint violations; specifically the foreign key IP.IPOwnerID - IPOwner.IPOwnerID. It's trying to add a row to the IP table using an IPOwnerID that does not exist.

Wait, what ? There aren't any rows in IPOwner ?

Suspecting magic GUID foul play and thinking that Sitecore is perhaps expecting a row to be there, we crack open a fresh copy of Sitecore 6.4's OMS database and sure enough, hiding in there was this little fella: -

IpOwnerID Name Country VisitorIdentification ExternalUser
9F28FB1A-BDDA-4CE6-9521-D7E6C6D8BB9D (Pending) (Pending) 0

And it all makes sense. While waiting for MaxMind GeoIP service to return something, Sitecore is using this guy as the IPOwner for a new session until the queued service lookup completes (hopefully), so it can write some meaningful location data to the IP record.

Added this row.. tested the site.. analytics starts recording stuff !

So now we know. Delete all the IPOwner rows if you want, just make sure this row finds its way back in.

Tuesday, March 19, 2013

SQL Row_Number() OVER and how to cure indeterminate sort order

There's been plenty of wibbling on Stack Overflow about how paging and sorting at SQL Server level using the Row_Number() function can produce records ordered unpredictably between repeated executions of the same query; perhaps you wouldn't normally do this, perhaps you would and as required by my scenario, I did.

And saw it myself - the variations weren't great but they were there and that's annoying.

I checked and double-checked my stored procedure - it was doing what it was supposed to.

Came across this via Stack Overflow - - and I'll repeat the most pertinent part here: -

There is no guarantee that the rows returned by a query using ROW_NUMBER() will be ordered exactly the same with each execution unless the following conditions are true: -

  • Values of the partitioned column are unique.
  • Values of the ORDER BY columns are unique.
  • Combinations of values of the partition column and ORDER BY columns are unique.

How to fix this ?

So - Microsoft themselves admit this is a problem. There is of course a solution, and mine was a table variable.

  InvoiceID INT,
  RowNumber BIGINT,
  TotalRowCount BIGINT

Because the actual results my query returns contained a number of identical field values in many cases (same Customer Name, same Status.. etc) the three rules outlined in the MSDN article weren't being met; hence the problem. You can guarantee the order by stripping the columns returned from the actual query back to a bare minimum - a key field (InvoiceID in my example), the RowNumber itself and (optionally) the result of the partition column expression.

Having defined our table, I then run the complete query but returning only those 3 fields needed: -

INSERT INTO @IDTable (InvoiceID, RowNumber, TotalRowCount)
  Row_Number() OVER
    WHEN @SortExpression = 'DateCreated'  
     THEN  i.DateCreated

    -- snip - long list of fields to order by

  ) AS RowNum,
  COUNT(*) OVER(PARTITION BY 1) as TotalRowCount 

  FROM ..
  WHERE ..

 ) results

 WHERE RowNum 
                BETWEEN (@PageIndex * @PageSize + 1) 
                AND     ((@PageIndex * @PageSize) + @PageSize)

That deals with correct ordering of results; now run another query to join back to @IDTable, this time also retrieving all the coulmns needed: -

 SELECT i.InvoiceID, c.CustomerName, .. etc
 FROM @IDTable idt
  INNER JOIN Invoice i WITH (NOLOCK) ON idt.InvoiceID = i.InvoiceID
  INNER JOIN Customer c ..

    -- and so on 

Ok so there's some overhead involved with running two queries as opposed to one; but the execution time is still fast and it gives me exactly what I want. You should have seen what was happening before..

Wednesday, March 6, 2013

Sitecore - Illegal characters in path


        /// checks for the existence of illegal characters in the URL, these being " < > | and SPACE
        /// for some reason Sitecore's Sitecore.Pipelines.PreprocessRequest.FilterUrlExtensions pipeline
        /// component doesn't handle the exception
        /// FilterUrlExtensions.Process > Sitecore.Web.RequestFilter.Process > RequestFilter.IsBlocked > RequestFilter.GetExtension
        ///     > System.IO.Path.GetExtension > System.IO.Path.CheckInvalidPathChars = where it goes bang, and isn't handled
        /// This component fixes this behaviour by stripping illegal chars and then re-writing the URL using HttpContext.RewritePath
        /// Note that for some reason, if IIS Static File Compression is enabled for the site, this can cause resources to
        /// be returned to the browser with the correct encoding type (GZIP) but the browser fails to parse them
        /// Don't know why this is
        /// For this reason, the URL is only rewritten if the request URL contains these illegal chars

Let's just say this has caused problems with custom pipeline components I have worked on.

Friday, May 11, 2012

Diagnosis vs Design

This is a subject close to my heart, and my abilities too.

I am ok at diagnosis, but by no means a ninja - and sometimes as a solution architect you're going to need to diagnose a problem. Some are easy. Some are not.

I am from the design school of systems architecture which means doing it right in the first place. I can hold court on OO design til the cows come home, and I can design you a domain model that will model your system's data and - most importantly - its behaviour. Cleanly, concisely and to the point of your requirement.

I can do this across a huge range of technologies - web front end, Sitecore, Kentico, WCF, ServiceStack, Biztalk, MSMQ, SQL, Oracle. The building blocks of a lot of .NET solutions.

What I'm not so good at - compared to the above - is diagnosing where and how things are going wrong in an existing solution. I tip my hat to a select few who can do this better than me.

I can diagnose things that are wrong to a certain degree, and you can and should too. Today I had to sort out a non-working ASP.NET app. Here's what I did, in 10 minutes: -

  • Installed the IIS6 metabase compatibility components
  • Ran aspnet_regiis /i to install ASP.NET
  • Set the App Pool's identity to Local System
  • Set permissions on folders
This got the guy going, and I revelled slightly in my ability to spot what was wrong and correct it.

But problems I've seen in the past (and recently) have been a whole lot hairier and have required more tools, and more knowledge.
  • .NET Reflector - this thing is a life saver in so many situations. What does this DLL do ? Let's find out..
  • SQL Profiler - now this one I do know well. What's being sent to the server ? Answer in confidence. SQL Profiler is your friend
  • ANTS Memory Profiler - nice, but a bit bloated. Handy, but the daddy is...
  • WinDebug - see the Matrix as it truly is. Here is where your memory is being allocated. And why
I'll tip my hat to some colleagues of mine past and present who can do this diagnosis phase of systems engineering way better and quicker than I can: -

A guy I knew at Datacom who showed me The Way regarding debugging a live app using WinDebug

Another guy at Datacom who worked for me on an app of unfathomable architecture, and is the only person I know to date who got WS-AT working over MS DTC. We spent a pleasant evening, actually entire day and  night, debugging a WCF sync service that if I'd had more say would never have existed.. another story ..

A person I work for now, who can cut razor-like to the cause of and solution to many problems using the weapons in his arsenal, which are mostly Reflector but he also brings his networking knowledge to bear on things I don't get. Like Nick, an awesome troubleshooter

Working with all of these dudes has humbled me when finding problems. These guys can pick the bones of a solution to the n-th degree and tell me what is and isn't right.

I can do this to a degree, but I think I have a different mindset. I design. I usually do it well.

Both skillsets are needed when doing big stuff. 

Which are you ? Can you do both at the outer edges of ability ? I'm not sure anyone can.

Be careful what you delete for...

Be very careful deleting stuff from a Sitecore Analytics database before you hand it over to your hosting company.

The usual approach is to clear the Page, PageEvent and Session tables because these will be full - many months worth of team dev, in my case - of locally registered page views. BUT - Don't get overzealous and delete the contents of the IPOwner table.

 Sitecore Analytics refused to do anything when I did this. Required inspection of the logs, which revealed a problem with some record not actually existing in IPOwner, even though a cursory inspection showed that this logged unique IP's for a session.

Delete these with extreme caution... because analytics stops working, period !

Tuesday, April 17, 2012

Javascript Frameworks

Richard sent this list of Javascript / Web frameworks around the office today.

The stupendously named batman.js caught my eye, as I am prone to following daft names, although my knowledge of Coffeescript is not great (I've had a play).

However, the even more daftly named Mustache and it's big brother Handlebar.js (where do people get these names from ?) are more my thing and I am playing with them now. More to follow.