diff --git a/lib/pluginlib.php b/lib/pluginlib.php index fd4c32b6819..8635bd309d3 100644 --- a/lib/pluginlib.php +++ b/lib/pluginlib.php @@ -812,7 +812,7 @@ class plugin_manager { 'repository' => array( 'alfresco', 'boxnet', 'coursefiles', 'dropbox', 'equella', 'filesystem', 'flickr', 'flickr_public', 'googledocs', 'local', 'merlot', - 'picasa', 'recent', 's3', 'upload', 'url', 'user', 'webdav', + 'picasa', 'recent', 'skydrive', 's3', 'upload', 'url', 'user', 'webdav', 'wikimedia', 'youtube' ), diff --git a/repository/skydrive/db/access.php b/repository/skydrive/db/access.php new file mode 100644 index 00000000000..6fe7ea25da2 --- /dev/null +++ b/repository/skydrive/db/access.php @@ -0,0 +1,33 @@ +. + +/** + * Capability definitions for skydrive repository + * + * @package repository_skydrive + * @copyright 2012 Lancaster University Network Services Ltd + * @author Dan Poltawski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +$capabilities = array( + 'repository/skydrive:view' => array( + 'captype' => 'read', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array( + 'user' => CAP_ALLOW + ) + ) +); diff --git a/repository/skydrive/db/caches.php b/repository/skydrive/db/caches.php new file mode 100644 index 00000000000..8b61e339ba6 --- /dev/null +++ b/repository/skydrive/db/caches.php @@ -0,0 +1,31 @@ +. + +/** + * Cache definitions. + * + * @package repository_skydrive + * @copyright 2013 Dan Poltawski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$definitions = array( + 'foldername' => array( + 'mode' => cache_store::MODE_SESSION, + ) +); diff --git a/repository/skydrive/lang/en/repository_skydrive.php b/repository/skydrive/lang/en/repository_skydrive.php new file mode 100644 index 00000000000..d3ade24d69d --- /dev/null +++ b/repository/skydrive/lang/en/repository_skydrive.php @@ -0,0 +1,31 @@ +. + +/** + * Language file definitions for skydrive repository + * + * @package repository_skydrive + * @copyright 2012 Lancaster University Network Services Ltd + * @author Dan Poltawski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +$string['cachedef_foldername'] = 'Folder name cache'; +$string['clientid'] = 'Client ID'; +$string['configplugin'] = 'Configure Microsoft Skydrive'; +$string['oauthinfo'] = '

To use this plugin, you must register your site with Microsoft.

As part of the registration process, you will need to enter the following URL as \'Redirect domain\':

{$a->callbackurl}

Once registered, you will be provided with a client ID and secret which can be entered here.

'; +$string['pluginname'] = 'Microsoft Skydrive'; +$string['secret'] = 'Secret'; +$string['skydrive:view'] = 'View Skydrive'; diff --git a/repository/skydrive/lib.php b/repository/skydrive/lib.php new file mode 100644 index 00000000000..50b69333903 --- /dev/null +++ b/repository/skydrive/lib.php @@ -0,0 +1,202 @@ +. + +/** + * Microsoft Live Skydrive Repository Plugin + * + * @package repository_skydrive + * @copyright 2012 Lancaster University Network Services Ltd + * @author Dan Poltawski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once('microsoftliveapi.php'); + +/** + * Microsoft skydrive repository plugin. + * + * @package repository_skydrive + * @copyright 2012 Lancaster University Network Services Ltd + * @author Dan Poltawski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class repository_skydrive extends repository { + /** @var microsoft_skydrive skydrive oauth2 api helper object */ + private $skydrive = null; + + /** + * Constructor + * + * @param int $repositoryid repository instance id. + * @param int|stdClass $context a context id or context object. + * @param array $options repository options. + */ + public function __construct($repositoryid, $context = SYSCONTEXTID, $options = array()) { + parent::__construct($repositoryid, $context, $options); + + $clientid = get_config('skydrive', 'clientid'); + $secret = get_config('skydrive', 'secret'); + $returnurl = new moodle_url('/repository/repository_callback.php'); + $returnurl->param('callback', 'yes'); + $returnurl->param('repo_id', $this->id); + $returnurl->param('sesskey', sesskey()); + + $this->skydrive = new microsoft_skydrive($clientid, $secret, $returnurl); + $this->check_login(); + } + + /** + * Checks whether the user is logged in or not. + * + * @return bool true when logged in + */ + public function check_login() { + return $this->skydrive->is_logged_in(); + } + + /** + * Print the login form, if required + * + * @return array of login options + */ + public function print_login() { + $popup = new stdClass(); + $popup->type = 'popup'; + $url = $this->skydrive->get_login_url(); + $popup->url = $url->out(false); + return array('login' => array($popup)); + } + + /** + * Given a path, and perhaps a search, get a list of files. + * + * See details on {@link http://docs.moodle.org/dev/Repository_plugins} + * + * @param string $path identifier for current path + * @param string $page the page number of file list + * @return array list of files including meta information as specified by parent. + */ + public function get_listing($path='', $page = '') { + $ret = array(); + $ret['dynload'] = true; + $ret['nosearch'] = true; + $ret['manage'] = 'https://skydrive.live.com/'; + $ret['list'] = $this->skydrive->get_file_list($path); + + // Generate path bar, always start with the plugin name. + $ret['path'] = array(); + $ret['path'][] = array('name'=> $this->name, 'path'=>''); + + // Now add each level folder. + $trail = ''; + if (!empty($path)) { + $parts = explode('/', $path); + foreach ($parts as $folderid) { + if (!empty($folderid)) { + $trail .= ('/'.$folderid); + $ret['path'][] = array('name' => $this->skydrive->get_folder_name($folderid), + 'path' => $trail); + } + } + } + + return $ret; + } + + /** + * Downloads a repository file and saves to a path. + * + * @param string $id identifier of file + * @param string $filename to save file as + * @return array with keys: + * path: internal location of the file + * url: URL to the source + */ + public function get_file($id, $filename = '') { + $path = $this->prepare_file($filename); + return $this->skydrive->download_file($id, $path); + } + + /** + * Return names of the options to display in the repository form + * + * @return array of option names + */ + public static function get_type_option_names() { + return array('clientid', 'secret', 'pluginname'); + } + + /** + * Setup repistory form. + * + * @param moodleform $mform Moodle form (passed by reference) + * @param string $classname repository class name + */ + public static function type_config_form($mform, $classname = 'repository') { + $a = new stdClass; + $a->callbackurl = microsoft_skydrive::callback_url()->out(false); + $mform->addElement('static', null, '', get_string('oauthinfo', 'repository_skydrive', $a)); + + parent::type_config_form($mform); + $strrequired = get_string('required'); + $mform->addElement('text', 'clientid', get_string('clientid', 'repository_skydrive')); + $mform->addElement('text', 'secret', get_string('secret', 'repository_skydrive')); + $mform->addRule('clientid', $strrequired, 'required', null, 'client'); + $mform->addRule('secret', $strrequired, 'required', null, 'client'); + $mform->setType('clientid', PARAM_RAW_TRIMMED); + $mform->setType('secret', PARAM_RAW_TRIMMED); + } + + /** + * Logout from repository instance and return + * login form. + * + * @return page to display + */ + public function logout() { + $this->skydrive->log_out(); + return $this->print_login(); + } + + /** + * This repository doesn't support global search. + * + * @return bool if supports global search + */ + public function global_search() { + return false; + } + + /** + * This repoistory supports any filetype. + * + * @return string '*' means this repository support any files + */ + public function supported_filetypes() { + return '*'; + } + + /** + * This repostiory only supports internal files + * + * @return int return type bitmask supported + */ + public function supported_returntypes() { + return FILE_INTERNAL; + } +} diff --git a/repository/skydrive/microsoftliveapi.php b/repository/skydrive/microsoftliveapi.php new file mode 100644 index 00000000000..ef5b563f4df --- /dev/null +++ b/repository/skydrive/microsoftliveapi.php @@ -0,0 +1,243 @@ +. + +/** + * Functions for operating with the skydrive API + * + * @package repository_skydrive + * @copyright 2012 Lancaster University Network Services Ltd + * @author Dan Poltawski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir.'/oauthlib.php'); + +/** + * A helper class to access microsoft live resources using the api. + * + * This uses the microsfot API defined in + * http://msdn.microsoft.com/en-us/library/hh243648.aspx + * + * @package repository_skydrive + * @copyright 2012 Lancaster University Network Services Ltd + * @author Dan Poltawski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class microsoft_skydrive extends oauth2_client { + /** @var string OAuth 2.0 scope */ + const SCOPE = 'wl.skydrive'; + /** @var string Base url to access API */ + const API = 'https://apis.live.net/v5.0'; + /** @var cache_session cache of foldernames */ + var $foldernamecache = null; + + /** + * Construct a skydrive request object + * + * @param string $clientid client id for OAuth 2.0 provided by microsoft + * @param string $clientsecret secret for OAuth 2.0 provided by microsoft + * @param moodle_url $returnurl url to return to after succseful auth + */ + public function __construct($clientid, $clientsecret, $returnurl) { + parent::__construct($clientid, $clientsecret, $returnurl, self::SCOPE); + // Make a session cache + $this->foldernamecache = cache::make('repository_skydrive', 'foldername'); + } + + /** + * Should HTTP GET be used instead of POST? + * + * The Microsoft API does not support POST, so we should use + * GET instead (with the auth_token passed as a GET param). + * + * @return bool true if GET should be used + */ + protected function use_http_get() { + return true; + } + + /** + * Returns the auth url for OAuth 2.0 request + * @return string the auth url + */ + protected function auth_url() { + return 'https://oauth.live.com/authorize'; + } + + /** + * Returns the token url for OAuth 2.0 request + * @return string the auth url + */ + protected function token_url() { + return 'https://oauth.live.com/token'; + } + + /** + * Downloads a file to a file from skydrive using authenticated request + * + * @param string $id id of file + * @param string $path path to save file to + * @return array stucture for repository download_file + */ + public function download_file($id, $path) { + $url = self::API."/${id}/content"; + // Microsoft live redirects to the real download location.. + $this->setopt(array('CURLOPT_FOLLOWLOCATION' => true, 'CURLOPT_MAXREDIRS' => 3)); + $content = $this->get($url); + file_put_contents($path, $content); + return array('path'=>$path, 'url'=>$url); + } + + /** + * Returns a folder name property for a given folderid. + * + * @param string $folderid the folder id which is passed + * @return mixed folder name or false in case of error + */ + public function get_folder_name($folderid) { + if (empty($folderid)) { + throw new coding_exception('Empty folderid passed to get_folder_name'); + } + + // Cache based on oauthtoken and folderid. + $cachekey = $this->folder_cache_key($folderid); + + if ($foldername = $this->foldernamecache->get($cachekey)) { + return $foldername; + } + + $url = self::API."/{$folderid}"; + $ret = json_decode($this->get($url)); + if (isset($ret->error)) { + $this->log_out(); + return false; + } + + $this->foldernamecache->set($cachekey, $ret->name); + return $ret->name; + } + + /** + * Returns a list of files the user has formated for files api + * + * @param string $path the path which we are in + * @return mixed Array of files formated for fileapoi + */ + public function get_file_list($path = '') { + global $OUTPUT; + + $precedingpath = ''; + if (empty($path)) { + $url = self::API."/me/skydrive/files/"; + } else { + $parts = explode('/', $path); + $currentfolder = array_pop($parts); + $url = self::API."/{$currentfolder}/files/"; + } + + $ret = json_decode($this->get($url)); + + if (isset($ret->error)) { + $this->log_out(); + return false; + } + + $files = array(); + + foreach ($ret->data as $file) { + switch($file->type) { + case 'folder': + case 'album': + // Cache the foldername for future requests. + $cachekey = $this->folder_cache_key($file->id); + $this->foldernamecache->set($cachekey, $file->name); + + $files[] = array( + 'title' => $file->name, + 'path' => $path.'/'.$file->id, + 'size' => 0, + 'date' => strtotime($file->updated_time), + 'thumbnail' => $OUTPUT->pix_url(file_folder_icon(90))->out(false), + 'children' => array(), + ); + break; + case 'photo': + $files[] = array( + 'title' => $file->name, + 'size' => $file->size, + 'date' => strtotime($file->updated_time), + 'thumbnail' => $OUTPUT->pix_url(file_extension_icon($file->name, 90))->out(false), + 'realthumbnail' => $file->picture, + 'source' => $file->id, + 'url' => $file->link, + 'image_height' => $file->height, + 'image_width' => $file->width, + 'author' => $file->from->name, + ); + break; + case 'video': + $files[] = array( + 'title' => $file->name, + 'size' => $file->size, + 'date' => strtotime($file->updated_time), + 'thumbnail' => $OUTPUT->pix_url(file_extension_icon($file->name, 90))->out(false), + 'realthumbnail' => $file->picture, + 'source' => $file->id, + 'url' => $file->link, + 'author' => $file->from->name, + ); + break; + case 'audio': + $files[] = array( + 'title' => $file->name, + 'size' => $file->size, + 'date' => strtotime($file->updated_time), + 'thumbnail' => $OUTPUT->pix_url(file_extension_icon($file->name, 90))->out(false), + 'source' => $file->id, + 'url' => $file->link, + 'author' => $file->from->name, + ); + break; + case 'file': + $files[] = array( + 'title' => $file->name, + 'size' => $file->size, + 'date' => strtotime($file->updated_time), + 'thumbnail' => $OUTPUT->pix_url(file_extension_icon($file->name, 90))->out(false), + 'source' => $file->id, + 'url' => $file->link, + 'author' => $file->from->name, + ); + break; + } + } + return $files; + } + + /** + * Returns a key for foldernane cache + * + * @param string $folderid the folder id which is to be cached + * @return string the cache key to use + */ + private function folder_cache_key($folderid) { + // Cache based on oauthtoken and folderid. + return $this->get_tokenname().'_'.$folderid; + } +} diff --git a/repository/skydrive/pix/icon.png b/repository/skydrive/pix/icon.png new file mode 100644 index 00000000000..26b8549d077 Binary files /dev/null and b/repository/skydrive/pix/icon.png differ diff --git a/repository/skydrive/version.php b/repository/skydrive/version.php new file mode 100644 index 00000000000..fdb7213dc9f --- /dev/null +++ b/repository/skydrive/version.php @@ -0,0 +1,30 @@ +. + +/** + * Version details for skydrive repository + * + * @package repository_skydrive + * @copyright 2012 Lancaster University Network Services Ltd + * @author Dan Poltawski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->version = 2013071500; // The current plugin version (Date: YYYYMMDDXX). +$plugin->requires = 2012120300; // Requires this Moodle version. +$plugin->component = 'repository_skydrive'; // Full name of the plugin (used for diagnostics).