Uncluttered Convenience

Icon

the simplest thing that could possibly work

Integration: Project Hamster, RTM, and Trac with Greasemonkey & Ticket2RTM

Or how to achieve tracking of almost everything with no data re-entry using Firefox, Greasemonkey, Remember The Milk (RTM henceforth), Project Hamster for general time tracking, Trac and it’s Timing and Estimation and Ticket2RTM plugins for ticket & hour tracking and RTM integration, and Git or SVN for version control…or any combination of these desired. My condolences if you’re using Windoze or IE. I don’t use either and the following assumes Firefox and something Linuxy.

Project Hamster is a wonderfully easy and robust time tracking system written in python (cross-platform) and has a nice applet for the gnome toolbar among other features. I already use Trac and it’s Timing and Estimation plugin to track time related to code, tickets, and changesets, but not everything I want to track can be related to such things. I use Remember The Milk for pretty much everything I need to do aside from sleeping, eating, and various other things I can’t or don’t care to have on a “to-do” list – the Trac plugin Ticket2RTM ensures tickets are also in RTM, RTM becoming the base center of operations for my life.

Hamster, while I used it religiously for a time some years ago, fell by the wayside as just another task after the initial “wow!” realizations on how I spent my time. Recently I needed Hamster to step in and fill the gaps…but only the gaps. Re-entry of data is a very big no-no in my world, so I set about brainstorming a way to get data from Remember The Milk into Hamster.  After all, if I already wrote down what I need to do in RTM or Trac, no sense rewriting it for Hamster when I’m doing it.

Enter Greasemonkey, a Firefox plugin that lets you add and alter javascript on any webpage. Using a Greasemonkey script I added a “copy” link to tasks in RTM. The original idea of just clicking a link and having it go directly to Hamster failed due to Dbus issues with scripts, but I find I prefer the copy method as it lets me edit tasks before I enter them into Hamster (usually by prepending -10 because I’ve been working on something different for the last ten minutes and not noticed).

On to the Greasemonkey code!
// ==UserScript==
// @name           Copy Links for Hamster Tracking
// @namespace      http://jaciss.wordpress.com
// @description    Adds a copy button next to each task for starting Project Hamster tracking via a local python script
// @include        https://www.rememberthemilk.com/home/*
// @require        http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js
// ==/UserScript==
/*
jQuery inclusion code courtesy of: http://joanpiedra.com/jquery/greasemonkey/
jQuery is extreme overkill here, but it's (obviously) a quick hack at this stage.
!NOTE: The below are just examples to show what can be done!  I use Trac (ticket/bug tracking) and the timingandestimation (track hours by commit messages, now generated by these scripts) and ticket2rtm plug-ins for my work - ignore,  remove, or edit to suit your system(s) of choice where necessary.
*/
var $;

// Add jQuery
(function() {
if (typeof unsafeWindow.jQuery == 'undefined') {
var GM_Head = document.getElementsByTagName('head')[0] || document.documentElement,
GM_JQ = document.createElement('script');
GM_JQ.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js';
GM_JQ.type = 'text/javascript';
GM_JQ.async = true;
GM_Head.insertBefore(GM_JQ, GM_Head.firstChild);
}
GM_wait();
})();

// Check if jQuery's loaded
function GM_wait() {
if (typeof unsafeWindow.jQuery == 'undefined') {
window.setTimeout(GM_wait, 100);
} else {
$ = unsafeWindow.jQuery.noConflict(true);
letsJQuery();
}
}

// All your GM code must be inside this function
function letsJQuery() {
//alert($); // check if the dollar (jquery) function works
//alert($().jquery); // check jQuery version
var lists = new Array();

function compileLists() {
$.each(unsafeWindow.listList.list.entries,
function() {
lists[parseInt(this[0])] = this[1];
return true;
});
}

function htmlEntities(str) {
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '/quote');
}

function addCopyLinksToTasks() {
var entries = unsafeWindow.taskList.list.hashMap;
var locs = unsafeWindow.locationList.list.hashMap;

$('#tasks table.xtable tr.xtr').each(function() {
var entry_id = parseInt(/_(\d+)$/.exec($(this).find('td.xtd_check form').attr('id'))[1]);
if (entry_id > 0) {
var entry = entries[entry_id];
var loc = unsafeWindow.locationMgr.getTaskLocation(entry.series_id);
var tags = unsafeWindow.tagMgr.getTagsForTaskSeries(entry.series_id);
var url = entry.url == null ? '': entry.url;
var name = entry.name;
var src = entry.location_id;
var list = lists[entry.list_id];
url = url.replace('http:\/\/', '');
#I preface projects with p- for the GTD RTM script
#project = list.replace('p-', '');
name = htmlEntities(name);
name = name.replace(/\'/g, '\\\'');
tagstr = '';
if (tags.length > 0) {
for (i = 0; i < tags.length; i++) {
tagstr += ' #' + tags[i];
}
}
if (loc == 'none') loc = 'computer';
#if it's a trac ticket
if (url.match("trac")) {
pathparts = url.split('/');
ticket = pathparts[pathparts.length - 1];
ticket = ' #working ticket ' + ticket;
} else ticket = '';
text = 'python /usr/share/pyshared/myhamster.py &quot;' + project + '@' + loc + ', ' + name + tagstr + ticket + "&quot;";
if ($(this).html().match('copier')) {
$('copier_' + entry_id).replaceWith('<td id="copier_' + entry_id + '"><a onclick="netscape.security.PrivilegeManager.enablePrivilege(\'UniversalXPConnect\');Components.classes[\'@mozilla.org/widget/clipboardhelper;1\'].getService(Components.interfaces.nsIClipboardHelper).copyString(\'' + text + '\')">copy </a></td>');
} else {
$(this).append('<td id="copier_' + entry_id + '"><a onclick="netscape.security.PrivilegeManager.enablePrivilege(\'UniversalXPConnect\');Components.classes[\'@mozilla.org/widget/clipboardhelper;1\'].getService(Components.interfaces.nsIClipboardHelper).copyString(\'' + text + '\')">copy </a></td>');
}
}
});
};

compileLists();
setInterval(addCopyLinksToTasks, 3000);
}

So this adds a copy link that will alert you to the danger of allowing “outside” sources access to your clipboard. When clicked, it copies something like

python /usr/share/pyshared/myhamster.py "webpresence@computer, write blog about greasemonkey key to tying hamster & rtm together #focus #na"

Where, for me, webpresence is both a list in RTM and a project/activity in Hamster (having a positive web presence is a goal of mine). Now we paste this text onto a command line and it calls a script (myhamster.py) which looks something like this:
import sys
import re
import hamster.client
from decimal import Decimal

#where to add commit log entries
commit_file = '/path/to/commit.txt'
storage = hamster.client.Storage()
facts = storage.get_todays_facts()
last_fact = facts[len(facts)-1]

#is there an ongoing task?
if last_fact['end_time'] == None:
try:
#is it a trac ticket?
for tag in last_fact['tags']:
if "#" in tag:
hours = last_fact['delta'].seconds
hours = Decimal(hours/60)
hours = Decimal(hours/60)
commit_str = "%s closes %s (%f), " % (last_fact['description'],tag,hours)
#print "Ticket was being worked on, adding line to commit.txt"
print commit_str
cf = open(commit_file,'a')
cf.write(commit_str)
cf.close()
except KeyError:
print "no tags"
else:
print "No active activity!"

new_fact=' '.join(sys.argv[1:99])
new_fact = new_fact.replace('/quote','"')
if "#" in new_fact:
reg = re.compile('ticket\s\d+')
tractag = reg.findall(new_fact);
if len(tractag)>0:
print "Trac tag initiated."
tractag = tractag[0]
tractag = tractag.replace('ticket ','#')
else: tractag=''
reg2 = re.compile('#[a-zA-Z0-9]+')
tags = reg2.findall(new_fact)
i=0
for tag in tags:
tags[i]=tags[i].replace('#','')
i=i+1
if(len(tractag)>0): tags.append(tractag)
else:
tags=''

print new_fact
#print tags

print storage.add_fact(new_fact,tags)

Note that the above script writes Timing and Estimation formatted lines (for tracking hours/ticket in Trac) to a commit.txt file if it detects a # in the tag. I left this in as an example of what can be done with this sort of system. I usually have to edit the commit file before use, but I rarely forget to add things anymore, and certainly don’t have to re-enter the data, which was the point of this exercise.

Filed under: how-to, , , , , , , , ,

Posting Tickets to Trac from Komodo Edit or Command Line

Tools: Python, Trac, XML-RPC Trac plug-in, Komodo Edit or command line

While I love Trac, creating new tickets can be annoying. Web interfaces will never be as fast as direct text input and I don’t usually need all of the options the interface provides. I do, however, wish to use Trac’s system instead of directly editing the database, as I have things tied into ticket creation that would be bypassed otherwise (crossposting to remember the milk, for one). Enter XML-RPC. With this handy plug-in one can interact with a Trac environment from outside of the web interface. My new_ticket.py code will work from within Komodo or on the command line and assumes a multiple project Trac setup. It goes something like this (feel free to use this hack, free as in beer, etc):

import xmlrpclib
import sys

print "Issue to submit:"
issue = sys.stdin.readline()

#get default project from passed arg
project = sys.argv[1]
project = project.split('/');
curproject = project[len(project)-1]
print "Project ["+curproject+"]"
project = sys.stdin.readline()
#if they didn't enter a project, use default
if(project.strip()==''):
project = curproject

print "Milestone [milestone1]:"
milestone = sys.stdin.readline()
if(milestone.strip()==''):
milestone = "milestone1"

#for example only, remove if you don't use timingandestimation plugin
print "Estimated Hours [1]:"
estimatedhours = sys.stdin.readline()
if(estimatedhours.strip()==''):
estimatedhours = '1'

attr = {}
attr['milestone'] = milestone
attr['reporter'] = 'Jaciss'
attr['owner']= 'Jaciss'
attr['status'] = 'assigned'
attr['type'] = 'enhancement'
attr['priority'] = 'major'
attr['estimatedhours'] = estimatedhours
print attr


#notice the /login - if anon access is disabled for xmlrpc
#you'll need extra code in this section
server = xmlrpclib.ServerProxy("http://YOUR.TRAC.SERVER/trac/"+project+"/login/xmlrpc")

newticket = server.ticket.create(issue, '', attr)

#the below will print the new ticket number
print newticket

Obviously this will need to be modified with your own server domain and user name, at the very least. You may also need additional code to handle permissions. It should be fairly self-explanatory otherwise, excepting the project variable.

From the command line I type something like:
python new_ticket.py [project_name]

In Komodo Edit I created a command that opens in a new console:
python /path/to/new_ticket.py %p

The extra project code involves the above command, where %p is Komodo’s substitution variable for the path to the active project. Assuming project directories and Trac environments are named the same way, the python tidbit above which pulls the last part of the path would provide the name of the current project/desired environment, able to be used in the script and neatly skipping a step.

In summary: This is awesome because I can run the command, type the issue, and hit enter until it exits, getting back into the flow of writing code before my brain realizes it’s no longer in “the zone”.  My employer and myself use the wonderful grid modify plugin and regularly go over and modify lists of tickets, anyway.  It’s a simple case of doing the right thing at the right time.

Updated 12-12-10: formatting, adding comments to the code, general cleanup

Filed under: how-to, , , ,

Organization for [true] Success

There are often a thousand tiny ‘I need to do x’, ‘I want to y’ thoughts in our heads on any given day, each adding a tiny bit of stress for being undone, and a tiny draw on our energy and wellbeing whilst we struggle to remember and file and recall these thousand tiny thoughts on demand, often without any reference as to what is truly important for us to accomplish right now. We as humans are not very good about storing and recalling information when needed, so extraneous things keep interrupting our thoughts ‘Oh! Don’t forget to get toilet paper!’ and when we’re shopping we, of course, forget the toilet paper. Computers are terribly efficient at this sort of storage and recall, and could save us a lot of stress, brain-drain, and time if used properly. Beyond the store-file-recall comparison, having a written record of what you intend and what you have done adds immeasurable value.

Instead of being swept through the days pushed by all the little thoughts and tasks, we can take control and push those thoughts and tasks – beyond and even more important than that, we can push those thoughts and tasks which are most important for us to achieve our goals and have a written record showing each little task is moving us forward toward those goals. This sounds very high-minded, but there is much down-to-earth about having a plan and intentionally working through it step by step. There is also much to be said for a system that can show the progress you’re making toward your goals with each little step. A system must not only be useful, it must be used.

So I’ll just jump into how I do this – examples are worth much and my system can be altered to fit someone else’s needs. As with anything, there is no one right way. It’s the core concepts that are important. I picked Remember the Milk and Springpad or Evernote for tools because they do what needs done with a minimum of fuss and training. I include little by way of usage because there is fairly little to do. Put stuff in properly and the systems do the rest, as it should be. The features on these services are great and part of why I chose them, but features are not why I’m writing this, so we will instead focus on the basics of efficient and worthwhile input and output.

Step One
Create a list of projects or goals – e.g., ‘buy a house’, ‘plan trip to Seattle’ and make sure each goal is SMART – specific, measurable, attainable, relevant, and time-bound. ‘Buy a house’ becomes ‘Buy a 3br 2ba condo in upper Manhattan before Jan 1st’ – assuming that’s a realistic goal that actually is relevant to what you want in life. I personally find the time/due date is useless or even harmful – I have an issue with organization, not laziness.

Each of these goals will have a list in Remember the Milk and a notebook in Evernote or Springpad – tasks go into RTM and research/bookmarks/information/etc goes into Evernote or Springpad (Springpad does a great job of organizing and supplying additional features for what you put into it, Evernote is faster and more flexible). I use short project names and put the goals themselves onto the Evernote or Springpad project’s/notepad’s [white]board. You will need the Springpad addon or bookmarklet or the Evernote addon for bookmarking or clipping web content, and any RTM addons or Evernote addons you’ll use (gmail, twitter, smartphone, etc). Combine twitterbar (tweet from your browser’s location bar) with RTM’s twitter service and all one has to do to add an item is type “d rtm remember the milk #Personal” and viola – a new task on a “Personal” list or tagged with “Personal”. With the gmail addon you can get you todo list right next to your inbox, not to mention the ability to reference google contacts or calendar events with RTM. But enough about features.

Step Two
Dump stuff out of your brain. Sort through bookmarks and documents and whatnot that you’ve accumulated for your projects and put it all into the appropriate notebook. Take an hour or three and get it all out…and into Springpad or Evernote and RTM – everything that’s been rattling around in your head taking up space and CPU cycles. This should be the mental equivalent of the emotional release one can get from “a good cry”.

The key part to this is making each task in RTM an action, GTD style. Much as making goals concrete is vital, making tasks concrete is also vital. Break vague todo items up into discrete actions – “buy a house” becomes “research neighbourhoods on neighborhoodscout.com”, “call bank about a home loan”, “call Jane Realtor about 3br 2ba homes in Y neighbourhood”, etc. Only add due dates for things which require them – once everything is available at a glance I find having fixed due dates a depressing bother. Life does not structure itself around my calendar, I choose instead to structure my life around my life and my goals.

Rinse and Repeat Steps
As it Happens
Whenever something necessary pops into your head or onto your screen put it into RTM or Springpad/Evernote. No excuses, no procrastination, just do it now and sort it later. It becomes easier as time goes on and there is less rattling loose. I often text myself todo items when I am away from my computer – look under account settings in RTM to get the address. I had to set up a tarpipe to alter the messages my cell sends out to work with RTM as my cell provider used to hijack the subject line.

Daily
We now need a way to reference tasks quickly whilst we’re in productive mode – at least once a day I go through each project list in RTM and tag “next actions” with “na”. Each “na” or “next action” task must be something you could do now if you had the time. I have created a smart list on RTM to display these things, and I work from that (search for tag:na and save the resulting list for future use). Update: I’ve also started a “focus” list for things that I’d like to accomplish in the next few days and should have mentioned my “lowfruit” list for things tagged “focus” (or or “na” if you prefer) which would take less than 21 minutes to accomplish. Pick the low hanging fruit first.

I also add time estimates and contexts/locations to RTM items which don’t have them each day, but find this isn’t vital for me. Important and worth doing most times, but not vital – most of my activity is at my computer and it’s debatable that the effort of adding locations is less than ignoring tasks which are out of context. For me. I doubt this is the case for most, and I always tag errands to ensure I don’t forget something as I’m running out the door. I have smart lists to show tags without locations and time estimates so I can do this quickly (isLocated:false aaaaaaaaand not(timeEstimate:"> 0 min") AND NOT list:Someday AND NOT tag:someday, respectively). You may note that I have a Someday list and tag – this is again inspired by GTD. Not everything needs done now, but it all needs to get out of my head so I can focus on what is important.

In everything, as always: make use of hotkeys – vital if you don’t want to waste a lot of time clicking on things. Smartlists, web services, and hotkeys are the main reasons I don’t use Springpad for my tasks. Springpad is, quite simply, too slow and cumbersome. Update: Springpad added hotkeys! Not to the degree RTM has them, but useful indeed.

Weekly
Once each week I review my completed tasks and the goals I’m trying to reach, then brainstorm new tasks to achieve those goals. This includes a look at my Someday list, in case anything on there can be moved forward. Pretty simple, but vital – it gives a sense of accomplishment and progress that is priceless and keeps me going, in large part because I can see that I am going, and going where I want to go.

Update Dec 13, 2010: I switched back to Evernote from Springpad due to Springpad’s editor, lack of API, and general slowness. Springpad is awesome, but Evernote works better for me and with the API I can utilize it in my own creations, export data if I want to leave, etc. As I’ve previously written, I hate using things that don’t have an API or at least implement Google Gears sqlite storage.

Update Dec 26, 2010: …and I’m back to Springpad. I’ve been vaguely researching Springpad for weeks and like what I’m seeing (thanks for the hotkeys and del.icio.us import!). In a show of good faith for a good product I’m going to assume the promised API will materialize, the hotkeys will improve, and there will be a decent advanced search and saved searches implemented. For some reasons I’ve yet to cement I’ve never been able to get myself to use Evernote reliably.

Filed under: Articles, how-to, , , , ,

Installing Gearman on Ubuntu 9.10

This is a bit deceptive, but not difficult.  Gearman can be installed via apt:

sudo apt-get install gearman

This will include all necessary files.  If you wish, you can then use PECL to install the PHP extension:

sudo pecl install gearman-0.6.0

The newest version is 0.7.0 but has issues when installing on Ubuntu.  I will likely go into this later.  At this point I had to create a gearman.ini file in /etc/php5/conf.d/gearman.ini containing “extension=gearman.so”:

sudo sh -c 'echo "extension=gearman.so" > /etc/php5/conf.d/gearman.ini'

Then restart Apache:

sudo apache2ctl restart

And all should work as expected.

Caveats: after much idiocy, I habitually install any *-dev packages I find for things I use.  These may or may not be required.  I also hear that uuid and libevent are required:

sudo apt-get install libev-libevent-dev uuid-dev

UPDATE:

After much tinkering I was able to install Gearman using the PPA info on Launchpad.  There is a conflict with required packages – gearman requires libmemcached4 but libdrizzle looks for libraries from libmemcached2.  The error was such:

gearmand: error while loading shared libraries: libmemcached.so.2: cannot open shared object file: No such file or directory

The ‘solution’ was to symlink libmemcached.so to libmemcached.so.2.

Filed under: how-to

Custom Pages in WordPress

WordPress is pretty stupid-proof, which makes it really annoying to do custom work with.  The short story on custom pages, which I had to discover through round-a-bout means which annoyed me to the point of ‘Do I really want to use WordPress at all?’ is this:

  1. Create a ‘template’ aka make a PHP file and save it into your current theme’s main directory.  An easy way to get this right is to copy something already there.
  2. Add a special ‘template header’ to the file consisting of /*Template Name: your-page-name*/
  3. Use WordPress’ (understandably) annoying GUI to create a new page which uses that template

Note that no page template options will show up when you go to make a new page unless you’ve first created your template and added the special header into it.  You can include javascript files in the header of your PHP page as well.

Filed under: how-to,

Efficient Use of Remember The Milk

Is it worth it?

First we have to decide if Remember The Milk (RTM henceforth) is worth trying.  I have some rules about new programs/systems/belongings that I used to decide this.

  1. anything I do, use, or buy has to improve my life
  2. (corollary) the net gain of something must be greater than the loss (loss could be money, time, sanity, whatever)

These sound simple, but periodically asking yourself if something actually improves your life (remembering gain vs cost) will prove it’s a question worth asking.  If it takes more time and effort to manage and complete tasks with RTM versus my usual methods, RTM gets the boot.  I have an additional rule for applications I don’t strictly ‘need’ – they have to give me access to my data and/or let me modify them.

I always have a sinking feeling when putting information into a program I can’t control or get my data out of – that is time lost if I ever decide to use something else, or stick with something that isn’t working well because the transition is too costly.  Additionally, I always find something I’d like to do differently, or use in another way, and I like to have the option to be able to do so.  It’s the best of both worlds – applications that (usually) keep improving with no effort on my part, and the option to tweak things to my personal preferences.

RTM does all of the above and more.  I already keep todo lists around, and RTM has integration with many applications I already use, so I knew it was likely it would be worth the effort before I ever started – it’s a simple matter to add a gadget/addon/plugin to something I’m already using.  My old systems of ‘todo’ lists were varied, involving either Google Notebook, web forms and databases, or (usually) a text document.  Not the most efficient, so RTM doesn’t need to do much to improve matters.

Usage

There are a variety of ways to use RTM, and you’ll have to figure out what works best for you.  I find RTM fits well enough into what I do anyway that I don’t have to think about it – I install the addons/gadgets/plugins and it’s there to be used.  Below is a summary of some of the options I’ve found useful for making RTM the most efficient process it can be with the least amount of effort.

RTM Website

Most users will interact with the website – it’s a good site, so I’ve little to say aside from the expected: learn the hotkeys.  Note: many of the below options use user-specific information like webcal and email addresses from the website.  You can currently find these by clicking ‘Settings’ at the top right and selecting the ‘Info’ tab on the page which loads.  The plugins can be found through the ‘Services’ link at the bottom.

RTM via Firefox via Twitter

I don’t like being forced to visit a site to do something simple, so the first thing I did was set up RTM’s twitter support.  As I use a twitter plugin that lets me post from the location bar of Firefox, adding tasks is usually as simple as typing something like ‘d rtm some task here on some date’.  This means I’ll actually use the service, because it’s just as easy, or easier, as opening up my todo file and typing the task in (though echo “task title” >> todo.txt is just as fast).  I’ll probably set up a hotkey that’ll type the ‘d rtm’ prefix in for me at some point, or write something for myself with a similar functionality.
Drawback: the supported syntax will only allow you to add a task and date

RTM via Email

When you sign up for RTM, you are assigned an email address you can use to send yourself tasks.  Anything in the title of the email will become the title of the task, and the body of the email will become a note on the task.  This means you can send yourself tasks from a cell phone, the right-click menu of most file-explorers, upon an error in a program, or from any site that has a ‘email this’ option.

RTM via Google Reader via Email

By adding aforementioned address to your google address book under RTM, you can utilize the email function available in Google Reader to email yourself RSS items to check out later with minimal effort (thank you auto-complete).  I tried abusing tags and stars for this previously, but found I rarely got back to things and the buildup meant I rarely bothered to check everything out.

RTM via webcal

This is extremely handy.  RTM will give you a webcal address which you can use to add your tasks into calendars which support the webcal scheme.  Most modern calendar applications should support webcal – I know both Google Calendar and Evolution (Linux) do so.

Drawback: tasks must have a time and date to show up, and you can’t edit the tasks, only view them.

RTM via Gmail in Firefox

RTM has a Gmail plugin for Firefox which will add a very well-designed column of tasks to the right of your messages.  You can even set filters of sorts so email which is tagged with specific terms (like ‘todo’) is automatically added to your task inbox.  The RTM plugin also, brilliantly, uses data from your address book and calendar when you’re creating a new task.  Typing something like ‘email John after staff meeting’ will, if ‘John’ is in your address book and ‘staff meeting’ is in your calendar, create a task which has a link to email John and automagically enters the due date based on when ‘staff meeting’ occurs.

Drawback: RTM will only do this if certain keywords (like ‘email’ or ‘call’) are present.  I would, personally, prefer a different syntax and more options versus being able to type in ‘normal’ english, but that’s just me.

RTM in Firefox Sidebar (via iGoogle)

I have the handy Google Sidebar addon installed in Firefox, and RTM provides an iGoogle widget.  Perfect.  Adding the widget means my task list now shows up in my sidebar with a simple ctrl+alt+i hotkey.  I still get to keep my RSS feeds, inbox, notebook, and various other widgets easily accessible, too.  Google owns me ’tis true, but at least I can get to most of my data (where is the API for Notebook?!).

Drawback: the iGoogle widget isn’t nearly as nice as the Gmail addon.

RTM via Google Calendar (Firefox only)

In addition to webcal, RTM provides a Google Calendar addon for Firefox which will add small icons you can use to view or add tasks.  These are present even in the iGoogle calendar widget.

Advanced
RTM via SQL

This may be a bit unintuitive, but RTM uses Google Gears for offline support, and Google Gears uses SQLite, and SQLite supports most SQL statements.  If you regularly synchronize your data, this can be a very easy way to use and modify that data.

Drawback: It isn’t real-time – if you don’t use caution this method may cause more problems than it solves.

RTM via API

RTM provides an Application Programmer Interface (API) which means you can write programs which interact with the RTM website for real-time operations.  It’s not as easy as using the SQLite database, but it’s real-time.  I don’t know of any good ready-to-go classes yet, but some are in progress and it’s not overly complex.  RTM supports JSON and something they call REST, which doesn’t look very RESTful to me, but I’m quite thankful for the API regardless.

Drawback: you need a developer key (this is common) which takes quite a while to get (this isn’t so common).

Filed under: how-to, , , , , ,

Installing xdebug w/PHP5 and Debian

The usual pecl install won’t do, oh no. Nor will the instructions on the xdebug site. It’s not that bad, though – in fact, it’s probably better. The PHP modules are stored somewhere like:

/usr/lib/php5/20060613+lfs/

You’ll find xdebug.so in there with the rest of ‘em after the pecl install. Use the system and add:

zend_extension=”/usr/lib/php5/20060613+lfs/xdebug.so”

To a module loading file you create:

/etc/php5/apache2/conf.d/xdebug.ini

Who wants eight mile long php.ini files, anyway? *cough*

Filed under: how-to, , ,

On ++var and var++

I’ve seen people state that ++var and var++ are the same thing in several places, and even heard statements that one should be used over the other for speed reasons. While it won’t usually cause problems, it’s handy to know that ++var and var++ mean two different things. One is a pre-increment and one is a post-increment. For example, in PHP:

$a = 0;
echo ++$a * 3;

//prints 3

$a = 0;
echo $a++ * 3;

//prints 0

This is because in the first example $a is incremented before it is multiplied by three and in the second $a is multiplied by three and then incremented. The same principal applies in C++ and Java and likely most languages that support this format.

Filed under: how-to, ,

Hacking URLs for N00bs

Hacking URLs is something I do on a fairly regular basis, and find of use. It’s also something not everyone knows about, so I thought to share a few pointers. You should keep in mind that hacking URLs is not always a benign activity, and while I won’t be covering that side of things it’s almost inevitable that, at some point, you’ll end up seeing something you weren’t supposed to see. In my opinion, this is bad design, and should not reflect badly upon the hacking of URLs.

Most sites have a logical order to things – if you watch the URLs as you’re clicking you’ll notice things like /page/1 and then /page/2 and then /page/3 – any guess what the next in the series will be? So if you want to jump to page 50 and there is no option, you can ‘hack’ the url to /page/50 and if it exists and you’re allowed to access it, you’ll be taken there. This can be used in forums and search results as well, where something like ?startat=50 can be manipulated. Once you start watching URLs you’ll start to notice patterns and be able to use them to shortcut to what you want and speed things up. For example, I use google.com/search?q=some+terms quite often when posting about running a specific search on Google.

You can, of course, subtract things from the URL as well as add and modify. If you’re on http://example.com/page/50 and there is no link to the main page, removing /page/50 will probably take you there. Removing the /50 part may take you to an index of pages – but not all sites are so logical. The site icanhascheezburger.com (lolcats), which I’m using for an example here, has a /page/50 but no /page/ …er, page. It is my firm belief that all sites should try for hackable URLs. I’m using lolcats as an example because there are pages on the site you can’t get to without hacking the URL. For example, on occasion there are suddenly no ‘next page’ links, even though there are further pages.

Problems arise when /page/50 is not meant to be seen and the only method of keeping people from seeing it is simply not showing the URL on any of the other pages. LiveJournal used to be like this many years ago, and you were able to view ‘private’ entries by typing in something like /page/50 manually. It was also fairly obvious – if you noticed there was a /page/49 and the next visible entry was /page/51 it didn’t take much to realize there was a ‘missing’ entry between. No, I never did this, but someone else read my entries and told me about it (I didn’t mind, but it’s something to keep in mind).

In a similar fashion, sometimes the root directory /page/ is not meant to be visited. The most common problem here is when there is no index page to show and the server makes one, including all of the files in that directory in a listing, some of which may not be things the site owner wants visible. This is easily fixed by adding an index.html page to the directory, but not all site owners know or remember to do this. Keep this in mind when hacking URLs. You’re in someone else’s house, don’t go digging through their drawers.

Filed under: how-to,

US Phone Number Regex

This was annoying, so I figured I’d post it. It’ll match most any normal US number entry style (sorry, international is on the list, but not today).

'/^[1]?\s*-?\s*[\(]?(\d{3})[\)]?\s*-?\s*(\d{3})\s*-?\s*(\d{4})\s*([x|#|ext])?\s*(\d*)$/'

Revolting, eh? Let’s walk through it beginner-style.

^ – starts with
[1]? – possibly a one
\s* – followed by any number of spaces (including none)
-? – possibly followed by a dash
\s* – any number of spaces
[\(]? – possibly followed by a (
(\d{3}) – followed by three digits, the parenthesis mean capture the digits in $matches[]
[\)]? – possibly followed by a )
\s* – any number of spaces
-? – possibly followed by a dash
\s* – any number of spaces
(\d{3}) – three more digits to capture
\s* – any number of spaces
-? – possibly followed by a dash
\s* – any number of spaces
(\d{4}) – four digits to capture now
\s* – any number of spaces
(x|#|ext)? – possibly an extension character, which we capture
\s* – any number of spaces
(\d*)$ – ends with any number of digits, which we capture

So with the expression

preg_match($pattern, $phone_number, $matches);

where $pattern is the regex we walked through, we would end up with:

$matches[0] = everything
$matches[1] = area code
$matches[2] = prefix
$matches[3] = last four digits
$matches[4] = extension character
$matches[5] = extension number

Filed under: how-to, ,

Photostream

IMG014-1.jpg

Morning Walk, Fairbanks AK

Mudderflies

More Photos

Twitter Updates

Recently Bookmarked

Follow

Get every new post delivered to your Inbox.