About the author
Boaz I'm a programmer, working at a small software company in the Netherlands. Currently I'm mostly using techniques like .NET, C#, SQL and jQuery, but I have experience with JAVA and PHP as well.

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

Categories
Almost a year ago Scott Hanselman blogged about a NuGet package called AspNetSprites that automatically generates sprite images for you. When using a lot of small icon images, sprites can speedup your website quite a lot. This is due to minimizing the number of HTTP requests.

For AspNetSprites there are helper packages for both WebForms and ASP.NET MVC, but wouldn't it be nice to use this with MonoRail too? This is not to difficult to accomplish because there is a AspNetSprites-Core package containing all the logic so all we need to do is write a MonoRail View Helper class.

I downloaded the source from the ASP.NET codeplex page and modified the Razor view helper so it can be used with MonoRail. Here the result:

using System;
using System.Linq;
using System.Text;
using Microsoft.Web.Samples;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Collections;

namespace MonoRail.Samples.SpriteHelper
{
    public class Sprite
    {
        private static Control helperControl = CreateHelperControl();

        public static string ImportStylesheet(string virtualPath)
        {
            ImageOptimizations.EnsureInitialized();

            if (Path.HasExtension(virtualPath))
            {
                virtualPath = Path.GetDirectoryName(virtualPath);
            }

            HttpContextBase httpContext = new HttpContextWrapper(HttpContext.Current);

            string cssFileName = ImageOptimizations.LinkCompatibleCssFile(httpContext.Request.Browser) ?? ImageOptimizations.LowCompatibilityCssFileName;

            virtualPath = Path.Combine(virtualPath, cssFileName);
            string physicalPath = HttpContext.Current.Server.MapPath(virtualPath);

            if (File.Exists(physicalPath))
            {
                StringWriter sw = new StringWriter();

                using (HtmlTextWriter html = new HtmlTextWriter(sw))
                {
                    html.AddAttribute(HtmlTextWriterAttribute.Href, ResolveUrl(virtualPath));
                    html.AddAttribute(HtmlTextWriterAttribute.Rel, "stylesheet");
                    html.AddAttribute(HtmlTextWriterAttribute.Type, "text/css");
                    html.AddAttribute("media", "all");
                    html.RenderBeginTag(HtmlTextWriterTag.Link);
                }

                return sw.ToString();
            }

            return String.Empty;
        }


        public static string MakeCssClassName(string pathToImage)
        {
            return ImageOptimizations.MakeCssClassName(pathToImage);
        }

        public static string Image(string virtualPath)
        {
            return Image(virtualPath, null);
        }

        public static string Image(string virtualPath, IDictionary htmlAttributes)
        {
            ImageOptimizations.EnsureInitialized();

            HttpContextBase httpContext = new HttpContextWrapper(HttpContext.Current);

            StringWriter sw = new StringWriter();

            using (HtmlTextWriter html = new HtmlTextWriter(sw))
            {
                if (htmlAttributes != null)
                {
                    foreach (DictionaryEntry entry in htmlAttributes)
                    {
                        html.AddAttribute((entry.Key ?? String.Empty).ToString(), (entry.Value ?? String.Empty).ToString());
                    }
                }

                if (ImageOptimizations.LinkCompatibleCssFile(httpContext.Request.Browser) == null)
                {
                    html.AddAttribute(HtmlTextWriterAttribute.Src, ResolveUrl(virtualPath));
                }
                else
                {
                    html.AddAttribute(HtmlTextWriterAttribute.Class, ImageOptimizations.MakeCssClassName(virtualPath));
                    html.AddAttribute(HtmlTextWriterAttribute.Src, ResolveUrl(ImageOptimizations.GetBlankImageSource(httpContext.Request.Browser)));
                }

                html.RenderBeginTag(HtmlTextWriterTag.Img);
            }

            return sw.ToString();
        }

        private static Control CreateHelperControl()
        {
            var control = new Control();
            control.AppRelativeTemplateSourceDirectory = "~/";
            return control;
        }

        private static string ResolveUrl(string path)
        {
            return helperControl.ResolveClientUrl(path);
        }

    }
}
This helper exposes three methods (and one overload) here are some usage examples:
$Sprite.ImportStylesheet("~/App_Sprites/categories/")

<li class="$Sprite.MakeCssClassName("~/App_Sprites/categories/dotNet.png")"><a href="#" class="categories">Programming</a></li>

<a href="#">$Sprite.Image("~/App_Sprites/popular/visualStudio.png", "%{alt='visualStudio'}")
</a>
Besides this code file a small change to the web.config file is required and you should off course add the [Helper(typeof(Sprite))] attribute to your controller.

To get you started here is the NuGet packages I created for personal usage:
AspNetSprites-MonoRailHelper.0.1.nupkg


January 1, 2012 - Comments [0] - Posted in ASP.NET | MonoRail
FireDotNet is now available as an official NuGet package on nuget.org:



FireDotNet is a library I created to show ASP.NET server-side log messages in the Firebug console on the client-side. A few months ago I updated the library to a NLog target so all NLog messages are send to Firebug.



I used this opportunity to make some changes too. First of all I automated the builds using TeamCity. I installed a NuGet plugin for TeamCity so dependencies are automatically resolved. This means that I no longer need to have the NLog.dll files under version control. Besides managing dependencies it can also automatically make a NuGet package and publish it to both my private NuGet server and the official NuGet server.

Another nice feature of TeamCity is that it can manage version numbers for you. There is a build feature named AssemblyInfo patcher and - as the name already implies - it modifies the version number of your AssemblyInfo.cs files. So from now on all FireDotNet builds will have a version number that consists of:
MajorVersion.MinorVersion.BuildNumber.SvnRevisionNumber eg. 0.2.30.34
Where I control the first two numbers and the last two are automatically set by TeamCity.

Last but not least I managed to add a new feature too! From now on FireDotNot by default only outputs to requests form localhost. This means that you, when deploying your project, no longer have to think of removing FireDotNet from it. Except of course if you changed the allowRemote property.

December 11, 2011 - Comments [0] - Posted in ASP.NET | FireDotNet | NLog | NuGet
I read somewhere that it's very easy to backup a site using wget on linux. That made me realize that, at the moment, I do not have any backups of my sites. The only backups that (hopefully) exist are the ones my shared host creates. So I figured it would be safe to have my own backups. Since I run my own Ubuntu home-server, already for a few years now, I have a backup target too.

The only problem left is: the databases. Luckily dasBlog does not use a database to store it's posts, but a XML datastore. This makes it less painful to make a backup, especially on shared hosting. My MySQL databases can be accessed externally, so they can be backuped using a (separate) script too. Currently I already do this for my e-mail database.

So, I created the following script:
#!/bin/bash

cd "$(dirname "$0")"

backupfile=$(date +%Y%m%d)
savetodir=./backup-$(date +%Y)/
host=ftp.example.com
user=backup
pass=

if [ -d $host ]; then
    rm -r $host
fi

wget --recursive --level=inf --quiet --user=$user --password=$pass ftp://$host/

if [ $? -ne 0 ]; then
    echo "wget faild during the backup of $host" >&2
    exit 1
fi

tar -cf $backupfile.tar $host && gzip -c $backupfile.tar > $backupfile.tar.gz && rm $backupfile.tar

if [ $? -ne 0 ]; then
    echo "faild to create a tar.gz during the backup of $host" >&2
    exit 1
fi

if [ ! -d $savetodir ]; then
    mkdir $savetodir
fi

mv $backupfile.tar.gz $savetodir

exit 0
Fist a few variables are set, like the ftp site, username and password. I created a special backup user that only has read privileges, since no writing is required when making backups.

By default wget creates a directory named after the host, so I start with removing that directory and it's contents but only if it exists (from a previous backup).

This is follwed by the wget command that does a fully-recursive download (note the: --level=inf) after finishing the exit code of wget is checked for errors. The backup is then compressed and stored in a directory per-year.

Finally I scheduled a cronjob, to automatically create a backup once a week:
0 3 * * 3 boaz sh /mnt/array1/backup/n3rd.nl-sohosted/backup.sh

November 21, 2011 - Comments [0] - Posted in Backup | Ubuntu

The first question that comes to mind when creating a blog is off course why? But that is not the question I'm gong to answer, not now any way, maybe I'll get back to that later. This blog should be mostly technical and that is where this series is about.

First of all, to start blogging I needed to choose a blogging application. At first I started with Blogger but soon I realized, I am a technician, I have my own domain and I'm not even hosting my own blog?

So the next step was to search for the proper application, should it be WordPress like any other blog uses? No, I prefer an ASP.NET solution. And then I stumbled upon DasBlog. DasBlog seemed good to me, and hey Scott Hanselman is using it too!

I decided to install DasBlog and see for my self if it will work. And guess what? It does! But not without some trouble at first.

I uploaded the files, made the required security changes and visited the url. But it didn't work and gave me a very helpful *cough* "System.Security.SecurityException". I'm hosting my site at SoHosted for years now and I'm a very satisfied customer. I knew SoHosted only allows applications running in medium-trust, and I read before I started that DasBlog does run under medium-trust, so what is the problem? After searching for a while and almost giving up  I finally found the solution in this post. I commented out the system.diagnostics  section in the web.config and ta-da it worked!

Probably coming up in Part 2: The design. Because at the time I write this post the blog looks a bit to general. But I warn you on beforehand, I'm a technician and not (and I repeat) NOT a designer!

November 8, 2011 - Comments [0] - Posted in Blogging
Logging in on a site using apache can be done in a lot of different ways e.g. .htpasswd or mysql. All of them have one thing in common: you get yet another password. What I wanted to accomplish is to be able to login using my normal Ubuntu username/password. This makes it a lot easier to change my password once in a while.

The first step is to install the required applications:
sudo apt-get install libapache2-mod-authz-unixgroup pwauth
Enable the apache module:
sudo a2enmod authnz_external
Edit the appropriate apache site in /etc/apache2/sites-available, make sure the site is only available over SSL otherwise you password will travel over the Internet unencrypted!
AddExternalAuth pwauth /usr/sbin/pwauth
SetExternalAuthMethod pwauth pipe

<location /sickbeard/>
    order deny,allow
    deny from all
    allow from all

    ProxyPass http://localhost:8081/sickbeard/
    ProxyPassReverse http://localhost:8081/sickbeard/

    AuthType Basic
    AuthName "Boaz' Sick Beard"
    AuthBasicProvider external
    AuthExternal pwauth
    Require valid-user
</location>
Finally restart apache and you're ready to go!
sudo /etc/init.d/apache2 restart
October 23, 2011 - Comments [0] - Posted in Ubuntu