CSS: Always Show the Vertical Scrollbar

08.05.2011 08:09 by kbeckman | Comments

A quick post to show how to solve an issue I’ve come across more than once… Depending on the layout of your site’s web pages, some pages might not be long enough to invoke the browser’s vertical scrollbar. This isn’t a big deal until you navigate to a page that requires the scrollbar to show its full length. When this happens (and if your content is centered in the page), your page will appear to jump a few pixels to the left to adjust for the invocation of the scrollbar. It’s annoying to QA testers and users, but it’s easy to fix… Here’s a quick CSS snippet to always show the scrollbar on your site whether a page requires it or not.

 

html {
    overflow: -moz-scrollbars-vertical;
    overflow-y: scroll;
}

A Good Burn…

08.05.2011 08:04 by kbeckman | Comments

Nothing technical here… I just wanted to call mention to an email that I received from arguably my most famous subscriber – the 16-time World Champion Superstar professional wrestler, Ric “The Nature Boy” Flair. Ah Ric, this takes me back about 11 years when I first discovered how to send SMTP emails without including my actual email address. Spoofing email addresses is simply GOOD CLEAN FUN. The short email I received sent me down Memory Lane and jogged my memory to some great office burns and practical jokes I’ve seen and been on the receiving end of over the years.

 

Subject: your blog sucks!!!

From: Ric Flair (tobethebest@youhavetobeatthebest.com)

Body: “WHOOOOOOOOOOOOOOOOOOOO!!!”

http://youtu.be/iy-LQH8N6Ug (for those of you who get this via RSS)

 

Meeting Invites: Parking Lot Ass-Kickin’

This is my favorite – probably because it caught me so off-guard the first time it happened… I had just started my first full-time .NET development gig about 6 ago. Not more than a week in, I got an Outlook meeting invite from one of the Sr. Devs that I barely knew. I was a mandatory attendee for a meeting the next day…

Time: 09:15

Location: Parking Lot

Subject: Kickin’ Keith’s Ass

Classic.

 

You’re Not Checking In Enough Code

[WTF?!] This happened at the same gig as the one I mentioned in the Parking Lot Ass Kickin’. Our development manager called the team in for an impromptu mandatory meeting just prior to lunch. The meeting started with our manager explaining a conversation he had with the VP of Development regarding the team’s performance. We were hitting all of our early project deadlines, but nonetheless our VP was concerned about the amount of code we were checking in. “There weren’t enough lines of code being checked in… I don’t think they’re working hard enough.” [Are you shitting me?] He went on to say that the development group was going to implement a new bonus structure not based on performance and meeting deadlines, but one based solely on the number of lines of code we checked-in to source control. Our poker-faced manager went on for another few minutes describing the new bonus structure and everyone else in the room was thinking about the new content their resumes would contain before the next work day started… Finally he broke a smile and rolled lunch in for everyone. …and then gave everyone the afternoon off!

 

Always Lock Your Tailgate

This happened at easily the worst software development gig I’ve held in my 10+ year career. Thankfully though, I worked with the coolest group of people who were able to find some humor despite the sweat-shop environment. I rode to lunch with a couple of the developers one afternoon. That day in particular, we were lucky enough to find time for lunch… I got back to my office an hour or so later to find the tailgate of my truck unhitched and sitting against the wall. Thankfully no one saw me toting it down the 4th floor elevator and out through the lobby after work. My tailgate remained locked after that.

 

Honorable Mention:

1) PrankPlace.com for some good ideas…

2) Tape over the optical mouse lense rendering it useless…

3) Randomly injecting business cards of the company’s all-time worst hire into your buddy’s box of business cards…

 

I welcome any reader comments describing some good office burns. I’m looking for some new content… :)

Add Google Analytics to your ASP.NET WebForms Application

07.12.2011 07:10 by kbeckman | Comments

Whether you build or maintain websites for your employer, clients or personal use, it is imperative to know how your users interact with your site(s). Compiling usage statistics is a integral part of site administration and well-informed admins are able to identify the primary areas of your site that users interact with as well as the areas of your site that are light on traffic. This can be a great tool for a number of reasons – it can help identify areas where development teams should concentrate their efforts for optimization and scalability; it can help your business owners determine enhancement priority based on popular features and features that aren’t meeting revenue requirements; and it can be used to gauge the effectiveness of marketing campaigns targeted at your site.

 

Google Analytics is probably the most widely known and easy to implement of all statistics providers. Google Analytics is a free service that  provides functionality useful for sites of all traffic levels. Below, I’ve included an ASP.NET server control that you can use to emit the HTML <head> tag JavaScript necessary to take advantage of Google Analytics. You can use the code below in your site or you can get the full .NET solution from my public C4SC GitHub repository.

 

GoogleAnalytics.cs

using System;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace C4SC.Common.WebControls.Google
{
    /// <summary>
    /// Emits Google Analytics JavaScript. Be sure to include this control on your site's MasterPage or a base page
    /// that will ensure this JavaScript gets emitted inside the HEAD element on every rendered page. 
    /// </summary>
    [DefaultProperty("AccountId")]
    [ToolboxData("<{0}:GoogleAnalytics runat=server></{0}:GoogleAnalytics>")]
    [ToolboxBitmap(typeof(GoogleAnalytics), @"GoogleAnalytics.bmp")]
    public class GoogleAnalytics : WebControl
    {
        private static string _googleAnalyticsJavaScript    = String.Empty;
        private static readonly object _lockObject            = new object();
        private const char Quote                            = '"';

        /// <summary>
        /// Google Analytics Account Id.
        /// </summary>
        public string AccountId
        {
            get { return ((string)ViewState["GoogleAnalyticsAccountId"]); }
            set { ViewState["GoogleAnalyticsAccountId"] = value; }
        }

        /// <summary>
        /// Renders control's begin HTML tag.
        /// </summary>
        /// <remarks>
        /// Pass-through call to ignore the default SPAN tag rendered by WebControl base class. 
        /// </remarks>
        /// <param name="writer"><see cref="HtmlTextWriter"/></param>
        public override void RenderBeginTag(HtmlTextWriter writer)
        {
            return;
        }

        /// <summary>
        /// Renders control's end HTML tag.
        /// </summary>
        /// <remarks>
        /// Pass-through call to ignore the default SPAN tag rendered by WebControl base class.
        /// </remarks>
        /// <param name="writer"><see cref="HtmlTextWriter"/></param>
        public override void RenderEndTag(HtmlTextWriter writer)
        {
            return;
        }

        /// <summary>
        /// Renders control's contents.
        /// </summary>
        /// <param name="output"><see cref="HtmlTextWriter"/></param>
        protected override void RenderContents(HtmlTextWriter output)
        {
            output.Write(GetGoogleAnalyticsJavaScript());
        }
         
        /// <summary>
        /// Builds the Google Analytics JavaScript block.
        /// </summary>
        /// <returns>Google Analytics JavaScript block.</returns>
        protected string GetGoogleAnalyticsJavaScript()
        {
            if (_googleAnalyticsJavaScript.Length == 0)
            {
                StringBuilder bldr = new StringBuilder();

                bldr.AppendLine(@"<script type=" + Quote + "text/javascript" + Quote + ">");
                bldr.AppendLine(@"    var _gaq = _gaq || [];");
                bldr.AppendLine(@"    _gaq.push(['_setAccount', '" + AccountId + "']);");
                bldr.AppendLine(@"    _gaq.push(['_trackPageview']);");
                bldr.AppendLine(@"    (function() {");
                bldr.AppendLine(@"        var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;");
                bldr.AppendLine(@"        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';");
                bldr.AppendLine(@"        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);");
                bldr.AppendLine(@"    })();");
                bldr.AppendLine(@"</script>");

                lock (_lockObject) { _googleAnalyticsJavaScript = bldr.ToString(); }
            }

            return _googleAnalyticsJavaScript;
        }
    }
}

Web.config <system.Web>

<pages>
    <controls>
        <add namespace="C4SC.Common.WebControls.Google" tagPrefix="c4sc"/>
    </controls>
</pages>

 

Site.master or AnyPage.aspx

<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="SiteMasterPage" %>
<%@ Register Assembly="C4SC.Common.WebControls" TagPrefix="c4sc" Namespace="C4SC.Common.WebControls.Google"%>

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

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Site's Master Page</title>
    <!-- JavaScript and CSS includes... -->
    <c4sc:GoogleAnalytics ID="GoogleAnalytics" runat="server" AccountId="YOUR GOOGLE ANALYTICS ID HERE"></c4sc:GoogleAnalytics>
</head>
<body>
    <form id="form1" runat="server">
        <!-- WebForm Contents -->
    </form>
</body>
</html>

 

A Couple of Extra Notes…

--You’ll notice that I simply return from the RenderBeginTag() and RenderEndTag() methods. I had to override these methods to return nothing or else the WebCotrol base class would wrap the emitted JavaScript in <span> HTML tags. I didn’t want anything wrapping the emitted <script> tags.

--Make sure you add the GoogleAnalytics server control to the HTML <head> tag of your site’s master page(s).

--This code in this post only emits the JavaScript that provide the basic Google Analytics tracking for a single top-level domain. JavaScript for a single domain (with multiple subdomains) or multiple top-level domains require a few more lines of JavaScript. I’ll eventually provide this functionality in my GitHub project along with public properties on the control for configuration.

Configure DiffMerge for Your Git DiffTool

07.08.2011 22:57 by kbeckman | Comments

A while ago I wrote about the importance of choosing cross-platform development tools when available. As a consultant, being able to work proficiently with multiple platforms provides flexibility in the projects you can choose from. The ability to take a familiar tool with you from one platform to another saves you from having to ramp up on an alternative you’re not used to. It also adds value for your clients allowing you to concentrate precious time on ramping up on the more important domain knowledge specific to the project.

 

DiffMerge is an excellent diff tool that works on all platforms and one I’ve recently adopted as my primary diff tool. Git is easily the best source control system under the sun and also works across platforms. Here I’ll show you how to use them in tandem across Windows, Mac OS X and Ubuntu Linux. Please keep in mind that the actual DiffMerge and Git installation locations may differ on your system requiring a slight tweak or two to the commands below.

 

Mac OS X

Configuring Git to use DiffMerge in Mac OS X is probably the easiest configuration of the three platforms. After installing Git and DiffMerge, you’ll need to run the following Git configuration commands to get up and running.

git config --global merge.tool diffmerge
git config --global mergetool.diffmerge.cmd "/Applications/DiffMerge.app/Contents/MacOS/diffmerge --merge --result=\$MERGED \$LOCAL \$BASE \$REMOTE"
git config --global mergetool.keepBackup false

git config --global diff.tool diffmerge
git config --global difftool.diffmerge.cmd "/Applications/DiffMerge.app/Contents/MacOS/diffmerge \$LOCAL \$REMOTE"

 

Ubuntu Linux

Configuring Git to use DiffMerge on Ubuntu Linux is very similar to the Mac OS X configuration. The commands are the same here except for the DiffMerge installation location.

git config --global merge.tool diffmerge
git config --global mergetool.diffmerge.cmd "/usr/bin/diffmerge --merge --result=\$MERGED \$LOCAL \$BASE \$REMOTE"
git config --global mergetool.keepBackup false

git config --global diff.tool diffmerge
git config --global difftool.diffmerge.cmd "/usr/bin/diffmerge \$LOCAL \$REMOTE"

 

Windows

The windows configuration is the most difficult of the three configurations. I wouldn’t even bother trying to set this up using the Git configuration commands through the Git Bash command prompt. You’ll save yourself A LOT of time by just hacking your .gitconfig file manually to point to the external Git scripts required to launch DiffMerge. Here’s what you need to do…

 

1) Create a directory to hold your external Git command scripts – this can be anywhere. I use the ../cmd directory that is created when you install Git on Windows – C:\Program Files (x86)\Git\cmd.

2) Add your Git external command directory to your PATH System Environment Variable.

Right Click ‘Computer’ and choose “Properties” from the context menu.

Click “Advanced System Settings”.

Click the “Environment Variables” button to launch the necessary modal dialog.

 

image

 

3) Drop the following scripts into your Git external commands directory.

git-difftool-diffmerge-wrapper.sh

# place this file in the Windows Git installation directory /cmd folder
# be sure to add the ../cmd folder to the Path environment variable

# diff is called by git with 7 parameters:
# path old-file old-hex old-mode new-file new-hex new-mode

"C:/Program Files (x86)/SourceGear/DiffMerge/DiffMerge.exe" "$1" "$2" | cat

 

git-mergetool-diffmerge-wrapper.sh

# place this file in the Windows Git installation directory /cmd folder
# be sure to add the ../cmd folder to the Path environment variable

# passing the following parameters to mergetool:
# local base remote merge_result

"C:/Program Files (x86)/SourceGear/DiffMerge/DiffMerge.exe" "$1" "$2" "$3" --result="$4" --title1="Mine" --title2="Merge" --title3="Theirs"

 

4) Edit your global .gitconfig file to include the necessary difftool and mergetool commands.

[merge]
    tool = diffmerge
[diff]
    tool = diffmerge
[mergetool]
    keepBackup = false
[mergetool "diffmerge"]
    cmd = git-mergetool-diffmerge-wrapper.sh "$LOCAL" "$BASE" "$REMOTE" "$MERGED"
[difftool "diffmerge"]
    cmd = git-difftool-diffmerge-wrapper.sh "$LOCAL" "$REMOTE"

 

Launch DiffMerge

git mergetool
git difftool

 

Resources

http://adventuresincoding.com/2010/04/how-to-setup-git-to-use-diffmerge

http://support.sourcegear.com/viewtopic.php?f=33&t=13195

http://www.davesquared.net/2009/02/setting-up-diff-and-merge-tools-for-git.html

http://www.davesquared.net/2009/05/setting-up-git-difftool-on-windows.html

Common Git Usage

06.28.2011 09:18 by kbeckman | Comments

Before you read much farther here, I just want to let you know that this post is mostly for my own personal use as a consolidation of scattered Git notes I’ve been keeping around… It’s my Git crib sheet describing some how-to’s and the why’s behind them. I’ll probably be updating this post on occasion as I find or figure out new Git goodies. Hopefully C4SC readers can find a gem or two in here.

 

Create a New Local Branch

The following command creates a new local branch in your Git repository. The workflow first switches to the local master branch and pulls all of the latest remote changes. The --rebase argument tells Git not to merge the remote master branch into your local master branch, but to replay all of the most recent individual remote commits on local master instead. Git will replay all of the commits in succession since your local master’s HEAD commit rather than forcing you to perform a single commit for the whole merge. The checkout -b command tells Git to create a new local branch and then check it out.

git checkout master
git pull --rebase
git checkout -b <new-branch-name>

Pull Remote Changes Locally

Most of the commands here look very similar to the example above, but we’re assuming an existing child branch here (off master) rather than needing to create one. The additional step here is that after updating master, we update the child branch as well. After switching to the child branch, calling git rebase master replays all of the commits to master (since your child branch’s HEAD commit) onto your child branch. Just as in the example before, the rebase command forces Git to avoid a single merge commit in favor of replaying all of the individual commits in master onto the child branch.

git checkout master
git pull --rebase
git checkout <child-branch>
git rebase master

 

Merge Child Branch Into Master (and Push It to Origin Master)

Before merging a child branch into master, you’ll want to repeat all 4 commands in “Pull Remote Changes Locally” prior to the merge to ensure you have the latest remote changes before merging any code. If you don’t do this, Git will force you to do it anyway… One more reminder – after getting all the remote changes and performing your merge, run your unit test suite locally to make sure nothing is broken. If you don’t do this, you deserve whatever hazing you get if you break the build.

A quick note here (because this is different than the last two steps)… When merging a child branch into master, since you updated your child branch by rebasing from master in the previous step “Pull Remote Changes Locally”, your child branch changes are fast-forwarded onto the master branch HEAD commit. This ensures that only the changes you performed on the child branch are committed to master just as if they had been done on master rather than the child branch. This keeps all of your local branch information from showing up in your remote repository (git push origin master). Why? I may have performed LOTS of commits and applied LOTS of refactorings to my child branch to get it ready to merge – I may have even used several local branches to accomplish the task. I don’t want to sei all of the local branch information in the remote repository. This is beneficial for the overall upkeep of the project because other developers may have local branches named the same as yours causing serious conflicts if all the local branch information appeared in the remote repository. This makes everyone’s job easier to integrate local changes when pulling remote changes.

git checkout master
git merge <child-branch>
git push origin master

 

Undelete a Deleted [Staged] File and Roll It Back

As far as Git is concerned, this is one of the easiest operations to perform. However, if you’re used to a nice right-click context menu option in Visual Studio or some other IDE, it might be one of the hardest to remember… In your head you’re saying “Undo Changes” or “Revert File”, but Git requires a two-step approach. The workflow here is to unstage the deleted file and then get the latest repository version by checking it out.

git reset HEAD /some-directory/file.txt
git checkout /some-directory/file.txt

 

Rename / Move a File

The following command is a shortcut. If you chose not to do it this way, you’ll end up with a few more calls. This prevents you from having to add the new file (along with tracking it) and remove the old file in separate commands. If you haven’t yet begun tracking the file you want to move, just remove the git mv command in favor of just mv since Git doesn’t know about it yet.

git mv <file-name> /new-directory/new-file-name

 

Resources

Pro Git -- Scott Chacon (ProGit.org)

Git for Subversion Users – Derrick Bailey (Code-Magazine)