Tim

Footprints in the snow of a warped mind

dasBlog

Where to find me

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

Tag Cloud

AJAX (4) ASP (6) ASP.Net (50) Error Reporting (4) Web Service (1) WSDL (1) Atlas (2) Business (76) Business Start-up Advice (25) Client (14) Expanding Your Business (17) C# (16) Canoeing (4) Canoe Racing (5) Cheshire Ring Race (5) Racing (2) Training (4) CIMA (1) Cisco (1) 7970G (1) CSS (3) dasBlog (4) DDD (1) Design (9) Icons (1) Development (12) General (39) Christmas (6) Fun and Games (11) Internet (22) Random (46) RX-8 (8) Home Cinema (2) Hosting (2) IIS (10) iPhone (1) JavaScript (4) jQuery (1) Marketing (5) Email (1) Multipack (1) Networking (2) Nintendo (1) OS Commerce (1) Photography (1) PHP (1) PowerShell (2) Press Release (1) Productivity (2) Security (2) SEO (5) Server Maintenance (4) Server Management (9) Social Media (1) Social Networking (2) Experiment (1) Software (9) Office (5) Visual Studio (12) Windows (4) Vista (1) SQL (1) SQL Server (13) Stored Procedure (1) Testing (1) The Site Doctor (104) Turnover Challenge (1) Twitter (2) Umbraco (17) 2009 (1) Web Development (54) WebDD (33) Wii (1)

Blog Archive

Search

<July 2010>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

Recent Comments

Blog Archive

Various Links

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!

Recent Tracks

last.fm - The Social Music Revolution

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)

© 2010 Tim Gaunt.

Sign In

    # Friday, February 27, 2009

    Advanced Error Reporting in Umbraco, dasBlog and other ASP.Net sites

    Friday, February 27, 2009 3:51:13 PM (GMT Standard Time, UTC+00:00)

    If you've been following my blog you'll know that I've been raving about error reporting within ASP.Net (you can see my ASP.Net Error Reporting category for a couple of them if you like) but until now it's been limited to those sites that you have access to the global.asax file.

    One of the irritations I've found with Umbraco and dasBlog is that I don't get notified of errors as they're just logged to a text file/database somewhere. This is fine if you run 2 or 3 sites but we manage too many to check them all everyday. Instead we rely on email error notifications which until today have been a PITA to integrate into Umbraco.

    Today I'd like to introduce to you Error Handling v2.0 which instead of relying on the global.asax file for the error hooks, uses a HttpModule which means you can install it into any existing/pre-built application such as Umbraco and dasBlog.

    Adding it into the site is simple, you'll need to install the module into the web.config file and add the configuration section a sample (cut down) web.config is below:

    <?xml version="1.0"?> 
    <configuration> 
        <configSections> 
            <section name="tsdErrorsConfigSection" allowExeDefinition="MachineToApplication" restartOnExternalChanges="true" type="System.Configuration.NameValueFileSectionHandler, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> 
        </configSections> 
     
        <tsdErrorsConfigSection file="ErrorHandling.config"/> 
     
        <system.net> 
            <mailSettings> 
                <smtp from="you@yourdomain.com"> 
                    <network host="127.0.0.1" port="25" /> 
                </smtp> 
            </mailSettings> 
        </system.net> 
     
        <system.web> 
            <httpModules> 
                <add name="ErrorModule" type="TheSiteDoctor.ErrorHandling.ErrorModule, TheSiteDoctor.ErrorHandling" /> 
            </httpModules> 
        </system.web> 

    <!--  
    IIS 7 Settings 
        <system.webServer> 
            <validation validateIntegratedModeConfiguration="false" /> 
            <modules> 
                <add name="ErrorModule" type="TheSiteDoctor.ErrorHandling.ErrorModule, TheSiteDoctor.ErrorHandling" /> 
            </modules> 
        </system.webServer> 
    -->
    </configuration>

    Then you'll need to check all the settings -I recommend storing these in another .config file for clarities sake. Make sure you've configured your SMTP settings and you should be good to go.

    If you want to test your settings, I've included a test page for you that will check your settings and show you the defaults if you've not set them. I've got this running now on a couple of Umbraco and dasBlog installs without an issue.

    There's also a useful logging system in it which I'll look to overview in a later post but if you want to see it, check out the included aspx file.

    Download ErrorHandling_v2.0.zip (25Kb)

    If you do use this code I'd be interested to hear how you get on, I think it requires a little more refinement un some areas but it's pretty robust.

    Enjoy.

    # Tuesday, February 17, 2009

    Recent comments macro for DasBlog

    Tuesday, February 17, 2009 9:25:05 AM (GMT Standard Time, UTC+00:00)

    One of the issues I had with John Forsythe's Recent Comments macro for DasBlog was that the dasBlog recent comments weren't ordered by date (descending). I found that as people commented on older posts they were getting buried which irritated me as many were very still valid comments.

    The fix was actually fairly simple, it was just a matter of adding  a sort and thanks to Lamba expressions, this is something we can do fairly simply. If you want to add recent comments to your dasBlog installation, use the following macro:

    Recent Comments Macro

    public virtual Control RecentComments(int count, int adminComments, int trimTitle, int trimContent, int trimAuthor, bool showTitle, bool showCommentText, bool showCommentCount)
    {
        int commentsToShow;
        int totalComments;

        CommentCollection allComments = this.requestPage.DataService.GetAllComments();
        totalComments = allComments.Count;

        //Sort the comments in date order (descending)
        allComments.Sort((c1, c2) => c1.CreatedUtc.CompareTo(c2.CreatedUtc));

        if (!this.requestPage.HideAdminTools && SiteSecurity.IsInRole("admin"))
            commentsToShow = totalComments - adminComments;
        else
            commentsToShow = totalComments - count;

        if (commentsToShow < 0)
            commentsToShow = 0;

        StringBuilder sb = new StringBuilder();

        sb.AppendLine("<div class=\"recentComments\">");

        if (showCommentCount)
            sb.AppendFormat("<div class=\"totalComments\">Total Comments: {0}</div>", totalComments);

        sb.AppendLine("<ul class=\"comments\">");

        #region Loop through the comments

        for (int i = totalComments - 1; i >= commentsToShow; i--)
        {
            Comment current = allComments[i];

            bool showComment;
            if (!current.IsPublic || (current.SpamState == SpamState.Spam))
            {
                if (!this.requestPage.HideAdminTools && SiteSecurity.IsInRole("admin"))
                {
                    showComment = true;
                }
                else
                {
                    showComment = false;
                    if (commentsToShow > 0)
                        commentsToShow--;
                }
            }
            else
            {
                showComment = true;
            }

            if (showComment)
            {
                if ((current.SpamState == SpamState.Spam))
                    sb.Append("<li class=\"spam\">");
                else if (!current.IsPublic)
                    sb.Append("<li class=\"hidden\">");
                else
                    sb.Append("<li>");

                string link = String.Format("{0}{1}{2}", SiteUtilities.GetCommentViewUrl(current.TargetEntryId), "#", current.EntryId);
                string title = current.TargetTitle;
                string desc = current.Content;
                string author = current.Author;

                if (showTitle)
                {
                    sb.AppendFormat("<div class=\"recent{0}CommentsTitle\"><a href=\"{1}\">",
                        current.SpamState,
                        link
                        );

                    if ((title.Length > trimTitle) && (trimTitle > 0))
                        sb.AppendFormat("RE: {0}...", title.Substring(0, trimTitle));
                    else
                        sb.AppendFormat("RE: {0}", title);

                    sb.Append("</a></div>");
                }

                if (showCommentText)
                {
                    sb.AppendFormat("<div class=\"recentCommentsContent\"><a href=\"{0}\">",
                        link
                        );

                    if ((desc.Length > trimContent) && (trimContent > 0))
                    {
                        sb.Append(desc.Substring(0, trimContent));
                        sb.Append("...");
                    }
                    else
                    {
                        sb.Append(desc);
                    }

                    sb.Append("</a></div>");
                }

                sb.Append("<div class=\"recentCommentsAuthor\">");

                if ((author.Length > trimAuthor) && (trimAuthor > 0))
                {
                    int num3 = (trimAuthor > author.Length) ? author.Length : trimAuthor;
                    sb.Append("by " + author.Substring(0, num3));
                    sb.Append("...");
                }
                else
                {
                    sb.Append("by " + author);
                }
                sb.Append("</div></li>");
            }
        }
        #endregion

        sb.AppendLine("</ul>");
        sb.AppendLine("</div>");

        return new LiteralControl(sb.ToString());
    }

    I've since been working on extending it further so you can add a "All Comments" link which I'll post up later as it needs a little more work :)

    If you want this wrapped up as a DLL let me know and I'll upload it.

    Update 26th Feb 2009: You can download the dll here (it's also got a few other things in there if you want to look around).

    Update 27th Feb 2009: I noticed that the above code was messing up everynow and again so I've updated it to use Linq instead which seems to work well. I've updated the DLL but not the source yet.

    Recent comments macro for DasBlog
    Useful Links:  #  digg it!  del.icio.us  Technorati  email it!  Post CommentsComments [1]  Trackback LinkTrackback
    CategoriesTags: C# | dasBlog | Web Development
    # Saturday, February 24, 2007

    DasBlog RSS Feed Macro

    Saturday, February 24, 2007 2:39:04 PM (GMT Standard Time, UTC+00:00)

    As part of my blog’s re-design I wanted to integrate my statistics from Last.FM which monitors what music you’re listening to and generates a stack of statistics about your listening habit (see About Last FM for more information).

    Anyways, I started writing my own RSS macro when I came across one already developed by John Forsythe (http://www.jforsythe.com/) which did pretty much exactly what I was planning on developing, the only difference though was that his was hard-coded to preset node names whereas I was planning on using an XSL file to format mine to offer maximum flexibility in the long run so I updated his with the use of reflector (thanks to John Forsythe though!!).

    There are a couple of difference to note with this code and John Forsythe's:

    • The RSS retrieval is no longer handled by an external library -in this instance I wanted to keep this as simple and stand-alone as possible.
    • There is no max item count at present -this is mainly because I didn't need it for the Last.FM Feed, I may alter that later.

    Source code for a dasBlog XSL based RSS reader

    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Diagnostics;
    using System.Text;
    using System.Web;
    using System.Web.UI;

    using newtelligence.DasBlog.Runtime;
    using newtelligence.DasBlog.Web.Core;

    namespace TSDMacros
    {
        public class TheSiteDoctor
        {
            protected SharedBasePage requestPage;
            protected Entry currentEntry;

            public TheSiteDoctor(SharedBasePage page, Entry entry)
            {
                requestPage = page;
                currentEntry = entry;
            }

            /// <summary>
            /// A dasBlog macro to retrieve an RSS feed and apply XSL to 
            /// it before caching it for x minutes
            /// </summary>
            /// <param name="xslVPath">The virtual path of the XSL file</param>
            /// <param name="rssPath">The RSS feed URL</param>
            /// <param name="minutesToCache">Number of minutes to cache the file for</param>
            /// <param name="debugMode">Output the debug information</param>
            /// <returns>A control that can be inserted into a dasBlog template</returns>
            public virtual Control GetRSS(string xslVPath, string rssPath, int minutesToCache, bool debugMode)
            {
                string cacheVDir = "./content/getrsscache/";
                string cachedFileLoc = String.Empty;
                StringBuilder output = new StringBuilder();

                bool writeToCache = false;
                bool cacheExpired = false;
                bool cacheExists = false;

                #region Debug output
                if (debugMode)
                {
                    output.Append("<strong>&lt;start debug&gt;</strong><hr />\r\n");
                    output.AppendFormat("<i>RssPath</i>: {0}<br />\r\n", rssPath);
                    output.AppendFormat("<i>minutesToCache</i>: {0}<br />\r\n", minutesToCache);
                    output.AppendFormat("<i>CacheStorageFolder</i>: {0}<br />\r\n", cacheVDir);
                    output.Append("<hr />\r\n");
                }
                #endregion

                #region Check whether we need to cache or not
                if (minutesToCache > 0)
                {
                    writeToCache = true;
                    //Find the cache directory
                    string cacheDir = HttpContext.Current.Server.MapPath(cacheVDir);
                    //Work out what the file would be called based on the RSS URL
                    cachedFileLoc = Path.Combine(cacheDir, HttpUtility.UrlEncode(TheSiteDoctor.GetMd5Sum(rssPath)) + ".cache");
                    #region Debug output
                    if (debugMode)
                    {
                        output.AppendFormat("<i>cache file</i>: {0}\r\n", cachedFileLoc);
                    }
                    #endregion
                    if (!File.Exists(cachedFileLoc))
                    {
                        cacheExpired = true;
                        #region Debug output
                        if (debugMode)
                        {
                            output.Append("<i>cache age</i>: no file exists<br />");
                        }
                        #endregion
                    }
                    else
                    {
                        FileInfo info1 = new FileInfo(cachedFileLoc);
                        TimeSpan span1 = (TimeSpan)(DateTime.Now - info1.LastWriteTime);
                        if (span1.TotalMinutes > minutesToCache)
                        {
                            cacheExists = true;
                            cacheExpired = true;
                        }
                        #region Debug output
                        if (debugMode)
                        {
                            output.AppendFormat("<i>cache age</i>: : {0} min old <br />\r\n", span1.TotalMinutes);
                        }
                        #endregion
                    }
                }
                else
                {
                    #region Debug output
                    if (debugMode)
                    {
                        output.Append("<strong>caching disabled - CacheStorageAgeLimit=0</strong><br /><span style=\"color:red; font-weight: bold;\">FYI: All requests to this page will cause a new server request to the RssPath</span><br />");
                    }
                    #endregion
                    cacheExpired = true;
                }

                #endregion

                #region Debug output
                if (debugMode)
                {
                    output.Append("<hr />");
                }
                #endregion
                //Check whether or not the cache has expired
                if (cacheExpired)
                {
                    #region Debug output
                    if (cacheExists & debugMode)
                    {
                        output.Append("<strong>file cache is expired, getting a new copy right now</strong><br />");
                    }
                    else if (debugMode)
                    {
                        output.Append("<strong>no cache, getting file</strong><br />");
                    }
                    #endregion
                    //The cache has expired so retrieve a new copy
                    output.Append(TheSiteDoctor.delegateRss(xslVPath, rssPath, 0, writeToCache, cachedFileLoc, debugMode));
                }
                else
                {
                    #region Debug output
                    if (debugMode)
                    {
                        output.Append("<strong>cool, we got the file from cache</strong><br />");
                    }
                    #endregion
                    //The cache still exists and is valid
                    StreamReader reader1 = File.OpenText(cachedFileLoc);
                    output.Append(reader1.ReadToEnd());
                    reader1.Close();
                }
                #region Debug output
                if (debugMode)
                {
                    output.Append("<hr /><strong>&lt;end debug&gt;</strong>");
                }
                #endregion

                output.Append("\r\n<!-- \r\ndasBlog RSS feed produced using the macro from Tim Gaunt\r\nhttp://blogs.thesitedoctor.co.uk/tim/\r\n-->");

                return new LiteralControl(output.ToString());
            }

            /// <summary>
            /// RSS feed retrieval worker method. Retrieves the RSS feed 
            /// and applies the specified XSL document to it before caching 
            /// a copy to the disk -this should be called after it has been 
            /// established the cache is out of date.
            /// </summary>
            /// <param name="xslVPath">The virtual path of the XSL file</param>
            /// <param name="rssPath">The RSS feed URL</param>
            /// <param name="timeoutSeconds">Number of seconds before the request should timeout</param>
            /// <param name="writeCache">Whether to cache a copy on disk</param>
            /// <param name="xmlPath">Physical path of the XML file on the disk</param>
            /// <param name="debugMode">Output the debug information</param>
            /// <returns>An XML document as a string</returns>
            private static string delegateRss(string xslVPath, string rssPath, int timeoutSeconds, bool writeCache, string xmlPath, bool debugMode)
            {
                StringBuilder output = new StringBuilder();
                bool errorThrown = false;
                string cacheVDir = "./content/getrsscache/";
                string xslPath = HttpContext.Current.Server.MapPath(xslVPath);

                try
                {
                    //TODO: Replace this with a HttpRequest and timeout to ensure the visitor is not left waiting for the file to load
                    //Load the XML
                    System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
                    xmlDoc.Load(rssPath);

                    //Load the XSL
                    System.Xml.Xsl.XslTransform xslDoc = new System.Xml.Xsl.XslTransform();
                    xslDoc.Load(xslPath);
                    
                    StringBuilder sb = new StringBuilder();
                    StringWriter sw = new StringWriter(sb);

                    //Apply the XSL to the XML document
                    xslDoc.Transform(xmlDoc, null, sw);

                    //Append the resulting code to the output file
                    output.Append(sb.ToString());
                }
                catch (Exception ex)
                {
                    errorThrown = true;
                    #region Debug output
                    if (debugMode)
                    {
                        //Log the exception to the dasBlog exception handler
                        ErrorTrace.Trace(TraceLevel.Error, ex);
                        output.AppendFormat("<ul style=\"\"><li><strong>RSS request failed :(</strong> <br />{0}</li></ul>", ex.ToString());
                    }
                    #endregion
                }

                //Save a cache of the returned RSS feed if no errors occured
                if (writeCache & !errorThrown)
                {
                    //Find the cache's storage directory
                    DirectoryInfo dir = new DirectoryInfo(HttpContext.Current.Server.MapPath(cacheVDir));
                    //Check it exists
                    if (!dir.Exists)
                    {
                        dir.Create();
                        #region Debug output
                        if (debugMode)
                        {
                            output.AppendFormat("<strong>just created the directory:</strong> {0}<br />"HttpContext.Current.Server.MapPath(cacheVDir));
                        }
                        #endregion
                    }
                    //Create the file
                    StreamWriter writer1 = File.CreateText(xmlPath);
                    writer1.Write(output);
                    writer1.Flush();
                    writer1.Close();
                    #region Debug output
                    if (debugMode)
                    {
                        output.Append("<strong>just wrote the new cache file</strong><br />");
                    }
                    #endregion
                }

                return output.ToString();
            }

            /// <summary>
            /// Worker method to identify the MD5 checksum of a string
            /// in this instance used to ensure the RSS file isn't already
            /// cached (based on the URL supplied)
            /// </summary>
            /// <param name="str"></param>
            /// <returns></returns>
            public static string GetMd5Sum(string str)
            {
                Encoder encoder1 = Encoding.Unicode.GetEncoder();
                byte[] buffer1 = new byte[str.Length * 2];
                encoder1.GetBytes(str.ToCharArray(), 0, str.Length, buffer1, 0true);
                byte[] buffer2 = new MD5CryptoServiceProvider().ComputeHash(buffer1);
                StringBuilder builder1 = new StringBuilder();
                for (int minsToCache = 0; minsToCache < buffer2.Length; minsToCache++)
                {
                    builder1.Append(buffer2[minsToCache].ToString("X2"));
                }
                return builder1.ToString();
            }

        }
    }

    XSL that I use for Last.FM

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" /> 
      <xsl:template match="/">
        <h2>Recent Tracks</h2>
        <ul>
        <xsl:for-each select="recenttracks/track">
            <li>
                <a href="{url}">
                    <xsl:value-of select="artist" /> - <em><xsl:value-of disable-output-escaping="yes" select="name" /></em>
                </a>
            </li>
        </xsl:for-each>
        </ul>
        <p><a href="About-Last-FM.aspx" title="last.fm - The Social Music Revolution"><img alt="last.fm - The Social Music Revolution" src="images/lastfm_mini_black.gif" /></a></p>
      </xsl:template>
    </xsl:stylesheet>

    To use it on the blog template

    <% GetRSS("LastFM.xsl", "http://ws.audioscrobbler.com/1.0/user/timgaunt/recenttracks.xml", 25, false)|tsd %>

    This is a pretty crude way of doing it IMHO because the XSL transforms the stream directly, eventually I’ll update the code so it includes a timeout (as John’s did) and having seen the performance implications on my blog, make sure the request is made asynchronously.

    FWIW I have set my cache value to 25minutes, I did have it as 1min for fun but it killed the blog, why have I set it to 25mins? Well, most of my tracks I would think are 2-3minutes long, as I list 10 tracks at a time that’s 20-30minutes listening time so it’ll still keep a fairly accurate overview of my tracks without having massive performance issues on my blog :)

    Incase you don't want to or know how to create this macro as a DLL I have created it for you :)

    dasBlog RSS feed macro" onclick="javascript:urchinTracker('/download/zip/TSDMacros_v1_23-02-07');" href="/tim/files/TSDMacros_v1_23-02-07.zip">Download the complete dasBlog RSS feed macro (4KB - MD5 Hash: e3d7d6320109fd07259e8d246b754f13)

    DasBlog RSS Feed Macro
    Useful Links:  #  digg it!  del.icio.us  Technorati  email it!  Post CommentsComments [2]  Trackback LinkTrackback
    CategoriesTags: ASP.Net | dasBlog | The Site Doctor | Web Development
    # Wednesday, January 03, 2007

    DasBlog Login issues

    Wednesday, January 03, 2007 11:26:03 PM (GMT Standard Time, UTC+00:00)

    While updating the layout of the DasBlog I needed to alter some of the source code to output lists rather than tables etc and after uploading the latest version of the blog the login form suddenly failed to work. When logging into DasBlog it would accept the parameters and either kick me out to the error page or redirect me to the logged in page.

    DasBlog includes some fairly indepth error logging (not using Phil Winstanley’s Error Reporting code mind you), one of the events it logs is logging in. I noticed that I would get one of two errors when logging in, when I was shown the error page it would say: “Password Challenge was null in ViewState! <br>   at newtelligence.DasBlog.Web.LoginBox.doSignIn_Click(Object sender, EventArgs e)” etc and when it took me to the front page it would log: “Audit: SecuritySuccess” and the standard who logged in from which IP etc.

    As I’d edited the DasBlog source my immediate thought was that I’d changed something in the login process so I copied the original DLLs back onto the site and tried again without any success. After a fair amount of searching into the issue on Google and the DasBlog forums I was at a loss as to the issue so I asked on the DasBlog forum (http://www.dasblog.us/viewtopic.php?t=417).

    A couple of people responded including Tom but sadly none of the suggestions applied to my situation. In the end I found that the issue was being caused because I was running the site under ASP.Net 2.0. When running DasBlog under ASP.Net 2.0 you need to make additional changes to the web.config file as outlined in the install documentation. When setting up the staging site I copied the directory from my main blog so I had the same content to test with, although I remembered to alter all the relevant areas in the site.config etc files, I had neglected to alter the cookie path setting in the web.config. So if you’re getting the same issues, check the web.config cookie path values.

    DasBlog Login issues
    Useful Links:  #  digg it!  del.icio.us  Technorati  email it!  Post CommentsComments [6]  Trackback LinkTrackback
    CategoriesTags: ASP.Net | dasBlog