Bookmarklet to Send To Do Items to Basecamp

I have been developing a sub-site on an existing WordPress installation. This includes creating templates based on designs from our art department and developing a plugin to display recipes. After I got the design and code working, I began entering all the recipes by hand. Then I passed the test site URL along to my team members to let them do some quality control. I then received an email saying that I had to-do items on Basecamp. Some of the to do items were hard to track down because my coworkers simply put in a title. We have ~50 recipes and so I had to scroll through the recipes to find the offending one.

So I started thinking. What if there were a way to streamline the entering of to-do items and add the URL as well? Originally, I was going to use a page with frames but I couldn’t keep track of the URL after navigating away from the source page. Then I started thinking about the Evernote bookmarklet. It can copy a URL and even an entire web page. So I did a few searches on “bookmarklets” and came across an article by John Resig about using a bookmarklet to insert jQuery and then modify So I took the idea and began to got a basic jQuery dialog box loading into my page through a bookmark. Then I wrote a separate page to pull the relevant info from Basecamp and then populate some dropdown menus.

So let’s take a look at the bookmark. I can write my javascript as I normally would but it will be encapsulated in the href of an anchor tag. You have to use the `document.CreateElement` to get jQuery loaded and then you can use the jQuery syntax. I also learned about the `$.getScript()` function in jQuery. So I could save several lines of code per js include. In the next phase I want to put the loading of files and DOM creation in a separate file to streamline what’s in the bookmark and make it easier to edit.

<!DOCTYPE html>
<!—- bookmark.htm —->   
    <a href="
       javascript:var%20c=document.createElement('link');c.href = '/wp-content/plugins/treu-quality-control/css/ui-lightness/jquery-ui-1.8.17.custom.css';
c.type = 'text/css';
c.rel = 'stylesheet'; = 'screen';

var%20c2=document.createElement('link');c2.href = '/wp-content/plugins/treu-quality-control/css/quality_control.css';
c2.type = 'text/css';
c2.rel = 'stylesheet'; = 'screen';

    s.type = 'text/javascript';


    $.getScript('/wp-content/plugins/treu-quality-control/js/jquery-ui-1.8.17.min.js', function(){

                height: 350,
                width: 400,

                title: 'Create%20To%20Do%20Item%20on%20BaseCamp',
                buttons: {
                        'Submit': function() {
                                        url: $('#qc_url').val(),
                                        comment: $('#qc_comment').val(),
                                        responsible_party: $('#qc_username').val(),
                                        ToDoListId: $('#qc_todolist').val()
                                function(data) {
                                    $( this ).dialog( 'close' );
                            $( this ).dialog( 'close' );
                        Cancel: function() {
                            $( this ).dialog( 'close' );

            var%20input = document.createElement('input');
            input.type = 'hidden';
   = 'wd_url';
            input.value = window.location;

            var%20para = document.createElement('p');

            var%20newtext = document.createTextNode('Add%20your%20To%20Do%20Item');

            var%20input = document.createElement('textarea');
   = 'wd_comment';

            var%20para2 = document.createElement('p');

            var%20newtext = document.createTextNode('Select%20Project:');

            var%20input = document.createElement('select');
   = 'wd_project';

            $.get('/wp-content/plugins/treu-quality-control/ajax.php?action=getProjects', function(data) {

            $('#qc_project').change(function() {
                $.get('/wp-content/plugins/treu-quality-control/ajax.php?action=getProjectToDoLists&projectid='+$('#qc_project').val(), function(data) {

            var%20para3 = document.createElement('p');

            var%20newtext = document.createTextNode('Select%20To%20Do%20List:');

            var%20input = document.createElement('select');
   = 'wd_todolist';

            $.get('/wp-content/plugins/treu-quality-control/ajax.php?action=getProjectToDoLists', function(data) {

            var%20para4 = document.createElement('p');

            var%20newtext = document.createTextNode('Select%20User:');

            var%20input = document.createElement('select');
   = 'wd_username';

            $.get('/wp-content/plugins/treu-quality-control/ajax.php?action=getPersons', function(data) {


I load bookmark.htm and drag the link to my bookmark bar. Now I just need to find a page that is in need of correcting. I click on the bookmark and a dialog will appear. I can enter my to-do item and choose the appropriate project, to-do list and the person responsible for fixing the issue.

This site is in Spanish. Let me submit a to-do item to have it changed to English.

Now let’s go over to Basecamp. As we can see, my to-do item was added to my list and the URL to the page was appended to the item.

My to-do item was added to my list and the URL was added.

I made this to scratch my own itch and I’m hoping that it’s easy enough to use that my coworkers will start using it as well. I also added a settings menu in the administration panel to enter the Basecamp API key, the Basecamp subdomain and the company id.

The admin page where the API key, subdomain and company id can be entered.

As a footnote, as I worked on the functions to pull the XML, I came across an issue using usort on SimpleXML objects. I came across an answer on StackOverflow. The author’s code sample got me on the right track.

I created a generic class and stored all the elements in it. However, after I did this I still couldn’t sort the new array of generic objects. The properties were retaining their simpleXML DNA. After some more research I learned I needed to cast each property as a string. Then I combined the first name and last name into a new property so I could sort the names.

function getPersons()
    /* ... */
    /* Get the XML from Basecamp */
    $people = connectBasecamp($url);

    /* Combine the first and last names into a new full_name property so I can sort them. */
    foreach($people as $key => $r) { 
    // I had problems with dashes in the property names so changed them to underscores when creating my new object and then combine them here
        $people[$key]->full_name = $people[$key]->last_name . ', ' . $people[$key]->first_name ;
    /* Sort using my new function I created */
    usort($people, "sortLastFirst");       
    /* ... */
function connectBasecamp($url=false, $data=false, $args=array()) {
    /* ........ */
    $xml = new SimpleXMLElement($page);
    $i = 0;
    foreach($xml as $key => $r) {
        $array[$i] = new StdClass;
        foreach($r as $k => $col) {
            $k = str_replace('-','_',$k); /* Also learned that PHP doesn't like hypens in property names when you type them out. So just replaced them with underscores. */
            $array[$i]->$k = (string)$col; /* Cast the property to a string and it removes the SimpleXML DNA and now I can sort */
    /* ... */

function sortLastFirst($a, $b) {
    if ($a->full_name == $b->full_name) {
        return 0;
    return ($a->full_name < $b->full_name) ? -1 : 1;

I’m including a zip file with all the files if you would like to install it on one of your WordPress sites. My next idea is to figure out a way to use this concept to generate a “screenshot” that could be uploaded to Basecamp.

Download Quality Control Plugin

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>