Monthly Archives: January 2010

Adsense appeal

I rarely see blogger’s, at least the one’s I read, talk about their Adsense revenue so I thought I’d throw this out there and see where it goes.

I’ve been blogging on my own domain since October of 2006 and one of the things I’ve experimented with is Google’s Adsense. I say experimented because the $1.80 in average daily earnings isn’t going to change my lifestyle anytime soon and I consider it a good day when it covers the cost of a mocha. When I think about it, Adsense is pure genius. Full stop.

Google didn’t do anything other than offer a tiny (and unknown) fraction of money it earns from these ad placements. Starting last May, through a new feature in Google Analytics, I started to gain some insight as to specifically which pages generate the most revenue and I can tell you it’s not what I typically what I blog about, at all.

In fact, if I wrote based on “Adsense appeal” I’d hardly ever write about programming or development. Looking at my “top” Adsense pages they are unrelated, detours I’ve taken along the way that have garnered Google Juice for some reason or other. I now believe Google could write a tool that would predict Adsense revenue for blogs posts in real time as they’re being written. Perhaps they could even auction posts while they’re being written and with the right feedback guide writer’s to higher revenue by suggesting links and images. I’m sure all this is possible yet seemingly corrupt or something close to it.

I wonder what the saturation rate of Adsense is throughout the web? I’d think Google must have an idea and therefore must know when they’re going to “hit a wall” in terms of being able to grow their Adsense revenue. On a different note, I’d love to know what, if any impact Twitter has had on Adsense revenue since many of the bloggers I used to follow have more or less given up on blogging and now write 140 characters or less which simply doesn’t interest me.

Note: Table of stats removed upon re-read of Google policy.

Based on data from Analytics I made a few changes back in November that seem to have boosted my ad revenue over the past three months and in November, I made my highest monthly total ever.

Google Calendar on a Simile Timeline

At Falafel Software Google calendar is a core tool used by the company. I’ve been working to leverage the data calendar in a number of interesting ways such as in the DIV below rendered using JavaScript based on calendar data from Falafel training calendar displaying upcoming training events over the next 120 days.

February 2

Telerik Advanced RadGrid Data Editing and Validation
FREE! (Click to register)

February 23

Telerik Advanced RadGrid Paging, Sorting, and Filtering
FREE! (Click to register)

March 1

Telerik Sitefinity Online Training
$399/person

I’ve mentioned the above example before from Google’s playground and I found another example which uses $g(MIT’s Simile) project Timeline control to render calendar data. As you can see from that example it’s not actually working so I thought I’d post an update with some changes I made to get it working. Here is my example. I’ve made changes to get the import of the data working though there remains a number of other improvements that could be made. I’ve yet to determine if this timeline view is really all that useful and haven’t settled on a view that I really like. At any rate, here is the code, again take a look at the source for my example above for a complete working page.

var gEventSource;
function loadGDataCallback(json) {
  var entries = json.feed.entry;
  var timelinerEntries = [];
  for (var i = 0; i < entries.length; ++i) {
    var entry = entries[i];
    if(entry["gd$when"] == null) continue;
    var when = entry["gd$when"][0];
    var start = convertFromGDataDate(when.startTime);
    var end = convertFromGDataDate(when.endTime);
    var webContent = null;
    var links = entry.link;
    for (var j = 0; j < links.length; ++j) {
      if (links[j].rel == "alternate") {
        webContent = links[j];
        break;
      }
    }
    var title = entry.title.$t;
    var link = entry.link;
    var icon = entry.link[0].href;
    var description = '<img src="' + link + '">';
    timelinerEntries.push(new Timeline.DefaultEventSource.Event(
      start,
      end, // end - when not set, event displayed with icon (looks better)
      null, // latestStart
      null, // latestEnd
      false, // not isDuration
      entry.title.$t,
      entry.content.$t,
      icon, // image
      entry.link[0].href, // link - destination when clicking on title
      entry.link[0].href,
      undefined, // color
      undefined  // textColor
    ));
  }
  gEventSource.addMany(timelinerEntries);
};
function zeroPad(n) {
  if (n < 0) throw new Error('n is negative');
  return (n < 10) ? '0' + n : n;
}
function convertToGDataDate(/*Date*/ date) {
  return date.getFullYear() + '-' +
         zeroPad(date.getMonth() + 1) + '-' +
         zeroPad(date.getDate());
}
function convertFromGDataDate(/*string<YYYY-MM-DD>*/ date) {
  var match = date.match(/(\d{4})-(\d{2})-(\d{2})/);
  return new Date(parseInt(match[1], 10), parseInt(match[2], 10) - 1, parseInt(match[3], 10));
}
function onLoad() {
  gEventSource = new Timeline.DefaultEventSource();
  var theme = Timeline.ClassicTheme.create();
  theme.event.bubble.width = 320;
  theme.event.bubble.height = 180;
  // centering the timeline three months previous makes it look nicer on load
  var threeDaysFromNow =
      new Date(((new Date).getTime()) + 3 * 24 * 60 * 60 * 1000);
  var bandInfos = [
    Timeline.createBandInfo({
        eventSource:    gEventSource,
        date:           threeDaysFromNow,
        width:          "40%",
        intervalUnit:   Timeline.DateTime.WEEK,
        intervalPixels: 300,
        theme:          theme
    }),
    Timeline.createBandInfo({
        eventSource:    gEventSource,
        date:           threeDaysFromNow,
        width:          "60%",
        intervalUnit:   Timeline.DateTime.MONTH,
        intervalPixels: 550,
        theme:          theme
    })
/*    ,
    Timeline.createBandInfo({
        showEventText:  false,
        trackHeight:    0.5,
        trackGap:       0.2,
        eventSource:    gEventSource,
        date:           threeDaysFromNow,
        width:          "10%",
        intervalUnit:   Timeline.DateTime.YEAR,
        intervalPixels: 200
    })
*/
  ];
  bandInfos[1].syncWith = 0;
  bandInfos[1].highlight = true;
/*
  bandInfos[2].syncWith = 0;
  bandInfos[2].highlight = true;
*/
  tl = Timeline.create(document.getElementById("my-timeline"), bandInfos);
  // Atom feed from a Google calendar
  var feedUrl = "http://www.google.com/calendar/feeds/4u13qtkl3bcpao0ofgod94s960@group.calendar.google.com/public/full";
  var startDate = new Date((new Date).getDate());
  var endDate = new Date(((new Date).getTime()) + 3 * 30 * 24 * 60 * 60 * 1000);
  var getParams = '?start-min=' + convertToGDataDate(startDate) +
                  '&start-max=' + convertToGDataDate(endDate) +
                  '&alt=json-in-script' +
                  '&callback=loadGDataCallback' +
                  '&max-results=5000'; // choose 5000 as an arbitrarily large number
  feedUrl += getParams;
  var scriptTag = document.createElement('script');
  scriptTag.src = feedUrl;
  document.body.appendChild(scriptTag);
}
var resizeTimerID = null;
function onResize() {
    if (resizeTimerID == null) {
        resizeTimerID = window.setTimeout(function() {
            resizeTimerID = null;
            tl.layout();
        }, 500);
    }
}

I’m interested in any other ways people are using Google’s calendar data so let me know if you done/seen anything cool!

Moving from dasBlog to BlogEngine.NET

In addition to moving this blog to BlogEngine.NET (BE) I’ve recently moved my personal home blog from dasBlog to BE and I’ve had to lookup the steps to export/import blog data more than once so I decided to capture them once and for all here. The process shouldn’t really take more then about 30 minutes to complete.

  1. Download Paul Van Brenk’s dasBlog BlogML importer
  2. Run the importer to convert your dasBlog content to BlogML. I ran into a few issues trying to import 1500+ comments from my blog so it required a bit of debugging/tweaking.
  3. Download BlogEngine.NET and set it up either locally on an ISP (importing can work with either)
  4. To avoid a Username/Password error upon importing into BlogEngine.NET add the line indicated below to BlogEngine.NET\api\BlogImporter.asmx (solution originally sourced from this work item on CodePlex)
    [SoapHeader("AuthenticationHeader")]
    [WebMethod]
    public string AddPost(ImportPost import, string previousUrl, bool removeDuplicate) {
        if (!IsAuthenticated())
            throw new InvalidOperationException("Wrong credentials");
        ...snip...
        Post post = new Post();
        post.Title = import.Title;
        post.Author = import.Author;
        post.DateCreated = import.PostDate;
        post.DateModified = import.PostDate; // or "DateTime.Now"  <- LINE ADDED
        post.Content = import.Content;
  5. Log into your BE install click on the Settings tab and scroll all the way to the bottom to view the Import & Export section.
  6. Click the Import button which launches the Blog Importer tool that looks like this:

     image

  7. Enter the name of your BlogML XML file, the URL to your BE blog and your credentials
  8. Click Import
  9. Hit your BE blog and enjoy your imported data!

Btw, here is a post to a few other items I had to address to really get my blog moved over.

A few minor BlogEngine.NET TextBox widget improvements

I really like the ability to use multiple widget zones, it’s a great feature that offers a lot of flexibility WRT theming. There is one drawback which is BE makes some assumptions and places hardcoded markup in the TextBox widget which is not desired. I’d like to see two minor changes along these lines which I think would help improve the usability. First, WidgetBase.cs has an assumption that if the title of the widget is not displayed a gets inserted which IMO is not desired. Second, the $g(tinyMCE) editor used by the TextBox widget wraps all content in <p> tags which is also not desired IMO. I like to use the TextBox widget to place any markup on my theme including <script> blocks therefore in WidgetBase.cs I’d like to see the following change:

if (ShowTitle)
  sb.Append("<h4>" + Title + "</h4>");
else
  sb.Append("<br />");

to…

if (ShowTitle)
  sb.Append("<h4>" + Title + "</h4>");

And in Widgets\TextBox\edit.ascx add the option for TinyMCE for:

forced_root_block: false

I’ve already made these changes for my blog which makes the TextBox widget much more useful. I now have an editable sidebar, banner region, page footer and footer script block.

If you’d like to join a conversation on the topic I’ve posted this to CodePlex.

Update on my switch to BlogEngine.NET

While I updated my blog awhile ago I thought I post an update as to how my conversion when and mention a few of the things I had to do to convert. BlogEngine.NET (BE) has been a refreshing break from dasBlog and the ASP.NET v2.0 design of BE is so much more enjoyable to work with. Porting my theme was a no brainer and while I had several hurdles in order to actually move it has been well work the effort.

For example, my blog now loads much faster, pages are now compressed, and login problems are a thing of the past among many other benefits. Some of the issues I faced in the move included:

  • Moving my posts and comments
  • Preserving the dasBlog style title-based links to my posts
  • Preserving non-post articles I’d written
  • Porting dasBlog’s macro for quickly linking text to a BE Extension

I first tried using a dasBlog provider but eventually decided against that approach as it would have prolonged the aforementioned comment loss issue. Eventually, I settled on a minor change to the slug (URL) handling for BE coupled with a dasBlog URL rewriter which handles various redirects and any referrer related issues as I find them. Additionally, I added the blocked IP logic from dasBlog to BE allowing me to cut down on spam attacks (at least a little).

Selenium Training

Update: Oct 2010 FYI, I’ve since changed jobs and I’m no longer conducting training. 

Selenium Logo

If you are or would be interested in training on the Open Source web testing tool called Selenium please leave a comment or contact me and let me know some specifics about what type of training you’re looking for. I’m focused on .NET development and would be looking at covering Selenium from a .NET perspective.

Switch back to Yahoo Mail classic

Since I still get tons of email and comments on this post, and recently received yet another email about it I decided to post this hint about switching back to Yahoo Mail Classic:

  • Browse to Yahoo Mail and use the Options drop down on the right side of the browser window:

    My Yahoo Mail Classic

Of course this is only current as of the time of this posting but it works for now. Sadly, I’m no longer using Yahoo these days.

HTH

Setting the version number for a CCNET build

I’m working on a custom EDI solution and generally build CCNet from the command line. Recently, I wanted to up date the build of CCNet and wanted to be sure the version number was set correctly. Since I’ve had to look this up a few times, as I never seem to recall the steps I thought I’d post them here.

This assumes you have a complete SVN tree of CCNET on your machine.

  1. Start a CMD prompt
  2. CD to the directory where CCNET is located
  3. Edit ccnet.build and change the following line setting the “value” attribute to the desired version # (ex: 1.4.4):

    <property name="CCNetLabel" value="1.4.4" overwrite="false" />

  4. Build from the command line using the createAssemblyInfo target:

    b.bat createAssemblyInfo

  5. From the command line using the all target:

    b.bat compile

This will build ccnet with the specified version number.

What should UI developers know about TestComplete

TestComplete Online TrainingThis question came up in the AutomatedQA newsgroup and this was my response:

A short list off the top of my head…

For Windows Apps 

  • Avoid use of painted controls or use controls that support UI Automation or MSAA particularly in the event they are painted
  • Avoid non-standard custom UI skinning while it can look good it often will seriously thwart automation.
  • Provide UI cues when the state of the application is changing or when there is a long running operation (title/status bar messages)
  • Use keyboard shortcuts wherever possible "&" in menus, labels etc.
  • Be sure to name controls dynamically created at run time

For Web Apps

  • Avoid using frames
  • Try to adhere to the Accessibility Standards (Section 508 here in the US)

I’d also mention that if the code they (the developer’s) are writing is hard to read and they dread debugging it, automation it is likely to be a nightmare. No matter how many hints about automation one can give there really is no substitute for good design.

Any other suggestions you would add?

      iPod Touch home screen now on my Motorola Droid

      I’ve mostly duplicated the functionality found on the home screen of my iPod Touch on the Motorola Droid though I think it’s fairly evident in the screenshots that, out-of-the-box, Android rather lacks the polish of Apple’s OS. I don’t really find that to be a big deal because one of the things I like about Google is their iterative approach to development and I expect things will incrementally improve at a much faster rate then the iPhone though time will tell. Btw, the images below approximate the actual screen sizes on my 17” Macbook Pro monitor at 1900×1200.

      image

      image 

      There are a few things about Android icons I don’t quite get:

      • The spacing is too large with too much whitespace
      • The icons are too small
      • They don’t appear to support dynamic content like the Calendar on the iPhone or icon overlays like on the iPhone’s mail application (refer to the email, Byline and Facebook apps on the Touch)
      • The phone defaults to only three screens of icons and the center screen is the “home” screen

      I understand there are themes for Android but IMO it seems that some of these things should be the default not where you have to find and install a theme to improve usability.

      [Updated: Jan 14, 2010] Nexus One, iPhone and Droid: Which Is Brightest? Long story short, Droid won. 🙂