Tim

Footprints in the snow of a warped mind

March, 2007

Where to find me

Flickr Icon  Twitter Icon  Linked In Icon  FaceBook Icon  Windows Live Alerts Butterfly  RSS 2.0 

The Site Doctor is Hiring!

Enjoy what you read here?
Think you can do better?
Join our team and get paid
up-to £35,000.
Apply now.

Tag Cloud

AJAX (4) Analysis (3) ASP (6) ASP.Net (59) Error Reporting (4) Web Service (2) WSDL (1) Atlas (2) Azure (1) Born In The Barn (1) Business (95) Business Start-up Advice (37) Client (17) Expanding Your Business (24) Recruitment (1) C# (25) Canoeing (4) Canoe Racing (5) Cheshire Ring Race (5) Racing (2) Training (4) Christmas (1) CIMA (1) Cisco (1) 7970G (1) CMS (1) Code Management (1) Cohorts (4) Commerce4Umbraco (1) Content (1) Content Management (1) Content Management System (1) CSS (4) dasBlog (5) DDD (2) DDDSW (1) Design (12) Icons (1) Development (28) Domain Names (1) eCommerce (13) Email (1) Employment (2) Festive Sparkle (1) General (39) Christmas (6) Fun and Games (11) Internet (22) Random (46) RX-8 (8) Git (2) Google (1) Google AdWords (1) Google Analytics (1) Hacking (1) Helpful Script (3) Home Cinema (2) Hosting (2) HTML (3) IIS (11) iPhone (1) JavaScript (5) jQuery (2) LINQPad (1) Marketing (6) Email (1) Multipack (1) MVC (3) Networking (3) Nintendo (1) Nuget (1) OS Commerce (1) Payment (1) Photography (1) PHP (1) Plugin (1) PowerShell (3) Presentation (1) Press Release (1) Productivity (3) Random Thought (1) Script (2) Security (2) SEO (6) Server Maintenance (7) Server Management (12) Social Media (2) Social Networking (3) Experiment (1) Software (11) Office (5) Visual Studio (14) Windows (6) Vista (1) Source Control (2) SQL (13) SQL Server (19) Starting Something New (2) Statistics (2) Stored Procedure (1) Sublime Text 2 (1) SVN (1) TeaCommerce (1) Testing (2) The Cloud (1) The Site Doctor (140) Turnover Challenge (1) Twitter (3) uCommerce (20) Umbraco (37) 2009 (1) 2011 (1) Useful Script (6) Virtual Machine (1) Web Development (72) WebDD (33) Wii (1) Windows Azure (1) XSLT (1)

Blog Archive

Search

<September 2014>
SunMonTueWedThuFriSat
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

Recent Comments

Blog Archive

Various Links

Google+

Blogs I Read

[Feed] Google Blog
Official Google Webmaster Central Blog
[Feed] Matt Cutts
Gadgets, Google, and SEO
[Feed] Ol' Deano's Blog
My mate Dean's blog on my space, equally as random as mine but not off on as much of a tangent!
[Feed] Sam's Blog
Sam is one of my younger brothers studying Product Design and Manufacture at Loughborough, this is his blog :) Enjoy!

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

newtelligence dasBlog 2.2.8279.16125

Send mail to the author(s) Email Me (Tim Gaunt)

© 2014 Tim Gaunt.

Sign In

    # Friday, March 30, 2007

    Discounts on organic aromatherapy products including essential oils and more

    Friday, March 30, 2007 2:05:09 PM (GMT Daylight Time, UTC+01:00)

    I don't want to be seen to be using my blog as another way of pimping out my client's services to my lovely readers -I'm really not, it's just that I've been so busy recently I've not had a chance to finalize some of the content that I'm going to upload shortly. In the mean time though, if you're into aromatherapy -or more to the point organic aromatherapy check out Florame organic aromatherapy's special offers on all sorts of great items.

    Here's the email (there's no need to use a special offer code with this one)

    Hello and thank you for signing up to receive our newsletters which are designed to inform you of new and exciting changes at Florame - including our first ever sale - our Spring Sale

    Our Spring Sale begins today (Friday 30 March 2007). There are over 30 organic items on sale with discounts ranging from 25% to 50% including:

    However stocks are very limited and once they're gone, they're gone! 

    View all the items in our Spring Sale here

    Don't forget, if you've got loyalty points you can use these to buy your bargains and remember you'll collect more points for every purchase you make. 

    All orders over £30 (exc VAT) receive free shipping. 

    All orders made before noon will be shipped the same day (except weekends, when they'll be shipped the following Monday).

    We hope to welcome you to www.florame.co.uk soon and happy bargain hunting!  

     

    Don't forget to follow me on Twitter.

    # Friday, March 23, 2007

    Threshers do it again -another 40% discount voucher! This time for Easter

    Friday, March 23, 2007 12:00:23 PM (GMT Standard Time, UTC+00:00)

    As you're probably aware, at Christmas the Threshers Group put out a 40% discount voucher for the Threshers Group, well it would appear they’ve decided to do it again so I thought I should share it again, as before, the Threshers Group 40% Easter discount voucher can has a couple of T&Cs including no Champagne, Sparkling or Fortified Wine, other than that, it doesn’t appear to have the same £500 limit as before so drink up guys and girls!

    Download the 40% Easter discount voucher for the Threshers Group

    The voucher is valid until 1st April 2007

    Remember: If you're looking for some naughty fun this Easter, check out the Miss Mays Adult Store for all sorts of adult toys and games

     

    Don't forget to follow me on Twitter.

    # Wednesday, March 21, 2007

    Simple accounting database

    Wednesday, March 21, 2007 8:27:16 AM (GMT Standard Time, UTC+00:00)

    As part of my mini-series on Business start-up advice I posted a simple accounting database (see: Finances (VAT, Accountants etc)) but today I realised I had just uploaded the mdb which won't be served by IIS so I just wanted to let you know I've uploaded it as a zip (incase you were one of the people that tried to download it).

    Example Microsoft Access Accounting Database (21KB)

     

    Don't forget to follow me on Twitter.

    # Monday, March 19, 2007

    How to use Phil's error reporting code

    Monday, March 19, 2007 7:34:20 PM (GMT Standard Time, UTC+00:00)

    I’ve done a number of posts now on Phil Whinstanley’s error reporting class and this blog appears to be getting a lot of hits because of that which is pretty neat, as a result I’ve had a couple of people write to me asking similar questions about the code so I thought it would be an idea to write a little summary.

    Where can I download the code?

    It would appear that most of the old copies of Phil’s code have disappeared from the web, I’m not sure why so I’ve uploaded the versions I’ve got below. For convenience I have compiled the code into DLLs for those that don’t know/want to do this and I’ve also included the Visual Studio solutions. I’m not sure if these are based on the original codebase but I don’t think I’ve made any major alterations to these versions:

    1 This is a version I was sent as his original including changes and example email was lost...

    DLLs only:

    If you have Visual Studio:

    If you don’t have Visual Studio you can either download one of the above projects and delete the solution/project files or download the original WebException code. Ok, now you have the files :) -FWIW I can accept no responsibility for any of the files or the code, I just zipped them!

    How to do I use the WebException class?

    I’m now using a slightly modified version of the code to enable error reporting within AJAX (see: Reporting errors from AJAX using the WebException Class) which I’ll try and upload later but whichever version of the code you choose the use is pretty much the same.

    Once you have referenced the DLL in your project (see: Importing/Referencing DLLs in Visual Studio) you will be able to use the WebException. As I’ve covered what you need to do to use the code from within an AJAX application in another post (see: Reporting errors from AJAX using the WebException Class) I’ll just cover how to use it to report global errors. To capture and respond to all application errors you will need to place this code within the global.asax, your project should automatically have one, if it doesn’t then you will need to add one.

    Using the global.asax file, the first thing you need to do is add a reference to the DLL at the top of your code (this will allow you to call the methods and access the properties):

    <%@ Import Namespace="ErrorReporting" %>

    Next locate the Application_Error event handler, this is the method that handles all errors within the application (with exception of those thrown from within an AJAX application, read this post to report errors from within an AJAX application). Now replace your Application_Error and Application_PreRequestHandlerExecute handlers with (for more information on what I'm doing here see: ASP.Net WebException and Error Reporting useful code):

    void Application_Error(object sender, EventArgs e)
    {
        bool reportErrors = Convert.ToBoolean(System.Configuration.ConfigurationManager.AppSettings["SendErrors"]);

        if (reportErrors)
        {
            Exception currentError = Server.GetLastError();

            #region Deal with 404's

            //Redirect the user to a friendly page
            if(CheckForErrorType(currentError, "FileNotFound"))
                RedirectToFriendlyUrl("");

            #endregion
            #region Deal with Spambots

            if (CheckForErrorType(currentError, "System.FormatException"))
            {
                if (HttpContext.Current.Request.Form.Count > 0)
                {
                    foreach (string key in HttpContext.Current.Request.Form)
                    {
                        if (key.IndexOf("_VIEWSTATE") > 0 && HttpContext.Current.Request.Form[key].ToString().IndexOf("Content-Type") > 0)
                            return;
                    }
                }
            }

            #endregion

            //Enable the trace for the duration of the error handling
            TraceContext t = HttpContext.Current.Trace;
            bool bCurrentState = t.IsEnabled;
            t.IsEnabled = true;

            #region Handle the Exception

            ErrorHandling.WebException WE = new ErrorHandling.WebException();
            WE.CurrentException = Server.GetLastError();
            WE.MailFrom = "you@yourdomain.com";
            WE.MailTo = "you@yourdomain.com";
            WE.MailAdmin = "you@yourdomain.com";
            WE.Site = "Your Site's Name or URL";
            WE.SmtpServer = "localhost";
            WE.FloodCount = 10;
            WE.FloodMins = 5;

            #endregion
            #region Choose what you're interested in

            WE.ReturnCache = true;
            WE.DrillDownInCache = true;
            WE.IncludeApplication = true;
            WE.IncludeBrowser = true;
            WE.IncludeEnvironmentVariables = true;
            WE.IncludeForm = true;
            WE.IncludeProcess = true;
            WE.IncludeQueryString = true;
            WE.IncludeRequestCookies = true;
            WE.IncludeRequestHeader = true;
            WE.IncludeResponseCookies = true;
            WE.IncludeServerVariables = true;
            WE.IncludeSession = true;
            WE.IncludeTrace = true;
            WE.IncludeVersions = true;
            WE.IncludeAuthentication = true;

            #endregion

            WE.Handle();

            //Return the trace to its original state
            t.IsEnabled = bCurrentState;

            //Redirect the user to a friendly page
            RedirectToFriendlyUrl("");
        }
    }

    protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
    {
        if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState)
            ErrorReporting.SessionTracker.AddRequest("Pre Request Handler Execute"truetruefalse);
    }

    private bool CheckForErrorType(Exception ex, string errorText)
    {
        if (ex != null)
        {
            //Check the exception
            if (ex.GetType().ToString().IndexOf(errorText) > 0)
                return true;
            else
                return CheckForErrorType(ex.InnerException, errorText);
        }
        else
        {
            return false;
        }
    }

    private void RedirectToFriendlyUrl(string Url)
    {
        if (!String.IsNullOrEmpty(Url) && (Request.Url.Host.IndexOf("localhost") < 0))
            Response.Redirect(Url);
    }

    This will create a new instance of the WebException object, assign the various properties accordingly (you will need to configure these) and then finally handle the error.

    That’s it! That’s all you really need to do to have super error reporting instantly installed in your application! If that wasn't enough it's overloaded with a couple of filters for you :). I recommend you read one of my previous posts I’ve added which overviews a few simple tips and tricks when using the WebException class to that improves on its functionality (see: ASP.Net WebException and Error Reporting useful code).

    All that's left to do is to test it works (see below).

    What should I get from it?

    That’s the million dollar question! Once the WebException class has been added to your application you should receive an email every time the application throws an error (which of course means you’ll never get an email from the system!)

    View an example of the email you’ll get with all outputs set to true.

    More tips/Warnings!

    Ok so it’s installed and you’re getting no errors through (because your codes perfect) but there are a couple of other little tweaks I would make to the WebException class to make it a little more useable.

    Create a centralised class for it

    A while ago I posted a set of “useful” tips for reducing the number of spambot related emails, redirecting the user etc (see: ASP.Net WebException and Error Reporting useful code). That’s fine until you start including the WebException class into multiple projects, managing tweaks to the codebase gets a little cumbersome (i.e. adding the spambot check to all our projects that use the WebException meant a couple of hours of copying and pasting). The work around for me was to wrap it all up into a central static method (see: Reporting errors from AJAX using the WebException Class). I did this rather than fiddling with Phil’s WebException class itself incase he ever got around to releasing another version which would mean a bunch of changes etc.

    Limit the page request log

    If you have a site where every user is likely to have a high page visit count with most of the pages involving some form of form submission then it may be worth limiting the number of request’s stored as we have found that without limiting these we start receiving very large emails (some topping 10MB).

    The reason this is happening is because the session tracker logs all the form elements for the request so if you had i.e. a CMS that submits a page of content every other page request all that data will be stored in the tracker, sticking with the idea of a CMS, your typical text word is around 10bytes (see: How many bytes for...), so say the user writes 500 words per page (which isn’t really a lot) that’s 4.9Kb per form submission plus on the re-display of the page you've got ViewState... That’s just the data submitted by the user, around that, you’ve got all the form fields, field names, session info, query string etc, see how it starts to add up?

    The solution is fairly straight forward, what you need to do is alter SessionTracker.cs1:

    1I thought I'd done this in a project already but cannot find the source so this may not work.

    public class SessionTracker
    {
        public static void AddRequest(string Comments, bool DoForm, bool DoQueryString, bool DoCookies)
        {
            Request R = new Request();
            R.Time = DateTime.Now;
            R.Comments = Comments;
            
            if (System.Web.HttpContext.Current != null)
            {
                R.Path = System.Web.HttpContext.Current.Request.Path.ToString();
                if (System.Web.HttpContext.Current.Request.UrlReferrer != null)
                {
                    R.Referrer = System.Web.HttpContext.Current.Request.UrlReferrer.ToString();
                }
                if (DoForm)
                {
                    R.Form = System.Web.HttpContext.Current.Request.Form;
                }
                if (DoQueryString)
                {
                    R.QueryString = System.Web.HttpContext.Current.Request.QueryString;
                }
                if (DoCookies)
                {
                    R.Cookies = System.Web.HttpContext.Current.Request.Cookies;
                }
            }

            if (System.Web.HttpContext.Current.Session["RequestCollection"] != null)
            {
                RequestCollection RC = ((RequestCollection)System.Web.HttpContext.Current.Session["RequestCollection"]);
                RC.Add(R);
                if(RC.Count > 10)
                    RC.RemoveAt(0);
                System.Web.HttpContext.Current.Session["RequestCollection"] = RC;
            }
            else
            {
                RequestCollection RC = new RequestCollection();
                RC.Add(R);
                System.Web.HttpContext.Current.Session["RequestCollection"] = RC;
            }
        }

        public static void AddRequest(string Comments)
        {
            Request R = new Request();
            R.Time = DateTime.Now;
            R.Comments = Comments;
            
            if (System.Web.HttpContext.Current != null)
            {
                R.Path = System.Web.HttpContext.Current.Request.Path.ToString();
                if (System.Web.HttpContext.Current.Request.UrlReferrer != null)
                {
                    R.Referrer = System.Web.HttpContext.Current.Request.UrlReferrer.ToString();
                }
                R.Form = System.Web.HttpContext.Current.Request.Form;
                R.QueryString = System.Web.HttpContext.Current.Request.QueryString;
                R.Cookies = System.Web.HttpContext.Current.Request.Cookies;
            }

            if (System.Web.HttpContext.Current.Session["RequestCollection"] != null)
            {
                RequestCollection RC = ((RequestCollection)System.Web.HttpContext.Current.Session["RequestCollection"]);
                RC.Add(R);
                if (RC.Count > 10)
                    RC.RemoveAt(0);
                System.Web.HttpContext.Current.Session["RequestCollection"] = RC;
            }
            else
            {
                RequestCollection RC = new RequestCollection();
                RC.Add(R);
                System.Web.HttpContext.Current.Session["RequestCollection"] = RC;
            }
        }

        public static void AddRequest()
        {
            Request R = new Request();
            R.Time = DateTime.Now;
            
            if (System.Web.HttpContext.Current.Session["RequestCollection"] == null)
            {
                RequestCollection RC = ((RequestCollection)System.Web.HttpContext.Current.Session["RequestCollection"]);
                RC.Add(R);
                if (RC.Count > 10)
                    RC.RemoveAt(0);
                System.Web.HttpContext.Current.Session["RequestCollection"] = RC;
            }
            else
            {
                RequestCollection RC = new RequestCollection();
                RC.Add(R);
                System.Web.HttpContext.Current.Session["RequestCollection"] = RC;
            }
        }

        public SessionTracker()
        {
        }
    }

    Outputting the Trace with the WebException Class

    I know this is something I’ve posted about in the past but since moving to version 4 of the code and .Net 2.0 I was no longer getting the trace in my lovely error reports, after a little digging I’ve found a solution, in addition to the code that I posted earlier about enabling the trace using C#, the web.config needs to be set as follows:

    <trace enabled="true" requestLimit="100" pageOutput="false" traceMode="SortByTime" localOnly="true" />

    Storing the WebException code in App_Code Dir

    If you use the WebException class in an ASP.Net 2.0 site, be careful you don’t do what we did and throw the site online uncompiled with a compilation error as it won’t get reported. Luckily I found this issue on a test site but it’s still worth noting.

    Personally I wouldn’t put the error reporting code in the App_Code directory as this means you’ll end up needing to maintain a plethora of files throughout various projects. Instead compile a separate DLL and include that in your projects, then if like me you find a nice addition to the error reporting code you can easily update all sites to the latest version!

    Setup a simple generic test page

    Nothing fancy, just a button that throws an exception will do:

    TestErrorPage.aspx

    <%@ Page Language="C#" %>


    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <script runat="server">
        protected void btnError_Click(object sender, EventArgs e)
        {
            throw new ArgumentException("Test Error");
        }
    </script>

    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Test Error Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <p><asp:Button runat="server" ID="btnError" Text="Throw Error" OnClick="btnError_Click" /></p>
        </div>
        </form>
    </body>
    </html>

    Happy Error Reporting :) -I'm hoping this is the last time I need to blog about this code but what's the betting another post is around the corner ;)

     

    Don't forget to follow me on Twitter.

    # Friday, March 16, 2007

    Payment on acounts

    Friday, March 16, 2007 1:28:52 PM (GMT Standard Time, UTC+00:00)

    I recently released a mini-series of articles with a load of great business start-up advice (also available to download as a PDF) in which I talk about how the government is really going the extra mile for SMEs at the moment, helping them out in a load of different ways -really trying to encourage entrepreneurship within the United Kingdom. This was something I believed until I got my tax bill.

    The deadline for your self-assessment tax return is fast approaching and the government kindly sends you an invoice for your last tax return, mine arrived just before Christmas and to my surprise it was 50% more than I submitted. Seeing as I was off to Cornwall I decided I would deal with it on my return.

    I've never had an issue with paying tax, in fact I've always been proud of the fact that I've needed to pay tax, my logic is simple: if I need to pay tax, it means I'm earning! The more tax I pay, the more I've earned. See my logic?

    In the past, through claiming back expenses etc my tax bills have always been relatively small and although I seem to recall something called “Payment on account”, but it was IIRC an optional payment so next year the amount you have to pay is smaller. This is a good idea. As a small business owner I do put money aside for my tax bill but that’s always been 25% of each invoice. So what’s my issue?

    To me, payment on account seems like a half-arsed attempt by the government to help self-employed people out, while getting a couple more quid in the process, the issue however is I feel they’ve focused more on taking more money than helping out the self-employed people. The idea is simple: You submit your tax bill for the previous tax year, they take an additional payment (payment on account) which will go towards the next tax year, this payment is estimated on their data for you -in this case your tax bill. The first payment is due with your current tax bill, the second in July of the current tax year. Thus splitting the next year’s tax bill into two more manageable payments. -Bollocks does it. Please excuse my French but this is not at all thought out and this is why:

    I would consider myself to be one of the more sensible self-employed people when it comes to saving for tax, admittedly the first couple of years I was in business I paid the tax bill with a project we had on at the time but now I do put money aside for it with every invoice (now 50% of every invoice inc VAT goes aside). So come tax return time I have a nice chunk of cash to pay for my tax bill (always more than it needs to be because of expenses etc). I then follow the governments recommendation and fill out my self-assessment tax return online in plenty of time -a word of warning, I live with a chartered accountant who helps me out with this, it’s not something I just do on my own ;). In January I have the invoice for the bill and I pay it out of my savings, anything left I can use as I wish. All good so far! As far as the government is concerned I have followed what they’re recommending and that should be it. Payment on account however throws this into turmoil. What the government IMHO neglects to tell you is that you’re going to have to pay 50% more than you’re expecting in the first year you go over their threshold.

    What I don’t like about that is they’re encouraging a lot of self-employed people to complete their own return online (best to do this with an accountant really) and save for their bill but by not telling you about this additional payment they can in theory put someone out of business over night -and there’s nothing you can do about it. Take the average self-employed business owner with a turnover of £50,000. Assuming no expenses you should expect to walk away with around £38,300 (using rough maths). Cool, so you’re good and put £11,700 into savings in preparation and use the rest to pay the bills, buy a holiday, a car etc.

    You think all’s dandy until at the end of the year you get a tax bill for £17,550 with a further payment of £5,850 being required in July. That leaves you with £26,600 remaining rather than the initial £38,300 you were expecting. Why? That’s simple -payment on account, the tax office say "Well, you earned £50,000 this year, so you’ll do that next year so we’ll take that money from you now, that way next year you’ll already have some money on account -helping you out. Don’t worry though, if your tax bill for next year is lower, we’ll refund the money." -there are so many issues to this statement but I’ll come back to those.

    I've made a graph demonstrating the two differences. The light red segment is the amount you will need to pay in the July following your January tax payment, the dark red section must be paid in addition to your main tax bill.

    Comparison between the expected tax bill and actual tax bill

    Take my industry -the IT industry. It’s not unknown (or an infrequent occurrence) to have a large project (i.e. £100,000) which you can complete within a financial year. If you're already working in the sector and this £100,000 contract is your reason for going it alone this could be a serious issue for you. Ignoring how you take this money, by the end of a single fiscal year you take £100,000:

    • Turnover:£100,000
    • Tax Allowance:£5,000
    • Taxable Income:£95,000
    • Tax at 22%:£7,700
    • Tax at 40%:£24,000
    • Expected Tax Bill:£31,700
    • Expected to you:£68,300

    In the event you’ve taken £100,000 for a project you’re likely to spend a fair amount of that on things like credit cards, niceties after having gone without for a while etc, so you spend a fair whack, perhaps put a chunk on your mortgage, buy a house or invest a fair chunk. Say you get the payment a couple of months before the tax year ends so you take the time off and relax a little, basically using a fair amount of the money, but you’re ok as you’ve put £32,000 into savings in preparation for your tax bill. When it comes to filing time however you’re told that you owe them an additional £15,850 with your current tax bill followed by another payment of £15,850 in July. Starting to see where my issue lies?

    If you didn’t spend anymore of that £68,300 than you absolutely had to and some how had the additional £31,700 available you’re fine, but what if you decided to treat someone special, or invest the money where it’s not readily accessible, what can you do? I called the tax office to talk it through with them as I didn’t want to pay this payment on account as it would mean that things would be a little tight until the end of a current system development. The representative had absolutely no concern or understanding for my situation, when I asked her if I could spread the payments a little I was told that interest would be charged on the money if I didn’t pay it and a fine would be incurred. Furthermore she told me that this payment on account was ok because it was tax on money we had already earned (the payment on account is in theory for the current fiscal year), I did think about pointing out that a business’ earnings are not the same as a worker’s salary as they are frequently sporadic and go through highs and lows -in the case of The Site Doctor, the majority of our year’s income comes in during the final fiscal quarter.

    There were a couple of things I didn’t like about the representatives statement/government’s perceived understanding of the situation:

    1. Great they are trying to help you out with your business -hopefully making the next tax year’s tax bill a smaller payment (or not at all if you think about the logic) but how does making it a forced payment without making it very well known about help?
    2. It assumes that your business’ monthly turnover is the same as an employee in that your entire year’s earnings are the same (or similar) each month with no seasonal fluctuations. I know there are some more established businesses which do have a regular income but The Site Doctor certainly doesn’t. As already mentioned, The Site Doctor has the majority of the year’s earnings paid in the final fiscal quarter -after you have to pay your tax bill!
    3. They say they’ll refund the money if your next bill is lower than the last so it’s ok. But taking the example of the £50,000 turnover above, that’s a years worth of interest on £11,700 you’ve just lost potential interest of £936 or £2,536 in the example of our £100,000 contract. Can you afford to loose out on that?
    4. This can in theory put someone out of business, as it happened, I had to pay this bill mid contract when normally I wouldn’t have had any money for a fair while, to make things worse Stacey was having a short sabbatical. Luckily we had the money in savings but if we didn’t we would have without a doubt found it hard to pay.

    The solution?

    I don’t like bitching and moaning about things without having some form of solution and I can understand that the government wants to get this cash into the bank and after the first year or two it makes things better for them but for goodness sake make the payment optional, perhaps offer a monthly payment option without penalties or at least inform people about this so they can make provisions for it. Had I not found this out before this tax year I would be in serious trouble. Of course, having a tax specialist do your books should have highlighted this for you -and it goes to show that just because they’re an accountant, if they’re not a specialist they may not know about something that can break the bank (I’ve got no blame for Stacey before you wonder!).

    Note: These figures are derived from my own experience so please take professional advice on the matter as for all I know, there may be a sliding scale (I would hope there is) otherwise the government is killing businesses left right and centre. I’d be interested to know if anyone else knew of/has experienced this issue.

     

    Don't forget to follow me on Twitter.

    # Thursday, March 15, 2007

    When customer service goes mad

    Thursday, March 15, 2007 6:09:43 AM (GMT Standard Time, UTC+00:00)

    I rate good/great customer service very highly when it comes to retaining clients and obtaining new clients so it always make me laugh at how some companies value their customer service and more to the point wonder how they’re still in business!

    The other day I had a couple of conversations with Fasthosts that I simply had to share. A little background though –we’ve been with Fasthosts for a couple of years now, I think we originally signed up in 1999 through 4as1 and I know we were one of their first customers. Since then we’ve registered around 300 domains with their sister company UKReg without a problem.

    More recently however I’ve been feeling less easy about their service. Firstly they started charging for things left right and centre –ok, they’ve got a business to run, next they started to overload the servers (ok it’s a shared server, I can live with that), to combat the overloaded servers they started moving domains –I’m guessing higher demand sites onto their own boxes. That was ok, except they gave you no advanced warning and seeing as a fair few of the sites in question would reference MS Access databases elsewhere would cause us no-end of update fun.

    Then their support went down hill to the extent that we often had to wait for ages until the phone was answered (listening to “you are 17th in the queue”). At this point we had just opened an account with Rackspace so had the view to slowly move the domains over. That was until I got an email saying they would now be charging for a number of services that were historically free which cut into our margins –making them negative. That annoyed me somewhat so moving the sites away from Fasthosts was moved up my priority list.

    When we first signed up with Fasthosts they were the dogs, cheap hosting with all the bells and whistles –we had a Windows account which meant –unlike others at the time we had free use of ASP and all sorts but now all they seem interested in is the Yankee dollar rather than customer retention.

    The other day was the straw that broke the camels back in regards my tolerance of Fasthosts. I had two fairly simple questions and it wasn’t easy to get a straight response to either. The first related to a domain I was adding for a client, they don’t use it but have access to a personal control panel which allows them to manage their emails etc. The control panel although a little outdated does the job. The catch however is recently you need to pay for use of Fasthosts’ control panel or you can build your own through the API. I didn’t need half the stuff included in the “Standard Bundle” and only wanted Fasthosts’ personal control panel so thought it would be best to ask which package I needed:

    Conversation 1 – Fasthosts Personal Control Panels

    Massively long delay before a response finally comes

    Great! They’ve removed the control panels I highlighed (and only those ones!) but they were just the ones in the next month! In fairness, after a few more emails I did get the past invoices refunded as well as a fair few future ones taken off the account but it makes me very concerned as to who else is still paying this charge... I would never have questioned it as they did send a mailshot out saying they would now be charging for them etc. Most odd

    Conversation 2 – SQL Server Express

    As I’ve already mentioned, we’ve been with Fasthosts for around 8 years now (that’s at least £7k in the basic fees in case you’re interested) and for most of that we’ve had use of a shared SQL Server. When we signed up it was SQL Server 7 and although we’ve requested it nicely we’ve never been upgraded and so it resides on a very outdated machine to which we cannot connect using the new SQL Management studio.

    This shared SQL Server costs us £35pm and as we’ve only got 3 active clients using it, bringing in a total of £300pa I felt that is a slight waste of money so my plan was to use SQL Server Express in place (they’re relatively low traffic sites and MS Access was out of the question as they made use of various stored procedures).

    As you now have to pay extra for an ASP.Net account I thought I’d ask support to find out what the situation was...

    I felt that was direct and straight to the point, not asking anything too complicated to which one may expect a yes/no answer? Right?...

    Ok, I know sometimes I’m a little slow but “appropriate authentication method” that had me seriously confused, the only thing I could think was he was referring to Management Studio. Not only that I know for a fact that we’re on SQL Server 7…

    Ok, so perhaps my question wasn't clear enough, I thought that's what I asked, I thought (clearly incorrectly) that SQL Server Express was now released as a standard update through Microsoft updates -so they've disabled that. Perhaps I should have been a little clear in pointing out the fact that I HAVE a shared SQL Server account with them and that it was unsatisfactory but hey I thought they'd check what services we already had with them.

    Ok, so as this point you can tell I was slowly quickly loosing grip but this next one just finishes me off...

    Feel like you’re on a roundabout? I gave up and spent the weekend fast-forwarding my server transfer schedule.

    Ok, looking at those few emails perhaps it is a bit extreme to close down our account with them but this is a monthly occurrence, I would say all in all this set of emails took around 2 hours of my day which to me is time I could be billing a client so I think it’s justified.

    As it happens they did me a favour, by forcing my hand (or annoying me to the extent I did something about it) we're now finally on Rackspace and our sites are a lot more speedy :) Happy Days!

     

    Don't forget to follow me on Twitter.

    When customer service goes mad
    Useful Links:  #  digg it!  del.icio.us  Technorati  email it!  Post CommentsComments [5]  Trackback LinkTrackback
    CategoriesTags: Business | Random | The Site Doctor
    # Tuesday, March 13, 2007

    Give your site a pulse

    Tuesday, March 13, 2007 10:45:25 AM (GMT Standard Time, UTC+00:00)

    Get your finger on the pulse of your site with this great new (free) RSS statistics servicePulseRSS”. I met the developers of PulseRSS the other day at my first Multipack meet (West Midlands based new media meet) which, if you’re nearby you should check out in the future as they’re a lovely bunch of guys (and girls apparently but they were no-where to be seen on Saturday).

    Back to PulseRSS! As already mentioned, PulseRSS is a statistics service via an RSS/XML feed that works in a very similar way to Google Analytics but unlike Google Analytics, they’ve followed the principle of KISS which I think works really well, the interface is simple and easy to use and have I already mentioned it was free?

    So if you’re looking for a simple free statistics package then check out PulseRSS –I’ve got it running on my blog already so it’ll be interesting to see how the stats compare to Google Analytics...

    Pulse Logo

     

    Don't forget to follow me on Twitter.

    Give your site a pulse
    Useful Links:  #  digg it!  del.icio.us  Technorati  email it!  Post CommentsComments [0]  Trackback LinkTrackback
    CategoriesTags: JavaScript | SEO | Web Development | WebDD
    # Monday, March 12, 2007

    ASP not running on Windows 2003 with ASP.Net installed

    Monday, March 12, 2007 10:49:09 AM (GMT Standard Time, UTC+00:00)

    Another post from Doug Setzer from 27Seconds.com :)


    At my "day job", the systems guys are building new Windows 2003 servers to upgrade our aging Windows 2000 servers.  The plan is to:
     - Build the new Windows 2003 server
     - Install IIS
     - Install .NET
     - Run the IIS migration tool from the old Win2k server

    That all went as well as could go - little things got mixed up and had to be corrected.  But, the server would let you request plain HTML files and ASPX files, but classic ASP pages were returned blank.  In poking around Google and the server, we came to find that we had to enable ASP content via:
     - IIS Manager
     - Web Services Extensions
     - Specifically allow Active Server Pages

    But, we were still having the same issues.  Stopping and restarting IIS didn't help. Nor did a server reboot.

    I found a blog post that mentioned checking that the ASP ISAPI has the correct path.  It tried a random thought that Microsoft has changed the default name of the "Windows"/"Winnt" folder -- Windows NT4, 2000, etc. all use "Winnt", where as Windows 2003 uses the "Windows" folder.  Sure enough, double checking the path to the ASP ISAPI had the wrong path and fixing this path fixed our issues with classic ASP files.

     

    Don't forget to follow me on Twitter.

    # Friday, March 09, 2007

    Accessing properties by a string name

    Friday, March 09, 2007 5:12:02 PM (GMT Standard Time, UTC+00:00)

    This morning Julian Voelcker came to me with an interesting issue that I’ve looked into before but I’ve never really looked into a re-useable solution. Seeing as it’s fun Friday I thought why not ;)

    The scenario: I would like to offer my users a custom mail merge facility where by they can insert values stored in the database such as their name. The selection of columns is unlikely to be changed and if it does then I’ll be the one to do it. There are about 20 fields to choose from.

    Easy enough, in the past I’ve kept it to a minimum and then just done a simple find and replace on the body i.e.:

    //Create a dataset and add some test columns
    DataTable dt = new DataTable();
    dt.Columns.Add("Name");
    dt.Columns.Add("Email");

    #region Add some test data

    DataRow dr = dt.NewRow();
    dr["Name"] = "Julian";
    dr["Email"] = "julian@email.com";
    dt.Rows.Add(dr);

    dr = dt.NewRow();
    dr["Name"] = "Tim";
    dr["Email"] = "tim@email.com";
    dt.Rows.Add(dr);

    #endregion

    #region Create the example email body

    string emailBody = "<p>This is a test email to {{Name}} that would be sent to the email address: {{Email}}.</p>";

    #endregion

    #region Do the work

    //Loop through the rows
    for (int i = 0; i < dt.Rows.Count; i++)
    {
        //Get the data row for this instance
        DataRow row = dt.Rows[i];

        //Create a new body as this'll be updated for each user
        string body = String.Empty;

        //Update the body
        body = emailBody.Replace("##Name##", row["Name"]);
        body = body.Replace("##Email##", row["Email"]);

        litOutput.Text += String.Format("{0}<hr />", body);
    }

    #endregion

    The issue I see with this however is (among others) having 20 fields is a lot to be doing with a find/replace statement as it wouldn’t be very elegant and a nightmare to manage. Sticking with this method of using a dataset I suggested we use a regular expression to match the field delimiters and do a replace that way:

    //Create a dataset and add some test columns
    DataTable dt = new DataTable();
    dt.Columns.Add("Name");
    dt.Columns.Add("Email");

    #region Add some test data

    DataRow dr = dt.NewRow();
    dr["Name"] = "Julian";
    dr["Email"] = "julian@email.com";
    dt.Rows.Add(dr);

    dr = dt.NewRow();
    dr["Name"] = "Tim";
    dr["Email"] = "tim@email.com";
    dt.Rows.Add(dr);

    #endregion

    #region Create the example email body

    string emailBody = "<p>This is a test email to {{Name}} that would be sent to the email address: {{Email}}.</p>";

    #endregion

    #region Do the work

    //Loop through the rows
    for (int i = 0; i < dt.Rows.Count; i++)
    {
        //Get the data row for this instance
        DataRow row = dt.Rows[i];

        MatchEvaluator replaceField = delegate(Match m)
        {
            return row[m.Groups[1].ToString()].ToString();
        };

        //Create a new body as this'll be updated for each user
        string body = String.Empty;
        //Find the fields
        Regex r = new Regex(@"{{(\w{0,15}?)}}");
        body = r.Replace(emailBody, replaceField);

        litOutput.Text += String.Format("{0}<hr />", body);
    }

    #endregion

    This is alright and in many ways very scaleable. I’m not a fan of DataSets but in this instance it works nicely and does mean expanding the available fields at a later date would just be a matter of adding columns to the query.

    How does this relate to accessing a property of an object using a string value instead? Well there was a catch, Julian wasn’t using a DataSet and didn’t want to, he had a collection of custom objects all ready and waiting. As he uses a code generator to generate his Data Access Layer and Business Logic Layer there was a method already exposed allowing you to search for a property by string but it's not a standard .Net method so I decided to work out how it was done.

    The solution it turned out was a really rather elegant solution IMHO. Using reflection you can use the same concept as above but with custom objects and Robert is your father’s wife’s sister:

    Reflection.aspx

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Reflection.aspx.cs" Inherits="Reflection" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <h1>Reflection Demo</h1>
            <p>Choose from the following fields to build up your email message, the valid fields are (you can choose whether to use non-valid fields as a test if you like):</p>
            <ul>
                <li>Id</li>
                <li>Email</li>
                <li>Name</li>
                <li>JoinedDate</li>
            </ul>
            <p><asp:CheckBox ID="chkCaseSensitive" runat="server" Text="Make the property search case insensitive" /></p>
            <p><label for="txtEmailBody">Example email body:</label><br />
            <asp:TextBox runat="server" ID="txtEmailBody" TextMode="MultiLine" style="width: 500px; height: 200px;" /></p>
            <p><small>HTML submissions are not allowed and they're encoded anyways so no point in spamming -not that you were going to of course!</small></p>
            <p><asp:Button runat="server" ID="btnSubmit" Text="Merge It!" OnClick="btnSubmit_Click" /></p>
            <asp:Literal ID="litOutput" runat="server" />
        </div>
        </form>
    </body>
    </html>

    Reflection.aspx.cs

    using System;
    using System.Data;
    using System.Configuration;
    using System.Collections;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;

    using System.Text.RegularExpressions;
    using System.Collections.Generic;
    using System.Reflection;

    public class TestObject
    {
        private int __Id;
        private string __Name;
        private string __Email;
        private DateTime __JoinedDate;

        public int Id
        {
            get
            {
                return __Id;
            }
            set
            {
                __Id = value;
            }
        }
        public string Name
        {
            get
            {
                return __Name;
            }
            set
            {
                __Name = value;
            }
        }
        public string Email
        {
            get
            {
                return __Email;
            }
            set
            {
                __Email = value;
            }
        }
        public DateTime JoinedDate
        {
            get
            {
                return __JoinedDate;
            }
            set
            {
                __JoinedDate = value;
            }
        }

        public TestObject(int id, string name, string email, DateTime joinedDate)
        {
            __Id = id;
            __Name = name;
            __Email = email;
            __JoinedDate = joinedDate;
        }

        public bool GetPropertyValueByName(string propertyName)
        {
            object obj = null;
            return this.GetPropertyValueByName(propertyName, falseref obj);
        }

        public bool GetPropertyValueByName(string propertyName, ref object val)
        {
            return this.GetPropertyValueByName(propertyName, falseref val);
        }

        public bool GetPropertyValueByName(string propertyName, bool caseInsensitive, ref object val)
        {
            PropertyInfo p = null;
            BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

            //If it's a case-insensitive search then add the flag
            if (caseInsensitive)
                flags = flags | BindingFlags.IgnoreCase;

            p = this.GetType().GetProperty(
                   propertyName,
                   flags,
                   null,
                   null,
                   Type.EmptyTypes,
                   null);

            //Check the property exists and that it has read access
            if (p != null && p.CanRead)
            {
                //There is a property that matches the name, we can read it so get it
                val = this.GetType().InvokeMember(
                    propertyName,
                    BindingFlags.GetProperty | flags,
                    null,
                    this,
                    null);

                //We return true as the user may just want to check that it exists
                return true;
            }

            return false;
        }
    }

    public partial class Reflection : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                #region Create the example email body

                txtEmailBody.Text = "Dear {{Name}},\r\n\r\nThis is a test email that would be sent to the email address: {{Email}}.\r\n\r\n{{Name}} joined on: {{JoinedDate}}. This field should not be found {{Don't Find Me}}\r\n\r\nRegards,\r\n\r\nThe webmaster.";

                #endregion
            }
        }

        protected void btnSubmit_Click(object sender, EventArgs e)
        {
            if (Page.IsValid && !String.IsNullOrEmpty(txtEmailBody.Text))
            {
                litOutput.Text = "<h2>Output</h2>";

                #region Perform some basic tests
                litOutput.Text += "<h3>Perform some basic tests:</h3>";
                TestObject testObject = new TestObject(1"Tim""tim@email.com", DateTime.Today);

                object obj = null;
                if (testObject.GetPropertyValueByName("id"falseref obj))
                    litOutput.Text += String.Format("<li>{0}</li>", obj);
                else
                    litOutput.Text += "<li>Doesn't Exist</li>";

                if (testObject.GetPropertyValueByName("name"trueref obj))
                    litOutput.Text += String.Format("<li>{0}</li>", obj);
                else
                    litOutput.Text += "<li>Doesn't Exist</li>";

                if (testObject.GetPropertyValueByName("joineddate"trueref obj))
                    litOutput.Text += String.Format("<li>{0}</li>", obj);
                else
                    litOutput.Text += "<li>Doesn't Exist</li>";

                if (testObject.GetPropertyValueByName("nothere"trueref obj))
                    litOutput.Text += String.Format("<li>{0}</li>", obj);
                else
                    litOutput.Text += "<li>Doesn't Exist</li>";

                #endregion

                #region Create a collection and add a couple of items

                List<TestObject> testObjects = new List<TestObject>();
                testObjects.Add(new TestObject(1"Tim""tim@email.com", DateTime.Parse("01/02/2007")));
                testObjects.Add(new TestObject(2"Jim""jim@email.com", DateTime.Parse("20/02/2007")));
                testObjects.Add(new TestObject(3"John""john@email.com", DateTime.Parse("02/03/2007")));
                testObjects.Add(new TestObject(4"Gill""gill@email.com", DateTime.Parse("01/04/2007")));
                testObjects.Add(new TestObject(5"Bill""bill@email.com", DateTime.Parse("11/02/2007")));

                #endregion

                #region Do the work

                //Format it with <pre> for simplicity
                litOutput.Text += "<h3>Now for the reflection example:</h3><hr /><pre>";

                //Loop through the rows
                foreach (TestObject t in testObjects)
                {
                    MatchEvaluator replaceField = delegate(Match m)
                    {
                        //Get the property name (depending on your regex but
                        //mine groups the squigly brackets in there incase
                        //a match can't be found
                        string pName = m.Groups[2].ToString();

                        //Check it's not null
                        if (!String.IsNullOrEmpty(pName))
                        {
                            //Create an object that'll be returned from the method
                            object o = null;
                            //Check if that property exists, if it does return it
                            if (t.GetPropertyValueByName(pName, chkCaseSensitive.Checked, ref o))
                                return o.ToString();
                        }
                        //We've not found a match for the property in the object
                        //so return the match instead as it's probably a mistake
                        return m.Value;
                    };

                    //Create a new body as this'll be updated for each user
                    string body = String.Empty;

                    //Find the fields within the main body -this can be any of the properties of the object
                    Regex r = new Regex(@"({{)(\w{0,15}?)(}})");
                    body = r.Replace(txtEmailBody.Text, replaceField);
                    //Output the example content (HtmlEncoded so not to hurt us!!)
                    litOutput.Text += String.Format("{0}<hr />", Server.HtmlEncode(body));
                }

                litOutput.Text += "</pre>";

                #endregion
            }
        }
    }

    I’ve thrown up a quick demo if you want to test it out. I think in the longer run I’m going to look into having it generate some form of reporting system as that’d be seriously nice, but the suns out and I need to go for a paddle so that’ll have to wait for another day! So that's my first delve into reflection and so far I love it!

     

    Don't forget to follow me on Twitter.

    Accessing properties by a string name
    Useful Links:  #  digg it!  del.icio.us  Technorati  email it!  Post CommentsComments [6]  Trackback LinkTrackback
    CategoriesTags: ASP.Net | C# | Web Development

    Reporting errors from AJAX using the WebException Class

    Friday, March 09, 2007 7:57:18 AM (GMT Standard Time, UTC+00:00)

    I’ve been using Phil Whinstanley’s error reporting class1 within my applications for some time now and it really does help with diagnosing issues with the site’s during development (or client testing) but also alerting me to errors on live sites. I also like it because it can highlight hacking attempts and also spambot form submissions –allowing you to alter the site as needed. A lot of the time it also means we’re alerted to an issue with the site before the client has a chance to call.

    1 Note: I've been told the files Phil put online all those years ago are offline but don't panic, I'm posting another post with the relevant files shortly. If you don't want to use the search function (top right) or you're just keen, check out my comment within my post about ASP.Net WebException and Error Reporting useful code.

    I’m glad he developed it because before this was around I was using a very simple email alert system that didn’t contain even a third of what this one does. Historically in ASP we always reported 500-100 errors as I don’t like clients spotting issues before I do. It’s very important to include error reporting in your code otherwise you may miss a sequence of events that causes your client to loose out on a sale.

    Recently however we got in on the Atlas/AJAX scene pretty early on because we had a new application that would really benefit from a lack of postback and as it was an internal application only where we had complete control over the user’s environment, accessibility wasn’t so much of a concern (though FWIW you can still use the site in the same way without JavaScript activated).

    At present, our development server’s SMTP server isn’t working properly so I didn’t think anything of receiving no email when I threw an exception during the early stages of development but as soon as I threw it onto the live server I quickly noticed that I wasn’t receiving errors from the application (we’ve got a test page to ensure the error reporting is working as expected), on investigation I found that the errors were being caught by the Atlas/AJAX handler (in a similar way to a try/catch block) which meant no emails were being sent out –so what do you do?

    Note: Since I first started this article, Atlas has been released by Microsoft and is now AJAX and as part of the current release, Atlas/AJAX allows you to capture errors that are otherwise trapped by the framework and handle them as you like but for completeness I’ll overview things I tried.

    Firstly I tried simply bubbling the error up to the global.asax’s Application_Error event handler as I normally would but that won’t work as it will still be trapped by the Atlas/AJAX framework, further more, the error returned to the user isn’t very useful (it’s the text within the exception):

    Example standard Atlas/AJAX error - a pretty useless error message as far as the user is concerned!

    The next thing I tried was taking the exception and passing it to the WebException as you do within the Application_Error event handler, although this worked and for this project would have been an alright solution because the ScriptManager was contained within a single MasterPage, I wanted a solution that I could easily roll out to other projects.

    What I decided to do in the end was to wrap the WebException class and adding a single static method that takes an exception, then I replaced the code within the Global.asax and within the ScriptManager’s error event handler and responded to the user with a more informative message. The code below will output a user friendly message -still in a popup though you could redirect if desired. In the live application the user's location and a reference for the incoming error email is also shown to the user.

    Note: TSDGlobals is a settings class we use here, it just references the relevant setting and contains a set of useful methods that we use throughout most of our projects.

    aspx code

    <asp:ScriptManager runat="server" ID="sm" EnablePartialRendering="true" AllowCustomErrorsRedirect="true" OnAsyncPostBackError="atlasScriptManager_PageError"></asp:ScriptManager>

    codebehind

    protected void atlasScriptManager_PageError(object sender, AsyncPostBackErrorEventArgs e)
    {
        //A page reference for you (optional but useful)
        string __PageRef = "132";
        //Update the message the user will see
        sm.AsyncPostBackErrorMessage = String.Format("I'm sorry,  an error has occured, please contact us on 01234 567890. Quoting Page Ref: {0} - {1}", __PageRef, DateTime.Now.ToString());
        //Pass it through to the new Error Handler
        ErrorHandling.ErrorHandler.Handle(e.Exception);
    }

    global.asax

    void Application_Error(object sender, EventArgs e)
    {
        ErrorHandling.ErrorHandler.Handle(Server.GetLastError());
    }

    protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
    {
        if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState)
            ErrorReporting.SessionTracker.AddRequest("Pre Request Handler Execute"truetruefalse);
    }

    ErrorHandler.cs

    using System;
    using System.Data;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;

    namespace ErrorHandling
    {
        public class ErrorHandler
        {
            //Declare for the scope of the class
            private static HttpRequest context = HttpContext.Current.Request;

            public static void Handle(Exception currentError)
            {
                Handle(currentError, true);
            }

            public static void Handle(Exception currentError, bool redirectUser)
            {
                if (TSDGlobals.SendSiteErrors)
                {
                    #region Deal with 404's

                    //Redirect the user to a friendly page
                    if (CheckForErrorType(currentError, "FileNotFound") && redirectUser)
                        RedirectToFriendlyUrl(TSDGlobals.ErrorPage_PageNotFound);

                    #endregion
                    #region Deal with Spambots

                    //Check the error type
                    if (CheckForErrorType(currentError, "System.FormatException"))
                    {
                        if (context.Form.Count > 0)
                        {
                            foreach (string key in context.Form)
                            {
                                if (key.IndexOf("_VIEWSTATE") > 0 && context.Form[key].ToString().IndexOf("Content-Type") > 0)
                                    return;
                            }
                        }
                    }

                    #endregion

                    //Enable the trace for the duration of the error handling
                    TraceContext t = HttpContext.Current.Trace;
                    bool bCurrentState = t.IsEnabled;
                    t.IsEnabled = true;

                    #region Handle the Exception

                    WebException WE = new WebException();
                    WE.CurrentException = currentError;
                    WE.Site = context.Url.Host.ToString();
                    //Pull the information from the web.config here if desired
                    WE.FloodCount = 50;
                    WE.FloodMins = 5;

                    #endregion
                    #region Choose what you're interested in

                    WE.ReturnCache = true;
                    WE.DrillDownInCache = true;
                    WE.IncludeApplication = true;
                    WE.IncludeBrowser = true;
                    WE.IncludeEnvironmentVariables = true;
                    WE.IncludeForm = true;
                    WE.IncludeProcess = true;
                    WE.IncludeQueryString = true;
                    WE.IncludeRequestCookies = true;
                    WE.IncludeRequestHeader = true;
                    WE.IncludeResponseCookies = true;
                    WE.IncludeServerVariables = true;
                    WE.IncludeSession = true;
                    WE.IncludeTrace = true;
                    WE.IncludeVersions = true;
                    WE.IncludeAuthentication = true;

                    #endregion

                    WE.Handle();

                    //Return the trace to its original state
                    t.IsEnabled = bCurrentState;

                    //Redirect the user to a friendly page
                    if (redirectUser)
                        RedirectToFriendlyUrl(TSDGlobals.ErrorPage_CodeIssue);
                }
            }

            private static bool CheckForErrorType(Exception ex, string errorText)
            {
                if (ex != null)
                {
                    //Check the exception
                    if (ex.GetType().ToString().IndexOf(errorText) > 0)
                        return true;
                    else
                        return CheckForErrorType(ex.InnerException, errorText);
                }
                else
                {
                    return false;
                }
            }

            private static void RedirectToFriendlyUrl(string Url)
            {
                //Only redirect the user if the URL is not empty and we're not on a dev machine
                //TODO: Check the referrer to ensure we don't redirect the user to the page causing the error!
                //TODO: Pull the list of development server addresses from an XML file
                if (!String.IsNullOrEmpty(Url) && (context.Url.Host.IndexOf("localhost") < 0))
                    HttpContext.Current.Response.Redirect(Url);
            }
        }
    }

    I’m not sure if this is a recommended way of doing it but it works pretty well and in my case, the majority of settings from the code are the same regardless of the project but you can still alter those if required –as they’re not likely to change project-project I’ve kept the settings within the web.config. I decided to wrap Phil’s code in my own because that way if he ever releases an update (not sure what that’d do tbh) I could just drop the new WebException code into my project and be ready to go straight away.

    What do you think Phil? Use or Abuse of your code ;)

     

    Don't forget to follow me on Twitter.

    # Wednesday, March 07, 2007

    First thoughts on the BlackBerry Pearl

    Wednesday, March 07, 2007 11:30:48 AM (GMT Standard Time, UTC+00:00)
    The BlackBerry Pearl -the next generation in BlackBerrys

    I’ve always been adverse to getting a BlackBerry for a few of reasons:

    1. I don’t get away from work much as it is so I felt having emails on my BlackBerry on the go all the time would be one step too far.
    2. The size of the BlackBerry –they’re ridiculous, I like my phone to be as small as possible so it interferes with my life as little as possible.
    3. I’ve heard horror stories about the increase in bandwidths to manage the email push.

    Then while we were looking at new contracts over Christmas Stacey picked up the BlackBerry Pearl which was small and sleek and suggested I gave it a go. As she didn’t want to change from her V3 we agreed I’d give it a go for a month and if I didn’t like it I’d have her free upgrade (a Sony Ericsson w810i).

    I’ve had the BlackBerry Pearl for well over a month now so I thought I’d share my findings in case anyone else is thinking of getting one.

    How does the BlackBerry “work”?

    There are plenty of tutorials for the BlackBerry and far more advanced information on how the BlackBerry actually works but I thought it may be worth over viewing how it’s configured and the basic concepts.

    BlackBerrys use something called “Push” Technology to retrieve emails, your email client (Outlook, Eudora, Thunderbird etc) classically collects email from a server by contacting the server and getting a list of emails and then downloading the emails. BlackBerrys on the other hand have the email sent to it from the server.

    When you first get your BlackBerry you have to configure your email accounts with your BlackBerry’s ISP (in my case O2), you supply them with server details for your mail account (POP3 settings). Your ISP will then periodically check your email account and collect the new email messages which are then encrypted and sent (or pushed) to your BlackBerry (in a similar way to an SMS message). The BlackBerry then decrypts the email and allows you to read it. Simple eh :)

    My thoughts on the BlackBerry Pearl

    Things I like about the BlackBerry Pearl:

    • It’s size –it’s no larger than a lot of the other phones out there at the moment, in fact it’s smaller than my old Motorola V3 while it was closed (though it’s about 5mm longer)
    • It’s synchronisation with Outlook. I used to have a Palm LifeDrive to manage my calander, address book and tasks etc and a separate address book on my V3 which didn’t sync correctly with Outlook so the three were nearly always different –that and I hated having to carry around the LifeDrive as it was just extra bulk... The BlackBerry Pearl however seamlessly manages it all which has meant that I’ve started managing my to-do list a lot more efficiently as well as my address book.
    • The size and clarity of the screen.
    • Today Plus theme –an extra download but well worth it as it summarises all the info you need to know at a glance on the home screen.
    • Battery life –quoted at around 8½ hours talk time I’m certainly getting at least this. A lot of the time I plug it in via USB while I’m working which charges it but I’ve had a couple of weekends away recently which has meant it’s not got charged and it happily coped with the (for me) heavy use without an issue.
    • The complete call log of all incoming and out going calls on a user basis –this is great for me if I need to know how long I spent speaking to a client etc (I tend to use my mobile for most outgoing calls as they’re all free ;)).
    • The degree of customisation (though this is also a downside as it is somewhat complicated).
    • The fact you can customise what the side buttons do.
    • The standby button –why it has a keylock I don’t know as it gets in the way of the standby button.
    • The voice dialler –that’s awesome.
    • VoiceRecorder+ (a voice memo recorder for the BlackBerry Pearl from ShapeServiceswww.shapeservices.com)
    • The fact you can add delays into the number dialling to quickly and automatically navigate the IVR options for systems you regularly use.

    Things I dislike about the BlackBerry Pearl:

    • It’s pretty complicated. Unlike most phones these days each application has it’s own settings and finding where they are is sometimes a real PITA.
    • You can’t set ringing profiles to activate at certain times of the day.
    • You can’t send SMS messages from it through your computer.
    • It doesn’t ring and vibrate at the same time.
    • The ringer is a little quiet at times (though people suggest drilling holes in the back of the casing sorts this).
    • The key lock which sometimes gets in the way of taking the phone out of standby –I expect there’s a way of turning it off but I’ve not found it yet.

    All in all I think the BlackBerry Pearl is a superb phone and I’ll certainly be keeping it. I’ve setup a filter to ensure I don’t get spam coming through on it but I still get between 10 and 50 emails a day on it, the data transfer is still low (it’s still under 100KB) but I don’t surf the web etc.

    O2 offer a deal at the moment where you can have unlimited data for £10pm on top of your bill, my thinking is if I start to use more than £10 of data a month I’ll upgrade but atm it’s all good. In regards the additional load on our mail server I’ve not noticed anything significant but I’ll analyse this in a couple of months as the calls should be clear. To avoid spam emails I’ve setup a separate mail account that the O2 server collects from, then from my main email accounts I forward any that are sent directly to me and without my spam filter’s headers added to my GMail account which then filters pretty much everything else missed by my server’s filter before forwarding it onto my BlackBerry account. I realise this is a slightly long winded method of managing it but it has meant that 99.9% of all spam has been ignored. There have been a couple of emails missed (i.e. where I’ve been CC’d) but I can live without having those on the go ;)

    I had an issue when I first got the BlackBerry Pearl that all numbers would be dialled with the prefix of +44 and the leading 0. As it happens, the fix is pretty simple:

    1. Open the call log
    2. Press the menu key (the BlackBerry icon)
    3. Choose “General Options”
    4. Choose “Smart Dialing”
    5. Change the country code to +44
     

    Don't forget to follow me on Twitter.

    # Monday, March 05, 2007

    Why didn’t you pick up the phone?

    Monday, March 05, 2007 10:02:29 AM (GMT Standard Time, UTC+00:00)

    ** WARNING ** this post is most definitely a rant
    A couple of weeks ago Stacey and I were hit with some really nasty flu like bug (not man flu :P), I don’t think I’ve ever had flu before so I’m not sure if it was flu but this was nasty, I was (among other things) hallucinating the duvet wanted me to join a cult but that’s a whole other story!

    I like talking to people and running my own business I tend to have my phone on me wherever I go, very infrequently do I not answer within a couple of rings. A couple of days before I was hit with this bug, I said I would get back to one of the guys that passes us a little work every now and again about a potential client he had.

    Being ill however (and I mean bed-ridden) I didn’t manage it so he gave my mobile a call –great, only it was in the other room and I really couldn’t be bothered to get up and get it so I left it. A couple of minutes later I get an SMS through (which turned out to be from my answer phone) just before the house phone started ringing. As it was within arms length I answered to a very grumpy git bitching on about how I was ignoring him and why didn’t I answer my mobile –was it because I didn’t have caller id on that line etc.

    As it turns out, we do have caller id on all phones and I did know it was him and it wasn’t that I was avoiding him, it was merely that I was too ill to answer. FWIW I never ignore people when they call, if it’s you and you’re after something and I don’t want to talk to you I’ll just tell you so, I don’t have the time to arse around playing games so grow up!

     

    Don't forget to follow me on Twitter.

    Why didn’t you pick up the phone?
    Useful Links:  #  digg it!  del.icio.us  Technorati  email it!  Post CommentsComments [0]  Trackback LinkTrackback
    CategoriesTags: Random