diff --git a/lib/googleapi.php b/lib/googleapi.php index 51292947113..5994dd382d8 100644 --- a/lib/googleapi.php +++ b/lib/googleapi.php @@ -1,276 +1,62 @@ . + /** - * Moodle - Modular Object-Oriented Dynamic Learning Environment - * http://moodle.org - * Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com + * Simple implementation of some Google API functions for Moodle. * - * 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 2 of the License, or - * (at your option) any later version. + * @package core + * @copyright Dan Poltawski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir.'/filelib.php'); +require_once($CFG->libdir.'/oauthlib.php'); + +/** + * Class for manipulating google documents through the google data api. * - * 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 . + * Docs for this can be found here: + * {@link http://code.google.com/apis/documents/docs/2.0/developers_guide_protocol.html} * * @package core * @subpackage lib * @copyright Dan Poltawski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * - * Simple implementation of some Google API functions for Moodle. - */ - -defined('MOODLE_INTERNAL') || die(); - - /** Include essential file */ -require_once($CFG->libdir.'/filelib.php'); - -/** - * Base class for google authenticated http requests - * - * Most Google API Calls required that requests are sent with an - * Authorization header + token. This class extends the curl class - * to aid this - * - * @package moodlecore - * @subpackage lib - * @copyright Dan Poltawski - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -abstract class google_auth_request extends curl { - protected $token = ''; - private $persistantheaders = array(); - - // Must be overridden with the authorization header name - public static function get_auth_header_name() { - throw new coding_exception('get_auth_header_name() method needs to be overridden in each subclass of google_auth_request'); - } - - protected function request($url, $options = array()){ - if($this->token){ - // Adds authorisation head to a request so that it can be authentcated - $this->setHeader('Authorization: '. $this->get_auth_header_name().'"'.$this->token.'"'); - } - - foreach($this->persistantheaders as $h){ - $this->setHeader($h); - } - - $ret = parent::request($url, $options); - // reset headers for next request - $this->header = array(); - return $ret; - } - - protected function multi($requests, $options = array()) { - if($this->token){ - // Adds authorisation head to a request so that it can be authentcated - $this->setHeader('Authorization: '. $this->get_auth_header_name().'"'.$this->token.'"'); - } - - foreach($this->persistantheaders as $h){ - $this->setHeader($h); - } - - $ret = parent::multi($requests, $options); - // reset headers for next request - $this->header = array(); - return $ret; - } - - public function get_sessiontoken(){ - return $this->token; - } - - public function add_persistant_header($header){ - $this->persistantheaders[] = $header; - } -} - -/******* - * The following two classes are usd to implement AuthSub google - * authtentication, as documented here: - * http://code.google.com/apis/accounts/docs/AuthSub.html - *******/ - -/** - * Used to uprade a google AuthSubRequest one-time token into - * a session token which can be used long term. - * - * @package moodlecore - * @subpackage lib - * @copyright Dan Poltawski - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class google_authsub_request extends google_auth_request { - const AUTHSESSION_URL = 'https://www.google.com/accounts/AuthSubSessionToken'; - - /** - * Constructor. Calls constructor of its parents - * - * @param string $authtoken The token to upgrade to a session token - */ - public function __construct($authtoken){ - parent::__construct(); - $this->token = $authtoken; - } - - /** - * Requests a long-term session token from google based on the - * - * @return string Sub-Auth token - */ - public function get_session_token(){ - $content = $this->get(google_authsub_request::AUTHSESSION_URL); - - if( preg_match('/token=(.*)/i', $content, $matches) ){ - return $matches[1]; - }else{ - throw new moodle_exception('could not upgrade google authtoken to session token'); - } - } - - public static function get_auth_header_name(){ - return 'AuthSub token='; - } -} - -/** - * Allows http calls using google subauth authorisation - * - * @package moodlecore - * @subpackage lib - * @copyright Dan Poltawski - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class google_authsub extends google_auth_request { - const LOGINAUTH_URL = 'https://www.google.com/accounts/AuthSubRequest'; - const VERIFY_TOKEN_URL = 'https://www.google.com/accounts/AuthSubTokenInfo'; - const REVOKE_TOKEN_URL = 'https://www.google.com/accounts/AuthSubRevokeToken'; - - /** - * Constructor, allows subauth requests using the response from an initial - * AuthSubRequest or with the subauth long-term token. Note that constructing - * this object without a valid token will cause an exception to be thrown. - * - * @param string $sessiontoken A long-term subauth session token - * @param string $authtoken A one-time auth token wich is used to upgrade to session token - * @param mixed @options Options to pass to the base curl object - */ - public function __construct($sessiontoken = '', $authtoken = '', $options = array()){ - parent::__construct($options); - - if( $authtoken ){ - $gauth = new google_authsub_request($authtoken); - $sessiontoken = $gauth->get_session_token(); - } - - $this->token = $sessiontoken; - if(! $this->valid_token() ){ - throw new moodle_exception('Invalid subauth token'); - } - } - - /** - * Tests if a subauth token used is valid - * - * @return boolean true if token valid - */ - public function valid_token(){ - $this->get(google_authsub::VERIFY_TOKEN_URL); - - if($this->info['http_code'] === 200){ - return true; - }else{ - return false; - } - } - - /** - * Calls googles api to revoke the subauth token - * - * @return boolean Returns true if token succesfully revoked - */ - public function revoke_session_token(){ - $this->get(google_authsub::REVOKE_TOKEN_URL); - - if($this->info['http_code'] === 200){ - $this->token = ''; - return true; - }else{ - return false; - } - } - - /** - * Creates a login url for subauth request - * - * @param string $returnaddr The address which the user should be redirected to recieve the token - * @param string $realm The google realm which is access is being requested - * @return string URL to bounce the user to - */ - public static function login_url($returnaddr, $realm){ - $uri = google_authsub::LOGINAUTH_URL.'?next=' - .urlencode($returnaddr) - .'&scope=' - .urlencode($realm) - .'&session=1&secure=0'; - - return $uri; - } - - public static function get_auth_header_name(){ - return 'AuthSub token='; - } -} - -/** - * Class for manipulating google documents through the google data api - * Docs for this can be found here: - * {@link http://code.google.com/apis/documents/docs/2.0/developers_guide_protocol.html} - * - * @package moodlecore - * @subpackage lib - * @copyright Dan Poltawski - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class google_docs { - // need both docs and the spreadsheets realm + /** @var string Realm for authentication, need both docs and spreadsheet realm */ const REALM = 'https://docs.google.com/feeds/ https://spreadsheets.google.com/feeds/ https://docs.googleusercontent.com/'; + /** @var string Document list url */ const DOCUMENTFEED_URL = 'https://docs.google.com/feeds/default/private/full'; - const USER_PREF_NAME = 'google_authsub_sesskey'; + /** @var string Upload url */ + const UPLOAD_URL = 'https://docs.google.com/feeds/upload/create-session/default/private/full?convert=false'; - private $google_curl = null; + /** @var google_oauth oauth curl class for making authenticated requests */ + private $googleoauth = null; /** * Constructor. * - * @param object A google_auth_request object which can be used to do http requests + * @param google_oauth $googleoauth oauth curl class for making authenticated requests */ - public function __construct($google_curl){ - if(is_a($google_curl, 'google_auth_request')){ - $this->google_curl = $google_curl; - $this->google_curl->add_persistant_header('GData-Version: 3.0'); - }else{ - throw new moodle_exception('Google Curl Request object not given'); - } - } - - public static function get_sesskey($userid){ - return get_user_preferences(google_docs::USER_PREF_NAME, false, $userid); - } - - public static function set_sesskey($value, $userid){ - return set_user_preference(google_docs::USER_PREF_NAME, $value, $userid); - } - - public static function delete_sesskey($userid){ - return unset_user_preference(google_docs::USER_PREF_NAME, $userid); + public function __construct(google_oauth $googleoauth) { + $this->googleoauth = $googleoauth; + $this->googleoauth->setHeader('GData-Version: 3.0'); } /** @@ -279,21 +65,19 @@ class google_docs { * @param string $search A search string to do full text search on the documents * @return mixed Array of files formated for fileapoi */ - #FIXME - public function get_file_list($search = ''){ + public function get_file_list($search = '') { global $CFG, $OUTPUT; - $url = google_docs::DOCUMENTFEED_URL; + $url = self::DOCUMENTFEED_URL; - if($search){ + if ($search) { $url.='?q='.urlencode($search); } - $content = $this->google_curl->get($url); + $content = $this->googleoauth->get($url); $xml = new SimpleXMLElement($content); - $files = array(); - foreach($xml->entry as $gdoc){ + foreach ($xml->entry as $gdoc) { $docid = (string) $gdoc->children('http://schemas.google.com/g/2005')->resourceId; list($type, $docid) = explode(':', $docid); @@ -325,7 +109,7 @@ class google_docs { break; } - if(!empty($source)){ + if (!empty($source)) { $files[] = array( 'title' => $title, 'url' => "{$gdoc->link[0]->attributes()->href}", 'source' => $source, @@ -344,71 +128,98 @@ class google_docs { * @param object $file File object * @return boolean True on success */ - public function send_file($file){ - $this->google_curl->setHeader("Content-Length: ". $file->get_filesize()); - $this->google_curl->setHeader("Content-Type: ". $file->get_mimetype()); - $this->google_curl->setHeader("Slug: ". $file->get_filename()); + public function send_file($file) { + // First we create the 'resumable upload request'. + $this->googleoauth->setHeader("Content-Length: 0"); + $this->googleoauth->setHeader("X-Upload-Content-Length: ". $file->get_filesize()); + $this->googleoauth->setHeader("X-Upload-Content-Type: ". $file->get_mimetype()); + $this->googleoauth->setHeader("Slug: ". $file->get_filename()); + $this->googleoauth->post(self::UPLOAD_URL); - $this->google_curl->post(google_docs::DOCUMENTFEED_URL, $file->get_content()); + if ($this->googleoauth->info['http_code'] !== 200) { + throw new moodle_exception('Cantpostupload'); + } - if($this->google_curl->info['http_code'] === 201){ + // Now we http PUT the file in the location returned. + $location = $this->googleoauth->response['Location']; + if (empty($location)) { + throw new moodle_exception('Nouploadlocation'); + } + + // Reset the curl object for actually sending the file. + $this->googleoauth->clear_headers(); + $this->googleoauth->setHeader("Content-Length: ". $file->get_filesize()); + $this->googleoauth->setHeader("Content-Type: ". $file->get_mimetype()); + + // We can't get a filepointer, so have to copy the file.. + $tmproot = make_temp_directory('googledocsuploads'); + $tmpfilepath = $tmproot.'/'.$file->get_contenthash(); + $file->copy_content_to($tmpfilepath); + + // HTTP PUT the file. + $this->googleoauth->put($location, array('file'=>$tmpfilepath)); + + // Remove the temporary file we created.. + unlink($tmpfilepath); + + if ($this->googleoauth->info['http_code'] === 201) { return true; - }else{ + } else { return false; } } - public function download_file($url, $fp){ - return $this->google_curl->download(array( array('url'=>$url, 'file' => $fp) )); + /** + * Downloads a file using authentication + * + * @param string $url url of file + * @param string $path path to save file to + * @return array stucture for repository download_file + */ + public function download_file($url, $path) { + $content = $this->googleoauth->get($url); + file_put_contents($path, $content); + return array('path'=>$path, 'url'=>$url); } } /** - * Class for manipulating picasa through the google data api + * Class for manipulating picasa through the google data api. + * * Docs for this can be found here: * {@link http://code.google.com/apis/picasaweb/developers_guide_protocol.html} * - * @package moodlecore - * @subpackage lib + * @package core * @copyright Dan Poltawski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class google_picasa { + /** @var string Realm for authentication */ const REALM = 'http://picasaweb.google.com/data/'; - const USER_PREF_NAME = 'google_authsub_sesskey_picasa'; + /** @var string Upload url */ const UPLOAD_LOCATION = 'https://picasaweb.google.com/data/feed/api/user/default/albumid/default'; + /** @var string photo list url */ const ALBUM_PHOTO_LIST = 'https://picasaweb.google.com/data/feed/api/user/default/albumid/'; + /** @var string search url */ const PHOTO_SEARCH_URL = 'https://picasaweb.google.com/data/feed/api/user/default?kind=photo&q='; + /** @var string album list url */ const LIST_ALBUMS_URL = 'https://picasaweb.google.com/data/feed/api/user/default'; + /** @var string manage files url */ const MANAGE_URL = 'http://picasaweb.google.com/'; - private $google_curl = null; + /** @var google_oauth oauth curl class for making authenticated requests */ + private $googleoauth = null; + /** @var string Last album name retrievied */ private $lastalbumname = null; /** * Constructor. * - * @param object A google_auth_request object which can be used to do http requests + * @param google_oauth $googleoauth oauth curl class for making authenticated requests */ - public function __construct($google_curl){ - if(is_a($google_curl, 'google_auth_request')){ - $this->google_curl = $google_curl; - $this->google_curl->add_persistant_header('GData-Version: 2'); - }else{ - throw new moodle_exception('Google Curl Request object not given'); - } - } - - public static function get_sesskey($userid){ - return get_user_preferences(google_picasa::USER_PREF_NAME, false, $userid); - } - - public static function set_sesskey($value, $userid){ - return set_user_preference(google_picasa::USER_PREF_NAME, $value, $userid); - } - - public static function delete_sesskey($userid){ - return unset_user_preference(google_picasa::USER_PREF_NAME, $userid); + public function __construct(google_oauth $googleoauth) { + $this->googleoauth = $googleoauth; + $this->googleoauth->setHeader('GData-Version: 2'); } /** @@ -417,16 +228,16 @@ class google_picasa { * @param object $file File object * @return boolean True on success */ - public function send_file($file){ - $this->google_curl->setHeader("Content-Length: ". $file->get_filesize()); - $this->google_curl->setHeader("Content-Type: ". $file->get_mimetype()); - $this->google_curl->setHeader("Slug: ". $file->get_filename()); + public function send_file($file) { + $this->googleoauth->setHeader("Content-Length: ". $file->get_filesize()); + $this->googleoauth->setHeader("Content-Type: ". $file->get_mimetype()); + $this->googleoauth->setHeader("Slug: ". $file->get_filename()); - $this->google_curl->post(google_picasa::UPLOAD_LOCATION, $file->get_content()); + $this->googleoauth->post(self::UPLOAD_LOCATION, $file->get_content()); - if($this->google_curl->info['http_code'] === 201){ + if ($this->googleoauth->info['http_code'] === 201) { return true; - }else{ + } else { return false; } } @@ -439,10 +250,10 @@ class google_picasa { * @param string $path The path to files (assumed to be albumid) * @return mixed $files A list of files for the file picker */ - public function get_file_list($path = ''){ - if(!$path){ + public function get_file_list($path = '') { + if (!$path) { return $this->get_albums(); - }else{ + } else { return $this->get_album_photos($path); } } @@ -453,8 +264,8 @@ class google_picasa { * @param int $albumid Photo album to list photos from * @return mixed $files A list of files for the file picker */ - public function get_album_photos($albumid){ - $albumcontent = $this->google_curl->get(google_picasa::ALBUM_PHOTO_LIST.$albumid); + public function get_album_photos($albumid) { + $albumcontent = $this->googleoauth->get(self::ALBUM_PHOTO_LIST.$albumid); return $this->get_photo_details($albumcontent); } @@ -475,8 +286,8 @@ class google_picasa { * @param string $query Search terms * @return mixed $files A list of files for the file picker */ - public function do_photo_search($query){ - $content = $this->google_curl->get(google_picasa::PHOTO_SEARCH_URL.htmlentities($query)); + public function do_photo_search($query) { + $content = $this->googleoauth->get(self::PHOTO_SEARCH_URL.htmlentities($query)); return $this->get_photo_details($content); } @@ -487,17 +298,17 @@ class google_picasa { * * @return mixes $files Array in the format get_listing uses for folders */ - public function get_albums(){ - $content = $this->google_curl->get(google_picasa::LIST_ALBUMS_URL); + public function get_albums() { + $content = $this->googleoauth->get(self::LIST_ALBUMS_URL); $xml = new SimpleXMLElement($content); $files = array(); - foreach($xml->entry as $album){ + foreach ($xml->entry as $album) { $gphoto = $album->children('http://schemas.google.com/photos/2007'); $mediainfo = $album->children('http://search.yahoo.com/mrss/'); - //hacky... + // Hacky... $thumbnailinfo = $mediainfo->group->thumbnail[0]->attributes(); $files[] = array( 'title' => (string) $album->title, @@ -505,7 +316,7 @@ class google_picasa { 'size' => (int) $gphoto->bytesUsed, 'path' => (string) $gphoto->id, 'thumbnail' => (string) $thumbnailinfo['url'], - 'thumbnail_width' => 160, // 160 is the native maximum dimension + 'thumbnail_width' => 160, // 160 is the native maximum dimension. 'thumbnail_height' => 160, 'children' => array(), ); @@ -522,22 +333,22 @@ class google_picasa { * @param string $rawxml XML from picasa api * @return mixed $files A list of files for the file picker */ - public function get_photo_details($rawxml){ + public function get_photo_details($rawxml) { $xml = new SimpleXMLElement($rawxml); $this->lastalbumname = (string)$xml->title; $files = array(); - foreach($xml->entry as $photo){ + foreach ($xml->entry as $photo) { $gphoto = $photo->children('http://schemas.google.com/photos/2007'); $mediainfo = $photo->children('http://search.yahoo.com/mrss/'); $fullinfo = $mediainfo->group->content->attributes(); - //hacky... + // Hacky... $thumbnailinfo = $mediainfo->group->thumbnail[0]->attributes(); - // Derive the nicest file name we can + // Derive the nicest file name we can. if (!empty($mediainfo->group->description)) { $title = shorten_text((string)$mediainfo->group->description, 20, false, ''); $title = clean_filename($title).'.jpg'; @@ -551,7 +362,7 @@ class google_picasa { 'size' => (int) $gphoto->size, 'path' => $gphoto->albumid.'/'.$gphoto->id, 'thumbnail' => (string) $thumbnailinfo['url'], - 'thumbnail_width' => 72, // 72 is the native maximum dimension + 'thumbnail_width' => 72, // 72 is the native maximum dimension. 'thumbnail_height' => 72, 'source' => (string) $fullinfo['url'], 'url' => (string) $fullinfo['url'] @@ -560,54 +371,36 @@ class google_picasa { return $files; } - } /** - * Beginings of an implementation of Clientogin authenticaton for google - * accounts as documented here: - * {@link http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#ClientLogin} + * OAuth 2.0 client for Google Services * - * With this authentication we have to accept a username and password and to post - * it to google. Retrieving a token for use afterwards. - * - * @package moodlecore - * @subpackage lib - * @copyright Dan Poltawski + * @package core + * @copyright 2012 Dan Poltawski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class google_authclient extends google_auth_request { - const LOGIN_URL = 'https://www.google.com/accounts/ClientLogin'; - - public function __construct($sessiontoken = '', $username = '', $password = '', $options = array() ){ - parent::__construct($options); - - if($username and $password){ - $param = array( - 'accountType'=>'GOOGLE', - 'Email'=>$username, - 'Passwd'=>$password, - 'service'=>'writely' - ); - - $content = $this->post(google_authclient::LOGIN_URL, $param); - - if( preg_match('/auth=(.*)/i', $content, $matches) ){ - $sessiontoken = $matches[1]; - }else{ - throw new moodle_exception('could not upgrade authtoken'); - } - - } - - if($sessiontoken){ - $this->token = $sessiontoken; - }else{ - throw new moodle_exception('no session token specified'); - } +class google_oauth extends oauth2_client { + /** + * Returns the auth url for OAuth 2.0 request + * @return string the auth url + */ + protected function auth_url() { + return 'https://accounts.google.com/o/oauth2/auth'; } - public static function get_auth_header_name(){ - return 'GoogleLogin auth='; + /** + * Returns the token url for OAuth 2.0 request + * @return string the auth url + */ + protected function token_url() { + return 'https://accounts.google.com/o/oauth2/token'; + } + + /** + * Clear any headers in the curl object + */ + public function clear_headers() { + $this->header = array(); } } diff --git a/portfolio/googledocs/lang/en/portfolio_googledocs.php b/portfolio/googledocs/lang/en/portfolio_googledocs.php index f1c9262f823..0fb864dac40 100644 --- a/portfolio/googledocs/lang/en/portfolio_googledocs.php +++ b/portfolio/googledocs/lang/en/portfolio_googledocs.php @@ -1,5 +1,4 @@ To use the google docs portfolio you must be registered with Google. Instructions for registing your installation with Google are described in Moodle Docs. The redirect url should be set to:

{$a->callbackurl}

'; $string['noauthtoken'] = 'An authentication token has not been recieved from google. Please ensure you are allowing moodle to access your google account'; $string['nosessiontoken'] = 'A session token does not exist preventing export to google.'; $string['pluginname'] = 'Google Docs'; $string['sendfailed'] = 'The file {$a} failed to transfer to google'; +$string['secret'] = 'Secret'; diff --git a/portfolio/googledocs/lib.php b/portfolio/googledocs/lib.php index 2223f9d4831..c641f096df1 100644 --- a/portfolio/googledocs/lib.php +++ b/portfolio/googledocs/lib.php @@ -1,4 +1,19 @@ . + /** * Google Documents Portfolio Plugin * @@ -9,18 +24,10 @@ require_once($CFG->libdir.'/portfolio/plugin.php'); require_once($CFG->libdir.'/googleapi.php'); class portfolio_plugin_googledocs extends portfolio_plugin_push_base { - private $sessiontoken; + private $googleoauth = null; public function supported_formats() { - return array( - PORTFOLIO_FORMAT_PLAINHTML, - PORTFOLIO_FORMAT_IMAGE, - PORTFOLIO_FORMAT_TEXT, - PORTFOLIO_FORMAT_PDF, - PORTFOLIO_FORMAT_DOCUMENT, - PORTFOLIO_FORMAT_PRESENTATION, - PORTFOLIO_FORMAT_SPREADSHEET - ); + return array(PORTFOLIO_FORMAT_FILE); } public static function get_name() { @@ -28,29 +35,27 @@ class portfolio_plugin_googledocs extends portfolio_plugin_push_base { } public function prepare_package() { - // we send the files as they are, no prep required + // We send the files as they are, no prep required. return true; } - public function get_interactive_continue_url(){ + public function get_interactive_continue_url() { return 'http://docs.google.com/'; } public function expected_time($callertime) { - // we trust what the portfolio says + // We trust what the portfolio says. return $callertime; } public function send_package() { - - if(!$this->sessiontoken){ - throw new portfolio_plugin_exception('nosessiontoken', 'portfolio_googledocs'); + if (!$this->googleoauth) { + throw new portfolio_plugin_exception('noauthtoken', 'portfolio_googledocs'); } - $gdocs = new google_docs(new google_authsub($this->sessiontoken)); - + $gdocs = new google_docs($this->googleoauth); foreach ($this->exporter->get_tempfiles() as $file) { - if(!$gdocs->send_file($file)){ + if (!$gdocs->send_file($file)) { throw new portfolio_plugin_exception('sendfailed', 'portfolio_gdocs', $file->get_filename()); } } @@ -62,20 +67,12 @@ class portfolio_plugin_googledocs extends portfolio_plugin_push_base { return false; } - $sesskey = google_docs::get_sesskey($this->get('user')->id); - - if($sesskey){ - try{ - $gauth = new google_authsub($sesskey); - $this->sessiontoken = $sesskey; - return false; - }catch(Exception $e){ - // sesskey is not valid, delete store and re-auth - google_docs::delete_sesskey($this->get('user')->id); - } + $this->initialize_oauth(); + if ($this->googleoauth->is_logged_in()) { + return false; + } else { + return $this->googleoauth->get_login_url(); } - - return google_authsub::login_url($CFG->wwwroot.'/portfolio/add.php?postcontrol=1&id=' . $this->exporter->get('id') . '&sesskey=' . sesskey(), google_docs::REALM); } public function post_control($stage, $params) { @@ -83,43 +80,50 @@ class portfolio_plugin_googledocs extends portfolio_plugin_push_base { return; } - if(!array_key_exists('token', $params)){ - throw new portfolio_plugin_exception('noauthtoken', 'portfolio_googledocs'); + $this->initialize_oauth(); + if ($this->googleoauth->is_logged_in()) { + return false; + } else { + return $this->googleoauth->get_login_url(); } - - // we now have our auth token, get a session token.. - $gauth = new google_authsub(false, $params['token']); - $this->sessiontoken = $gauth->get_sessiontoken(); - - google_docs::set_sesskey($this->sessiontoken, $this->get('user')->id); } public static function allows_multiple_instances() { return false; } -} -/** - * Registers to the user_deleted event to revoke any - * subauth tokens we have from them - * - * @param $user user object - * @return boolean true in all cases as its only minor cleanup - */ -function portfolio_googledocs_user_deleted($user){ - // it is only by luck that the user prefstill exists now? - // We probably need a pre-delete event? - if($sesskey = google_docs::get_sesskey($user->id)){ - try{ - $gauth = new google_authsub($sesskey); - - $gauth->revoke_session_token(); - }catch(Exception $e){ - // we don't care that much about success- just being good - // google api citzens - return true; - } + public static function has_admin_config() { + return true; } - return true; + public static function get_allowed_config() { + return array('clientid', 'secret'); + } + + public function admin_config_form(&$mform) { + $a = new stdClass; + $a->docsurl = get_docs_url('Google_OAuth2_Setup'); + $a->callbackurl = google_oauth::callback_url()->out(false); + + $mform->addElement('static', null, '', get_string('oauthinfo', 'portfolio_googledocs', $a)); + + $mform->addElement('text', 'clientid', get_string('clientid', 'portfolio_googledocs')); + $mform->addElement('text', 'secret', get_string('secret', 'portfolio_googledocs')); + + $strrequired = get_string('required'); + $mform->addRule('clientid', $strrequired, 'required', null, 'client'); + $mform->addRule('secret', $strrequired, 'required', null, 'client'); + } + + private function initialize_oauth() { + $returnurl = new moodle_url('/portfolio/add.php'); + $returnurl->param('postcontrol', 1); + $returnurl->param('id', $this->exporter->get('id')); + $returnurl->param('sesskey', sesskey()); + + $clientid = $this->get_config('clientid'); + $secret = $this->get_config('secret'); + + $this->googleoauth = new google_oauth($clientid, $secret, $returnurl->out(false), google_docs::REALM); + } } diff --git a/portfolio/googledocs/version.php b/portfolio/googledocs/version.php index 20446664e67..a64c628b0b5 100644 --- a/portfolio/googledocs/version.php +++ b/portfolio/googledocs/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2011112900; // The current plugin version (Date: YYYYMMDDXX) -$plugin->requires = 2011112900; // Requires this Moodle version -$plugin->component = 'portfolio_googledocs'; // Full name of the plugin (used for diagnostics) +$plugin->version = 2012051400; // The current plugin version (Date: YYYYMMDDXX). +$plugin->requires = 2012051100; // Requires this Moodle version. +$plugin->component = 'portfolio_googledocs'; // Full name of the plugin (used for diagnostics). $plugin->cron = 0; diff --git a/portfolio/picasa/lang/en/portfolio_picasa.php b/portfolio/picasa/lang/en/portfolio_picasa.php index fec154b9391..10c12e66152 100644 --- a/portfolio/picasa/lang/en/portfolio_picasa.php +++ b/portfolio/picasa/lang/en/portfolio_picasa.php @@ -1,5 +1,4 @@ To use the Picasa portfolio you must be registered with Google. Instructions for registing your installation with Google are described in Moodle Docs. The redirect url should be set to:

{$a->callbackurl}

'; $string['noauthtoken'] = 'An authentication token has not been recieved from google. Please ensure you are allowing moodle to access your google account'; $string['pluginname'] = 'Picasa'; $string['sendfailed'] = 'The file {$a} failed to transfer to picasa'; +$string['secret'] = 'Secret'; diff --git a/portfolio/picasa/lib.php b/portfolio/picasa/lib.php index a9ba23fd886..6d21336214b 100644 --- a/portfolio/picasa/lib.php +++ b/portfolio/picasa/lib.php @@ -1,4 +1,19 @@ . + /** * Picasa Portfolio Plugin * @@ -9,7 +24,7 @@ require_once($CFG->libdir.'/portfolio/plugin.php'); require_once($CFG->libdir.'/googleapi.php'); class portfolio_plugin_picasa extends portfolio_plugin_push_base { - private $sessionkey; + private $googleoauth = null; public function supported_formats() { return array(PORTFOLIO_FORMAT_IMAGE, PORTFOLIO_FORMAT_VIDEO); @@ -20,11 +35,11 @@ class portfolio_plugin_picasa extends portfolio_plugin_push_base { } public function prepare_package() { - // we send the files as they are, no prep required + // We send the files as they are, no prep required. return true; } - public function get_interactive_continue_url(){ + public function get_interactive_continue_url() { return 'http://picasaweb.google.com/'; } @@ -33,40 +48,31 @@ class portfolio_plugin_picasa extends portfolio_plugin_push_base { } public function send_package() { - if(!$this->sessionkey){ + if (!$this->googleoauth) { throw new portfolio_plugin_exception('noauthtoken', 'portfolio_picasa'); } - $picasa = new google_picasa(new google_authsub($this->sessionkey)); - + $picasa = new google_picasa($this->googleoauth); foreach ($this->exporter->get_tempfiles() as $file) { - if(!$picasa->send_file($file)){ + if (!$picasa->send_file($file)) { throw new portfolio_plugin_exception('sendfailed', 'portfolio_picasa', $file->get_filename()); } } } public function steal_control($stage) { - global $CFG; if ($stage != PORTFOLIO_STAGE_CONFIG) { return false; } - $sesskey = google_picasa::get_sesskey($this->get('user')->id); + $this->initialize_oauth(); - if($sesskey){ - try{ - $gauth = new google_authsub($sesskey); - $this->sessionkey = $sesskey; - return false; - }catch(Exception $e){ - // sesskey is not valid, delete store and re-auth - google_picasa::delete_sesskey($this->get('user')->id); - } + if ($this->googleoauth->is_logged_in()) { + return false; + } else { + return $this->googleoauth->get_login_url(); } - - return google_authsub::login_url($CFG->wwwroot.'/portfolio/add.php?postcontrol=1&id=' . $this->exporter->get('id') . '&sesskey=' . sesskey(), google_picasa::REALM); } public function post_control($stage, $params) { @@ -74,44 +80,50 @@ class portfolio_plugin_picasa extends portfolio_plugin_push_base { return; } - if(!array_key_exists('token', $params)){ - throw new portfolio_plugin_exception('noauthtoken', 'portfolio_picasa'); + $this->initialize_oauth(); + if ($this->googleoauth->is_logged_in()) { + return false; + } else { + return $this->googleoauth->get_login_url(); } + } - // we now have our auth token, get a session token.. - $gauth = new google_authsub(false, $params['token']); - - $this->sessionkey = $gauth->get_sessiontoken(); - - google_picasa::set_sesskey($this->sessionkey, $this->get('user')->id); + public static function has_admin_config() { + return true; } public static function allows_multiple_instances() { return false; } -} -/** - * Registers to the user_deleted event to revoke any - * subauth tokens we have from them - * - * @param $user user object - * @return boolean true in all cases as its only minor cleanup - */ -function portfolio_picasa_user_deleted($user){ - // it is only by luck that the user prefstill exists now? - // We probably need a pre-delete event? - if($sesskey = google_picasa::get_sesskey($user->id)){ - try{ - $gauth = new google_authsub($sesskey); - - $gauth->revoke_session_token(); - }catch(Exception $e){ - // we don't care that much about success- just being good - // google api citzens - return true; - } + public static function get_allowed_config() { + return array('clientid', 'secret'); } - return true; + public function admin_config_form(&$mform) { + $a = new stdClass; + $a->docsurl = get_docs_url('Google_OAuth2_Setup'); + $a->callbackurl = google_oauth::callback_url()->out(false); + + $mform->addElement('static', null, '', get_string('oauthinfo', 'portfolio_picasa', $a)); + + $mform->addElement('text', 'clientid', get_string('clientid', 'portfolio_picasa')); + $mform->addElement('text', 'secret', get_string('secret', 'portfolio_picasa')); + + $strrequired = get_string('required'); + $mform->addRule('clientid', $strrequired, 'required', null, 'client'); + $mform->addRule('secret', $strrequired, 'required', null, 'client'); + } + + private function initialize_oauth() { + $returnurl = new moodle_url('/portfolio/add.php'); + $returnurl->param('postcontrol', 1); + $returnurl->param('id', $this->exporter->get('id')); + $returnurl->param('sesskey', sesskey()); + + $clientid = $this->get_config('clientid'); + $secret = $this->get_config('secret'); + + $this->googleoauth = new google_oauth($clientid, $secret, $returnurl->out(false), google_picasa::REALM); + } } diff --git a/portfolio/picasa/version.php b/portfolio/picasa/version.php index 7906a251af3..113c9cf9e0d 100644 --- a/portfolio/picasa/version.php +++ b/portfolio/picasa/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2011112900; // The current plugin version (Date: YYYYMMDDXX) -$plugin->requires = 2011112900; // Requires this Moodle version -$plugin->component = 'portfolio_picasa'; // Full name of the plugin (used for diagnostics) +$plugin->version = 2012051400; // The current plugin version (Date: YYYYMMDDXX). +$plugin->requires = 2012051100; // Requires this Moodle version. +$plugin->component = 'portfolio_picasa'; // Full name of the plugin (used for diagnostics). $plugin->cron = 0; diff --git a/portfolio/picasa/db/events.php b/repository/googledocs/db/upgrade.php similarity index 58% rename from portfolio/picasa/db/events.php rename to repository/googledocs/db/upgrade.php index 09ef7cca20c..76876a19ba2 100644 --- a/portfolio/picasa/db/events.php +++ b/repository/googledocs/db/upgrade.php @@ -15,19 +15,19 @@ // along with Moodle. If not, see . /** - * Add event handlers for the picasa portfolio. - * - * @package portfolio_picasa - * @category event - * @copyright 2009 Penny Leach - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @param int $oldversion the version we are upgrading from + * @return bool result */ +function xmldb_repository_googledocs_upgrade($oldversion) { + global $CFG, $DB; -$handlers = array ( - 'user_deleted' => array ( - 'handlerfile' => '/portfolio/picasa/lib.php', - 'handlerfunction' => 'portfolio_picasa_user_deleted', - 'schedule' => 'cron', - 'internal' => 0, - ), -); \ No newline at end of file + $dbman = $DB->get_manager(); + + if ($oldversion < 2012051400) { + // Delete old user preferences containing authsub tokens. + $DB->delete_records('user_preferences', array('name' => 'google_authsub_sesskey')); + upgrade_plugin_savepoint(true, 2012051400, 'repository', 'googledocs'); + } + + return true; +} diff --git a/repository/googledocs/lang/en/repository_googledocs.php b/repository/googledocs/lang/en/repository_googledocs.php index 38f1436b27a..8e8cf8034e1 100644 --- a/repository/googledocs/lang/en/repository_googledocs.php +++ b/repository/googledocs/lang/en/repository_googledocs.php @@ -1,5 +1,4 @@ To use the Google Docs repository you must be registered with Google. Instructions for registing your installation with Google are described in Moodle Docs. The redirect url should be set to:

{$a->callbackurl}

'; $string['pluginname'] = 'Google Docs'; -$string['configplugin'] = 'Configurate Google Docs plugin'; +$string['secret'] = 'Secret'; + diff --git a/repository/googledocs/lib.php b/repository/googledocs/lib.php index 36648bdb81e..6ccb33bc436 100644 --- a/repository/googledocs/lib.php +++ b/repository/googledocs/lib.php @@ -34,55 +34,40 @@ require_once($CFG->libdir.'/googleapi.php'); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class repository_googledocs extends repository { - private $subauthtoken = ''; + private $googleoauth = null; public function __construct($repositoryid, $context = SYSCONTEXTID, $options = array()) { - global $USER; parent::__construct($repositoryid, $context, $options); - // TODO: I wish there was somewhere we could explicitly put this outside of constructor.. - $googletoken = optional_param('token', false, PARAM_RAW); - if($googletoken){ - $gauth = new google_authsub(false, $googletoken); // will throw exception if fails - google_docs::set_sesskey($gauth->get_sessiontoken(), $USER->id); - } + $returnurl = new moodle_url('/repository/repository_callback.php', + array('callback' => 'yes', 'repo_id' =>$this->id)); + + $clientid = get_config('googledocs', 'clientid'); + $secret = get_config('googledocs', 'secret'); + $this->googleoauth = new google_oauth($clientid, $secret, $returnurl->out(false), google_docs::REALM); + $this->check_login(); } public function check_login() { - global $USER; - - $sesskey = google_docs::get_sesskey($USER->id); - - if($sesskey){ - try{ - $gauth = new google_authsub($sesskey); - $this->subauthtoken = $sesskey; - return true; - }catch(Exception $e){ - // sesskey is not valid, delete store and re-auth - google_docs::delete_sesskey($USER->id); - } - } - - return false; + return $this->googleoauth->is_logged_in(); } - public function print_login($ajax = true){ - global $CFG; - if($ajax){ - $ret = array(); - $popup_btn = new stdClass(); - $popup_btn->type = 'popup'; - $returnurl = $CFG->wwwroot.'/repository/repository_callback.php?callback=yes&repo_id='.$this->id; - $popup_btn->url = google_authsub::login_url($returnurl, google_docs::REALM); - $ret['login'] = array($popup_btn); - return $ret; + public function print_login() { + $url = $this->googleoauth->get_login_url(); + + if ($this->options['ajax']) { + $popup = new stdClass(); + $popup->type = 'popup'; + $popup->url = $url->out(false); + return array('login' => array($popup)); + } else { + echo ''.get_string('login', 'repository').''; } } public function get_listing($path='', $page = '') { - $gdocs = new google_docs(new google_authsub($this->subauthtoken)); + $gdocs = new google_docs($this->googleoauth); $ret = array(); $ret['dynload'] = true; @@ -91,7 +76,7 @@ class repository_googledocs extends repository { } public function search($search_text, $page = 0) { - $gdocs = new google_docs(new google_authsub($this->subauthtoken)); + $gdocs = new google_docs($this->googleoauth); $ret = array(); $ret['dynload'] = true; @@ -99,37 +84,44 @@ class repository_googledocs extends repository { return $ret; } - public function logout(){ - global $USER; - - $token = google_docs::get_sesskey($USER->id); - - $gauth = new google_authsub($token); - // revoke token from google - $gauth->revoke_session_token(); - - google_docs::delete_sesskey($USER->id); - $this->subauthtoken = ''; - + public function logout() { + $this->googleoauth->log_out(); return parent::logout(); } public function get_file($url, $file = '') { - global $CFG; + $gdocs = new google_docs($this->googleoauth); + $path = $this->prepare_file($file); - - $fp = fopen($path, 'w'); - $gdocs = new google_docs(new google_authsub($this->subauthtoken)); - $gdocs->download_file($url, $fp); - - return array('path'=>$path, 'url'=>$url); + return $gdocs->download_file($url, $path); } public function supported_filetypes() { - return array('document'); + return '*'; } public function supported_returntypes() { return FILE_INTERNAL; } + + public static function get_type_option_names() { + return array('clientid', 'secret', 'pluginname'); + } + + public static function type_config_form($mform, $classname = 'repository') { + + $a = new stdClass; + $a->docsurl = get_docs_url('Google_OAuth2_Setup'); + $a->callbackurl = google_oauth::callback_url()->out(false); + + $mform->addElement('static', null, '', get_string('oauthinfo', 'repository_googledocs', $a)); + + parent::type_config_form($mform); + $mform->addElement('text', 'clientid', get_string('clientid', 'repository_googledocs')); + $mform->addElement('text', 'secret', get_string('secret', 'repository_googledocs')); + + $strrequired = get_string('required'); + $mform->addRule('clientid', $strrequired, 'required', null, 'client'); + $mform->addRule('secret', $strrequired, 'required', null, 'client'); + } } -//Icon from: http://www.iconspedia.com/icon/google-2706.html +// Icon from: http://www.iconspedia.com/icon/google-2706.html. diff --git a/repository/googledocs/version.php b/repository/googledocs/version.php index 79c6db7ff39..7269aa52195 100644 --- a/repository/googledocs/version.php +++ b/repository/googledocs/version.php @@ -25,6 +25,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2011112900; // The current plugin version (Date: YYYYMMDDXX) -$plugin->requires = 2011112900; // Requires this Moodle version -$plugin->component = 'repository_googledocs'; // Full name of the plugin (used for diagnostics) +$plugin->version = 2012051400; // The current plugin version (Date: YYYYMMDDXX). +$plugin->requires = 2012051100; // Requires this Moodle version. +$plugin->component = 'repository_googledocs'; // Full name of the plugin (used for diagnostics). diff --git a/portfolio/googledocs/db/events.php b/repository/picasa/db/upgrade.php similarity index 58% rename from portfolio/googledocs/db/events.php rename to repository/picasa/db/upgrade.php index b0d3d0c702f..545ce3b4fa5 100644 --- a/portfolio/googledocs/db/events.php +++ b/repository/picasa/db/upgrade.php @@ -15,21 +15,19 @@ // along with Moodle. If not, see . /** - * Add event handlers for the googledocs portfolio. - * - * @package portfolio_googledocs - * @category event - * @copyright 2009 Penny Leach - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @param int $oldversion the version we are upgrading from + * @return bool result */ +function xmldb_repository_picasa_upgrade($oldversion) { + global $CFG, $DB; -$handlers = array ( - 'user_deleted' => array ( - 'handlerfile' => '/portfolio/googledocs/lib.php', - 'handlerfunction' => 'portfolio_googledocs_user_deleted', - 'schedule' => 'cron', - 'internal' => 0, - ), -); + $dbman = $DB->get_manager(); + if ($oldversion < 2012051400) { + // Delete old user preferences storing authsub tokens. + $DB->delete_records('user_preferences', array('name' => 'google_authsub_sesskey_picasa')); + upgrade_plugin_savepoint(true, 2012051400, 'repository', 'picasa'); + } + return true; +} diff --git a/repository/picasa/lang/en/repository_picasa.php b/repository/picasa/lang/en/repository_picasa.php index 23d9dbe66bb..3c03022c12b 100644 --- a/repository/picasa/lang/en/repository_picasa.php +++ b/repository/picasa/lang/en/repository_picasa.php @@ -1,5 +1,4 @@ To use the Picasa repository you must be registered with Google. Instructions for registing your installation with Google are described in Moodle Docs. The redirect url should be set to:

{$a->callbackurl}

'; $string['picasa:view'] = 'View picasa repository'; $string['pluginname'] = 'Picasa web album'; -$string['configplugin'] = 'Picasa repository configuration'; +$string['secret'] = 'Secret'; diff --git a/repository/picasa/lib.php b/repository/picasa/lib.php index 6a83ca08238..902238025fa 100644 --- a/repository/picasa/lib.php +++ b/repository/picasa/lib.php @@ -36,58 +36,40 @@ require_once($CFG->libdir.'/googleapi.php'); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class repository_picasa extends repository { - private $subauthtoken = ''; + private $googleoauth = null; public function __construct($repositoryid, $context = SYSCONTEXTID, $options = array()) { - global $USER; parent::__construct($repositoryid, $context, $options); - // TODO: I wish there was somewhere we could explicitly put this outside of constructor.. - $googletoken = optional_param('token', false, PARAM_RAW); - if($googletoken){ - $gauth = new google_authsub(false, $googletoken); // will throw exception if fails - google_picasa::set_sesskey($gauth->get_sessiontoken(), $USER->id); - } + $returnurl = new moodle_url('/repository/repository_callback.php', + array('callback' => 'yes', 'repo_id' =>$this->id)); + + $clientid = get_config('picasa', 'clientid'); + $secret = get_config('picasa', 'secret'); + $this->googleoauth = new google_oauth($clientid, $secret, $returnurl->out(false), google_picasa::REALM); + $this->check_login(); } public function check_login() { - global $USER; - - $sesskey = google_picasa::get_sesskey($USER->id); - - if($sesskey){ - try{ - $gauth = new google_authsub($sesskey); - $this->subauthtoken = $sesskey; - return true; - }catch(Exception $e){ - // sesskey is not valid, delete store and re-auth - google_picasa::delete_sesskey($USER->id); - } - } - - return false; + return $this->googleoauth->is_logged_in(); } - public function print_login(){ - global $CFG; - $returnurl = $CFG->wwwroot.'/repository/repository_callback.php?callback=yes&repo_id='.$this->id; - $authurl = google_authsub::login_url($returnurl, google_picasa::REALM); - if($this->options['ajax']){ - $ret = array(); - $popup_btn = new stdClass(); - $popup_btn->type = 'popup'; - $popup_btn->url = $authurl; - $ret['login'] = array($popup_btn); - return $ret; + public function print_login() { + $url = $this->googleoauth->get_login_url(); + + if ($this->options['ajax']) { + $popup = new stdClass(); + $popup->type = 'popup'; + $popup->url = $url->out(false); + return array('login' => array($popup)); } else { - echo 'Login'; + echo ''.get_string('login', 'repository').''; } } public function get_listing($path='', $page = '') { - $picasa = new google_picasa(new google_authsub($this->subauthtoken)); + $picasa = new google_picasa($this->googleoauth); $ret = array(); $ret['dynload'] = true; @@ -101,7 +83,7 @@ class repository_picasa extends repository { } public function search($search_text, $page = 0) { - $picasa = new google_picasa(new google_authsub($this->subauthtoken)); + $picasa = new google_picasa($this->googleoauth); $ret = array(); $ret['manage'] = google_picasa::MANAGE_URL; @@ -109,22 +91,12 @@ class repository_picasa extends repository { return $ret; } - public function logout(){ - global $USER; - - $token = google_picasa::get_sesskey($USER->id); - - $gauth = new google_authsub($token); - // revoke token from google - $gauth->revoke_session_token(); - - google_picasa::delete_sesskey($USER->id); - $this->subauthtoken = ''; - + public function logout() { + $this->googleoauth->log_out(); return parent::logout(); } - public function get_name(){ + public function get_name() { return get_string('pluginname', 'repository_picasa'); } public function supported_filetypes() { @@ -133,7 +105,27 @@ class repository_picasa extends repository { public function supported_returntypes() { return (FILE_INTERNAL | FILE_EXTERNAL); } + + public static function get_type_option_names() { + return array('clientid', 'secret', 'pluginname'); + } + + public static function type_config_form($mform, $classname = 'repository') { + $a = new stdClass; + $a->docsurl = get_docs_url('Google_OAuth2_Setup'); + $a->callbackurl = google_oauth::callback_url()->out(false); + + $mform->addElement('static', null, '', get_string('oauthinfo', 'repository_picasa', $a)); + + parent::type_config_form($mform); + $mform->addElement('text', 'clientid', get_string('clientid', 'repository_picasa')); + $mform->addElement('text', 'secret', get_string('secret', 'repository_picasa')); + + $strrequired = get_string('required'); + $mform->addRule('clientid', $strrequired, 'required', null, 'client'); + $mform->addRule('secret', $strrequired, 'required', null, 'client'); + } } // Icon for this plugin retrieved from http://www.iconspedia.com/icon/picasa-2711.html -// Where the license is said documented to be Free +// Where the license is said documented to be Free. diff --git a/repository/picasa/version.php b/repository/picasa/version.php index 5ee9d18b00a..471007a4cac 100644 --- a/repository/picasa/version.php +++ b/repository/picasa/version.php @@ -26,6 +26,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2011112900; // The current plugin version (Date: YYYYMMDDXX) -$plugin->requires = 2011112900; // Requires this Moodle version -$plugin->component = 'repository_picasa'; // Full name of the plugin (used for diagnostics) +$plugin->version = 2012051400; // The current plugin version (Date: YYYYMMDDXX). +$plugin->requires = 2012051100; // Requires this Moodle version. +$plugin->component = 'repository_picasa'; // Full name of the plugin (used for diagnostics).