LavaBlast Software Blog

Help your franchise business get to the next level.
AddThis Feed Button

BlogEngine.net Post Security

clock August 13, 2008 15:23 by author EtienneT

BlogEngine.net 1.3/1.4 supports user roles.  But we can't seem to be able to make it mandatory for users to sign in to see blog posts.  That's not something you usually want on a public blog, but for a corporate blog, maybe you want to make sure your news only gets to the people you want.  This seemed like a perfect candidate for a BlogEngine extension.

User Filtering

In our scenario, we don’t want any unregistered users to be able to see blog posts.  This can be easily checked by calling Membership.GetUser() and ensuring the returned value is not null.  We could filter out specific users as well, but we didn’t implement this feature in our extension.

Post Filtering

It could be interesting to restrict who can see the posts in a specific blog category.  For example, a blog category “Top Secret” which can only be read by your company's upper management…  Not very likely in a blog, but you get the point.  Our extension does this filtering by associating a blog Category with a membership Role in the extension’s settings.

image

By associating a membership role with a blog category name, the extension ensures the user has this role before displaying a post associated with this blog category name.  If you add two roles for the same category, posts with this category will only be served if the user has both roles.

Adding a setting with an empty category name will ensure that all posts require a particular role.

Code

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using BlogEngine.Core;
using BlogEngine.Core.Web.Controls;
using System.Collections.Generic;
 
/// <summary>
/// Summary description for PostSecurity
/// </summary>
[Extension("Checks to see if a user can see this blog post.",
            "1.0", "<a href=\"http://www.lavablast.com\">LavaBlast.com</a>")]
public class PostSecurity
{
    static protected ExtensionSettings settings = null;
 
    public PostSecurity()
    {
        Post.Serving += new EventHandler<ServingEventArgs>(Post_Serving);
 
        ExtensionSettings s = new ExtensionSettings("PostSecurity");
 
        s.AddParameter("Role", "Role", 50, true);
        s.AddParameter("Category", "Category", 50);
 
        // describe specific rules for entering parameters
        s.Help = "Checks to see if the user has any of those roles before displaying the post. ";
        s.Help += "You can associate a role with a specific category. ";
        s.Help += "All posts having this category will require that the user have the role. ";
        s.Help += "A parameter with only a role without a category will enable to filter all posts to this role. ";
 
        s.AddValues(new string[] { "Registered", "" });
 
        ExtensionManager.ImportSettings(s);
        settings = ExtensionManager.GetSettings("PostSecurity");
    }
 
    protected void Post_Serving(object sender, ServingEventArgs e)
    {
        Post post = (Post)sender;
        bool continu = false;
 
        MembershipUser user = Membership.GetUser();
 
        continu = user != null;
 
        if (user != null)
        {
            List<string> categories = new List<string>();
            foreach (Category cat in post.Categories)
                categories.Add(cat.Title);
 
            string[] r = Roles.GetRolesForUser();
 
            List<string> roles = new List<string>(r);
 
            DataTable table = settings.GetDataTable();
            foreach (DataRow row in table.Rows)
            {
                if (string.IsNullOrEmpty((string)row["Category"]))
                    continu &= roles.Contains((string)row["Role"]);
                else
                {
                    if (categories.Contains((string)row["Category"]))
                        continu &= roles.Contains((string)row["Role"]);
                }
            }
        }
 
        e.Cancel = !continu;
    }
}

 

Simply saving this code in a .cs and putting it in your App_Code/Extensions for BlogEngine.net shall enable the plugin.

kick it on DotNetKicks.com


jQuery Content Slider Tutorial

clock July 22, 2008 15:22 by author EtienneT

Simple Demo (Firefox, IE7, IE6, Opera, Safari) | Demo with content | Source Code

LavaBlast launched a new front page recently and we have incorporated a new jQuery slider. You can see it in action below (in the Flash animation) or you can go see it directly on our website home page.

Scroll down to find out how this little puppy was implemented!

 

 

HTML & CSS

HTML

This is the basic ASP.NET in the ASPX page:

<div class="FrontMenu">
    <div class="Bar">
        <asp:Repeater ID="Repeater" runat="server" onitemdatabound="Repeater_ItemDataBound">
            <ItemTemplate>
                <span class="item">
                    <a href="#" class="jFlowControl">
                        <asp:Label ID="lbl" runat="server" />
                    </a>
                    <div class="spike" style="z-index:999999">
                    </div>
                    <div class="right">
                    </div>
                </span>
            </ItemTemplate>
        </asp:Repeater>
    </div>
    <div class="Panel">
        <div><cms:Paragraph ID="paragraph1" runat="server" ContentName="frontMenu1" /></div>
        <div><cms:Paragraph ID="paragraph2" runat="server" ContentName="frontMenu2" /></div>
        <div><cms:Paragraph ID="paragraph3" runat="server" ContentName="frontMenu3" /></div>
        <div><cms:Paragraph ID="paragraph4" runat="server" ContentName="frontMenu4" /></div>
        <div><cms:Paragraph ID="paragraph6" runat="server" ContentName="frontMenu5" /></div>
    </div>
</div>

This is pretty simple HTML.  Everything is enclosed in the main div with the css class FrontMenu.  We have the animated bar at the top and the content panel underneath it.  The menu bar is generated by a simple repeater control bound to data in our ASP.NET page. Each menu item is a span containing a link that we’ll use to change the selected menu item.  The Panel div contains multiple dynamic paragraphs from our content management system (SubSonic CMS).  You could easily change this code to bind to data from another source.

Here is the code behind for this control.  We kept it pretty simple:

public partial class FrontPageMenu : UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            List<string> list = new List<string>();
            list.Add("How can we help?");
            list.Add("Our Products");
            list.Add("Hot Features");
            list.Add("Testimonials");
            list.Add("Read");
 
            Repeater.DataSource = list;
            Repeater.DataBind();
        }
    }
 
    protected void Repeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
    {
        if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
        {
            Label lbl = e.Item.FindControl("lbl") as Label;
            lbl.Text = (string)e.Item.DataItem;
        }
    }
}

 

image

The following picture shows how the html menu item elements are rendered.  The CSS section of this article will dive deeper in the inner workings of the menu.

image

JQuery

jFlow

We used jFlow to scroll our panels when we click on a menu item.  The code was fairly straightforward to use.  We included this script in our user control.

$(".FrontMenu").jFlow({
    slides: ".FrontMenu .Panel",
    width: "974px",
    height: "344px",
    duration: 300
});

And then we included a simple reference to our script manager.

<asp:ScriptManagerProxy ID="proxy" runat="server">
    <Scripts>
        <asp:ScriptReference Path="~/js/jquery.flow.1.0.min.js" />
    </Scripts>
</asp:ScriptManagerProxy>

 

We could have used Next and Previous buttons but decided not to.

 

IE6 Problems

This menu did require some basic jQuery.  Except for the scrolling animation, we could have built almost all of this in pure CSS thanks to the hover pseudo-class. However, Internet Explorer 6 came back from the grave to haunt us…  As we still have some visitors who are using IE6, we could not afford to let our home page break down in IE6.  Microsoft gods: please please, hear our prayers and find a way to erase IE6 from the face of earth.  But hey, we've got work to do and can't wait five years for the browser to die like IE 5 did.

We discovered that using a:hover in IE6 to change our background image will make the browser crash.  We used IETester to test the menu in IE6: it made IETester crash.  We then tried Virtual PC running Win98 and IE6: Internet Explorer crashed again when we hovered over one of the links with a:hover CSS styles.

The solution to this problem was simply to apply a style to the hovered link (.hover). Then we could easily style the children of this element to our liking without breaking IE6.

$(".FrontMenu .Bar a").mouseover(function() { $(this).addClass("hover"); });
$(".FrontMenu .Bar a").mouseout(function() { $(this).removeClass("hover"); });

To change the style of the selected item we added the css class .sel to the span.item (the parent of the clicked link).  First of all, when a link is clicked, remove the currently selected item.  Second, set the parent of the link as the current selected item.  It’s important to return false as otherwise the browser will follow the link and scroll to the top of the page.

$("div.FrontMenu div.Bar a").click(function()
{
    $("div.FrontMenu div.Bar").children("span.sel").removeClass("sel");
    $(this).parent().addClass("sel");
    return false;
});

 

CSS

Let’s take a deeper look at the menu’s CSS.  Let’s start with the FirstMenu class which is not too complicated.

.FrontMenu
{
    padding: 5px 5px 0px 5px;
    margin-left: 2px;
    margin-top: -4px;
}
 
.FrontMenu .Bar
{
    background: #F6ECA4 url(images/MainPage/bar.jpg) no-repeat top left;
    width: 980px;
    height: 48px;
    position: relative;
}
 
.FrontMenu .Bar a
{
    color: #FFFFFF;
    font-size: large;
    font-family: Tahoma;
    position: relative;
    top: 7px;
    display: block;
    text-decoration: none;
    padding-right: 6px;
    margin-right: -6px;
    cursor: pointer;
}

Now let’s take a closer look at the Bar menu.  Here are the images we used to style our menu items.

CSS Images    
.FrontMenu .Bar span.sel

selLeft.gif

selLeft

selRight.gif

selRight

spike.gif

spike

.FrontMenu .Bar a.hover span

hoverLeft.gif

hoverLeft

hoverRight.gif

 

 

hoverRight

 

Like you saw in the jQuery part, we change the class in JavaScript to bypass some IE6 issues, so you should not be surprised by the CSS.

The code for the main span for each menu item:

.FrontMenu .Bar span.item
{
    line-height: 30px;
    margin: 0px 0px;
    float: left;
    position: relative;
    display: inline;
    cursor: pointer;
    width: 188px;
    text-align: center;
    margin-left: 6px;
}

Here is the code when you hover over the link inside the menu item:

/* We have to handle hover with jQuery because :hover makes IE6 crash when we change the background image. */
.FrontMenu .Bar a.hover
{
    background: transparent url(images/MainPage/hoverRight.gif) no-repeat top right;
    height: 30px;
}
 
/* We have to handle hover with jQuery because :hover makes IE6 crash when we change the background image. */
.FrontMenu .Bar a.hover span
{
    background: transparent url(images/MainPage/hoverLeft.gif) no-repeat top left;
    height: 30px;
    display: block;
}

 

Like you can see, the link contains the hoverLeft  background image and the span inside the link contains the hoverRight.  This enables the link to have any length and the control will resize easily.  If you ever get a link that is wider than the left image, simply make the image wider...

Then we only needed the CSS to change the menu item to make it look selected.

 

 

 

 

.FrontMenu .Bar span.sel a:hover { background: none; padding-left: 0px; margin-left: 0px; }
.FrontMenu .Bar span.sel a:hover span { background: none; padding-left: 0px; margin-left: 0px; }
 
.FrontMenu .Bar span.sel
{
    background: transparent url(images/MainPage/selLeft.gif) no-repeat top left;
    height: 48px;
}
 
.FrontMenu .Bar span.item .spike, .FrontMenu .Bar span.sel .spike
{
    background: transparent url(images/MainPage/spike.gif) no-repeat top left;
    display:none;
    position: absolute;
    top: 44px;
    left: 50%;
    margin-left: -11px;
    width: 22px;
    height: 17px;
    z-index: 9999;
}
 
.FrontMenu .Bar span.sel .spike
{
    display: block;
}
 
.FrontMenu .Bar span.sel .right
{
    background: transparent url(images/MainPage/selRight.gif) no-repeat top right;
    position: absolute;
    height: 48px;
    width: 4px;
    right: 0px;
    top: 0px;
}
 
.FrontMenu .Bar span.sel a { color: #d43300; }

 

The parent span for the menu item with the .sel class contains the selLeft image and the div.right inside this span contains the selRight image.  We have to make sure too that the hover style does not get applied when the item is selected.

The spike spike is applied in absolute position in the center.  To do that in absolute position, you have to set the following:

left: 50%;
margin-left: -11px;  // <—This is the width/2
width: 22px;

Even with a higher z-index, we were not able to make the spike go on top of the content panel in IE6.  Therefore, we had to put a margin on top of content panel just to make sure the spike was not overlapping the content panel below:

.FrontMenu div.Panel
{
    height: 328px;
    width: 974px;
    margin-top: 15px;
}

 

 

Simple Demo (Firefox, IE7, IE6, Opera, Safari) | Demo with content | Source Code

kick it on DotNetKicks.com



Image Post Processing Caching

clock July 15, 2008 13:41 by author EtienneT

Complete Source Code | Small Demo

This article present a small class library that abstracts opening, modifying (applying effects, resizing, etc.), and caching images in ASP.NET.  Everything needs to be abstracted to ensure the code is easily testable (opening, modifying, and caching of the images).  You may want to resize your images or convert them to black and white and cache the result, and want to test these operations.

You want to be able to read image data from different sources:

  • An image on the local disk on the web server
  • A remote image on the Internet that you want to download and cache
  • An image in your database

You want to be able to apply any number of post processing algorithms to the resulting image:

  • Resize the image (generate thumbnails)
  • Apply an image filter such as convert to black and white
  • Do anything on the image that requires computing and where caching the result proves beneficial from a performance standpoint. 

CacheManager

First of all, let's look at a nice caching class I found on a DotNetKicks kicked story.  Zack Owens gave a nice piece of code in his blog to help you manage your ASP.NET cached objects.  The goal of this class is simply to let you have a strongly typed way to access your cached objects.  Here is the code for the class with some slight modifications:

public class CacheManager<T> where T : class
{
    private Cache cache;
    private static CacheItemRemovedCallback callback;
    private static object _lock = new object();
 
    private TimeSpan cacheTime = new TimeSpan(1, 0, 0, 0); // Default 1 day
 
    public CacheManager(Cache cache)
    {
        this.cache = cache;
        if(callback == null)
            callback = new CacheItemRemovedCallback(RemovedFromCache);
    }
 
    public T Get(string key)
    {
        try
        {
            lock (_lock)
            {
                if (cache[key] == null)
                    return default(T);
 
                T b = CastToT(cache[key]);
 
                return b;
            }
        }
        catch (ArgumentException ex) // The object was disposed by something! return null;
        {
            return null;
        }
    }
 
    public void Add(string key, T obj, TimeSpan cacheTime)
    {
        lock (_lock)
        {
            if (obj != null)
                cache.Add(key, CastFromT(obj), null, DateTime.Now.Add(cacheTime), Cache.NoSlidingExpiration, CacheItemPriority.Default, callback);
        }
    }
 
    protected void RemovedFromCache(string key, object o, CacheItemRemovedReason reason)
    {
        T obj = o as T;
        if (obj != null)
        {
            lock (_lock)
            {
                DisposeObject(obj);
            }
        }
    }
 
    protected virtual void DisposeObject(T obj) { }
 
    protected virtual T CastToT(object obj) { return obj as T; }
 
    protected virtual object CastFromT(T obj) { return obj as T; }
 
    public TimeSpan CacheTime
    {
        get { return cacheTime; }
        set { cacheTime = value; }
    }
}

As you can see, this is a pretty simple class.  We defined some virtual methods to be implemented in our child class, for example DisposeObject if you want to cache disposable objects (continue to read if you want to know why this is a really bad idea).

The constructor requires a Cache object; we can simply pass along the Page's Cache (Page.Cache) to make it happy.  We now want to derive from CacheManager to help us in our main task which is to cache modified images.

ImageCacheManager

To create our image-specific cache manager, we defined a new class called ImageCacheManager which is a subclass of CacheManager  and will cache byte arrays (our images).  We implemented this feature in the past, but did a big mistake that led to a mysterious bug.  We were caching Bitmap objects in the ASP.NET cache but this was a big, big mistake.  Bitmap objects are GDI+ managed objects and they need to get disposed.  Even if we had methods to dispose the Bitmap when they were removed from the cache, some Bitmap objects were disposed while still in the cache (because of a memory leak elsewhere in the application). This caused errors downstream when we tried to use those objects later.  The lesson: we'll only cache only byte[] of the images.

The default image format is PNG in our case, but you can specify your own in the constructor.  In our case we are using PNG because we are in a controlled environment where we know everyone is using IE7, so we can use transparent PNG.  You probably want to use a different format for general public web sites since IE6 doesn't support transparent PNG.

This class will enable to download remote images and cache them locally, as well.  We needed this feature since we have a lot of remote point of sales which synchronize their product list from a central database.  We didn't want to send product images during synchronization because it would have been too much data.  Instead we decided to store our images on a central server and since our stores always have Internet access, they download and cache the images via this image cache manager.  In our product, when a franchisor changes a product image in the main database, the cached version of the picture in the point of sale will expire within the next day and the new picture would be downloaded when used.

ImageCacheManager is an abstract class.  It implements image caching and handles the fact that you want to apply post processing to an image with options (interface IImagePostProcess) and it abstracts the way you load the image (interface IImageReader).  Here is the code:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Caching;
using System.Drawing;
using System.IO;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Web;
using System.Net;
using LavaBlast.ImageCaching.PostProcess;
using LavaBlast.ImageCaching.Reader;
 
namespace LavaBlast.ImageCaching
{
    /// <summary>
    /// This class specialize in caching modified images.  Images are cached as
    /// byte[].  You can apply different modifications in serial to the image
    /// before caching it.
    /// 
    /// Construct a cache key depending on the options of your image post processing.
    /// This enables to cache copies of an image with different post processing applied to it.
    /// 
    /// Control how the image will be read.  On local disk, via Internet etc.
    /// </summary>
    public abstract class ImageCacheManager : CacheManager<byte[]>
    {
        public ImageCacheManager(Cache cache) : this(cache, ImageFormat.Png) { }
 
        protected Dictionary<string, IImagePostProcess> postProcess = new Dictionary<string, IImagePostProcess>();
 
        protected ImageFormat format = ImageFormat.Png; // Default image format PNG
 
        public ImageCacheManager(Cache cache, ImageFormat format) : base(cache)
        {
            this.format = format;
 
            InitImagePostProcess();
        }
 
        /// <summary>
        /// Determine which image reader will be used to read this image.
        /// </summary>
        /// <param name="uriPath"></param>
        /// <returns></returns>
        protected abstract IImageReader GetReader(Uri uriPath);
 
        /// <summary>
        /// Fill the variable postProcess with post processing to apply
        /// to an image each time.
        /// </summary>
        protected abstract void InitImagePostProcess();
 
        /// <summary>
        /// This method shall return a unique key depending on the path of the
        /// image plus the options of it's post processing process.
        /// </summary>
        /// <param name="path"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        protected abstract string ConstructKey(Uri path, Dictionary<string, object> options);
 
        /// <summary>
        /// Get an image from the following path.  Use the provided options to use in post processing.
        /// If refresh is true, don't use the cached version.
        /// </summary>
        /// <param name="uriPath"></param>
        /// <param name="options"></param>
        /// <param name="refresh"></param>
        /// <returns></returns>
        protected byte[] GetImage(Uri uriPath, Dictionary<string, object> options, bool refresh)
        {
            string key = ConstructKey(uriPath, options);
            byte[] cached = Get(key);
 
            if (cached != null && !refresh)
                return cached;
            else
            {
                try
                {
                    byte[] image = ReadBitmap(uriPath); // Get the original data from the image
 
                    byte[] modified = PostProcess(image, options); // Do any post processing on the image (resize it or apply some effects)
 
                    Add(key, modified, CacheTime); // Add this modified version to the cache
 
                    return modified;
                }
                catch
                {
                    return null;
                }
            }
        }
 
        /// <summary>
        /// Run all post processing process on the image and return the resulting image.
        /// </summary>
        /// <param name="input"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        protected byte[] PostProcess(byte[] input, Dictionary<string, object> options)
        {
            byte[] result = input;
 
            foreach (string key in postProcess.Keys)
                result = postProcess[key].Process(result, options[key]);
 
            return result;
        }
 
        /// <summary>
        /// From a path, return a byte[] of the image.
        /// </summary>
        /// <param name="uriPath"></param>
        /// <returns></returns>
        protected byte[] ReadBitmap(Uri uriPath)
        {
            using (Stream stream = GetReader(uriPath).GetData(uriPath))
            {
 
                byte[] data = new byte[0];
 
                Bitmap pict = null;
 
                try
                {
                    pict = new Bitmap(stream);
                    data = ImageHelper.GetBytes(pict, format);
                }
                catch
                {
                    return null;
                }
                finally
                {
                    if (pict != null)
                        pict.Dispose();
                }
 
                stream.Close();
 
                return data;
            }
        }
    }
}

Because this is an abstract base class, we need a concrete implementation of ImageCacheManager.  We created ThumbnailCacheManager.  ThumbnailCacheManager checks the URI if it’s a local or remote file and uses the right image reader.  It has only one post processing task (resizing the image), but it could have more.  It construct the unique key for the cache from the processing task’s options.

Image Resizing

Here is a quick example of a typical image processing task: resizing it.  The class implement the simple method Process(byte[] input, object op) where op is in fact the options of the post processing process.  I could not use generics in my IImagePostProcess interface because of the way I store them later…  Here is a quick code example of how to resize an image.

namespace LavaBlast.ImageCaching.PostProcess
{
    /// <summary>
    /// Post processing that take an image and resize it
    /// </summary>
    public class ImageResizePostProcess : IImagePostProcess
    {
        public byte[] Process(byte[] input, object op)
        {
            byte[] oThumbNail;
 
            ImageResizeOptions options = (ImageResizeOptions)op;
 
            Bitmap pict = null, thumb = null;
 
            try
            {
                using (MemoryStream s = new MemoryStream(input))
                {
                    pict = new Bitmap(s); // Initial picture
 
                    s.Close();
                }
 
                thumb = new Bitmap(options.Size.Width, options.Size.Height); // Future thumb picture
 
                using (Graphics oGraphic = Graphics.FromImage(thumb))
                {
                    oGraphic.CompositingQuality = CompositingQuality.HighQuality;
                    oGraphic.SmoothingMode = SmoothingMode.HighQuality;
                    oGraphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    oGraphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
                    Rectangle oRectangle = new Rectangle(0, 0, options.Size.Width, options.Size.Height);
 
                    oGraphic.DrawImage(pict, oRectangle);
 
                    oThumbNail = ImageHelper.GetBytes(thumb, options.ImageFormat);
 
                    oGraphic.Dispose();
 
                    return oThumbNail;
                }
            }
            catch
            {
                return null;
            }
            finally
            {
                if (thumb != null)
                    thumb.Dispose();
                if (pict != null)
                    pict.Dispose();
            }
        }
    }
 
    public class ImageResizeOptions
    {
        public Size Size = Size.Empty;
        public ImageFormat ImageFormat = ImageFormat.Png;
    }
}

 

Reading the picture

Reading the picture is the easy part and I have included two implementations of IImageReader: one for a local images and one for a remote images.  You could easily implement one which loads images from you database.

LocalImageReader

/// <summary>
    /// An image reader to read images on local disk.
    /// </summary>
    public class LocalImageReader : IImageReader
    {
        public Stream GetData(Uri path)
        {
            FileStream stream = new FileStream(path.LocalPath, FileMode.Open);
 
            return stream;
        }
    }

RemoteImageReader

/// <summary>
    /// Image reader to read remote image on the web.
    /// </summary>
    public class RemoteImageReader : IImageReader
    {
        public Stream GetData(Uri url)
        {
            string path = url.ToString();
            try
            {
                if (path.StartsWith("~/"))
                    path = "file://" + HttpRuntime.AppDomainAppPath + path.Substring(2, path.Length - 2);
 
                WebRequest request = (WebRequest)WebRequest.Create(new Uri(path));
 
                WebResponse response = request.GetResponse() as WebResponse;
 
                return response.GetResponseStream();
            }
            catch { return new MemoryStream(); } // Don't make the program crash just because we have a picture which failed downloading
        }
    }

HttpHandler

Finally, you want to serve those images with an HttpHandler. The code for the HttpHandler is pretty simple as you only need to parse the parameters from the QueryString and pass them to the ThumbnailCacheManager presented above.  The handler receives a parameter “p” for the path of the image (local or remote) and a parameter “refresh” which can be used to ignore the cached version of the image.  Additionally, we can pass parameters such as “width” and “height” for our image resizing.  Warning: you must adapt this code to your environment otherwise you are exposing a security hole because of the path parameter.

When debugging your image caching HttpHandler, don't forget to clear your temporary Internet files from IE or FireFox because your images will also be cached in your web browser otherwise your code will not be executed!

using System;
using System.Collections;
using System.Data;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using LavaBlast.ImageCaching;
 
namespace WebApplication
{
    /// <summary>
    /// Really simple HttpHandler to output an image.
    /// </summary>
    public class ImageCaching : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            string path = context.Request.Params["p"] ?? "";
            bool refresh = context.Request.Params["refresh"] == "true";
 
            int width, height;
            if (!int.TryParse(context.Request.QueryString["width"], out width))
                width = 300;
            if (!int.TryParse(context.Request.QueryString["height"], out height))
                height = 60;
 
            byte[] image = null;
            ThumbnailCacheManager manager = new ThumbnailCacheManager(context.Cache);
            image = manager.GetThumbnail(path, width, height);
 
            if (image != null)
            {
                context.Response.ContentType = "image/png";
                try
                {
                    context.Response.OutputStream.Write(image, 0, image.Length);
                }
                catch { }
 
                context.Response.End();
            }
        }
 
        public bool IsReusable
        {
            get
            {
                return true;
            }
        }
    }
}

 

 

Source Code | Small Demo

kick it on DotNetKicks.com



Skinned Login Control

clock April 14, 2008 14:16 by author EtienneT
Here is our login form in FranchiseBlast.  We think it's a pretty cool login form and it was not that hard to do.  It only requires basic CSS and some jQuery.
 
 

How we did it

The only things you need is an image like this one here:

inputLogin

Then we used the following CSS to define our text boxes style.  The "Login" css class is applied to the ASP.NET Login control and the class "TextBox" are applied to both textboxes in the login control.

.Login .Textbox, .Login .Hover
{
    width: 337px;
    height: 17px;
    background:transparent url(images/inputlogin.png) no-repeat top left;
    color: Black;
    border: none;
    padding: 5px;
    font-weight: bold;
}
  
.Login .Hover
{
    background:transparent url(images/inputlogin.png) no-repeat bottom left;
}

 

Has you can see, the only difference for .Hover class is that we tell the background to show the bottom of the picture (the orange part) instead of the top of the image.  If Internet Explorer supported the "focus" CSS pseudo class then it would be much simpler, but IE doesn't support it, so we have to use jQuery to achieve the effect.

Don't forget to add jQuery.js somewhere in the page and then you can add the following script to your page:

$('.Login .Textbox').focus(function(){
  $(this).attr('class', 'Hover');
});
  
$('.Login .Textbox').blur(function(){
  $(this).attr('class', 'Textbox');
});

 

Basically the code above registers an event to all DOM elements which have the "Textbox" CSS class and are children of a control of the "Login" CSS class. The first call registers an event handler on the focus event of the text box which changes the class to Hover.  We do the exact opposite for the blur event when we the text box loses it's focus.   There may be a better way to do this why jQuery; if you know how, let us know.

Finally, as a special added touch, we use an AnimationExtender after a successful login:

<ajax:AnimationExtender ID="animLogin" runat="server" TargetControlID="LoginButton">
<Animations>
    <OnClick>
        <Sequence>
            <FadeOut Duration=".5" Fps="20" AnimationTarget="pnlLogin" />
        </Sequence>
    </OnClick>
</Animations>
</ajax:AnimationExtender>

 

One last thing, if you use this AnimationExtender, you have to make sure your validators don't run on the client side. Validation must occur on the server otherwise the fade out animation will still occur and the login control will disappear. For example, we used a RequiredFieldValidator for both the username and password text boxes and we had to set the EnableClientScript property to false on both these validators.

This concludes how to do a skinned Login control à la LavaBlast.

kick it on DotNetKicks.com


10th Annual Qu&eacute;bec Entrepreneurship Contest

clock April 10, 2008 14:05 by author EtienneT

contest As stated in one of our recent software start-up posts, we submitted our full-length business plan in this contest : 10th annual Québec Entrepreneurship Contest.  In the east part of Montreal alone, over eighty business plans were submitted.  Two weeks ago, we learned we were amongst the twenty-some finalists for the first round of the contest (there are three rounds: local, regional, national). On Monday, we had a short interview with the jury who decided which of the finalists would win in each category. It was a good way to practice our pitch, not only with the jury but also the other entrepreneurs that were being interviewed. Yesterday, at the awards ceremony for Montreal-East, we won the first prize in the Technological & Technical Innovation category.  Woohoo :). We won a cash prize and a one year membership to the chamber of commerce. 

We wanted to congratulate our good friends at Web Estate Management who also won a prize in the Services category! We had a long chat with the winners of the transformation / manufacturing category, Brik-a-Blok Inc.

Writing a business plan is a complex exercise if you want to do it properly. However, participating in a business plan contest can give you the small push you need to actually write it down and define your vision and your short/long term plan. If you aim to be a well-rounded software engineer doing more than coding, writing your plan brings a bit of variety to your day and allows you to distance yourself from the code for a few hours. We tackle business issues as if they were engineering problems and learned a lot during the whole process. (It is a known fact that it is the exercise of writing the plan that is valuable, not the document itself, which will evolve with the company.)

Even at this stage of the contest, we've already met some really interesting people that showed interest in our solution and had a few business propositions. The visibility that such a contest can offer in the media cannot be underestimated; some newspapers will most probably mention our company along with our sexy faces.

Yesterday was the ceremony for Montreal-East; we are now participating in the regional contest.  We'll know at the end of the month if we are in the finalists!  Wish us luck!  Regardless of the outcome, this was a very gratifying experience.



Software Startup Lessons (Part 2): Communication and Collaboration

clock March 17, 2008 09:39 by author EtienneT

Welcome to part two of our three part series describing the various lessons we learned during our first year of a software startup.  You are encouraged to read the first part if you haven't already!

Problem Statement

In Part 1, we described our business' context and how we eat our own dog food. Indeed, we use the software we produce on a daily basis, helping us create quality products. A parallel can be made with our distributed team's communication patterns. Simply put, we're not all at the same central office, talking to each other all day. Our structure resembles that of a franchisor with franchisees distributed a several physical locations. The main reason we are distributed is to keep costs down while we develop our core infrastructure, but it does have the interest side-effect of forcing us to use the same communication tools a franchisor would.

Not only do we not work in the same building, we're not even in the same city! Because we work in independent offices, we have a greater control over our environment, which increases our productivity. Spolsky often talks about how developers should have private offices because outside disturbances kick us out of our zone. You can respond to your email every 15 minutes, but you can't make someone wait at your door for 15 minutes while you polish off a complicated algorithm.

In any case, given the fact that most geeks are not social creatures, you probably think that doing the majority of your interactions over the Internet is great, right? In reality, it's not always convenient  but we've started to become really creative (and efficient) with the tools we found to maximize our interactions.

We spend most our time chatting coordinating on Google Talk and move to Skype when voice conversations are required. Over time, we discovered some pretty nice software to communicate and we want to share these tools with you in this article.

Problems:

  • How do we talk to each other and with people outside our company?
  • How do we exchange files?
  • How do we manage our requirements?
  • How do we demo our software, how do we program in pairs?

Lesson 4) Talking to People

Carrier pigeons might have played a vital part in WWII communications, but we're using more modern techniques.

Skype

imageSkype is the obvious choice for voice communication over Internet.  Don't tell us that you haven't heard about this tool before!  Because we are four partners in LavaBlast, we often need to have conference calls. All our meetings are done online via Skype conference calls, at no cost to us. Furthermore, we can call up a client on their regular phone using SkypeOut.  SkypeOut will call the client's real phone and will connect them to our conversation, at very affordable rates. Indeed, for $3 a month, we obtained unlimited outgoing calls to Canada and the US. Setting up a conference call is really easy and the quality is good (most of the time).  You simply need a good microphones and you're good to go.  In addition, if you have a webcam and a good Internet connection, the video quality of Skype is just amazing especially compared to MSN.  Talking to someone who also has a good webcam and a good Internet connection is almost lag free, in my experience.

Having a central phone number for a distributed office

Since we have a distributed office, we need to have a central phone number.  The most affordable solution that we found was Skype for business.  Basically you simply need to register a SkypeIn number anywhere in the US (not available in Canada yet) and when people call this number, it calls you on Skype.   The cheapest way to get your incoming phone number is to purchase Skype Pro (at $3/month) and purchase the number for $38 a year (and it was operational within minutes of purchase!). You can forward calls to another user or normal phone and even get voicemail with Skype Pro. Optionally, you may install the business version of the Skype program for additional functionality. Therefore, all the incoming calls go to our main salesperson and, if more technical details are required, the call can be forwarded to us.  Skype for business has a nice business control panel to control Skype credit purchases, phone number assignments, and much more. Additionally, because the phone number is associated to the company, we can redirect it to a new person if an employee is replaced or if someone is on vacation. We could have a 1-800 number, but I don't think we have the kind of call volume that would justify having one just yet.

We found the perfect phone number for LavaBlast: 1-201-467-LAVA (5282).  SkypeIn personal has a nice interface to check if a number is available, but the business panel interface doesn't.  If you want to purchase a specific number, I wish you good luck because you have to generate the numbers one by one!  We wanted a number that ended either with BLAST or LAVA;  we found the number with the SkypeIn personal interface and then tried to regenerate it, one number at a time, in the business panel.  We finally found one that ended with LAVA but couldn't find one that ended with BLAST.  The best way we found to generate multiple numbers was to press Ctrl and click like crazy on the generate button to open new pages in new tabs.  We eventually found the number we wanted!

Lesson 5) Exchanging Files

There are lots of cool startups focusing on better ways to exchange files and we're keeping an eye on them. For now, here's what we're using.

Subversion

As software developers, we obviously use source control to manage our code. No surprise here. We even deploy our web applications using Subversion.

FranchiseBlast

image We've developed a document repository in our franchisee/franchisor collaboration solution. We post our user manuals and a few other documents on there; it is our distribution center for completed documents.

Microsoft Groove

We use Microsoft Groove which is a party of Office 2007, as our main internal document management tool. Groove is much more than a document repository, but we only use this feature.  Groove is simple to use and install because it distributes the files on all the peers instead of having to setup a complex server. Furthermore, it is better than a shared network drive because it has change notifications plus it can be accessed even when you are offline. There are a few drawbacks, but in general, it's a good simple solution for the low volume of Office documents we work on. 

Lesson 6) Requirements Engineering

The first post on the LavaBlast blog was related to requirements engineering and how we managed our pile of requirements written on the back of (sometimes used) paper napkins. More seriously, we've found it very beneficial to collaboratively define our software requirements with our clients. Constant feedback is the key to writing good requirements, and using collaboration tools is a must.

Wikis

We've been playing with ScrewTurn wiki as our main wiki over the last year, for both for our internal use and to interact with our clients during the requirements engineering process. It is very easy to install (trivial) and it is very very fast. Editing a page is very pleasant because it is nearly instantaneous when you save a page... it just feels blazingly fast. Furthermore, because it is a free and open source project, you get to spend money on Super Mario Galaxy instead.

During our software engineering capstone project, we used TWiki as our primary requirements engineering tool, a couple years before the strategy was discussed in IEEE Software. Although we've enjoyed ScrewTurn's simplicity during the last year, I think we're ready to revert back to the more feature-rich TWiki. Installation is a pain (less now than before), but the syntax much more natural. Furthermore, we already have the infrastructure in place (using page meta-data) to let us manage our requirements.

Lesson 7) Desktop Sharing

Today, sharing your desktop is an essential operation for any software startup. You can easily demonstrate applications to potential clients or perform pair programming, without having to be at the same physical location.

Lotus Sametime Unyte

Trying to explain something to someone over Skype is not always fun, especially when the client is not a technical person. Because a picture is worth a thousand words, showing software is much quicker!  During most of our first year, we used Unyte to do just that.  With Unyte, you simply have to start the application, decide which window to share (or share the entire desktop) and then send a link to the person (or group of people) that need to see it.  Recipients click on a hyperlink and load up a browser-based Java applet to see the shared windows. It's as simple as that.  The client doesn't have to install anything and it's fast! The host will receive an alert when the viewers can see his screen.  Unyte, used in conjunction with Skype, is really a great mix for meetings and sales demonstrations. We used the version for five people and it worked well, until we found Microsoft SharedView.

Microsoft SharedView

A few weeks back, we discovered Microsoft SharedView while it was still in beta. SharedView is similar to Unyte, but 15 people can participate in a shared session.  The only big drawback is that everyone has to download the software.  So if you want to quickly present something to a client, it's a little bit more complicated if they have to install it.  However, SharedView does allow any participant to share their desktop for the others to see (only one person can share at any given time). During a software demo, we used this capability to have the client show us their current point of sale! Additionally, the software gives other people the possibility to take control of the session and perform operations on the computer that is sharing an application. You can also share documents pretty easily with everyone and send chat messages, but we still use Skype for voice communication. We now prefer SharedView to Unyte when it is a possibility to install software on every participant's computer.

Lesson 8) Blogging!

We've been able to connect with tons of great people thanks to our blog, and have lots of fun perfecting our writing skills. We find that figuring out what to say (and what your audience is interested in hearing) is the hardest task when you start a blog. We still don't know if we should focus on giving away code or writing more experiences pieces like this one. Let us know!

BlogEngine.Net

Our blogging software, BlogEngine.Net, is simply terrific.  Free, fast, really easy to theme, has some good extensions and integrates really well with Windows Live Writer.  One of the main advantages for us is that it is open source and it is written in .NET, allowing us to perform minor behavior changes as needed.   It still has a few bugs that can be annoying, but from what we have seen so far, it is performing really well.  It supports multiple authors, an important feature for us because we have two people posting on the blog.

Lesson 9) When not to use communication tools

The most important lesson we learned during our first year of operations as a distributed development team wasn't a revelation but rather a confirmation of our expectations. Communication and collaboration tools are great, but technology doesn't solve conflicts. We're a closely knit team and have worked together for a number of years, greatly reducing the number of conflicts we may have. The conflicts we have are generally superficial, but as an example, let's consider an architectural decision where the two decision makers have divergent opinions on the best solution. Although instant messaging is great for efficiency reasons, it is not the best medium to argument on the merits of your solution. Indeed, we've all seen the politically incorrect "Arguing on the Internet is like running in the Special Olympics; even if you win, you're still retarded". The main problem is related to the lack of emotional expressiveness of concise instant messages or emails and since it is discussed in great lengths by very good authors, we'll leave a detailed analysis of the situation to them! As a side note, firing people via email might have seemed like a good idea for RadioShack, but we at LavaBlast shall stay away from such brilliant strategies!

Conclusion

Communication is the most important part of starting a business. Even if you're the only person in your company, you still have to communicate with your customers! We hope that you'll discover at least one cool collaboration tool today! Remember to come back next week for Part 3 of our series.

kick it on DotNetKicks.com  



RESX file Web Editor

clock February 7, 2008 08:35 by author EtienneT

Source CodeDEMO

If you have a multi-language site, you have probably already worked with .resx files.  Resx files are resource files that can contain strings, images, sounds... pretty much anything.  However, in ASP.NET (at least in our typical scenarios), resource files mainly contain strings to be translated in multiple languages.  Those needing a refresher course on ASP.NET localization should take a peek at this article: ASP.NET Localization. Let us take a typical ASP.NET application as an example.  When you generate a resource file for a file named Default.aspx, VS.NET generates a new folder named App_LocalResources (if it doesn't exist) and it creates a new file named Default.aspx.resx in this folder.

image

This option will only be visible in the design view of an aspx or ascx file.  

Default.aspx.resx will contain all strings that can be localized from Default.aspx.  Default.aspx.resx is the default resource file for Default.aspx.  It will contain the default language strings, in our case English.  Should you need to offer the same application in more than one language, you will need to create locale-specific resource files with a similar filename. For example, Default.aspx.fr-CA.resx would be a resource file for Canadian French. The logic to retrieve a string from the appropriate resource file is built into the .NET framework (it depends on the current thread's culture).

Those who have built a dynamic web site supporting multiple languages know that managing resx files is a burden, especially when the application changes.  TeddyMountain.com is a web site we recently created which provides English, French, and Danish versions.  We use a CMS for most of the text, but some dynamic pages like the store locator contains text which needs to be in resources files.  We collaborated with a resident of Denmark to translate the website; the translator is not a programmer and could not be trusted with the XML-based resx files. Furthermore, as the site is in constant evolution, we wanted a dynamic solution to avoid losing time exchanging Excel files.

Although there are some commercial applications out there, we decided to make a a simple tool to unify a set of resource files.  We wanted to take advantage of the fact that the we were building a website and we could have the translator use a web-based tool to translate the website. This has the advantage of being able to see the changes immediately in-context, instead of simply translating text locally. We made a class that would merge our Default.aspx.resx, Default.aspx.fr-CA.resx, and Default.aspx.da.resx files into a single C# object that is easy to use.  Once the data is in the C# object, the data can be modified and saved back to the disk later on.

We called this C# object ResXUnified.  The only constructor to ResXUnified needs a path to a resx file.  Then it will find all resx files related to it from different languages.  Once you have constructed the object, you can access the data simply by using an indexor:

ResXUnified res = new ResXUnified("Default.aspx.resx");
string val = res["da"]["textbox1.Text"];

In the above code, we access the Danish language file and query the value of the "textbox1.Text" key.  This information can be changed and saved back to the disk:

res[""]["textbox1.Text"] = "Home"; // Default language we can pass an empty string
res["da"]["textbox1.Text"] = "Hjem"; // Danish
res["fr-CA"]["textbox1.Text"] = "Acceuil"; // French
res.Save();

When we call Save(), only the files that were changed will be written to the disk.  ResXUnified simply uses a Dictionary and a List to manage the keys and the languages.  To save, it uses the ResXResourceWriter class provided by the framework, which makes it easy to manipulate resx files. Similarly, we read resx files using the ResXResourceReader class.  Without these two classes, manipulating resx files would be much more complicated.

I won't include more code here since this is a pretty straightforward collection class.

Later, for the TeddyMountain.com website, we made a quick interface in ASP.NET (see the demo here) to display all the resx files in a project. We enable the user to add a language file and translate all the fields from the default language.  Here is an example:

image 

When a string needs to be translated, the textbox background color is not the same, this way it's easy for the translator to see what needs to be translated.

The generate local resource button in Visual Studio generates a lot of "useless" strings in resx files; we don't necessarily want to enter tooltips on every Label in our application. To make it easier to read, we designed an option to hide or show these empty strings.

This tool is pretty basic right now and there are more options that could be easily added.  For example, we could add a list of files that remain to be translated or allow for multiline strings (notice we don't support line-breaks in our strings). We encourage you to modify the code and show off your enhancements!

Final notes: If you try the code out, please remember to give write access to the ASP.NET worker process in all the App_LocalResources folders (and the App_GlobalResources folder if you use it). Also, since changing resx files for the default language restarts the web application, it is recommended you use the tool on a development copy of your website.  

Source CodeDEMO

kick it on DotNetKicks.com



ViewState property code snippet

clock January 23, 2008 12:47 by author EtienneT

One of the things I do frequently is making a new viewstate property to use in a UserControl in ASP.NET.  (Yes, we know, ViewState is evil. We store it in the session and use it in low-volume sites.) Just download this code snippet, open Visual Studio, open Tools->Code Snippets Manager, and finally Import.

ViewState Property.snippet (1.28 kb)

You can then use it just by writing the shortcut in the code editor and pressing TAB.

image

Here is the result:

image

Using the ?? operator is nice since we don't have to do ugly ifs to return a default value and we don't have to assign something in the ViewState either.  We just fill the ViewState when we need it. This avoids bloating up the ViewState with default values.

ViewState Property.snippet (1.28 kb)

kick it on DotNetKicks.com


Using { and } in string.Format

clock January 16, 2008 14:02 by author EtienneT

Have you ever tried to use the { and } characters in the c# string.Format() method?  If you have, then you probably ran into a problem.  I'm posting this because this little Gotcha might not turn up until much later if your string formats are dynamically loaded from some external source, such as RESX files.

In any case, I sometimes find it useful to write some JavaScript code directly in C# and then dynamically insert it into the page with the ScriptManager.  Here is a quick example:



Basically, I write my JavaScript code on multiple lines with the @"" notation.  Then, I have to escape all { and } characters using {{ or }} otherwise string.Format will run into a System.FormatException: Input string was not in a correct format exception.  I can customize my JavaScript (or insert control.ClientID parameters) this way while retaining a readable format in the C# code.



Attach to Process with one shortcut

clock January 11, 2008 11:40 by author EtienneT

In case you didn't know, there is a much faster way to debug an ASP.NET application when running IIS than hitting F5. You can directly attach the debugger to the existing w3wp.exe to quickly start your debug session by doing Debug -> Attach to Process -> w3wp.exe. After a while, you start using the "Attach to Process" command very often in a usual day.  I created a simple visual studio macro to automatically attach to the ASP.NET process when you need to debug a site.  Then you can call the macro with a simple keyboard shortcut.  Here is the code:

Option Strict Off
Option Explicit Off
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics
Imports System.Security.Principal

Public Module RecordingModule
    Sub AttachToAspNET()
        Try
            Dim dbg2 As EnvDTE80.Debugger2 = DTE.Debugger
            Dim trans As EnvDTE80.Transport = dbg2.Transports.Item("Default")
            Dim compName As String = WindowsIdentity.GetCurrent().Name
            compName = compName.Substring(0, compName.IndexOf("\"))
            Dim proc2 As EnvDTE80.Process2 = dbg2.GetProcesses(trans, compName).Item("w3wp.exe")
            proc2.Attach2(dbgeng)
        Catch ex As System.Exception
            MsgBox(ex.Message)
        End Try
    End Sub
End Module


To create the macro in your Visual Studio, just open the Macro Explorer in Tools->Macro->Macro Explorer, make a new macro and copy paste the code. The only thing you could need to change is "w3wp.exe" or "aspnet_wp.exe" depending on your version. Note that I'm assuming you're working with only one w3wp active at a certain point in time. Those of you who have multiple worker processes running simultaneously should feel free to augment the code for your purposes. (Take a look at this to figure out which w3wp to attach to.)

Creating a keyboard shortcut afterwards is pretty easy:



Go to Tools->Options in Visual Studio.  Then just follow those easy steps:
  1. Click on Environment->Keyboard
  2. Type AttachToAspNET in the "Show commands containing" textbox.
  3. Press a keyboard shortcut you want to use.  I used Ctrl-Alt-K and it is working fine.
  4. Click on Assign


Happy coding in 2008!

kick it on DotNetKicks.com

View this post in Russian :)



Month List

Disclaimer

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

© Copyright 2017

Sign in