portfolio: MDL-16417 - Adding googledocs/picasa plugins
[moodle.git] / lib / googleapi.php
1 <?php // $Id$
2 /**
3  * Moodle - Modular Object-Oriented Dynamic Learning Environment
4  *          http://moodle.org
5  * Copyright (C) 1999 onwards Martin Dougiamas  http://dougiamas.com
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  * @package    moodle
21  * @subpackage lib
22  * @author     Dan Poltawski <talktodan@gmail.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
24  *
25  * Simple implementation of some Google API functions for Moodle.
26  */
28 require_once($CFG->libdir.'/filelib.php');
30 /**
31  * Base class for google authenticated http requests 
32  * 
33  * Most Google API Calls required that requests are sent with an 
34  * Authorization header + token. This class extends the curl class
35  * to aid this
36  */
37 abstract class google_auth_request extends curl{
38     protected $token = '';
40     // Must be overriden with the authorization header name
41     public abstract static function get_auth_header_name();
43     protected function request($url, $options = array()){
44         if($this->token){
45             // Adds authorisation head to a request so that it can be authentcated
46             $this->setHeader('Authorization: '. $this->get_auth_header_name().'"'.$this->token.'"');
47         }
49         $ret = parent::request($url, $options);
50         // reset headers for next request
51         $this->header = array();
52         return $ret;
53     }
55     public function get_sessiontoken(){
56         return $this->token;
57     }
58 }
60 /*******
61  * The following two classes are usd to implement AuthSub google 
62  * authtentication, as documented here:
63  * http://code.google.com/apis/accounts/docs/AuthSub.html
64  *******/
66 /**
67  * Used to uprade a google AuthSubRequest one-time token into
68  * a session token which can be used long term.
69  */
70 class google_authsub_request extends google_auth_request {
71     const AUTHSESSION_URL = 'https://www.google.com/accounts/AuthSubSessionToken';
73     /**
74      * Constructor. Calls constructor of its parents 
75      *
76      * @param string $authtoken The token to upgrade to a session token
77      */
78     public function __construct($authtoken){
79         parent::__construct();
80         $this->token = $authtoken;
81     }
83     /**
84      * Requests a long-term session token from google based on the 
85      *
86      * @return string Sub-Auth token 
87      */
88     public function get_session_token(){
89         $content = $this->get(google_authsub_request::AUTHSESSION_URL);
91         if( preg_match('/token=(.*)/i', $content, $matches) ){
92             return $matches[1];
93         }else{
94             throw new moodle_exception('could not upgrade google authtoken to session token');
95         }
96     }
98     public static function get_auth_header_name(){
99         return 'AuthSub token=';
100     }
103 /**
104  * Allows http calls using google subauth authorisation
105  */
106 class google_authsub extends google_auth_request {
107     const LOGINAUTH_URL    = 'https://www.google.com/accounts/AuthSubRequest';
108     const VERIFY_TOKEN_URL = 'https://www.google.com/accounts/AuthSubTokenInfo';
109     const REVOKE_TOKEN_URL = 'https://www.google.com/accounts/AuthSubRevokeToken';
111     /**
112      * Constructor, allows subauth requests using the response from an initial 
113      * AuthSubRequest or with the subauth long-term token. Note that constructing
114      * this object without a valid token will cause an exception to be thrown.
115      *
116      * @param string $sessiontoken A long-term subauth session token
117      * @param string $authtoken A one-time auth token wich is used to upgrade to session token
118      * @param mixed  @options Options to pass to the base curl object
119      */
120     public function __construct($sessiontoken = '', $authtoken = '', $options = array()){
121         parent::__construct($options);
123         if( $authtoken ){
124             $gauth = new google_authsub_request($authtoken);
125             $sessiontoken = $gauth->get_session_token();
126         }
128         $this->token = $sessiontoken;
129         if(! $this->valid_token() ){
130             throw new moodle_exception('Invalid subauth token');
131         }
132     }
134     /**
135      * Tests if a subauth token used is valid
136      *
137      * @return boolean true if token valid
138      */
139     public function valid_token(){
140         $this->get(google_authsub::VERIFY_TOKEN_URL);
142         if($this->info['http_code'] === 200){
143             return true;
144         }else{
145             return false;
146         }
147     }
149     /**
150      * Calls googles api to revoke the subauth token
151      *
152      * @return boolean Returns true if token succesfully revoked
153      */
154     public function revoke_session_token(){
155         $this->get(google_authsub::REVOKE_TOKEN_URL);
157         if($this->info['http_code'] === 200){
158             $this->token = '';
159             return true;
160         }else{
161             return false;
162         }
163     }
165     /**
166      * Creates a login url for subauth request
167      *
168      * @param string $returnaddr The address which the user should be redirected to recieve the token
169      * @param string $realm The google realm which is access is being requested 
170      * @return string URL to bounce the user to
171      */
172     public static function login_url($returnaddr, $realm){
173         $uri = google_authsub::LOGINAUTH_URL.'?next='
174             .urlencode($returnaddr)
175             .'&scope='
176             .urlencode($realm)
177             .'&session=1&secure=0';
179         return $uri;
180     }
182     public static function get_auth_header_name(){
183         return 'AuthSub token=';
184     }
187 /**
188  * Class for manipulating google documents through the google data api
189  * Docs for this can be found here:
190  * http://code.google.com/apis/documents/docs/2.0/developers_guide_protocol.html
191  */
192 class google_docs {
193     const REALM            = 'http://docs.google.com/feeds/documents';
194     const DOCUMENTFEED_URL = 'http://docs.google.com/feeds/documents/private/full';
195     const USER_PREF_NAME   = 'google_authsub_sesskey';
197     private $google_curl = null;
199     /**
200      * Constructor.
201      *
202      * @param object A google_auth_request object which can be used to do http requests
203      */
204     public function __construct($google_curl){
205         if(is_a($google_curl, 'google_auth_request')){
206             $this->google_curl = $google_curl;
207         }else{
208             throw new moodle_exception('Google Curl Request object not given');
209         }
210     }
212     public static function get_sesskey($userid){
213         return get_user_preferences(google_docs::USER_PREF_NAME, false, $userid);
214     }
216     public static function set_sesskey($value, $userid){
217         return set_user_preference(google_docs::USER_PREF_NAME, $value, $userid);
218     }
220     public static function delete_sesskey($userid){
221         return unset_user_preference(google_docs::USER_PREF_NAME, $userid);
222     }
224     /**
225      * Returns a list of files the user has formated for files api
226      *
227      * @param string $search A search string to do full text search on the documents
228      * @return mixed Array of files formated for fileapoi
229      */
230     #FIXME
231     public function get_file_list($search = ''){
232         $url = google_docs::DOCUMENTFEED_URL;
234         if($search){
235             $url.='?q='.urlencode($search);
236         }
237         $content = $this->google_curl->get($url);
239         $xml = new SimpleXMLElement($content);
241         $files = array();
242         foreach($xml->entry as $gdoc){
244             $files[] =  array( 'title' => "$gdoc->title",
245                 'url'  =>  "{$gdoc->content['src']}",
246                 'source' => "{$gdoc->content['src']}",
247                 'date'   => usertime(strtotime($gdoc->updated)),
248             );
249         }
251         return $files;
252     }
254     /**
255      * Sends a file object to google documents
256      *
257      * @param object $file File object
258      * @return boolean True on success
259      */
260     public function send_file($file){
261         $this->google_curl->setHeader("Content-Length: ". $file->get_filesize());
262         $this->google_curl->setHeader("Content-Type: ". $file->get_mimetype());
263         $this->google_curl->setHeader("Slug: ". $file->get_filename());
265         $this->google_curl->post(google_docs::DOCUMENTFEED_URL, $file->get_content());
267         if($this->google_curl->info['http_code'] === 201){
268             return true;
269         }else{
270             return false;
271         }
272     }
274     public function download_file($url, $fp){
275         return $this->google_curl->download(array( array('url'=>$url, 'file' => $fp) ));
276     }
279 /**
280  * Class for manipulating picasa through the google data api
281  * Docs for this can be found here:
282  * http://code.google.com/apis/picasaweb/developers_guide_protocol.html
283  */
284 class google_picasa {
285     const REALM             = 'http://picasaweb.google.com/data/';
286     const USER_PREF_NAME    = 'google_authsub_sesskey_picasa';
287     const UPLOAD_LOCATION   = 'http://picasaweb.google.com/data/feed/api/user/default/albumid/default';
289     private $google_curl = null;
291     /**
292      * Constructor.
293      *
294      * @param object A google_auth_request object which can be used to do http requests
295      */
296     public function __construct($google_curl){
297         if(is_a($google_curl, 'google_auth_request')){
298             $this->google_curl = $google_curl;
299         }else{
300             throw new moodle_exception('Google Curl Request object not given');
301         }
302     }
304     public static function get_sesskey($userid){
305         return get_user_preferences(google_picasa::USER_PREF_NAME, false, $userid);
306     }
308     public static function set_sesskey($value, $userid){
309         return set_user_preference(google_picasa::USER_PREF_NAME, $value, $userid);
310     }
312     public static function delete_sesskey($userid){
313         return unset_user_preference(google_picasa::USER_PREF_NAME, $userid);
314     }
316     /**
317      * Sends a file object to picasaweb
318      *
319      * @param object $file File object
320      * @return boolean True on success
321      */
322     public function send_file($file){
323         $this->google_curl->setHeader("Content-Length: ". $file->get_filesize());
324         $this->google_curl->setHeader("Content-Type: ". $file->get_mimetype());
325         $this->google_curl->setHeader("Slug: ". $file->get_filename());
327         $this->google_curl->post(google_picasa::UPLOAD_LOCATION, $file->get_content());
329         if($this->google_curl->info['http_code'] === 201){
330             return true;
331         }else{
332             return false;
333         }
334     }
337 /**
338  * Beginings of an implementation of Clientogin authenticaton for google 
339  * accounts as documented here:
340  * http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#ClientLogin
341  *
342  * With this authentication we have to accept a username and password and to post 
343  * it to google. Retrieving a token for use afterwards.
344  */
345 class google_authclient extends google_auth_request {
346     const LOGIN_URL = 'https://www.google.com/accounts/ClientLogin';
348     public function __construct($sessiontoken = '', $username = '', $password = '', $options = array() ){
349         parent::__construct($options);
351         if($username and $password){
352             $param =  array(
353                 'accountType'=>'GOOGLE',
354                 'Email'=>$username,
355                 'Passwd'=>$password,
356                 'service'=>'writely'
357             );
359             $content = $this->post(google_authclient::LOGIN_URL, $param);
361             if( preg_match('/auth=(.*)/i', $content, $matches) ){
362                 $sessiontoken = $matches[1];
363             }else{
364                 throw new moodle_exception('could not upgrade authtoken');
365             }
367         }
369         if($sessiontoken){
370             $this->token = $sessiontoken;
371         }else{
372             throw new moodle_exception('no session token specified');
373         }
374     }
376     public static function get_auth_header_name(){
377         return 'GoogleLogin auth=';
378     }
381 ?>