Using the Basecamp PHP Wrapper

It’s been a little while since I’ve published my little wrapper for the Basecamp API. And though I’ve been playing around with it, I haven’t let anyone in on my little ideas. I was Gargamel to this PHP gold.

I feel like more than people wondering why you would want to use the Basecamp API with PHP, it’s more of a question of how to get started.

Well, lets get started. First things first, lets use some Object Oriented magic to make our own library:
require 'basecamp.php';

class Customcamp extends Basecamp {

// our custom functions go here

}
By requiring and then extending the Basecamp class, we can add our own custom functions to the library without having to disturb the precious file. We can use our new ‘Customcamp’ by using it in the constructor:
require 'customcamp.php';

$camp = new Customcamp($user,$pass,$url);
Of course until we start adding functions to ‘Customcamp’, we’re not gaining anything over the original library. So let’s gain something.
class Customcamp extends Basecamp {

	

function load_projects_with_lists() {
$projects = $this->projects();
for ($i=0;$i < count($projects);$i++) {
$lists = $this->lists($projects[$i]->id);
$projects[$i]->lists = (is_array($lists))?$lists:array($lists);
}

return $projects;
}

}


This function is just a simple extension of the basic API calls. It pretty much does what is says – it returns an array of all the projects with every list in that project in an attribute called lists.

First we get an array of all the project objects:
$projects = $this->projects();
Then we iterate over the projects, getting all the lists for each one:
for ($i=0;$i < count($projects);$i++) {
$lists = $this->lists($projects[$i]->id);
The only line thats a little confusing is
$projects[$i]->lists = (is_array($lists))?$lists:array($lists);
All this is doing is making sure we always have an array of lists for each project. If there is only one list in a project, the API will only return the list instead of an array of one list. This quickie is useful for when we want to quickly iterate over the projects and then the lists as arrays of objects.

Well that was fun wasn’t it? Its really easy to add custom functions to the API so you can create quick calls on the Basecamp object itself instead of having to just use its results.

This is just the tip of the iceberg. I have a bunch more useful tidbits, but I’m going to save them for a (very) short while, so I can pull together a little demo.

Note: I know that 37Signals just added support for the time-tracking functions to the API. Unfortunately, my Basecamp account isn’t up to that level, so I can’t test any of that goodness. If anyone who can test that wants to add that functionality to the wrapper, let me know.

10 Responses to “Using the Basecamp PHP Wrapper”

Todd Says: #

Any chance you can publish a quick example for uploading files to basecamp using your class, then attaching it to a message?

Thanks.

Todd Says: #

I just wrote a CURL based function and added it to your class. Feel free to add it to yours. Now I just need to work on the attachements thing…

/*===================/
* Post a file
/*===================*/
// thanks to http://www.basecamphq.com/forum/viewtopic.php?pid=8755

function post_file($file_path) {
$postData = array();

//simulates
$postData[ ‘file_name’ ] = “@$file_path”;
$postData[ ‘submit’ ] = “UPLOAD”;

$ch = curl_init();

$request = $this->base.’/upload’;

error_reporting(E_ALL);

curl_setopt($ch, CURLOPT_URL, $request); // set url to post to
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POST, 1 );
curl_setopt($ch, CURLOPT_HTTPHEADER, array(‘Accept: application/xml’, ‘Content-Type: application/xml’));

//seems no need to tell it enctype=’multipart/data’ it already knows
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData );

// provide credentials if they’re established at the beginning of the script
if(!empty($this->user) && !empty($this->pass)) {
curl_setopt($ch,CURLOPT_USERPWD,$this->user . “:” . $this->pass);
}

// tell cURL to graciously accept an SSL certificate if presented
if(ereg(“^(https)”,$request)) {
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
}

$response_xml = curl_exec( $ch );

$response=$this->unserialize($response_xml);

return $response;
}

Todd Says: #

Added the attachments to the creat_message function:

function create_message($project_id, $message, $notify = false, $attachments = false) {
$request[‘post’] = $message;
if ($notify) {$request[‘notify’] = $notify;}
if ($attachments) {$request[‘attachments’] = $attachments ;}
return $this->hook(“/projects/{$project_id}/msg/create”,”post”,$request);
}

Todd Says: #

Sorry, I guess my upload function does not work. For some reason, the basecamp site does not strip off the formdata stuff. Let me know if there is a better way to do this.

Thanks.

Todd Says: #

Sorry, last time I’ll bother you. Here’s a function that seems to work:

/*===================/
* Post a file
/*===================*/
// thanks to http://www.basecamphq.com/forum/viewtopic.php?pid=8755 and http://playground.jwscripts.com/postdata.phps

function post_file($file_path) {
// read the file in
$fp=fopen($file_path,”r”);
$binary=fread($fp,filesize($file_path));
$filebasename=basename($file_path);
fclose($fp);

$body = array();
$headers = array();

// Parse binary data
// $boundary = uniqid();
// $body[] = “–$boundary\r\n”;
// $body[] = “Content-Disposition: form-data; filename=\”$filebasename\”\r\n”;
// $body[] = “Content-Type: image/jpeg\r\n\r\n”;
$body[] = “$binary”;
// $body[] = “–$boundary–\r\n”;

// Construct the headers
$the_host=str_replace(“https://”,””,str_replace(“http://”,””,$this->base));

// set the port, check for ssl
$the_port=80;
if(strstr($this->base,”https”)) {
$the_port=443;
$the_host=”ssl://”.$the_host;
}

$headers[] = “POST /upload HTTP/1.0\r\n”;
$headers[] = “Host: $the_host\r\n”;
$headers[] = “Authorization: Basic “.base64_encode(“{$this->user}:{$this->pass}”).”\r\n”;
$headers[] = “Accept: application/xml\r\n”;
$headers[] = “Content-Type: application/octet-stream\r\n”;
$headers[] = ‘Content-Length: ‘ . strlen(implode(”, $body)) . “\r\n\r\n”;

// open the connection
if ($fp = fsockopen($the_host, $the_port, $errno, $errmsg, 30)) {
foreach ($headers as $line) {
fwrite($fp, $line);
}
foreach ($body as $line) {
fwrite($fp, $line);
}

$fp_response=””;
while (!feof($fp)) {
$fp_response.=fgets($fp, 128);
}
fclose($fp);

// remove headers
$fp_noheaders=explode(“\r\n\r\n”,$fp_response);
// echo $fp_noheaders[1];

$response=$this->unserialize($fp_noheaders[1]);

return $response;
} else {
return false;
}
}

AQ Says: #

Hey Todd,
Thanks for all the work you put in to this. I’ll deffinately have to try out the attachments sending. In terms of using cURL v. the PEAR classes:
cURL is great becasue it is easy to port to different languages and its built into most PHP distributions. I ended up NOT using it for the primary reason that its not in ALL distributions and if you’re on a shared server, like me, its impossible to get something like that installed if its not there. Since the PEAR classes are just PHP, if neccesary you could just download the required classes and put them in the right directory. To me, thats ultimately more portable.

[…] More fun with the Basecamp PHP Wrapper First The Wrapper, Introduction, Part 1 So jumping right in . . . I’ve been working on a little application using the Basecamp Wrapper, and basically just fooling around with the UI now. Just because I’m dicking around, doesn’t mean you should have to suffer. So here are some nuggets. The idea for the app was that I wanted a big simple todo list that combined all the todos I had from all the different projects in a meaningful and useful order. So first and foremost its collecting all the todos in a single array and then making use of them. In my Customcamp class: […]

Dan Says: #

Hi There,

First off thanks for the great wrapper!

I can successfully log in and create messages using the wrapper, although trying to delete a message using the delete_message function returns a

“You are being redirected” response in html, not even in xml. do you have any idea why this might be happening?

Thanks in advance
Dan

AQ Says: #

@Dan: Maybe its a permissions thing with messages?

Dan Says: #

Hi Aaron, thanks for the response. it turns out that the delete_message function needs to be altered slightly. There needs to be a body to the post or it will return a redirect.

I’ve posted the code changed here: http://forum.37signals.com/basecamp/forums/8/topics/6420?page=1#posts-22139

This

// Delete the message with the given id.
function delete_message($message_id) {
return $this->hook(“/msg/delete/{$message_id}”,”post”);
}

needs to be changed to something like this

// Delete the message with the given id.
function delete_message($message_id) {
$request[‘post’] = “delete”;
return $this->hook(“/msg/delete/{$message_id}”,”post”, $request);
}

Thanks again for the great work!

About

QuirkeyBlog is Aaron Quint's perspective on the ongoing adventure of Code, Life, Work and the Web.

twitter/@aq.

instagram/@quirkey.

github/quirkey.

QuirkeyBlog is proudly powered by WordPress

Categories