[moodle] / moodle / lib / googleapi.php Repository:

Annotation of /moodle/lib/googleapi.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (view) (download)

1 : poltawski 1.6 <?php // $Id: googleapi.php,v 1.5 2009/01/19 06:42:09 moodler Exp $
2 : poltawski 1.1 /**
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 :     */
27 :    
28 :     require_once($CFG->libdir.'/filelib.php');
29 :    
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 = '';
39 :    
40 :     // Must be overriden with the authorization header name
41 :     public abstract static function get_auth_header_name();
42 :    
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 :     }
48 :    
49 :     $ret = parent::request($url, $options);
50 :     // reset headers for next request
51 :     $this->header = array();
52 :     return $ret;
53 :     }
54 :    
55 : poltawski 1.6 protected function multi($requests, $options = array()) {
56 :     if($this->token){
57 :     // Adds authorisation head to a request so that it can be authentcated
58 :     $this->setHeader('Authorization: '. $this->get_auth_header_name().'"'.$this->token.'"');
59 :     }
60 :    
61 :     $ret = parent::multi($requests, $options);
62 :     // reset headers for next request
63 :     $this->header = array();
64 :     return $ret;
65 :     }
66 :    
67 : poltawski 1.1 public function get_sessiontoken(){
68 :     return $this->token;
69 :     }
70 :     }
71 :    
72 :     /*******
73 :     * The following two classes are usd to implement AuthSub google
74 :     * authtentication, as documented here:
75 :     * http://code.google.com/apis/accounts/docs/AuthSub.html
76 :     *******/
77 :    
78 :     /**
79 :     * Used to uprade a google AuthSubRequest one-time token into
80 :     * a session token which can be used long term.
81 :     */
82 :     class google_authsub_request extends google_auth_request {
83 :     const AUTHSESSION_URL = 'https://www.google.com/accounts/AuthSubSessionToken';
84 :    
85 :     /**
86 :     * Constructor. Calls constructor of its parents
87 :     *
88 :     * @param string $authtoken The token to upgrade to a session token
89 :     */
90 :     public function __construct($authtoken){
91 :     parent::__construct();
92 :     $this->token = $authtoken;
93 :     }
94 :    
95 :     /**
96 :     * Requests a long-term session token from google based on the
97 :     *
98 :     * @return string Sub-Auth token
99 :     */
100 :     public function get_session_token(){
101 :     $content = $this->get(google_authsub_request::AUTHSESSION_URL);
102 :    
103 :     if( preg_match('/token=(.*)/i', $content, $matches) ){
104 :     return $matches[1];
105 :     }else{
106 :     throw new moodle_exception('could not upgrade google authtoken to session token');
107 :     }
108 :     }
109 :    
110 :     public static function get_auth_header_name(){
111 :     return 'AuthSub token=';
112 :     }
113 :     }
114 :    
115 :     /**
116 :     * Allows http calls using google subauth authorisation
117 :     */
118 :     class google_authsub extends google_auth_request {
119 :     const LOGINAUTH_URL = 'https://www.google.com/accounts/AuthSubRequest';
120 :     const VERIFY_TOKEN_URL = 'https://www.google.com/accounts/AuthSubTokenInfo';
121 :     const REVOKE_TOKEN_URL = 'https://www.google.com/accounts/AuthSubRevokeToken';
122 :    
123 :     /**
124 :     * Constructor, allows subauth requests using the response from an initial
125 :     * AuthSubRequest or with the subauth long-term token. Note that constructing
126 :     * this object without a valid token will cause an exception to be thrown.
127 :     *
128 :     * @param string $sessiontoken A long-term subauth session token
129 :     * @param string $authtoken A one-time auth token wich is used to upgrade to session token
130 :     * @param mixed @options Options to pass to the base curl object
131 :     */
132 :     public function __construct($sessiontoken = '', $authtoken = '', $options = array()){
133 :     parent::__construct($options);
134 :    
135 :     if( $authtoken ){
136 :     $gauth = new google_authsub_request($authtoken);
137 :     $sessiontoken = $gauth->get_session_token();
138 :     }
139 :    
140 :     $this->token = $sessiontoken;
141 :     if(! $this->valid_token() ){
142 :     throw new moodle_exception('Invalid subauth token');
143 :     }
144 :     }
145 :    
146 :     /**
147 :     * Tests if a subauth token used is valid
148 :     *
149 :     * @return boolean true if token valid
150 :     */
151 :     public function valid_token(){
152 :     $this->get(google_authsub::VERIFY_TOKEN_URL);
153 :    
154 :     if($this->info['http_code'] === 200){
155 :     return true;
156 :     }else{
157 :     return false;
158 :     }
159 :     }
160 :    
161 :     /**
162 :     * Calls googles api to revoke the subauth token
163 :     *
164 :     * @return boolean Returns true if token succesfully revoked
165 :     */
166 :     public function revoke_session_token(){
167 :     $this->get(google_authsub::REVOKE_TOKEN_URL);
168 :    
169 :     if($this->info['http_code'] === 200){
170 :     $this->token = '';
171 :     return true;
172 :     }else{
173 :     return false;
174 :     }
175 :     }
176 :    
177 :     /**
178 :     * Creates a login url for subauth request
179 :     *
180 :     * @param string $returnaddr The address which the user should be redirected to recieve the token
181 :     * @param string $realm The google realm which is access is being requested
182 :     * @return string URL to bounce the user to
183 :     */
184 :     public static function login_url($returnaddr, $realm){
185 :     $uri = google_authsub::LOGINAUTH_URL.'?next='
186 :     .urlencode($returnaddr)
187 :     .'&scope='
188 :     .urlencode($realm)
189 :     .'&session=1&secure=0';
190 :    
191 :     return $uri;
192 :     }
193 :    
194 :     public static function get_auth_header_name(){
195 :     return 'AuthSub token=';
196 :     }
197 :     }
198 :    
199 :     /**
200 :     * Class for manipulating google documents through the google data api
201 :     * Docs for this can be found here:
202 :     * http://code.google.com/apis/documents/docs/2.0/developers_guide_protocol.html
203 :     */
204 :     class google_docs {
205 : poltawski 1.6 // need both docs and the spreadsheets realm
206 :     const REALM = 'http://docs.google.com/feeds/ http://spreadsheets.google.com/feeds/';
207 : poltawski 1.1 const DOCUMENTFEED_URL = 'http://docs.google.com/feeds/documents/private/full';
208 :     const USER_PREF_NAME = 'google_authsub_sesskey';
209 :    
210 :     private $google_curl = null;
211 :    
212 :     /**
213 :     * Constructor.
214 :     *
215 :     * @param object A google_auth_request object which can be used to do http requests
216 :     */
217 :     public function __construct($google_curl){
218 :     if(is_a($google_curl, 'google_auth_request')){
219 :     $this->google_curl = $google_curl;
220 :     }else{
221 :     throw new moodle_exception('Google Curl Request object not given');
222 :     }
223 :     }
224 :    
225 :     public static function get_sesskey($userid){
226 :     return get_user_preferences(google_docs::USER_PREF_NAME, false, $userid);
227 :     }
228 :    
229 :     public static function set_sesskey($value, $userid){
230 :     return set_user_preference(google_docs::USER_PREF_NAME, $value, $userid);
231 :     }
232 :    
233 :     public static function delete_sesskey($userid){
234 :     return unset_user_preference(google_docs::USER_PREF_NAME, $userid);
235 :     }
236 :    
237 :     /**
238 :     * Returns a list of files the user has formated for files api
239 :     *
240 :     * @param string $search A search string to do full text search on the documents
241 :     * @return mixed Array of files formated for fileapoi
242 :     */
243 :     #FIXME
244 :     public function get_file_list($search = ''){
245 : poltawski 1.6 global $CFG;
246 : poltawski 1.1 $url = google_docs::DOCUMENTFEED_URL;
247 :    
248 :     if($search){
249 :     $url.='?q='.urlencode($search);
250 :     }
251 :     $content = $this->google_curl->get($url);
252 :    
253 :     $xml = new SimpleXMLElement($content);
254 :    
255 : poltawski 1.6
256 :    
257 : poltawski 1.1 $files = array();
258 :     foreach($xml->entry as $gdoc){
259 :    
260 : poltawski 1.6 // there doesn't seem to to be cleaner way of getting the id/type
261 :     // than spliting this..
262 :     if (preg_match('/^http:\/\/docs.google.com\/feeds\/documents\/private\/full\/([^%]*)%3A(.*)$/', $gdoc->id, $matches)){
263 :     $docid = $matches[2];
264 :    
265 :     // FIXME: We're making hard-coded choices about format here.
266 :     // If the repo api can support it, we could let the user
267 :     // chose.
268 :     switch($matches[1]){
269 :     case 'document':
270 :     $title = $gdoc->title.'.rtf';
271 :     $source = 'http://docs.google.com/feeds/download/documents/Export?docID='.$docid.'&exportFormat=rtf';
272 :     break;
273 :     case 'presentation':
274 :     $title = $gdoc->title.'.ppt';
275 :     $source = 'http://docs.google.com/feeds/download/presentations/Export?docID='.$docid.'&exportFormat=ppt';
276 :     break;
277 :     case 'spreadsheet':
278 :     $title = $gdoc->title.'.xls';
279 :     $source = 'http://spreadsheets.google.com/feeds/download/spreadsheets/Export?key='.$docid.'&fmcmd=4';
280 :     break;
281 :     }
282 :    
283 :     $files[] = array( 'title' => $title,
284 :     'url' => "{$gdoc->link[0]->attributes()->href}",
285 :     'source' => $source,
286 :     'date' => usertime(strtotime($gdoc->updated)),
287 :     'thumbnail' => $CFG->pixpath.'/f/'.mimeinfo('icon32', $title)
288 :     );
289 :     }
290 : poltawski 1.1 }
291 :    
292 :     return $files;
293 :     }
294 :    
295 :     /**
296 :     * Sends a file object to google documents
297 :     *
298 :     * @param object $file File object
299 :     * @return boolean True on success
300 :     */
301 :     public function send_file($file){
302 :     $this->google_curl->setHeader("Content-Length: ". $file->get_filesize());
303 :     $this->google_curl->setHeader("Content-Type: ". $file->get_mimetype());
304 :     $this->google_curl->setHeader("Slug: ". $file->get_filename());
305 :    
306 :     $this->google_curl->post(google_docs::DOCUMENTFEED_URL, $file->get_content());
307 :    
308 :     if($this->google_curl->info['http_code'] === 201){
309 :     return true;
310 :     }else{
311 :     return false;
312 :     }
313 :     }
314 :    
315 :     public function download_file($url, $fp){
316 :     return $this->google_curl->download(array( array('url'=>$url, 'file' => $fp) ));
317 :     }
318 :     }
319 :    
320 :     /**
321 :     * Class for manipulating picasa through the google data api
322 :     * Docs for this can be found here:
323 :     * http://code.google.com/apis/picasaweb/developers_guide_protocol.html
324 :     */
325 :     class google_picasa {
326 :     const REALM = 'http://picasaweb.google.com/data/';
327 :     const USER_PREF_NAME = 'google_authsub_sesskey_picasa';
328 :     const UPLOAD_LOCATION = 'http://picasaweb.google.com/data/feed/api/user/default/albumid/default';
329 : poltawski 1.2 const ALBUM_PHOTO_LIST = 'http://picasaweb.google.com/data/feed/api/user/default/albumid/';
330 :     const PHOTO_SEARCH_URL = 'http://picasaweb.google.com/data/feed/api/user/default?kind=photo&q=';
331 :     const LIST_ALBUMS_URL = 'http://picasaweb.google.com/data/feed/api/user/default';
332 : poltawski 1.1
333 :     private $google_curl = null;
334 :    
335 :     /**
336 :     * Constructor.
337 :     *
338 :     * @param object A google_auth_request object which can be used to do http requests
339 :     */
340 :     public function __construct($google_curl){
341 :     if(is_a($google_curl, 'google_auth_request')){
342 :     $this->google_curl = $google_curl;
343 :     }else{
344 :     throw new moodle_exception('Google Curl Request object not given');
345 :     }
346 :     }
347 :    
348 :     public static function get_sesskey($userid){
349 :     return get_user_preferences(google_picasa::USER_PREF_NAME, false, $userid);
350 :     }
351 :    
352 :     public static function set_sesskey($value, $userid){
353 :     return set_user_preference(google_picasa::USER_PREF_NAME, $value, $userid);
354 :     }
355 :    
356 :     public static function delete_sesskey($userid){
357 :     return unset_user_preference(google_picasa::USER_PREF_NAME, $userid);
358 :     }
359 :    
360 :     /**
361 :     * Sends a file object to picasaweb
362 :     *
363 :     * @param object $file File object
364 :     * @return boolean True on success
365 :     */
366 :     public function send_file($file){
367 :     $this->google_curl->setHeader("Content-Length: ". $file->get_filesize());
368 :     $this->google_curl->setHeader("Content-Type: ". $file->get_mimetype());
369 :     $this->google_curl->setHeader("Slug: ". $file->get_filename());
370 :    
371 :     $this->google_curl->post(google_picasa::UPLOAD_LOCATION, $file->get_content());
372 :    
373 :     if($this->google_curl->info['http_code'] === 201){
374 :     return true;
375 :     }else{
376 :     return false;
377 :     }
378 :     }
379 : poltawski 1.2
380 :     /**
381 :     * Returns list of photos for file picker.
382 :     * If top level then returns list of albums, otherwise
383 :     * photos within an album.
384 :     *
385 :     * @param string $path The path to files (assumed to be albumid)
386 :     * @return mixed $files A list of files for the file picker
387 :     */
388 :     public function get_file_list($path = ''){
389 :     if(!$path){
390 :     return $this->get_albums();
391 :     }else{
392 :     return $this->get_album_photos($path);
393 :     }
394 :     }
395 :    
396 :     /**
397 :     * Returns list of photos in album specified
398 :     *
399 :     * @param int $albumid Photo album to list photos from
400 :     * @return mixed $files A list of files for the file picker
401 :     */
402 :     public function get_album_photos($albumid){
403 :     $albumcontent = $this->google_curl->get(google_picasa::ALBUM_PHOTO_LIST.$albumid);
404 :    
405 :     return $this->get_photo_details($albumcontent);
406 :     }
407 :    
408 :     /**
409 :     * Does text search on the users photos and returns
410 :     * matches in format for picasa api
411 :     *
412 :     * @param string $query Search terms
413 :     * @return mixed $files A list of files for the file picker
414 :     */
415 :     public function do_photo_search($query){
416 :     $content = $this->google_curl->get(google_picasa::PHOTO_SEARCH_URL.htmlentities($query));
417 :    
418 :     return $this->get_photo_details($content);
419 :     }
420 :    
421 :     /**
422 :     * Gets all the users albums and returns them as a list of folders
423 :     * for the file picker
424 :     *
425 :     * @return mixes $files Array in the format get_listing uses for folders
426 :     */
427 :     public function get_albums(){
428 :     $content = $this->google_curl->get(google_picasa::LIST_ALBUMS_URL);
429 :     $xml = new SimpleXMLElement($content);
430 :    
431 :     $files = array();
432 :    
433 :     foreach($xml->entry as $album){
434 :     $gphoto = $album->children('http://schemas.google.com/photos/2007');
435 :    
436 :     $mediainfo = $album->children('http://search.yahoo.com/mrss/');
437 :     //hacky...
438 :     $thumbnailinfo = $mediainfo->group->thumbnail[0]->attributes();
439 :    
440 :     $files[] = array( 'title' => (string) $gphoto->name,
441 :     'date' => userdate($gphoto->timestamp),
442 :     'size' => (int) $gphoto->bytesUsed,
443 :     'path' => (string) $gphoto->id,
444 :     'thumbnail' => (string) $thumbnailinfo['url'],
445 : moodler 1.3 'thumbnail_width' => 160, // 160 is the native maximum dimension
446 :     'thumbnail_height' => 160,
447 : poltawski 1.2 'children' => array(),
448 :     );
449 :    
450 :     }
451 :    
452 :     return $files;
453 :     }
454 :    
455 :     /**
456 :     * Recieves XML from a picasa list of photos and returns
457 :     * array in format for file picker.
458 :     *
459 :     * @param string $rawxml XML from picasa api
460 :     * @return mixed $files A list of files for the file picker
461 :     */
462 :     public function get_photo_details($rawxml){
463 :    
464 :     $xml = new SimpleXMLElement($rawxml);
465 :    
466 :     $files = array();
467 :    
468 :     foreach($xml->entry as $photo){
469 :     $gphoto = $photo->children('http://schemas.google.com/photos/2007');
470 :    
471 :     $mediainfo = $photo->children('http://search.yahoo.com/mrss/');
472 :     $fullinfo = $mediainfo->group->content->attributes();
473 :     //hacky...
474 :     $thumbnailinfo = $mediainfo->group->thumbnail[0]->attributes();
475 :    
476 : moodler 1.4 // Derive the nicest file name we can
477 :     if (!empty($mediainfo->group->description)) {
478 :     $title = shorten_text((string)$mediainfo->group->description, 20, false, '');
479 :     $title = clean_filename($title).'.jpg';
480 :     } else {
481 :     $title = (string)$mediainfo->group->title;
482 :     }
483 :    
484 :     $files[] = array(
485 :     'title' => $title,
486 : poltawski 1.2 'date' => userdate($gphoto->timestamp),
487 :     'size' => (int) $gphoto->size,
488 :     'path' => $gphoto->albumid.'/'.$gphoto->id,
489 :     'thumbnail' => (string) $thumbnailinfo['url'],
490 : moodler 1.3 'thumbnail_width' => 72, // 72 is the native maximum dimension
491 :     'thumbnail_height' => 72,
492 : moodler 1.5 'source' => (string) $fullinfo['url'],
493 :     'url' => (string) $fullinfo['url']
494 : poltawski 1.2 );
495 :     }
496 :    
497 :     return $files;
498 :     }
499 :    
500 : poltawski 1.1 }
501 :    
502 :     /**
503 :     * Beginings of an implementation of Clientogin authenticaton for google
504 :     * accounts as documented here:
505 :     * http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#ClientLogin
506 :     *
507 :     * With this authentication we have to accept a username and password and to post
508 :     * it to google. Retrieving a token for use afterwards.
509 :     */
510 :     class google_authclient extends google_auth_request {
511 :     const LOGIN_URL = 'https://www.google.com/accounts/ClientLogin';
512 :    
513 :     public function __construct($sessiontoken = '', $username = '', $password = '', $options = array() ){
514 :     parent::__construct($options);
515 :    
516 :     if($username and $password){
517 :     $param = array(
518 :     'accountType'=>'GOOGLE',
519 :     'Email'=>$username,
520 :     'Passwd'=>$password,
521 :     'service'=>'writely'
522 :     );
523 :    
524 :     $content = $this->post(google_authclient::LOGIN_URL, $param);
525 :    
526 :     if( preg_match('/auth=(.*)/i', $content, $matches) ){
527 :     $sessiontoken = $matches[1];
528 :     }else{
529 :     throw new moodle_exception('could not upgrade authtoken');
530 :     }
531 :    
532 :     }
533 :    
534 :     if($sessiontoken){
535 :     $this->token = $sessiontoken;
536 :     }else{
537 :     throw new moodle_exception('no session token specified');
538 :     }
539 :     }
540 :    
541 :     public static function get_auth_header_name(){
542 :     return 'GoogleLogin auth=';
543 :     }
544 :     }
545 :    
546 :     ?>

Moodle CVS Admin
ViewVC Help
Powered by ViewVC 1.0.7