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).