diff --git a/repository/skydrive/classes/access.php b/repository/skydrive/classes/access.php
new file mode 100644
index 00000000000..8c3fd90edc6
--- /dev/null
+++ b/repository/skydrive/classes/access.php
@@ -0,0 +1,56 @@
+.
+
+/**
+ * Class for loading/storing access records from the DB.
+ *
+ * @package core
+ * @copyright 2017 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace repository_skydrive;
+
+defined('MOODLE_INTERNAL') || die();
+
+use core\persistent;
+
+/**
+ * Class for loading/storing issuer from the DB
+ *
+ * @copyright 2017 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class access extends persistent {
+
+ const TABLE = 'repository_skydrive_access';
+
+ /**
+ * Return the definition of the properties of this model.
+ *
+ * @return array
+ */
+ protected static function define_properties() {
+ return array(
+ 'permissionid' => array(
+ 'type' => PARAM_RAW
+ ),
+ 'itemid' => array(
+ 'type' => PARAM_RAW
+ )
+ );
+ }
+
+}
diff --git a/repository/skydrive/classes/remove_temp_access_task.php b/repository/skydrive/classes/remove_temp_access_task.php
new file mode 100644
index 00000000000..98247ec93db
--- /dev/null
+++ b/repository/skydrive/classes/remove_temp_access_task.php
@@ -0,0 +1,79 @@
+.
+
+/**
+ * A scheduled task.
+ *
+ * @package repository_skydrive
+ * @copyright 2017 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace repository_skydrive;
+
+use \core\task\scheduled_task;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Simple task to delete temporary permission records.
+ * @package core
+ * @copyright 2017 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class remove_temp_access_task extends scheduled_task {
+
+ /**
+ * Get a descriptive name for this task (shown to admins).
+ *
+ * @return string
+ */
+ public function get_name() {
+ return get_string('removetempaccesstask', 'repository_skydrive');
+ }
+
+ /**
+ * Do the job.
+ * Throw exceptions on errors (the job will be retried).
+ */
+ public function execute() {
+ $accessrecords = access::get_records();
+ $expires = new DateTime();
+ $expires->sub(new DateInterval("P7D"));
+ $timestamp = $expires->getTimestamp();
+
+ $issuerid = get_config('repository_skydrive', 'issuerid');
+ $issuer = \core\oauth2\api::get_issuer_by_id($issuerid);
+
+ // Add the current user as an OAuth writer.
+ $systemauth = \core\oauth2\api::get_system_oauth_client($issuer);
+
+ if ($systemauth === false) {
+ $details = 'Cannot connect as system user';
+ throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
+ }
+ $systemservice = new repository_skydrive\rest($systemauth);
+
+ foreach ($accessrecords as $access) {
+ if ($access->get('timemodified') < $timestamp) {
+ $params = ['permissionid' => $access->get('permissionid'), 'itemid' => $access->get('itemid')];
+ $systemservice->call('delete_permission', $params);
+ $access->delete();
+ }
+ }
+ }
+
+}
diff --git a/repository/skydrive/classes/rest.php b/repository/skydrive/classes/rest.php
new file mode 100644
index 00000000000..5a35baa0a33
--- /dev/null
+++ b/repository/skydrive/classes/rest.php
@@ -0,0 +1,159 @@
+.
+
+/**
+ * Microsoft Graph API Rest Interface.
+ *
+ * @package repository_skydrive
+ * @copyright 2017 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace repository_skydrive;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Microsoft Graph API Rest Interface.
+ *
+ * @copyright 2017 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class rest extends \core\oauth2\rest {
+
+ /**
+ * Define the functions of the rest API.
+ *
+ * @return array Example:
+ * [ 'listFiles' => [ 'method' => 'get', 'endpoint' => 'http://...', 'args' => [ 'folder' => PARAM_STRING ] ] ]
+ */
+ public function get_api_functions() {
+ return [
+ 'list' => [
+ 'endpoint' => 'https://graph.microsoft.com/v1.0/me/drive/{parent}/children',
+ 'method' => 'get',
+ 'args' => [
+ '$select' => PARAM_RAW,
+ '$expand' => PARAM_RAW,
+ 'parent' => PARAM_RAW,
+ '$skip' => PARAM_INT,
+ '$skipToken' => PARAM_RAW,
+ '$count' => PARAM_INT
+ ],
+ 'response' => 'json'
+ ],
+ 'search' => [
+ 'endpoint' => 'https://graph.microsoft.com/v1.0/me/drive/{parent}/search(q=\'{search}\')',
+ 'method' => 'get',
+ 'args' => [
+ 'search' => PARAM_NOTAGS,
+ '$select' => PARAM_RAW,
+ 'parent' => PARAM_RAW,
+ '$skip' => PARAM_INT,
+ '$skipToken' => PARAM_RAW,
+ '$count' => PARAM_INT
+ ],
+ 'response' => 'json'
+ ],
+ 'get' => [
+ 'endpoint' => 'https://graph.microsoft.com/v1.0/me/drive/items/{fileid}',
+ 'method' => 'get',
+ 'args' => [
+ 'fileid' => PARAM_RAW,
+ '$select' => PARAM_RAW,
+ '$expand' => PARAM_RAW
+ ],
+ 'response' => 'json'
+ ],
+ 'list_permissions' => [
+ 'endpoint' => 'https://graph.microsoft.com/v1.0/me/drive/items/{fileid}/permissions',
+ 'method' => 'get',
+ 'args' => [
+ '$select' => PARAM_RAW,
+ '$expand' => PARAM_RAW,
+ 'fileid' => PARAM_RAW,
+ '$skip' => PARAM_INT,
+ '$skipToken' => PARAM_RAW,
+ '$count' => PARAM_INT
+ ],
+ 'response' => 'json'
+ ],
+ 'create_permission' => [
+ 'endpoint' => 'https://graph.microsoft.com/v1.0/me/drive/items/{fileid}/invite',
+ 'method' => 'post',
+ 'args' => [
+ 'fileid' => PARAM_RAW
+ ],
+ 'response' => 'json'
+ ],
+ 'get_file_by_path' => [
+ 'endpoint' => 'https://graph.microsoft.com/v1.0/me/drive/root:/{fullpath}',
+ 'method' => 'get',
+ 'args' => [
+ 'fullpath' => PARAM_RAW,
+ '$select' => PARAM_RAW
+ ],
+ 'response' => 'json'
+ ],
+ 'create_folder' => [
+ 'endpoint' => 'https://graph.microsoft.com/v1.0/me/drive/items/{parentid}/children',
+ 'method' => 'post',
+ 'args' => [
+ 'parentid' => PARAM_RAW
+ ],
+ 'response' => 'json'
+ ],
+ 'create_link' => [
+ 'endpoint' => 'https://graph.microsoft.com/v1.0/me/drive/items/{fileid}/createLink',
+ 'method' => 'post',
+ 'args' => [
+ 'fileid' => PARAM_RAW
+ ],
+ 'response' => 'json'
+ ],
+ 'get_drive' => [
+ 'endpoint' => 'https://graph.microsoft.com/v1.0/me/drive',
+ 'method' => 'get',
+ 'args' => [],
+ 'response' => 'json'
+ ],
+ 'delete_file_by_path' => [
+ 'endpoint' => 'https://graph.microsoft.com/v1.0/me/drive/root:/{fullpath}',
+ 'method' => 'delete',
+ 'args' => [
+ 'fullpath' => PARAM_RAW,
+ ],
+ 'response' => 'json'
+ ],
+ 'copy_share' => [
+ 'endpoint' => 'https://graph.microsoft.com/v1.0/shares/{sharetoken}/root/copy',
+ 'method' => 'post',
+ 'args' => [
+ 'sharetoken' => PARAM_RAW,
+ ],
+ 'response' => 'json'
+ ],
+ 'delete_permission' => [
+ 'endpoint' => 'https://graph.microsoft.com/v1.0/me/drive/items/{fileid}/permissions/{permissionid}',
+ 'method' => 'delete',
+ 'args' => [
+ 'fileid' => PARAM_RAW,
+ 'permissionid' => PARAM_RAW
+ ],
+ 'response' => 'json'
+ ],
+ ];
+ }
+}
diff --git a/repository/skydrive/db/caches.php b/repository/skydrive/db/caches.php
index 8b61e339ba6..1050c238de2 100644
--- a/repository/skydrive/db/caches.php
+++ b/repository/skydrive/db/caches.php
@@ -25,7 +25,15 @@
defined('MOODLE_INTERNAL') || die();
$definitions = array(
- 'foldername' => array(
- 'mode' => cache_store::MODE_SESSION,
- )
+ // Used to store file ids for folders.
+ // The keys used are full path to the folder, the values are the id in google drive.
+ // The static acceleration size has been based upon the depths of a single path.
+ 'folder' => array(
+ 'mode' => cache_store::MODE_APPLICATION,
+ 'simplekeys' => false,
+ 'simpledata' => true,
+ 'staticacceleration' => true,
+ 'staticaccelerationsize' => 10,
+ 'canuselocalstore' => true
+ ),
);
diff --git a/repository/skydrive/db/install.xml b/repository/skydrive/db/install.xml
new file mode 100644
index 00000000000..f61ae0e88df
--- /dev/null
+++ b/repository/skydrive/db/install.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repository/skydrive/db/tasks.php b/repository/skydrive/db/tasks.php
new file mode 100644
index 00000000000..b55c4ea3bb6
--- /dev/null
+++ b/repository/skydrive/db/tasks.php
@@ -0,0 +1,43 @@
+.
+
+/**
+ * Definition of repository_skydrive scheduled tasks.
+ *
+ * The handlers defined on this file are processed and registered into
+ * the Moodle DB after any install or upgrade operation. All plugins
+ * support this.
+ *
+ * @package repository_skydrive
+ * @copyright 2017 Damyon Wiese
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/* List of handlers */
+
+$tasks = array(
+ array(
+ 'classname' => 'repository_skydrive\remove_temp_access_task',
+ 'blocking' => 0,
+ 'minute' => 'R',
+ 'hour' => 'R',
+ 'day' => '*',
+ 'dayofweek' => 'R',
+ 'month' => '*'
+ ),
+);
diff --git a/repository/skydrive/db/upgrade.php b/repository/skydrive/db/upgrade.php
new file mode 100644
index 00000000000..7a714846fcd
--- /dev/null
+++ b/repository/skydrive/db/upgrade.php
@@ -0,0 +1,54 @@
+.
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * @param int $oldversion the version we are upgrading from
+ * @return bool result
+ */
+function xmldb_repository_skydrive_upgrade($oldversion) {
+ global $DB;
+
+ $dbman = $DB->get_manager();
+
+ if ($oldversion < 2017031400) {
+
+ // Define table repository_skydrive_access to be created.
+ $table = new xmldb_table('repository_skydrive_access');
+
+ // Adding fields to table repository_skydrive_access.
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+ $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('permissionid', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('itemid', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
+
+ // Adding keys to table repository_skydrive_access.
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+ $table->add_key('usermodifiedkey', XMLDB_KEY_FOREIGN, array('usermodified'), 'user', array('id'));
+
+ // Conditionally launch create table for repository_skydrive_access.
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+ // Skydrive savepoint reached.
+ upgrade_plugin_savepoint(true, 2017031400, 'repository', 'skydrive');
+ }
+ return true;
+}
diff --git a/repository/skydrive/lang/en/repository_skydrive.php b/repository/skydrive/lang/en/repository_skydrive.php
index 1ed71f48fc5..015647fa9d1 100644
--- a/repository/skydrive/lang/en/repository_skydrive.php
+++ b/repository/skydrive/lang/en/repository_skydrive.php
@@ -22,10 +22,20 @@
* @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 OneDrive';
-$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['configplugin'] = 'Configure OneDrive plugin';
+$string['skydrive:view'] = 'View OneDrive repository';
$string['pluginname'] = 'Microsoft OneDrive';
-$string['secret'] = 'Secret';
-$string['skydrive:view'] = 'View OneDrive';
+$string['issuer'] = 'OAuth 2 service';
+$string['issuer_help'] = 'Select the OAuth 2 service that is configured to talk to the OneDrive API. If the services does not exist yet, you might need to create it.';
+$string['servicenotenabled'] = 'Access not configured.';
+$string['oauth2serviceslink'] = 'OAuth 2 Services Configuration';
+$string['searchfor'] = 'Search for {$a}';
+$string['internal'] = 'Internal (files stored in Moodle)';
+$string['external'] = 'External (only links stored in Moodle)';
+$string['both'] = 'Internal and External';
+$string['supportedreturntypes'] = 'Supported files';
+$string['defaultreturntype'] = 'Default return type';
+$string['fileoptions'] = 'The types and defaults for returned files is configurable here. Note that all files linked externally will be updated so that the owner is the Moodle system account.';
+$string['owner'] = 'Owned by: {$a}';
+$string['cachedef_folder'] = 'OneDrive File IDs for folders in the system account';
+
diff --git a/repository/skydrive/lib.php b/repository/skydrive/lib.php
index 69c6316454d..41c87dc8f8e 100644
--- a/repository/skydrive/lib.php
+++ b/repository/skydrive/lib.php
@@ -25,8 +25,6 @@
defined('MOODLE_INTERNAL') || die();
-require_once('microsoftliveapi.php');
-
/**
* Microsoft skydrive repository plugin.
*
@@ -36,46 +34,80 @@ require_once('microsoftliveapi.php');
* @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;
+ /**
+ * OAuth 2 client
+ * @var \core\oauth2\client
+ */
+ private $client = null;
/**
- * Constructor
+ * OAuth 2 Issuer
+ * @var \core\oauth2\issuer
+ */
+ private $issuer = null;
+
+ /**
+ * Additional scopes required for drive.
+ */
+ const SCOPES = 'files.readwrite.all';
+
+ /**
+ * Constructor.
*
* @param int $repositoryid repository instance id.
* @param int|stdClass $context a context id or context object.
* @param array $options repository options.
+ * @param int $readonly indicate this repo is readonly or not.
+ * @return void
*/
- public function __construct($repositoryid, $context = SYSCONTEXTID, $options = array()) {
- parent::__construct($repositoryid, $context, $options);
+ public function __construct($repositoryid, $context = SYSCONTEXTID, $options = array(), $readonly = 0) {
+ parent::__construct($repositoryid, $context, $options, $readonly = 0);
- $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();
+ $this->issuer = \core\oauth2\api::get_issuer(get_config('skydrive', 'issuerid'));
}
/**
- * Checks whether the user is logged in or not.
+ * Get a cached user authenticated oauth client.
*
- * @return bool true when logged in
+ * @param moodle_url $overrideurl - Use this url instead of the repo callback.
+ * @return \core\oauth2\client
+ */
+ protected function get_user_oauth_client($overrideurl = false) {
+ if ($this->client) {
+ return $this->client;
+ }
+ if ($overrideurl) {
+ $returnurl = $overrideurl;
+ } else {
+ $returnurl = new moodle_url('/repository/repository_callback.php');
+ $returnurl->param('callback', 'yes');
+ $returnurl->param('repo_id', $this->id);
+ $returnurl->param('sesskey', sesskey());
+ }
+
+ $this->client = \core\oauth2\api::get_user_oauth_client($this->issuer, $returnurl, self::SCOPES);
+
+ return $this->client;
+ }
+
+ /**
+ * Checks whether the user is authenticate or not.
+ *
+ * @return bool true when logged in.
*/
public function check_login() {
- return $this->skydrive->is_logged_in();
+ $client = $this->get_user_oauth_client();
+ return $client->is_logged_in();
}
/**
- * Print the login form, if required
+ * Print or return the login form.
*
- * @return array of login options
+ * @return void|array for ajax.
*/
public function print_login() {
- $url = $this->skydrive->get_login_url();
+ $client = $this->get_user_oauth_client();
+ $url = $client->get_login_url();
if ($this->options['ajax']) {
$popup = new stdClass();
@@ -88,124 +120,879 @@ class repository_skydrive extends repository {
}
/**
- * Given a path, and perhaps a search, get a list of files.
+ * Build the breadcrumb from a path.
*
- * See details on {@link http://docs.moodle.org/dev/Repository_plugins}
+ * @param string $path to create a breadcrumb from.
+ * @return array containing name and path of each crumb.
+ */
+ protected function build_breadcrumb($path) {
+ $bread = explode('/', $path);
+ $crumbtrail = '';
+ foreach ($bread as $crumb) {
+ list($id, $name) = $this->explode_node_path($crumb);
+ $name = empty($name) ? $id : $name;
+ $breadcrumb[] = array(
+ 'name' => $name,
+ 'path' => $this->build_node_path($id, $name, $crumbtrail)
+ );
+ $tmp = end($breadcrumb);
+ $crumbtrail = $tmp['path'];
+ }
+ return $breadcrumb;
+ }
+
+ /**
+ * Generates a safe path to a node.
*
- * @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.
+ * Typically, a node will be id|Name of the node.
+ *
+ * @param string $id of the node.
+ * @param string $name of the node, will be URL encoded.
+ * @param string $root to append the node on, must be a result of this function.
+ * @return string path to the node.
+ */
+ protected function build_node_path($id, $name = '', $root = '') {
+ $path = $id;
+ if (!empty($name)) {
+ $path .= '|' . urlencode($name);
+ }
+ if (!empty($root)) {
+ $path = trim($root, '/') . '/' . $path;
+ }
+ return $path;
+ }
+
+ /**
+ * Returns information about a node in a path.
+ *
+ * @see self::build_node_path()
+ * @param string $node to extrat information from.
+ * @return array about the node.
+ */
+ protected function explode_node_path($node) {
+ if (strpos($node, '|') !== false) {
+ list($id, $name) = explode('|', $node, 2);
+ $name = urldecode($name);
+ } else {
+ $id = $node;
+ $name = '';
+ }
+ $id = urldecode($id);
+ return array(
+ 0 => $id,
+ 1 => $name,
+ 'id' => $id,
+ 'name' => $name
+ );
+ }
+
+ /**
+ * List the files and folders.
+ *
+ * @param string $path path to browse.
+ * @param string $page page to browse.
+ * @return array of result.
*/
public function get_listing($path='', $page = '') {
- $ret = array();
- $ret['dynload'] = true;
- $ret['nosearch'] = true;
- $ret['manage'] = 'https://skydrive.live.com/';
-
- $fileslist = $this->skydrive->get_file_list($path);
- // Filter list for accepted types. Hopefully this will be done by core some day.
- $fileslist = array_filter($fileslist, array($this, 'filter'));
- $ret['list'] = $fileslist;
-
- // 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);
- }
- }
+ if (empty($path)) {
+ $path = $this->build_node_path('root', get_string('pluginname', 'repository_skydrive'));
}
+ // We analyse the path to extract what to browse.
+ $trail = explode('/', $path);
+ $uri = array_pop($trail);
+ list($id, $name) = $this->explode_node_path($uri);
+
+ // Handle the special keyword 'search', which we defined in self::search() so that
+ // we could set up a breadcrumb in the search results. In any other case ID would be
+ // 'root' which is a special keyword, or a parent (folder) ID.
+ if ($id === 'search') {
+ $q = $name;
+ $id = 'root';
+
+ // Append the active path for search.
+ $str = get_string('searchfor', 'repository_skydrive', $searchtext);
+ $path = $this->build_node_path('search', $str, $path);
+ }
+
+ // Query the Drive.
+ $parent = $id;
+ if ($parent != 'root') {
+ $parent = 'items/' . $parent;
+ }
+ $q = '';
+ $results = $this->query($q, $path, $parent);
+
+ $ret = [];
+ $ret['dynload'] = true;
+ $ret['path'] = $this->build_breadcrumb($path);
+ $ret['list'] = $results;
+ $ret['manage'] = 'https://www.office.com/';
return $ret;
}
/**
- * Downloads a repository file and saves to a path.
+ * Search throughout the Google Drive.
*
- * @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
+ * @param string $searchtext text to search for.
+ * @param int $page search page.
+ * @return array of results.
*/
- public function get_file($id, $filename = '') {
- $path = $this->prepare_file($filename);
- return $this->skydrive->download_file($id, $path);
+ public function search($searchtext, $page = 0) {
+ $path = $this->build_node_path('root', get_string('pluginname', 'repository_skydrive'));
+ $str = get_string('searchfor', 'repository_skydrive', $searchtext);
+ $path = $this->build_node_path('search', $str, $path);
+
+ // Query the Drive.
+ $parent = 'root';
+ $results = $this->query($searchtext, $path, 'root');
+
+ $ret = [];
+ $ret['dynload'] = true;
+ $ret['path'] = $this->build_breadcrumb($path);
+ $ret['list'] = $results;
+ $ret['manage'] = 'https://www.office.com/';
+ return $ret;
}
/**
- * Return names of the options to display in the repository form
+ * Query Google Drive for files and folders using a search query.
*
- * @return array of option names
+ * Documentation about the query format can be found here:
+ * https://developers.google.com/drive/search-parameters
+ *
+ * This returns a list of files and folders with their details as they should be
+ * formatted and returned by functions such as get_listing() or search().
+ *
+ * @param string $q search query as expected by the Google API.
+ * @param string $path parent path of the current files, will not be used for the query.
+ * @param int $page page.
+ * @return array of files and folders.
*/
- public static function get_type_option_names() {
- return array('clientid', 'secret', 'pluginname');
+ protected function query($q, $path = null, $parent = null, $page = 0) {
+ global $OUTPUT;
+
+ $files = [];
+ $folders = [];
+ $fields = "folder,id,lastModifiedDateTime,name,size,webUrl,thumbnails";
+ $params = ['$select' => $fields, '$expand' => 'thumbnails', 'parent' => $parent];
+
+ try {
+ // Retrieving files and folders.
+ $client = $this->get_user_oauth_client();
+ $service = new repository_skydrive\rest($client);
+
+ if (!empty($q)) {
+ $params['search'] = urlencode($q);
+
+ // MS does not return thumbnails on a search.
+ unset($params['$expand']);
+ $response = $service->call('search', $params);
+ } else {
+ $response = $service->call('list', $params);
+ }
+ } catch (Exception $e) {
+ if ($e->getCode() == 403 && strpos($e->getMessage(), 'Access Not Configured') !== false) {
+ throw new repository_exception('servicenotenabled', 'repository_skydrive');
+ } else {
+ throw $e;
+ }
+ }
+
+ $remotefiles = isset($response->value) ? $response->value : [];
+ foreach ($remotefiles as $remotefile) {
+ if (!empty($remotefile->folder)) {
+ // This is a folder.
+ $folders[$remotefile->id] = [
+ 'title' => $remotefile->name,
+ 'path' => $this->build_node_path($remotefile->id, $remotefile->name, $path),
+ 'date' => strtotime($remotefile->lastModifiedDateTime),
+ 'thumbnail' => $OUTPUT->pix_url(file_folder_icon(64))->out(false),
+ 'thumbnail_height' => 64,
+ 'thumbnail_width' => 64,
+ 'children' => []
+ ];
+ } else {
+ // We can download all other file types.
+ $title = $remotefile->name;
+ $source = json_encode([
+ 'id' => $remotefile->id,
+ 'name' => $remotefile->name,
+ 'link' => $remotefile->webUrl
+ ]);
+
+ // Adds the file to the file list. Using the itemId along with the name as key
+ // of the array because Google Drive allows files with identical names.
+ $thumb = '';
+ $thumbwidth = 0;
+ $thumbheight = 0;
+ $extendedinfoerr = false;
+
+ if (empty($remotefile->thumbnails)) {
+ // Try and get it directly from the item.
+ $params = ['fileid' => $remotefile->id, '$select' => $fields, '$expand' => 'thumbnails'];
+ try {
+ $response = $service->call('get', $params);
+ $remotefile = $response;
+ } catch (Exception $e) {
+ // This is not a failure condition - we just could not get extended info about the file.
+ $extendedinfoerr = true;
+ }
+ }
+
+ if (!empty($remotefile->thumbnails)) {
+ $thumbs = $remotefile->thumbnails;
+ if (count($thumbs)) {
+ $first = reset($thumbs);
+ if (!empty($first->medium) && !empty($first->medium->url)) {
+ $thumb = $first->medium->url;
+ $thumbwidth = min($first->medium->width, 64);
+ $thumbheight = min($first->medium->height, 64);
+ }
+ }
+ }
+
+ $files[$remotefile->id] = [
+ 'title' => $title,
+ 'source' => $source,
+ 'date' => strtotime($remotefile->lastModifiedDateTime),
+ 'size' => isset($remotefile->size) ? $remotefile->size : null,
+ 'thumbnail' => $thumb,
+ 'thumbnail_height' => $thumbwidth,
+ 'thumbnail_width' => $thumbheight,
+ ];
+ }
+ }
+
+ // Filter and order the results.
+ $files = array_filter($files, [$this, 'filter']);
+ core_collator::ksort($files, core_collator::SORT_NATURAL);
+ core_collator::ksort($folders, core_collator::SORT_NATURAL);
+ return array_merge(array_values($folders), array_values($files));
}
/**
- * Setup repistory form.
+ * Logout.
*
- * @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
+ * @return string
*/
public function logout() {
- $this->skydrive->log_out();
- return $this->print_login();
+ $client = $this->get_user_oauth_client();
+ $client->log_out();
+ return parent::logout();
}
/**
- * This repository doesn't support global search.
+ * Get a file.
*
- * @return bool if supports global search
+ * @param string $reference reference of the file.
+ * @param string $file name to save the file to.
+ * @return string JSON encoded array of information about the file.
*/
- public function global_search() {
- return false;
+ public function get_file($reference, $filename = '') {
+ global $CFG;
+
+ $client = $this->get_user_oauth_client();
+ $base = 'https://graph.microsoft.com/v1.0/';
+
+ $sourceinfo = json_decode($reference);
+ $sourceurl = new moodle_url($base . 'me/drive/items/' . $sourceinfo->id . '/content');
+ $source = $sourceurl->out(false);
+
+ // We use download_one and not the rest API because it has special timeouts etc.
+ $path = $this->prepare_file($filename);
+ $options = ['filepath' => $path, 'timeout' => 15, 'followlocation' => true, 'maxredirs' => 5];
+ $result = $client->download_one($source, null, $options);
+
+ if ($result) {
+ @chmod($path, $CFG->filepermissions);
+ return array(
+ 'path' => $path,
+ 'url' => $reference
+ );
+ }
+ throw new repository_exception('cannotdownload', 'repository');
}
/**
- * This repoistory supports any filetype.
+ * Prepare file reference information.
*
- * @return string '*' means this repository support any files
+ * We are using this method to clean up the source to make sure that it
+ * is a valid source.
+ *
+ * @param string $source of the file.
+ * @return string file reference.
+ */
+ public function get_file_reference($source) {
+ // We could do some magic upgrade code here.
+ return $source;
+ }
+
+ /**
+ * What kind of files will be in this repository?
+ *
+ * @return array return '*' means this repository support any files, otherwise
+ * return mimetypes of files, it can be an array
*/
public function supported_filetypes() {
return '*';
}
/**
- * This repostiory only supports internal files
+ * Tells how the file can be picked from this repository.
*
- * @return int return type bitmask supported
+ * @return int
*/
public function supported_returntypes() {
- return FILE_INTERNAL;
+ // We can only support references if the system account is connected.
+ if (!empty($this->issuer) && $this->issuer->is_system_account_connected()) {
+ $setting = get_config('skydrive', 'supportedreturntypes');
+ if ($setting == 'internal') {
+ return FILE_INTERNAL;
+ } else if ($setting == 'external') {
+ return FILE_CONTROLLED_LINK;
+ } else {
+ return FILE_CONTROLLED_LINK | FILE_INTERNAL;
+ }
+ } else {
+ return FILE_INTERNAL;
+ }
+ }
+
+ /**
+ * Which return type should be selected by default.
+ *
+ * @return int
+ */
+ public function default_returntype() {
+ $setting = get_config('skydrive', 'defaultreturntype');
+ $supported = get_config('skydrive', 'supportedreturntypes');
+ if (($setting == FILE_INTERNAL && $supported != 'external') || $supported == 'internal') {
+ return FILE_INTERNAL;
+ } else {
+ return FILE_CONTROLLED_LINK;
+ }
+ }
+
+ /**
+ * Return names of the general options.
+ * By default: no general option name.
+ *
+ * @return array
+ */
+ public static function get_type_option_names() {
+ return array('issuerid', 'pluginname', 'defaultreturntype', 'supportedreturntypes');
+ }
+
+ /**
+ * Store the access token.
+ */
+ public function callback() {
+ $client = $this->get_user_oauth_client();
+ // This will upgrade to an access token if we have an authorization code and save the access token in the session.
+ $client->is_logged_in();
+ }
+
+ /**
+ * Repository method to serve the referenced file
+ *
+ * @see send_stored_file
+ *
+ * @param stored_file $storedfile the file that contains the reference
+ * @param int $lifetime Number of seconds before the file should expire from caches (null means $CFG->filelifetime)
+ * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only
+ * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin
+ * @param array $options additional options affecting the file serving
+ */
+ public function send_file($storedfile, $lifetime=null , $filter=0, $forcedownload=false, array $options = null) {
+ $source = json_decode($storedfile->get_reference());
+
+ $fb = get_file_browser();
+ $context = context::instance_by_id($storedfile->get_contextid(), MUST_EXIST);
+ $info = $fb->get_file_info($context,
+ $storedfile->get_component(),
+ $storedfile->get_filearea(),
+ $storedfile->get_itemid(),
+ $storedfile->get_filepath(),
+ $storedfile->get_filename());
+
+ if ($info->is_writable()) {
+ // Add the current user as an OAuth writer.
+ $systemauth = \core\oauth2\api::get_system_oauth_client($this->issuer);
+
+ if ($systemauth === false) {
+ $details = 'Cannot connect as system user';
+ throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
+ }
+ $systemservice = new repository_skydrive\rest($systemauth);
+
+ // Get the user oauth so we can get the account to add.
+ $url = moodle_url::make_pluginfile_url($storedfile->get_contextid(),
+ $storedfile->get_component(),
+ $storedfile->get_filearea(),
+ $storedfile->get_itemid(),
+ $storedfile->get_filepath(),
+ $storedfile->get_filename(),
+ $forcedownload);
+ $url->param('sesskey', sesskey());
+ $userauth = $this->get_user_oauth_client($url);
+ if (!$userauth->is_logged_in()) {
+ redirect($userauth->get_login_url());
+ }
+ if ($userauth === false) {
+ $details = 'Cannot connect as current user';
+ throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
+ }
+ $userinfo = $userauth->get_userinfo();
+ $useremail = $userinfo['email'];
+
+ $this->add_temp_writer_to_file($systemservice, $source->id, $useremail);
+ }
+
+ if ($source->link) {
+ redirect($source->link);
+ } else {
+ $details = 'File is missing source link';
+ throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
+ }
+ }
+
+ /**
+ * List the permissions on a file.
+ *
+ * @param \repository_skydrive\rest $client Authenticated client.
+ * @param string $fileid The id of the file.
+ * @return array
+ */
+ protected function list_file_permissions(\repository_skydrive\rest $client, $fileid) {
+ $fields = "id,roles,link,grantedTo";
+ return $client->call('list_permissions', ['fileid' => $fileid, '$select' => $fields]);
+ }
+
+ /**
+ * See if a folder exists within a folder
+ *
+ * @param \repository_skydrive\rest $client Authenticated client.
+ * @param string $fullpath
+ * @return string|boolean The file id if it exists or false.
+ */
+ protected function get_file_id_by_path(\repository_skydrive\rest $client, $fullpath) {
+ $fields = "id";
+ try {
+ $response = $client->call('get_file_by_path', ['fullpath' => $fullpath, '$select' => $fields]);
+ } catch (\core\oauth2\rest_exception $re) {
+ return false;
+ }
+ return $response->id;
+ }
+
+ /**
+ * Delete a file by full path.
+ *
+ * @param \repository_skydrive\rest $client Authenticated client.
+ * @param string $fullpath
+ * @return boolean
+ */
+ protected function delete_file_by_path(\repository_skydrive\rest $client, $fullpath) {
+ try {
+ $response = $client->call('delete_file_by_path', ['fullpath' => $fullpath]);
+ } catch (\core\oauth2\rest_exception $re) {
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Get a file summary by full path.
+ *
+ * @param \repository_skydrive\rest $client Authenticated client.
+ * @param string $fullpath
+ * @return stdClass
+ */
+ protected function get_file_summary_by_path(\repository_skydrive\rest $client, $fullpath) {
+ $fields = "folder,id,lastModifiedDateTime,name,size,webUrl,createdByUser";
+ $response = $client->call('get_file_by_path', ['fullpath' => $fullpath, '$select' => $fields]);
+ if (empty($response->id)) {
+ $details = 'Cannot get file summary:' . $fullpath;
+ throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
+ }
+ return $response;
+ }
+
+ /**
+ * Create a folder within a folder
+ *
+ * @param \repository_skydrive\rest $client Authenticated client.
+ * @param string $foldername The folder we are creating.
+ * @param string $parentid The parent folder we are creating in.
+ *
+ * @return string The file id of the new folder.
+ */
+ protected function create_folder_in_folder(\repository_skydrive\rest $client, $foldername, $parentid) {
+ $params = ['parentid' => $parentid];
+ $folder = [ 'name' => $foldername, 'folder' => [ 'childCount' => 0 ]];
+ $created = $client->call('create_folder', $params, json_encode($folder));
+ if (empty($created->id)) {
+ $details = 'Cannot create folder:' . $foldername;
+ throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
+ }
+ return $created->id;
+ }
+
+ /**
+ * Get simple file info for humans.
+ *
+ * @param \repository_skydrive\rest $client Authenticated client.
+ * @param string $fileid The file we are querying.
+ *
+ * @return stdClass
+ */
+ protected function get_file_summary(\repository_skydrive\rest $client, $fileid) {
+ $fields = "folder,id,lastModifiedDateTime,name,size,webUrl,createdByUser";
+ $response = $client->call('get', ['fileid' => $fileid, '$select' => $fields]);
+ return $response;
+ }
+
+ /**
+ * Get the id of this users root drive.
+ *
+ * @param \repository_skydrive\rest $client Authenticated client.
+ *
+ * @return string id
+ */
+ protected function get_root_drive_id(\repository_skydrive\rest $client) {
+ $response = $client->call('get_drive', []);
+
+ if (empty($response->id)) {
+ $details = 'Cannot get driveid';
+ throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
+ }
+ return $response->id;
+ }
+
+ /**
+ * Add a writer to the permissions on the file (temporary).
+ *
+ * @param \repository_skydrive\rest $client Authenticated client.
+ * @param string $fileid The file we are updating.
+ * @param string $email The email of the writer account to add.
+ * @return boolean
+ */
+ protected function add_temp_writer_to_file(\repository_skydrive\rest $client, $fileid, $email) {
+ // Expires in 7 days.
+ $expires = new DateTime();
+ $expires->add(new DateInterval("P7D"));
+
+ $updateeditor = [
+ 'recipients' => [[ 'email' => $email ]],
+ 'roles' => ['write'],
+ 'requireSignIn' => true,
+ 'sendInvitation' => false
+ ];
+ $params = ['fileid' => $fileid];
+ $response = $client->call('create_permission', $params, json_encode($updateeditor));
+ if (empty($response->value[0]->id)) {
+ $details = 'Cannot add user ' . $email . ' as a writer for document: ' . $fileid;
+ throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
+ }
+ // Store the permission id in the DB. Scheduled task will remove this permission after 7 days.
+ if ($access = repository_skydrive\access::get_record(['permissionid' => $response->value[0]->id, 'itemid' => $fileid ])) {
+ // Update the timemodified.
+ $access->update();
+ } else {
+ $record = (object) [ 'permissionid' => $response->value[0]->id, 'itemid' => $fileid ];
+ $access = new repository_skydrive\access(0, $record);
+ $access->create();
+ }
+ return true;
+ }
+
+ /**
+ * Add a writer to the permissions on the file.
+ *
+ * @param \repository_skydrive\rest $client Authenticated client.
+ * @param string $fileid The file we are updating.
+ * @param string $userid The userid of the writer account to add.
+ * @return boolean
+ */
+ protected function add_writer_to_file(\repository_skydrive\rest $client, $fileid, $useremail) {
+ $updateeditor = [
+ 'recipients' => [ [ 'email' => $useremail ] ],
+ 'roles' => ['write'],
+ 'requireSignIn' => true,
+ 'sendInvitation' => false
+ ];
+ $params = [ 'fileid' => $fileid ];
+ $response = $client->call('create_permission', $params, json_encode($updateeditor));
+ if (empty($response->value)) {
+ $details = 'Cannot add user ' . $useremail . ' as a writer for document: ' . $fileid;
+ throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
+ }
+ return true;
+ }
+
+ /**
+ * Allow anyone with the link to read the file.
+ *
+ * @param \repository_skydrive\rest $client Authenticated client.
+ * @param string $fileid The file we are updating.
+ * @return boolean
+ */
+ protected function set_file_sharing_anyone_with_link_can_read(\repository_skydrive\rest $client, $fileid) {
+ $updateread = [
+ 'type' => 'view',
+ 'scope' => 'anonymous'
+ ];
+ $params = ['fileid' => $fileid];
+ $response = $client->call('create_link', $params, json_encode($updateread));
+ if (empty($response->link)) {
+ $details = 'Cannot update link sharing for the document: ' . $fileid;
+ throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
+ }
+ return true;
+ }
+
+ /**
+ * Get share info.
+ *
+ * @param \repository_skydrive\rest $client Authenticated client.
+ * @param string $sharetoken The share we are querying.
+ * @return stdClass
+ */
+ protected function copy_share(\repository_skydrive\rest $client, $sharetoken, $newdrive, $parentid) {
+ $folder = [
+ 'parentReference' => ['id' => $parentid, 'driveId' => $newdrive]
+ ];
+ $params = ['sharetoken' => $sharetoken];
+ $response = $client->call('copy_share', $params, json_encode($folder));
+ return true;
+ }
+
+ /**
+ * From MS docs - to get a share token from a url, do this:
+ * Reference: https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/shares_get
+ * To access a sharing URL using the shares API, the URL needs to be transformed into a sharing token.
+ * To transform a URL into a sharing token:
+ * Base64 encode the sharing URL.
+ * Convert the base64 encoded data to unpadded base64url format by:
+ * Trim trailing = characeters from the string.
+ * Replace unsafe URL characters with an equivelent character; replace / with _ and + with -.
+ * Append u! to the beginning of the string.
+ *
+ * @param string sharingUrl
+ * @return string sharingtoken
+ */
+ protected function get_share_token($shareurl) {
+ return 'u!' . str_replace(['/', '+'], ['_', '-'], rtrim(base64_encode($shareurl), '='));
+ }
+
+ /**
+ * Called when a file is selected as a "link".
+ * Invoked at MOODLE/repository/repository_ajax.php
+ *
+ * @param string $reference this reference is generated by
+ * repository::get_file_reference()
+ * @param context $context the target context for this new file.
+ * @param string $component the target component for this new file.
+ * @param string $filearea the target filearea for this new file.
+ * @param string $itemid the target itemid for this new file.
+ * @return string $modifiedreference (final one before saving to DB)
+ */
+ public function reference_file_selected($reference, $context, $component, $filearea, $itemid) {
+ // What we need to do here is transfer ownership to the system user (or copy)
+ // then set the permissions so anyone with the share link can view,
+ // finally update the reference to contain the share link if it was not
+ // already there (and point to new file id if we copied).
+ var_dump($reference);
+ $systemauth = \core\oauth2\api::get_system_oauth_client($this->issuer);
+
+ if ($systemauth === false) {
+ $details = 'Cannot connect as system user';
+ throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
+ }
+ $systemuserinfo = $systemauth->get_userinfo();
+ $systemuseremail = $systemuserinfo['email'];
+
+ $source = json_decode($reference);
+
+ $userauth = $this->get_user_oauth_client();
+ if ($userauth === false) {
+ $details = 'Cannot connect as current user';
+ throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
+ }
+ $userinfo = $userauth->get_userinfo();
+ $useremail = $userinfo['email'];
+
+ $userservice = new repository_skydrive\rest($userauth);
+ $systemservice = new repository_skydrive\rest($systemauth);
+
+ // Get the list of existing permissions so we can see if the owner is already the system account,
+ // and whether we need to update the link sharing options.
+ $permissions = $this->list_file_permissions($userservice, $source->id);
+
+ $readshareupdaterequired = true;
+ $ownerupdaterequired = true;
+ foreach ($permissions->value as $permission) {
+ if (!empty($permission->link)) {
+ if ($permission->link->scope == 'anonymous' &&
+ $permission->link->type == 'view') {
+ $shareurl = $permission->link->webUrl;
+ $readshareupdaterequired = false;
+ break;
+ }
+ }
+ }
+
+ // Add Moodle as writer.
+ $this->add_writer_to_file($userservice, $source->id, $systemuseremail);
+
+ // Now copy it to a sensible folder.
+ $contextlist = array_reverse($context->get_parent_contexts(true));
+
+ $cache = cache::make('repository_skydrive', 'folder');
+ $parentid = 'root';
+ $fullpath = '';
+ $allfolders = [];
+ foreach ($contextlist as $context) {
+ // Make sure a folder exists here.
+ $foldername = urlencode(clean_param($context->get_context_name(), PARAM_PATH));
+ $allfolders[] = $foldername;
+ }
+
+ $allfolders[] = urlencode(clean_param($component, PARAM_PATH));
+ $allfolders[] = urlencode(clean_param($filearea, PARAM_PATH));
+ $allfolders[] = urlencode(clean_param($itemid, PARAM_PATH));
+
+ foreach ($allfolders as $foldername) {
+ if ($fullpath) {
+ $fullpath .= '/';
+ }
+ $fullpath .= $foldername;
+
+ $folderid = $cache->get($fullpath);
+ if (empty($folderid)) {
+ $folderid = $this->get_file_id_by_path($systemservice, $fullpath);
+ }
+ if ($folderid !== false) {
+ $cache->set($fullpath, $folderid);
+ $parentid = $folderid;
+ } else {
+ // Create it.
+ $parentid = $this->create_folder_in_folder($systemservice, $foldername, $parentid);
+ $cache->set($fullpath, $parentid);
+ }
+ }
+
+ // Get the users drive id.
+ $newdrive = $this->get_root_drive_id($systemservice);
+
+ if ($readshareupdaterequired) {
+ $response = $this->set_file_sharing_anyone_with_link_can_read($userservice, $source->id);
+ $shareurl = $response->value->webUrl;
+ }
+
+ // Turn the share url into a sharing token.
+ $sharetoken = $this->get_share_token($shareurl);
+
+ // Delete any existing file at this path.
+ $path = $fullpath . '/' . $source->name;
+ $this->delete_file_by_path($systemservice, $path);
+
+ // Copy the file so we have a backup.
+ $this->copy_share($systemservice, $sharetoken, $newdrive, $parentid);
+
+ $summary = $this->get_file_summary_by_path($systemservice, $path);
+
+ // Update the details in the file reference before it is saved.
+ $source->id = $summary->id;
+ $source->link = $summary->webUrl;
+
+ $reference = json_encode($source);
+
+ return $reference;
+ }
+
+ /**
+ * Get human readable file info from the reference.
+ *
+ * @param string $reference
+ * @param int $filestatus
+ */
+ public function get_reference_details($reference, $filestatus = 0) {
+ if (empty($reference)) {
+ return get_string('unknownsource', 'repository');
+ }
+ $source = json_decode($reference);
+ $systemauth = \core\oauth2\api::get_system_oauth_client($this->issuer);
+
+ if ($systemauth === false) {
+ return '';
+ }
+ $systemservice = new repository_skydrive\rest($systemauth);
+ $info = $this->get_file_summary($systemservice, $source->id);
+
+ $owner = '';
+ if (!empty($info->createdByUser->displayName)) {
+ $owner = $info->createdByUser->displayName;
+ }
+ if ($owner) {
+ return get_string('owner', 'repository_skydrive', $owner);
+ } else {
+ return $info->name;
+ }
+ }
+
+ /**
+ * Edit/Create Admin Settings Moodle form.
+ *
+ * @param moodleform $mform Moodle form (passed by reference).
+ * @param string $classname repository class name.
+ */
+ public static function type_config_form($mform, $classname = 'repository') {
+ $url = new moodle_url('/admin/tool/oauth2/issuers.php');
+ $url = $url->out();
+
+ $mform->addElement('static', null, '', get_string('oauth2serviceslink', 'repository_skydrive', $url));
+
+ parent::type_config_form($mform);
+ $options = [];
+ $issuers = \core\oauth2\api::get_all_issuers();
+
+ foreach ($issuers as $issuer) {
+ $options[$issuer->get('id')] = s($issuer->get('name'));
+ }
+
+ $strrequired = get_string('required');
+
+ $mform->addElement('select', 'issuerid', get_string('issuer', 'repository_skydrive'), $options);
+ $mform->addHelpButton('issuerid', 'issuer', 'repository_skydrive');
+ $mform->addRule('issuerid', $strrequired, 'required', null, 'client');
+
+ $mform->addElement('static', null, '', get_string('fileoptions', 'repository_skydrive'));
+ $choices = [
+ 'internal' => get_string('internal', 'repository_skydrive'),
+ 'external' => get_string('external', 'repository_skydrive'),
+ 'both' => get_string('both', 'repository_skydrive')
+ ];
+ $mform->addElement('select', 'supportedreturntypes', get_string('supportedreturntypes', 'repository_skydrive'), $choices);
+
+ $choices = [
+ FILE_INTERNAL => get_string('internal', 'repository_skydrive'),
+ FILE_CONTROLLED_LINK => get_string('external', 'repository_skydrive'),
+ ];
+ $mform->addElement('select', 'defaultreturntype', get_string('defaultreturntype', 'repository_skydrive'), $choices);
}
}
+
+/**
+ * Callback to get the required scopes for system account.
+ *
+ * @return string
+ */
+function repository_skydrive_oauth2_system_scopes(\core\oauth2\issuer $issuer) {
+ if ($issuer->get('id') == get_config('skydrive', 'issuerid')) {
+ return repository_skydrive::SCOPES;
+ }
+ return '';
+}
diff --git a/repository/skydrive/microsoftliveapi.php b/repository/skydrive/microsoftliveapi.php
deleted file mode 100644
index 5fc52274eae..00000000000
--- a/repository/skydrive/microsoftliveapi.php
+++ /dev/null
@@ -1,245 +0,0 @@
-.
-
-/**
- * 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');
- }
-
- /**
- * Returns the auth url for OAuth 2.0 request
- * @return string the auth url
- */
- protected function auth_url() {
- return 'https://login.live.com/oauth20_authorize.srf';
- }
-
- /**
- * Returns the token url for OAuth 2.0 request
- * @return string the auth url
- */
- protected function token_url() {
- return 'https://login.live.com/oauth20_token.srf';
- }
-
- /**
- * Post request.
- *
- * Overridden to convert the data to a string, else curl will set the wrong headers.
- *
- * @param string $url The URL.
- * @param array|string $params The parameters.
- * @param array $options The options.
- * @return bool
- */
- public function post($url, $params = '', $options = array()) {
- return parent::post($url, format_postdata_for_curlcall($params), $options);
- }
-
- /**
- * 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->image_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->image_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->image_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->image_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->image_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/version.php b/repository/skydrive/version.php
index 8afca1d1f90..a3995847071 100644
--- a/repository/skydrive/version.php
+++ b/repository/skydrive/version.php
@@ -25,6 +25,6 @@
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2016120500; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version = 2017031400; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2016112900; // Requires this Moodle version.
$plugin->component = 'repository_skydrive'; // Full name of the plugin (used for diagnostics).