MDL-58128 googledocs: Support reference files.
Add options so the admin can control the types of files this repository can support (and the default). Part of MDL-58220
This commit is contained in:
@@ -64,7 +64,7 @@ abstract class rest {
|
||||
* @param string $functionname
|
||||
* @param array $functionargs
|
||||
*/
|
||||
public function call($functionname, $functionargs) {
|
||||
public function call($functionname, $functionargs, $rawpost = false) {
|
||||
$functions = $this->get_api_functions();
|
||||
$supportedmethods = [ 'get', 'put', 'post', 'patch', 'head', 'delete' ];
|
||||
if (empty($functions[$functionname])) {
|
||||
@@ -86,7 +86,30 @@ abstract class rest {
|
||||
}
|
||||
}
|
||||
|
||||
// Allow params in the URL path like /me/{parent}/children.
|
||||
foreach ($callargs as $argname => $value) {
|
||||
$newendpoint = str_replace('{' . $argname . '}', $value, $endpoint);
|
||||
if ($newendpoint != $endpoint) {
|
||||
$endpoint = $newendpoint;
|
||||
unset($callargs[$argname]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($rawpost !== false) {
|
||||
$queryparams = $this->curl->build_post_data($callargs);
|
||||
if (!empty($queryparams)) {
|
||||
$endpoint .= '?' . $queryparams;
|
||||
}
|
||||
$callargs = $rawpost;
|
||||
}
|
||||
|
||||
error_log('CALL REST');
|
||||
error_log($endpoint);
|
||||
error_log(json_encode($callargs));
|
||||
error_log($method);
|
||||
$this->curl->setHeader('Content-type: application/json');
|
||||
$response = $this->curl->$method($endpoint, $callargs);
|
||||
error_log($response);
|
||||
|
||||
if ($this->curl->errno == 0) {
|
||||
if ($responsetype == 'json') {
|
||||
|
||||
@@ -3458,6 +3458,34 @@ class curl {
|
||||
return $this->request($url, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP PATCH method
|
||||
*
|
||||
* @param string $url
|
||||
* @param array|string $params
|
||||
* @param array $options
|
||||
* @return bool
|
||||
*/
|
||||
public function patch($url, $params = '', $options = array()) {
|
||||
$options['CURLOPT_CUSTOMREQUEST'] = 'PATCH';
|
||||
if (is_array($params)) {
|
||||
$this->_tmp_file_post_params = array();
|
||||
foreach ($params as $key => $value) {
|
||||
if ($value instanceof stored_file) {
|
||||
$value->add_to_curl_request($this, $key);
|
||||
} else {
|
||||
$this->_tmp_file_post_params[$key] = $value;
|
||||
}
|
||||
}
|
||||
$options['CURLOPT_POSTFIELDS'] = $this->_tmp_file_post_params;
|
||||
unset($this->_tmp_file_post_params);
|
||||
} else {
|
||||
// $params is the raw post data
|
||||
$options['CURLOPT_POSTFIELDS'] = $params;
|
||||
}
|
||||
return $this->request($url, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP POST method
|
||||
*
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
* Active repository options
|
||||
* =====
|
||||
* this.active_repo.id
|
||||
* this.active_repo.defaultreturntype
|
||||
* this.active_repo.nosearch
|
||||
* this.active_repo.norefresh
|
||||
* this.active_repo.nologin
|
||||
@@ -1094,6 +1095,12 @@ M.core_filepicker.init = function(Y, options) {
|
||||
firstfilelink = (firstfilelink==null && allowed) ? filelinktypes[i] : firstfilelink;
|
||||
filelinkcount += allowed ? 1 : 0;
|
||||
}
|
||||
var defaultreturntype = this.options.repositories[this.active_repo.id].defaultreturntype;
|
||||
if (defaultreturntype) {
|
||||
if (filelink[defaultreturntype]) {
|
||||
firstfilelink = defaultreturntype;
|
||||
}
|
||||
}
|
||||
// make radio buttons enabled if this file-link-type is available and only if there are more than one file-link-type option
|
||||
// check the first available file-link-type option
|
||||
for (var linktype in filelink) {
|
||||
@@ -1415,6 +1422,7 @@ M.core_filepicker.init = function(Y, options) {
|
||||
this.objecttag = data.object?data.object:null;
|
||||
this.active_repo = {};
|
||||
this.active_repo.issearchresult = data.issearchresult ? true : false;
|
||||
this.active_repo.defaultreturntype = data.defaultreturntype?data.defaultreturntype:null;
|
||||
this.active_repo.dynload = data.dynload?data.dynload:false;
|
||||
this.active_repo.pages = Number(data.pages?data.pages:null);
|
||||
this.active_repo.page = Number(data.page?data.page:null);
|
||||
|
||||
@@ -54,7 +54,76 @@ class rest extends \core\oauth2\rest {
|
||||
'spaces' => PARAM_RAW
|
||||
],
|
||||
'response' => 'json'
|
||||
]
|
||||
],
|
||||
'get' => [
|
||||
'endpoint' => 'https://www.googleapis.com/drive/v3/files/{fileid}',
|
||||
'method' => 'get',
|
||||
'args' => [
|
||||
'fields' => PARAM_RAW,
|
||||
'fileid' => PARAM_RAW
|
||||
],
|
||||
'response' => 'json'
|
||||
],
|
||||
'copy' => [
|
||||
'endpoint' => 'https://www.googleapis.com/drive/v3/files/{fileid}/copy',
|
||||
'method' => 'post',
|
||||
'args' => [
|
||||
'fields' => PARAM_RAW,
|
||||
'fileid' => PARAM_RAW
|
||||
],
|
||||
'response' => 'json'
|
||||
],
|
||||
'create' => [
|
||||
'endpoint' => 'https://www.googleapis.com/drive/v3/files',
|
||||
'method' => 'post',
|
||||
'args' => [
|
||||
'fields' => PARAM_RAW
|
||||
],
|
||||
'response' => 'json'
|
||||
],
|
||||
'update' => [
|
||||
'endpoint' => 'https://www.googleapis.com/drive/v3/files/{fileid}',
|
||||
'method' => 'patch',
|
||||
'args' => [
|
||||
'fileid' => PARAM_RAW,
|
||||
'fields' => PARAM_RAW,
|
||||
'addParents' => PARAM_RAW,
|
||||
'removeParents' => PARAM_RAW
|
||||
],
|
||||
'response' => 'json'
|
||||
],
|
||||
'create_permission' => [
|
||||
'endpoint' => 'https://www.googleapis.com/drive/v3/files/{fileid}/permissions',
|
||||
'method' => 'post',
|
||||
'args' => [
|
||||
'fileid' => PARAM_RAW,
|
||||
'emailMessage' => PARAM_RAW,
|
||||
'sendNotificationEmail' => PARAM_RAW,
|
||||
'transferOwnership' => PARAM_RAW,
|
||||
],
|
||||
'response' => 'json'
|
||||
],
|
||||
'update_permission' => [
|
||||
'endpoint' => 'https://www.googleapis.com/drive/v3/files/{fileid}/permissions/{permissionid}',
|
||||
'method' => 'patch',
|
||||
'args' => [
|
||||
'fileid' => PARAM_RAW,
|
||||
'permissionid' => PARAM_RAW,
|
||||
'emailMessage' => PARAM_RAW,
|
||||
'sendNotificationEmail' => PARAM_RAW,
|
||||
'transferOwnership' => PARAM_RAW,
|
||||
],
|
||||
'response' => 'json'
|
||||
],
|
||||
'list_permissions' => [
|
||||
'endpoint' => 'https://www.googleapis.com/drive/v3/files/{fileid}/permissions',
|
||||
'method' => 'get',
|
||||
'args' => [
|
||||
'fileid' => PARAM_RAW,
|
||||
'fields' => PARAM_RAW
|
||||
],
|
||||
'response' => 'json'
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,9 @@ function xmldb_repository_googledocs_upgrade($oldversion) {
|
||||
|
||||
set_config('issuerid', $issuer->get('id'), 'googledocs');
|
||||
}
|
||||
if ($oldversion < 2017030600) {
|
||||
set_config('supportedfiles', 'both', 'googledocs');
|
||||
}
|
||||
upgrade_plugin_savepoint(true, 2017030500, 'repository', 'googledocs');
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,12 @@ $string['issuer_help'] = 'Select the OAuth 2 service that is configured to talk
|
||||
$string['servicenotenabled'] = 'Access not configured. Make sure the service \'Drive API\' is enabled.';
|
||||
$string['oauth2serviceslink'] = '<a href="{$a}" title="Link to OAuth Services configuration">OAuth 2 Services Configuration</a>';
|
||||
$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.';
|
||||
|
||||
// Deprecated since Moodle 3.3.
|
||||
$string['oauthinfo'] = '<p>To use this plugin, you must register your site with Google, as described in the documentation <a href="{$a->docsurl}">Google OAuth 2.0 setup</a>.</p><p>As part of the registration process, you will need to enter the following URL as \'Authorized Redirect URIs\':</p><p>{$a->callbackurl}</p><p>Once registered, you will be provided with a client ID and secret which can be used to configure all Google Drive and Picasa plugins.</p><p>Please also note that you will have to enable the service \'Drive API\'.</p>';
|
||||
|
||||
+421
-22
@@ -185,7 +185,6 @@ class repository_googledocs extends repository {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List the files and folders.
|
||||
*
|
||||
@@ -217,6 +216,7 @@ class repository_googledocs extends repository {
|
||||
|
||||
$ret = array();
|
||||
$ret['dynload'] = true;
|
||||
$ret['defaultreturntype'] = $this->default_returntype();
|
||||
$ret['path'] = $this->build_breadcrumb($path);
|
||||
$ret['list'] = $results;
|
||||
return $ret;
|
||||
@@ -266,7 +266,7 @@ class repository_googledocs extends repository {
|
||||
$files = array();
|
||||
$folders = array();
|
||||
$config = get_config('googledocs');
|
||||
$fields = "files(id,name,mimeType,webContentLink,fileExtension,modifiedTime,size,thumbnailLink,iconLink)";
|
||||
$fields = "files(id,name,mimeType,webContentLink,webViewLink,fileExtension,modifiedTime,size,thumbnailLink,iconLink)";
|
||||
$params = array('q' => $q, 'fields' => $fields, 'spaces' => 'drive');
|
||||
|
||||
try {
|
||||
@@ -284,7 +284,6 @@ class repository_googledocs extends repository {
|
||||
}
|
||||
}
|
||||
|
||||
$base = 'https://www.googleapis.com/drive/v3';
|
||||
$gfiles = isset($response->files) ? $response->files : array();
|
||||
foreach ($gfiles as $gfile) {
|
||||
if ($gfile->mimeType == 'application/vnd.google-apps.folder') {
|
||||
@@ -300,12 +299,11 @@ class repository_googledocs extends repository {
|
||||
);
|
||||
} else {
|
||||
// This is a file.
|
||||
$link = isset($gfile->webContentLink) ? $gfile->webContentLink : '';
|
||||
if (isset($gfile->fileExtension)) {
|
||||
// The file has an extension, therefore we can download it.
|
||||
$source = json_encode(['id' => $gfile->id, 'exportformat' => 'download', 'link' => $link]);
|
||||
$title = $gfile->name;
|
||||
$params = ['alt' => 'media'];
|
||||
$sourceurl = new moodle_url($base . '/files/' . $gfile->id, $params);
|
||||
$source = $sourceurl->out(false);
|
||||
} else {
|
||||
// The file is probably a Google Doc file, we get the corresponding export link.
|
||||
// This should be improved by allowing the user to select the type of export they'd like.
|
||||
@@ -317,7 +315,7 @@ class repository_googledocs extends repository {
|
||||
switch ($type){
|
||||
case 'document':
|
||||
$ext = $config->documentformat;
|
||||
$title = $item['title'] . '.'. $ext;
|
||||
$title = $gfile->name . '.'. $ext;
|
||||
if ($ext === 'rtf') {
|
||||
// Moodle user 'text/rtf' as the MIME type for RTF files.
|
||||
// Google uses 'application/rtf' for the same type of file.
|
||||
@@ -329,17 +327,17 @@ class repository_googledocs extends repository {
|
||||
break;
|
||||
case 'presentation':
|
||||
$ext = $config->presentationformat;
|
||||
$title = $item['title'] . '.'. $ext;
|
||||
$title = $gfile->name . '.'. $ext;
|
||||
$exporttype = $types[$ext]['type'];
|
||||
break;
|
||||
case 'spreadsheet':
|
||||
$ext = $config->spreadsheetformat;
|
||||
$title = $item['title'] . '.'. $ext;
|
||||
$title = $gfile->name . '.'. $ext;
|
||||
$exporttype = $types[$ext]['type'];
|
||||
break;
|
||||
case 'drawing':
|
||||
$ext = $config->drawingformat;
|
||||
$title = $item['title'] . '.'. $ext;
|
||||
$title = $gfile->name . '.'. $ext;
|
||||
$exporttype = $types[$ext]['type'];
|
||||
break;
|
||||
}
|
||||
@@ -347,9 +345,7 @@ class repository_googledocs extends repository {
|
||||
if (empty($title)) {
|
||||
continue;
|
||||
}
|
||||
$params = ['mimeType' => $exporttype];
|
||||
$sourceurl = new moodle_url($base . '/files/' . $gfile->id . '/export', $params);
|
||||
$source = $sourceurl->out(false);
|
||||
$source = json_encode(['id' => $gfile->id, 'exportformat' => $exporttype, 'link' => $link]);
|
||||
}
|
||||
// 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.
|
||||
@@ -400,10 +396,24 @@ class repository_googledocs extends repository {
|
||||
global $CFG;
|
||||
|
||||
$client = $this->get_user_oauth_client();
|
||||
$base = 'https://www.googleapis.com/drive/v3';
|
||||
|
||||
$source = json_decode($reference);
|
||||
|
||||
if ($source->exportformat == 'download') {
|
||||
$params = ['alt' => 'media'];
|
||||
$sourceurl = new moodle_url($base . '/files/' . $source->id, $params);
|
||||
$source = $sourceurl->out(false);
|
||||
} else {
|
||||
$params = ['mimeType' => $source->exportformat];
|
||||
$sourceurl = new moodle_url($base . '/files/' . $source->id . '/export', $params);
|
||||
$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($reference, null, $options);
|
||||
$result = $client->download_one($source, null, $options);
|
||||
|
||||
if ($result) {
|
||||
@chmod($path, $CFG->filepermissions);
|
||||
@@ -425,7 +435,8 @@ class repository_googledocs extends repository {
|
||||
* @return string file reference.
|
||||
*/
|
||||
public function get_file_reference($source) {
|
||||
return clean_param($source, PARAM_URL);
|
||||
// We could do some magic upgrade code here.
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -446,7 +457,34 @@ class repository_googledocs extends repository {
|
||||
* @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('googledocs', 'supportedreturntypes');
|
||||
if ($setting == 'internal') {
|
||||
return FILE_INTERNAL;
|
||||
} else if ($setting == 'external') {
|
||||
return FILE_REFERENCE;
|
||||
} else {
|
||||
return FILE_REFERENCE | FILE_INTERNAL;
|
||||
}
|
||||
} else {
|
||||
return FILE_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Which return type should be selected by default.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function default_returntype() {
|
||||
$setting = get_config('googledocs', 'defaultreturntype');
|
||||
$supported = get_config('googledocs', 'supportedreturntypes');
|
||||
if (($setting == FILE_INTERNAL && $supported != 'external') || $supported == 'internal') {
|
||||
return FILE_INTERNAL;
|
||||
} else {
|
||||
return FILE_REFERENCE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,7 +496,8 @@ class repository_googledocs extends repository {
|
||||
public static function get_type_option_names() {
|
||||
return array('issuerid', 'pluginname',
|
||||
'documentformat', 'drawingformat',
|
||||
'presentationformat', 'spreadsheetformat');
|
||||
'presentationformat', 'spreadsheetformat',
|
||||
'defaultreturntype', 'supportedreturntypes');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -466,10 +505,348 @@ class repository_googledocs extends repository {
|
||||
*/
|
||||
public function callback() {
|
||||
$client = $this->get_user_oauth_client();
|
||||
// This will upgrade to an access token if we have an authorization code.
|
||||
// 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) {
|
||||
// TODO.
|
||||
$source = json_decode($storedfile->get_reference());
|
||||
|
||||
if ($source->link) {
|
||||
header('Location: ' . $source->link);
|
||||
} else {
|
||||
$details = 'File is missing source link';
|
||||
throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List the permissions on a file.
|
||||
* @param \core\oauth2\client $client Authenticated client.
|
||||
* @param string $fileid The id of the file.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function list_file_permissions(\repository_googledocs\rest $client, $fileid) {
|
||||
$fields = "permissions(id,type,emailAddress,role,allowFileDiscovery,displayName)";
|
||||
return $client->call('list_permissions', ['fileid' => $fileid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* See if a folder exists within a folder
|
||||
*
|
||||
* @param \core\oauth2\client $client Authenticated client.
|
||||
* @param string $foldername The folder we are looking for.
|
||||
* @param string $parentid The parent folder we are looking in.
|
||||
*
|
||||
* @return string|boolean The file id if it exists or false.
|
||||
*/
|
||||
protected function folder_exists_in_folder(\repository_googledocs\rest $client, $foldername, $parentid) {
|
||||
$q = '\'' . addslashes($parentid) . '\' in parents and trashed = false and name = \'' . addslashes($foldername). '\'';
|
||||
$fields = 'files(id, name)';
|
||||
$params = [ 'q' => $q, 'fields' => $fields];
|
||||
$response = $client->call('list', $params);
|
||||
$missing = true;
|
||||
foreach ($response->files as $child) {
|
||||
if ($child->name == $foldername) {
|
||||
return $child->id;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a folder within a folder
|
||||
*
|
||||
* @param \core\oauth2\client $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_googledocs\rest $client, $foldername, $parentid) {
|
||||
$fields = 'id';
|
||||
$params = ['fields' => $fields];
|
||||
$folder = ['mimeType' => 'application/vnd.google-apps.folder', 'name' => $foldername, 'parents' => [$parentid]];
|
||||
$created = $client->call('create', $params, json_encode($folder));
|
||||
if (empty($created->id)) {
|
||||
$details = 'Cannot create folder:' . $foldername;
|
||||
throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
|
||||
}
|
||||
return $created->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get capabilities for a file.
|
||||
*
|
||||
* @param \core\oauth2\client $client Authenticated client.
|
||||
* @param string $fileid The file we are checking.
|
||||
*
|
||||
* @return stdClass The file info with capabilities.
|
||||
*/
|
||||
protected function get_file_capabilities(\repository_googledocs\rest $client, $fileid) {
|
||||
$fields = "id,capabilities,writersCanShare";
|
||||
$params = [
|
||||
'fileid' => $fileid,
|
||||
'fields' => $fields
|
||||
];
|
||||
return $client->call('get', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update file owner.
|
||||
*
|
||||
* @param \core\oauth2\client $client Authenticated client.
|
||||
* @param string $fileid The file we are updating.
|
||||
*
|
||||
* @return boolean Did it work?
|
||||
*/
|
||||
protected function update_file_owner(\repository_googledocs\rest $client, $fileid, $owneremail) {
|
||||
$updateowner = [
|
||||
'emailAddress' => $owneremail,
|
||||
'role' => 'owner',
|
||||
'type' => 'user'
|
||||
];
|
||||
$params = ['fileid' => $fileid, 'transferOwnership' => 'true'];
|
||||
try {
|
||||
$response = $client->call('create_permission', $params, json_encode($updateowner));
|
||||
} catch (\core\oauth2\rest_exception $re) {
|
||||
return false;
|
||||
}
|
||||
return !empty($response->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a file and return the new file details. A side effect of the copy
|
||||
* is that the owner will be the account authenticated with this oauth client.
|
||||
*
|
||||
* @param \core\oauth2\client $client Authenticated client.
|
||||
* @param string $fileid The file we are copying.
|
||||
*
|
||||
* @return stdClass file details.
|
||||
*/
|
||||
protected function copy_file(\repository_googledocs\rest $client, $fileid) {
|
||||
$fields = "id,name,mimeType,webContentLink,webViewLink,size,thumbnailLink,iconLink";
|
||||
$params = [
|
||||
'fileid' => $fileid,
|
||||
'fields' => $fields
|
||||
];
|
||||
$fileinfo = $client->call('copy', $params, ' ');
|
||||
if (empty($fileinfo->id)) {
|
||||
$details = 'Cannot copy file:' . $fileid;
|
||||
throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
|
||||
}
|
||||
return $fileinfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a writer to the permissions on the file.
|
||||
*
|
||||
* @param \core\oauth2\client $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_writer_to_file($client, $fileid, $email) {
|
||||
$updateeditor = [
|
||||
'emailAddress' => $email,
|
||||
'role' => 'writer',
|
||||
'type' => 'user'
|
||||
];
|
||||
$params = ['fileid' => $fileid];
|
||||
$response = $client->call('create_permission', $params, json_encode($updateeditor));
|
||||
if (empty($response->id)) {
|
||||
$details = 'Cannot add user ' . $email . ' as a writer for document: ' . $fileid;
|
||||
throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move from root to folder
|
||||
*
|
||||
* @param \core\oauth2\client $client Authenticated client.
|
||||
* @param string $fileid The file we are updating.
|
||||
* @param string $folderid The id of the folder we are moving to
|
||||
* @return boolean
|
||||
*/
|
||||
protected function move_file_from_root_to_folder($client, $fileid, $folderid) {
|
||||
// Set the parent.
|
||||
$params = [
|
||||
'fileid' => $fileid, 'addParents' => $folderid, 'removeParents' => 'root'
|
||||
];
|
||||
$response = $client->call('update', $params, ' ');
|
||||
if (empty($response->id)) {
|
||||
$details = 'Cannot move the file to a folder: ' . $fileid;
|
||||
throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent writers from sharing.
|
||||
*
|
||||
* @param \core\oauth2\client $client Authenticated client.
|
||||
* @param string $fileid The file we are updating.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function prevent_writers_from_sharing_file($client, $fileid) {
|
||||
// We don't want anyone but Moodle to change the sharing settings.
|
||||
$params = [
|
||||
'fileid' => $fileid
|
||||
];
|
||||
$update = [
|
||||
'writersCanShare' => false
|
||||
];
|
||||
$response = $client->call('update', $params, json_encode($update));
|
||||
if (empty($response->id)) {
|
||||
$details = 'Cannot prevent writers from sharing document: ' . $fileid;
|
||||
throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow anyone with the link to read the file.
|
||||
*
|
||||
* @param \core\oauth2\client $client Authenticated client.
|
||||
* @param string $fileid The file we are updating.
|
||||
* @return boolean
|
||||
*/
|
||||
protected function set_file_sharing_anyone_with_link_can_read($client, $fileid) {
|
||||
$updateread = [
|
||||
'type' => 'anyone',
|
||||
'role' => 'reader',
|
||||
'allowFileDiscovery' => 'false'
|
||||
];
|
||||
$params = ['fileid' => $fileid];
|
||||
$response = $client->call('create_permission', $params, json_encode($updateread));
|
||||
if (empty($response->id) || $response->id != 'anyoneWithLink') {
|
||||
$details = 'Cannot update link sharing for the document: ' . $fileid;
|
||||
throw new repository_exception('errorwhilecommunicatingwith', 'repository', '', $details);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return string $modifiedreference (final one before saving to DB)
|
||||
*/
|
||||
public function reference_file_selected($reference, $context) {
|
||||
// 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).
|
||||
$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_googledocs\rest($userauth);
|
||||
$systemservice = new repository_googledocs\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->permissions as $permission) {
|
||||
if ($permission->type == 'user' &&
|
||||
$permission->role == 'owner' &&
|
||||
isset($permission->emailAddress) &&
|
||||
$permission->emailAddress == $systemuseremail) {
|
||||
$ownerupdaterequired = false;
|
||||
}
|
||||
if ($permission->id == 'anyoneWithLink' &&
|
||||
$permission->type == 'anyone' &&
|
||||
$permission->role == 'reader' &&
|
||||
$permission->allowFileDiscovery == false) {
|
||||
$readshareupdaterequired = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Now move it to a sensible folder.
|
||||
$contextlist = array_reverse($context->get_parent_contexts(true));
|
||||
|
||||
$parentid = 'root';
|
||||
foreach ($contextlist as $context) {
|
||||
// Make sure a folder exists here.
|
||||
$folderid = $this->folder_exists_in_folder($systemservice, $foldername, $parentid);
|
||||
if ($folderid !== false) {
|
||||
$parentid = $folderid;
|
||||
} else {
|
||||
// Create it.
|
||||
$parentid = $this->create_folder_in_folder($systemservice, $foldername, $parentid);
|
||||
}
|
||||
}
|
||||
|
||||
// See if we have edit capability before the copy.
|
||||
$fileinfo = $this->get_file_capabilities($userservice, $source->id);
|
||||
$canedit = !empty($fileinfo->capabilities->canEdit);
|
||||
$writerscanshare = !empty($fileinfo->writersCanShare);
|
||||
|
||||
// The owner was not the system user so we have to update the file.
|
||||
if ($ownerupdaterequired) {
|
||||
|
||||
$worked = $this->update_file_owner($userservice, $source->id, $systemuseremail);
|
||||
if (!$worked) {
|
||||
// Updating the owner only works for "google files" like documents etc. For binary
|
||||
// files we will get here.
|
||||
$source = $this->copy_file($systemservice, $source->id);
|
||||
|
||||
$readshareupdaterequired = true;
|
||||
$writerscanshare = true;
|
||||
|
||||
if ($canedit) {
|
||||
$this->add_writer_to_file($systemservice, $source->id, $useremail);
|
||||
}
|
||||
}
|
||||
|
||||
$this->move_file_from_root_to_folder($systemservice, $source->id, $parentid);
|
||||
}
|
||||
|
||||
if ($writerscanshare) {
|
||||
// We don't want anyone but Moodle to change the sharing settings.
|
||||
$this->prevent_writers_from_sharing_file($systemservice, $source->id);
|
||||
}
|
||||
|
||||
if ($readshareupdaterequired) {
|
||||
$this->set_file_sharing_anyone_with_link_can_read($systemservice, $source->id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit/Create Admin Settings Moodle form.
|
||||
*
|
||||
@@ -477,7 +854,8 @@ class repository_googledocs extends repository {
|
||||
* @param string $classname repository class name.
|
||||
*/
|
||||
public static function type_config_form($mform, $classname = 'repository') {
|
||||
$url = (string)new moodle_url('/admin/tool/oauth2/issuers.php');
|
||||
$url = new moodle_url('/admin/tool/oauth2/issuers.php');
|
||||
$url = $url->out();
|
||||
|
||||
$mform->addElement('static', null, '', get_string('oauth2serviceslink', 'repository_googledocs', $url));
|
||||
|
||||
@@ -488,11 +866,26 @@ class repository_googledocs extends repository {
|
||||
foreach ($issuers as $issuer) {
|
||||
$options[$issuer->get('id')] = s($issuer->get('name'));
|
||||
}
|
||||
|
||||
$strrequired = get_string('required');
|
||||
|
||||
$mform->addElement('select', 'issuerid', get_string('issuer', 'repository_googledocs'), $options);
|
||||
$mform->addHelpButton('issuerid', 'issuer', 'repository_googledocs');
|
||||
$mform->addRule('issuerid', $strrequired, 'required', null, 'client');
|
||||
|
||||
$strrequired = get_string('required');
|
||||
$mform->addElement('static', null, '', get_string('fileoptions', 'repository_googledocs'));
|
||||
$choices = [
|
||||
'internal' => get_string('internal', 'repository_googledocs'),
|
||||
'external' => get_string('external', 'repository_googledocs'),
|
||||
'both' => get_string('both', 'repository_googledocs')
|
||||
];
|
||||
$mform->addElement('select', 'supportedreturntypes', get_string('supportedreturntypes', 'repository_googledocs'), $choices);
|
||||
|
||||
$choices = [
|
||||
FILE_INTERNAL => get_string('internal', 'repository_googledocs'),
|
||||
FILE_REFERENCE => get_string('external', 'repository_googledocs'),
|
||||
];
|
||||
$mform->addElement('select', 'defaultreturntype', get_string('defaultreturntype', 'repository_googledocs'), $choices);
|
||||
|
||||
$mform->addElement('static', null, '', get_string('importformat', 'repository_googledocs'));
|
||||
|
||||
@@ -529,7 +922,8 @@ class repository_googledocs extends repository {
|
||||
$presentationformat['txt'] = 'txt';
|
||||
core_collator::ksort($presentationformat, core_collator::SORT_NATURAL);
|
||||
|
||||
$mform->addElement('select', 'presentationformat', get_string('presentationformat', 'repository_googledocs'), $presentationformat);
|
||||
$str = get_string('presentationformat', 'repository_googledocs');
|
||||
$mform->addElement('select', 'presentationformat', $str, $presentationformat);
|
||||
$mform->setDefault('presentationformat', $presentationformat['pptx']);
|
||||
$mform->setType('presentationformat', PARAM_ALPHANUM);
|
||||
|
||||
@@ -541,9 +935,14 @@ class repository_googledocs extends repository {
|
||||
$spreadsheetformat['xlsx'] = 'xlsx';
|
||||
core_collator::ksort($spreadsheetformat, core_collator::SORT_NATURAL);
|
||||
|
||||
$mform->addElement('select', 'spreadsheetformat', get_string('spreadsheetformat', 'repository_googledocs'), $spreadsheetformat);
|
||||
$str = get_string('spreadsheetformat', 'repository_googledocs');
|
||||
$mform->addElement('select', 'spreadsheetformat', $str, $spreadsheetformat);
|
||||
$mform->setDefault('spreadsheetformat', $spreadsheetformat['xlsx']);
|
||||
$mform->setType('spreadsheetformat', PARAM_ALPHANUM);
|
||||
}
|
||||
}
|
||||
|
||||
// Icon from: http://www.iconspedia.com/icon/google-2706.html.
|
||||
function repository_googledocs_oauth2_system_scopes() {
|
||||
return 'https://www.googleapis.com/auth/drive';
|
||||
}
|
||||
|
||||
@@ -25,6 +25,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2017030500; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2017030600; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2016112900; // Requires this Moodle version.
|
||||
$plugin->component = 'repository_googledocs'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -1281,6 +1281,19 @@ abstract class repository implements cacheable_object {
|
||||
public function cache_file_by_reference($reference, $storedfile) {
|
||||
}
|
||||
|
||||
/**
|
||||
* reference_file_selected
|
||||
* 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.
|
||||
* @return string updated reference (final one before it's saved to db).
|
||||
*/
|
||||
public function reference_file_selected($reference, $context) {
|
||||
return $reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the source information
|
||||
*
|
||||
@@ -1890,6 +1903,17 @@ abstract class repository implements cacheable_object {
|
||||
return (FILE_INTERNAL | FILE_EXTERNAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells how the file can be picked from this repository
|
||||
*
|
||||
* Maximum value is FILE_INTERNAL | FILE_EXTERNAL | FILE_REFERENCE
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function default_returntype() {
|
||||
return FILE_INTERNAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide repository instance information for Ajax
|
||||
*
|
||||
@@ -1904,6 +1928,7 @@ abstract class repository implements cacheable_object {
|
||||
$meta->icon = $OUTPUT->image_url('icon', 'repository_'.$meta->type)->out(false);
|
||||
$meta->supported_types = file_get_typegroup('extension', $this->supported_filetypes());
|
||||
$meta->return_types = $this->supported_returntypes();
|
||||
$meta->defaultreturntype = $this->default_returntype();
|
||||
$meta->sortorder = $this->options['sortorder'];
|
||||
return $meta;
|
||||
}
|
||||
|
||||
@@ -226,6 +226,11 @@ switch ($action) {
|
||||
$record->contenthash = $sourcefile->get_contenthash();
|
||||
$record->filesize = $sourcefile->get_filesize();
|
||||
}
|
||||
|
||||
// This hook gives the repo a place to do some house cleaning, and update the $reference before it's saved
|
||||
// to the file store. E.g. transfer ownership of the file to a system account etc.
|
||||
$reference = $repo->reference_file_selected($reference, $context);
|
||||
|
||||
// Check if file exists.
|
||||
if (repository::draftfile_exists($itemid, $saveas_path, $saveas_filename)) {
|
||||
// File name being used, rename it.
|
||||
|
||||
Reference in New Issue
Block a user