Better Toodledo iCal

Saturday, 12 September 2009 12:15 PM
by Coose

UPDATE: This post is outdated.  See this post for an updated version.

Since the iPhone doesn’t have tasks at all, I had to find an external service to handle this.  I started with Remember-the-Milk, but was thoroughly unimpressed.  I stumbled across Toodledo and liked what they had to offer.  What I liked was:

  • Web access
  • Native iPhone application
  • Automatic and nearly transparent sync from iPhone back to web service
  • ICS feed for my Google and iPhone calendars

The last item is what I’m going to talk about today.  When using Windows Mobile, I DX’d the built-in calendar for Pocket Informant, then later for Agenda One.  One of the things I liked the best about those PIMs was that on the current day, it showed tasks that are due today as well as those that are overdue.  That last part is important.  If I had a task due four days ago, and I didn’t do it and check it off, I need to see it on my calendar TODAY.  Not when it was originally due.  Again, this is only true for OVERDUE tasks.  This functionality was not in Toodledo. :(

Toodledo has an ICS feed, which is great.  It allows me to show tasks on my Google calendar, as well as on my iPhone calendar.  They are not ICS task items, they are done as all-day events.  That’s cool because iPhone’s iCal and Google calendar don’t display ICS tasks correctly anyway.  BUT, Toodledo just uses the raw due date on the task for the start and end date of the ICS event.  This has been driving me nuts, so I decided to make a change myself.

My first attempt was to access the Toodledo APIs over the wire directly, and generate my own ICS feed based on the raw tasks.  I created a C# library, that I was also using for a Silverlight Toodledo interface that I’ve been tinkering with.  But I do have a handful of kids, and a full time job, so I decided on an easier way.

I figured that Toodledo is already generating an ICS feed, so let’s take their feed, and for any start and end date that is older than today, just switch it to today.  That way all overdue tasks will continue to show up on today’s date, whatever day today is.  Now I have a more constant reminder to get those overdue tasks DONE!!!

So, I created a new class in my own web site’s App_Code directory that derives from IHttpHandler.  Register that handler in the web.config of my web site for a specific file name, for example ‘mytasks.ics’ (don’t try to access that on my web site, that’s not what I really called it).  The handler uses a WebClient class to retrieve the data from Toodledo, then a regular expression to find expired dates and replace them with today.  I then write that data to the response stream, and viola!

Here’s my class:

    1 using System;

    2 using System.Collections.Generic;

    3 using System.Linq;

    4 using System.Web;

    5 using System.Net;

    6 using System.Text;

    7 using System.Text.RegularExpressions;

    8 using System.Globalization;

    9 using System.IO;

   10 

   11 namespace Funkymule

   12 {

   13     public class ToodledoHandler : IHttpHandler

   14     {

   15         #region IHttpHandler Members

   16 

   17         public bool IsReusable

   18         {

   19             get { return true; }

   20         }

   21 

   22         public void ProcessRequest(HttpContext context)

   23         {

   24             WebClient client = new WebClient();

   25             byte[] buffer = client.DownloadData("http://www.toodledo.com/path/to/your/toodledoo/feed.ics");

   26             StringBuilder ics = new StringBuilder(Encoding.Default.GetString(buffer));

   27 

   28             Regex rx = new Regex(@"^(DTSTART|DTEND);VALUE=DATE:(?<date>.*)\s*$", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Multiline);

   29             foreach (Match m in rx.Matches(ics.ToString()))

   30             {

   31                 Group g = m.Groups["date"];

   32                 DateTime endDate = DateTime.ParseExact(g.Value, "yyyyMMdd", CultureInfo.InvariantCulture);

   33                 if (endDate < DateTime.Now)

   34                 {

   35                     ics.Remove(g.Index, g.Length);

   36                     ics.Insert(g.Index, DateTime.Now.ToString("yyyyMMdd", CultureInfo.InvariantCulture));

   37                 }

   38             }

   39 

   40             context.Response.ContentType = client.ResponseHeaders[HttpResponseHeader.ContentType];

   41             context.Response.Output.Write(ics.ToString());

   42         }

   43 

   44         #endregion

   45     }

   46 }

I put this in my App_Code directory, then added the handler in the web.config:

<system.webServer>

    <handlers>

        <add name="MyTasks" verb="*" path="mytasks.ics" type="Funkymule.ToodledoHandler" />

    </handlers>

</system.webServer>

If you are using a version of IIS older than 7, your configuration will be slightly different from mine.

Now all lines of the Toodledo issued ICS response that are like this:

DTSTART;VALUE=DATE:20090901
DTEND;VALUE=DATE:20090901

Which says that this task was due on September 1, will be changed to:

DTSTART;VALUE=DATE:20090912
DTEND;VALUE=DATE:20090912

because today is the 12th and the example above is an overdue task.  Now when subscribing to this calendar in whatever iCal type app you want (iCal, Google calendar, iPhone calendar, Outlook, Sunbird/Lightning) any overdue tasks will show up as all-day event on the current day.

Quick and easy, and I didn’t have to write a line of iPhone code to get the iPhone to work the way I like it.  Even though the built-in iPhone calendar blows really bad, the ActiveSync/CalDAV/iCal flexibility is a homerun.

UPDATE: This post is outdated.  See this post for an updated version.

Comment on this
Mike's Blog
|

Comments (1) -

Mozoot | Reply
1/30/2011 5:04:16 PM #
Really nice post, thank you

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading