Permalink
Browse files

Commiting necessary files

  • Loading branch information...
1 parent 7473447 commit 8d297c2c26fea5b353b8d3230e167919891d3b5e Mike Helmick committed May 29, 2011
Showing with 315 additions and 0 deletions.
  1. +31 −0 README
  2. +73 −0 index.php
  3. +7 −0 src/config.php
  4. +204 −0 src/freshbooks.php
View
31 README
@@ -0,0 +1,31 @@
+/**
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * This is class written to work with Freshbooks OAuth methods. It
+ * authorizes a user and will let you make requests to Freshbooks API
+ * methods with OAuth headers.
+ *
+ * Last Edit: Oct. 1st, 2010
+ * @author Mike Helmick (mikeh@ydekproductions.com)
+ * @version 2.0
+ *
+**/
+
+For information on how to use this class, check out my blog post:
+http://blog.michaelhelmick.com/2010/07/30/freshbooks-api-using-oauth/
+
+Also, if you're using Code Ignitor.. Spicer Matthews converted this lib to work easily in CI:
+http://github.com/cloudmanic/php-freshbooks-codeigniter
View
73 index.php
@@ -0,0 +1,73 @@
+<?php
+session_start();
+require_once('src/config.php');
+require_once('src/freshbooks.php');
+
+if(isset($_SESSION['oauth_token']) && isset($_SESSION['oauth_token_secret']))
+{
+ $c = new Freshbooks(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, NULL, $_SESSION['subdomain'], $_SESSION['oauth_token'], $_SESSION['oauth_token_secret']);
+}
+else if(isset($_GET['oauth_token']) && isset($_GET['oauth_verifier']))
+{
+ $c = new Freshbooks(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, NULL, $_SESSION['subdomain']);
+
+ $access_token = $c->getAccessToken($_GET['oauth_token'], $_GET['oauth_verifier']);
+
+ $_SESSION['oauth_token'] = $access_token['oauth_token'];
+ $_SESSION['oauth_token_secret'] = $access_token['oauth_token_secret'];
+
+ header("Location: index.php");
+}
+else if(isset($_POST['subdomain']))
+{
+ $_SESSION['subdomain'] = $_POST['subdomain'];
+ $c = new Freshbooks(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, OAUTH_CALLBACK, $_SESSION['subdomain']);
+
+ echo '<a href="'.$c->getLoginUrl().'">Login with Freshbooks!</a>';
+}
+
+if(isset($_SESSION['oauth_token']) && isset($_SESSION['oauth_token_secret']))
+{
+ $request = '<?xml version="1.0" encoding="utf-8"?><request method="client.list"><page>1</page><per_page>15</per_page></request>';
+ try {
+ $clients = $c->post($request);
+ }
+ catch(FreshbooksError $e)
+ {
+ $error = $e->getMessage();
+ }
+}
+
+
+?>
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <title>Freshbooks</title>
+</head>
+
+<body>
+ <?php if(isset($_SESSION['oauth_token']) && isset($_SESSION['oauth_token_secret'])): ?>
+
+ <?php if($error): ?>
+ <ul id="error">
+ <li><?php echo $error; ?></li>
+ </ul>
+ <?php endif; ?>
+
+ <?php print_r($clients); ?>
+
+ <?php elseif(!isset($_SESSION['subdomain'])): ?>
+
+ <form action="#" method="POST">
+ http://<input type="text" name="subdomain" />.freshbooks.com
+ </form>
+
+ <?php endif; ?>
+</body>
+</html>
View
7 src/config.php
@@ -0,0 +1,7 @@
+<?php
+
+define('OAUTH_CONSUMER_KEY', 'xxxxxx');
+define('OAUTH_CONSUMER_SECRET', 'aBunchOfRandomLettersAndNumbers');
+define('OAUTH_CALLBACK', 'http://yoursite.com/freshbooks_callback.php');
+
+?>
View
204 src/freshbooks.php
@@ -0,0 +1,204 @@
+<?php
+/**
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * This is class written to work with Freshbooks OAuth methods. It
+ * authorizes a user and will let you make requests to Freshbooks API
+ * methods with OAuth headers.
+ *
+ * Last Edit: Oct. 1st, 2010
+ * @author Mike Helmick (mikeh@ydekproductions.com)
+ * @version 2.0
+ *
+**/
+
+class Freshbooks
+{
+ protected $oauth_consumer_key;
+ protected $oauth_consumer_secret;
+ protected $oauth_callback;
+ protected $oauth_token;
+ protected $oauth_token_secret;
+
+ // $subdomain is the subdomain of the users Freshbooks account
+ // (i.e. http://example.freshbooks.com; "example" being the subdomain).
+ protected $subdomain;
+
+ function __construct($oauth_consumer_key, $oauth_consumer_secret, $oauth_callback = NULL, $subdomain = NULL, $oauth_token = NULL, $oauth_token_secret = NULL)
+ {
+ $this->oauth_consumer_key = $oauth_consumer_key;
+ $this->oauth_consumer_secret = $oauth_consumer_secret;
+
+ // If a subdomain is not supplied, it will be set to the user who owns the application's subdomain,
+ // This would be good if you are only authenticating yourself, and not other users.
+ if($subdomain === NULL)
+ $this->subdomain = $this->oauth_consumer_key;
+ else
+ $this->subdomain = $subdomain;
+
+ if($oauth_callback) $this->oauth_callback = $oauth_callback;
+ if($oauth_token) $this->oauth_token = $oauth_token;
+ if($oauth_token_secret) $this->oauth_token_secret = $oauth_token_secret;
+ }
+
+
+ public function apiUrl() { return 'https://'.$this->subdomain.'.freshbooks.com/api/2.1/xml-in'; }
+ public function accessTokenUrl() { return 'https://'.$this->subdomain.'.freshbooks.com/oauth/oauth_access.php'; }
+ public function authorizeUrl() { return 'https://'.$this->subdomain.'.freshbooks.com/oauth/oauth_authorize.php'; }
+ public function requestTokenUrl() { return 'https://'.$this->subdomain.'.freshbooks.com/oauth/oauth_request.php'; }
+
+
+ public function createNonce($length)
+ {
+ $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
+ $nonce = '';
+ for ($p = 0; $p < $length; $p++) {
+ $nonce .= $characters[mt_rand(0, strlen($characters)-1)];
+ }
+ return $nonce;
+ }
+
+
+ private function urlEncodeParams($params)
+ {
+ $postdata ='';
+ foreach($params as $key => $value)
+ {
+ if(!empty($postdata)) $postdata .= '&';
+ $postdata .= $key.'=';
+ $postdata .= urlencode($value);
+ }
+ return $postdata;
+ }
+
+
+ private function getRequestToken()
+ {
+ $params = array(
+ 'oauth_consumer_key' => $this->oauth_consumer_key,
+ 'oauth_callback' => $this->oauth_callback,
+ 'oauth_signature' => $this->oauth_consumer_secret. '&',
+ 'oauth_signature_method' => 'PLAINTEXT',
+ 'oauth_version' => '1.0',
+ 'oauth_timestamp' => time(),
+ 'oauth_nonce' => $this->createNonce(20),
+ );
+
+ return $this->OAuthRequest($this->requestTokenUrl(), $params);
+ }
+
+
+ public function getLoginUrl()
+ {
+ $token = $this->getRequestToken();
+ $loginUrl = $this->authorizeUrl().'?oauth_token='.$token['oauth_token'];
+ return $loginUrl;
+ }
+
+
+ public function getAccessToken($token, $verifier)
+ {
+ $params = array(
+ 'oauth_consumer_key' => $this->oauth_consumer_key,
+ 'oauth_token' => $token,
+ 'oauth_verifier' => $verifier,
+ 'oauth_signature' => $this->oauth_consumer_secret. '&',
+ 'oauth_signature_method' => 'PLAINTEXT',
+ 'oauth_version' => '1.0',
+ 'oauth_timestamp' => time(),
+ 'oauth_nonce' => $this->createNonce(20),
+ );
+
+ return $this->OAuthRequest($this->accessTokenUrl(), $params);
+ }
+
+
+ private function OAuthRequest($url, $params = array())
+ {
+ // URL encode our params
+ $params = $this->urlEncodeParams($params);
+
+ // send the request to FreshBooks
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_POST, 1);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ $res= curl_exec($ch);
+
+ // parse the request
+ $r = array();
+ parse_str($res, $r);
+
+ return $r;
+ }
+
+
+ private function buildAuthHeader()
+ {
+ $params = array(
+ 'oauth_version' => '1.0',
+ 'oauth_consumer_key' => $this->oauth_consumer_key,
+ 'oauth_token' => $this->oauth_token,
+ 'oauth_timestamp' => time(),
+ 'oauth_nonce' => $this->createNonce(20),
+ 'oauth_signature_method' => 'PLAINTEXT',
+ 'oauth_signature' => $this->oauth_consumer_secret. '&' .$this->oauth_token_secret
+ );
+
+ $auth = 'OAuth realm=""';
+ foreach($params as $kk => $vv)
+ {
+ $auth .= ','.$kk . '="' . urlencode($vv) . '"';
+ }
+
+ return $auth;
+ }
+
+
+ public function post($request)
+ {
+ $headers = array(
+ 'Authorization: '.$this->buildAuthHeader().'',
+ 'Content-Type: application/xml; charset=UTF-8',
+ 'Accept: application/xml; charset=UTF-8',
+ 'User-Agent: My-Freshbooks-App-1.0');
+
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $this->apiUrl());
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_setopt($ch, CURLOPT_POST, 1);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
+
+ $response = curl_exec($ch);
+ curl_close($ch);
+ $response = new SimpleXMLElement($response);
+
+ if($response->attributes()->status == 'ok')
+ return $response;
+ elseif($response->attributes()->status == 'fail' || $response->error)
+ throw new FreshbooksAPIError($response->error);
+ else
+ throw new FreshbooksError('Oops, something went wrong. :(');
+ }
+
+}
+
+class FreshbooksError extends Exception {}
+class FreshbooksAPIError extends FreshbooksError {}

0 comments on commit 8d297c2

Please sign in to comment.