mirror of
https://github.com/moodle/moodle.git
synced 2025-01-17 13:38:32 +01:00
MDL-61768 repository_googledocs: Support shared drives
Enables the Google Drive repository to support browsing and searching for content from the existing shared drives.
This commit is contained in:
parent
241e778ca7
commit
54a850640d
120
repository/googledocs/classes/googledocs_content.php
Normal file
120
repository/googledocs/classes/googledocs_content.php
Normal file
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace repository_googledocs;
|
||||
|
||||
/**
|
||||
* Base class for presenting the googledocs repository contents.
|
||||
*
|
||||
* @package repository_googledocs
|
||||
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class googledocs_content {
|
||||
|
||||
/** @var rest The rest API object. */
|
||||
protected $service;
|
||||
|
||||
/** @var string The current path. */
|
||||
protected $path;
|
||||
|
||||
/** @var bool Whether sorting should be applied to the fetched content. */
|
||||
protected $sortcontent;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param rest $service The rest API object
|
||||
* @param string $path The current path
|
||||
* @param bool $sortcontent Whether sorting should be applied to the content
|
||||
*/
|
||||
public function __construct(rest $service, string $path, bool $sortcontent = true) {
|
||||
$this->service = $service;
|
||||
$this->path = $path;
|
||||
$this->sortcontent = $sortcontent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and return an array containing all repository node (files and folders) arrays for the existing content
|
||||
* based on the path or search query.
|
||||
*
|
||||
* @param string $query The search query
|
||||
* @param callable $isaccepted The callback function which determines whether a given file should be displayed
|
||||
* or filtered based on the existing file restrictions
|
||||
* @return array The array containing the repository content node arrays
|
||||
*/
|
||||
public function get_content_nodes(string $query, callable $isaccepted): array {
|
||||
$files = [];
|
||||
$folders = [];
|
||||
|
||||
foreach ($this->get_contents($query) as $gdcontent) {
|
||||
$node = helper::get_node($gdcontent, $this->path);
|
||||
// Create the repository node array.
|
||||
$nodearray = $node->create_node_array();
|
||||
// If the repository node array was successfully generated and the type of the content is accepted,
|
||||
// add it to the repository content nodes array.
|
||||
if ($nodearray && $isaccepted($nodearray)) {
|
||||
// Group the content nodes by type (files and folders). Generate unique array keys for each content node
|
||||
// which will be later used by the sorting function. Note: Using the item id along with the name as key
|
||||
// of the array because Google Drive allows files and folders with identical names.
|
||||
if (isset($nodearray['source'])) { // If the content node has a source attribute, it is a file node.
|
||||
$files["{$nodearray['title']}{$nodearray['id']}"] = $nodearray;
|
||||
} else {
|
||||
$folders["{$nodearray['title']}{$nodearray['id']}"] = $nodearray;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If sorting is required, order the results alphabetically by their array keys.
|
||||
if ($this->sortcontent) {
|
||||
\core_collator::ksort($files, \core_collator::SORT_STRING);
|
||||
\core_collator::ksort($folders, \core_collator::SORT_STRING);
|
||||
}
|
||||
|
||||
return array_merge(array_values($folders), array_values($files));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the navigation (breadcrumb) from a given path.
|
||||
*
|
||||
* @return array Array containing name and path of each navigation node
|
||||
*/
|
||||
public function get_navigation(): array {
|
||||
$nav = [];
|
||||
$navtrail = '';
|
||||
$pathnodes = explode('/', $this->path);
|
||||
|
||||
foreach ($pathnodes as $node) {
|
||||
list($id, $name) = helper::explode_node_path($node);
|
||||
$name = empty($name) ? $id : $name;
|
||||
$nav[] = [
|
||||
'name' => $name,
|
||||
'path' => helper::build_node_path($id, $name, $navtrail),
|
||||
];
|
||||
$tmp = end($nav);
|
||||
$navtrail = $tmp['path'];
|
||||
}
|
||||
|
||||
return $nav;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all relevant contents (files and folders) based on the given path or search query.
|
||||
*
|
||||
* @param string $query The search query
|
||||
* @return array The array containing the contents
|
||||
*/
|
||||
abstract protected function get_contents(string $query): array;
|
||||
}
|
67
repository/googledocs/classes/googledocs_content_search.php
Normal file
67
repository/googledocs/classes/googledocs_content_search.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace repository_googledocs;
|
||||
|
||||
/**
|
||||
* Utility class for displaying google drive content that matched a given search criteria.
|
||||
*
|
||||
* This class is responsible for generating the content that is returned based on a given search query.
|
||||
*
|
||||
* @package repository_googledocs
|
||||
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class googledocs_content_search extends googledocs_content {
|
||||
|
||||
/**
|
||||
* Returns all relevant contents based on the given path and/or search query.
|
||||
*
|
||||
* The method fetches all content (files) through an API call that matches a given search criteria.
|
||||
*
|
||||
* @param string $query The search query
|
||||
* @return array The array containing the contents
|
||||
*/
|
||||
protected function get_contents(string $query): array {
|
||||
$searchterm = str_replace("'", "\'", $query);
|
||||
|
||||
// Define the parameters required by the API call.
|
||||
// Query all contents which name contains $searchterm and have not been trashed.
|
||||
$q = "fullText contains '{$searchterm}' AND trashed = false";
|
||||
// The file fields that should be returned in the response.
|
||||
$fields = "files(id,name,mimeType,webContentLink,webViewLink,fileExtension,modifiedTime,size,iconLink)";
|
||||
|
||||
$params = [
|
||||
'q' => $q,
|
||||
'fields' => $fields,
|
||||
'spaces' => 'drive',
|
||||
];
|
||||
|
||||
// If shared drives exist, include the additional required parameters in order to extend the content search
|
||||
// into the shared drives area as well.
|
||||
$response = helper::request($this->service, 'shared_drives_list', []);
|
||||
if (!empty($response->drives)) {
|
||||
$params['supportsAllDrives'] = 'true';
|
||||
$params['includeItemsFromAllDrives'] = 'true';
|
||||
$params['corpora'] = 'allDrives';
|
||||
}
|
||||
|
||||
// Request the content through the API call.
|
||||
$response = helper::request($this->service, 'list', $params);
|
||||
|
||||
return $response->files ?? [];
|
||||
}
|
||||
}
|
143
repository/googledocs/classes/helper.php
Normal file
143
repository/googledocs/classes/helper.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace repository_googledocs;
|
||||
|
||||
use repository_googledocs\local\browser\googledocs_root_content;
|
||||
use repository_googledocs\local\browser\googledocs_shared_drives_content;
|
||||
use repository_googledocs\local\browser\googledocs_drive_content;
|
||||
use repository_googledocs\local\node\node;
|
||||
use repository_googledocs\local\node\file_node;
|
||||
use repository_googledocs\local\node\folder_node;
|
||||
|
||||
/**
|
||||
* Helper class for the googledocs repository.
|
||||
*
|
||||
* @package repository_googledocs
|
||||
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class helper {
|
||||
|
||||
/**
|
||||
* Generates a safe path to a node.
|
||||
*
|
||||
* Typically, a node will be id|name of the node.
|
||||
*
|
||||
* @param string $id The ID of the node
|
||||
* @param string $name The name of the node, will be URL encoded
|
||||
* @param string $root The path to append the node on (must be a result of this function)
|
||||
* @return string The path to the node
|
||||
*/
|
||||
public static function build_node_path(string $id, string $name = '', string $root = ''): string {
|
||||
$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.
|
||||
*
|
||||
* @param string $node The node string to extract information from
|
||||
* @return array The array containing the information about the node
|
||||
* @see self::build_node_path()
|
||||
*/
|
||||
public static function explode_node_path(string $node): array {
|
||||
if (strpos($node, '|') !== false) {
|
||||
list($id, $name) = explode('|', $node, 2);
|
||||
$name = urldecode($name);
|
||||
} else {
|
||||
$id = $node;
|
||||
$name = '';
|
||||
}
|
||||
$id = urldecode($id);
|
||||
|
||||
return [
|
||||
0 => $id,
|
||||
1 => $name,
|
||||
'id' => $id,
|
||||
'name' => $name,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relevant googledocs content browser class based on the given path.
|
||||
*
|
||||
* @param rest $service The rest API object
|
||||
* @param string $path The current path
|
||||
* @return googledocs_content The googledocs repository content browser
|
||||
*/
|
||||
public static function get_browser(rest $service, string $path): googledocs_content {
|
||||
$pathnodes = explode('/', $path);
|
||||
$currentnode = self::explode_node_path(array_pop($pathnodes));
|
||||
|
||||
// Return the relevant content browser class based on the ID of the current path node.
|
||||
switch ($currentnode['id']) {
|
||||
case \repository_googledocs::REPOSITORY_ROOT_ID:
|
||||
return new googledocs_root_content($service, $path, false);
|
||||
case \repository_googledocs::SHARED_DRIVES_ROOT_ID:
|
||||
return new googledocs_shared_drives_content($service, $path);
|
||||
default:
|
||||
return new googledocs_drive_content($service, $path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relevant repository content node class based on the Google Drive file's mimetype.
|
||||
*
|
||||
* @param \stdClass $gdcontent The Google Drive content (file/folder) object
|
||||
* @param string $path The current path
|
||||
* @return node The content node object
|
||||
*/
|
||||
public static function get_node(\stdClass $gdcontent, string $path): node {
|
||||
// Return the relevant content browser class based on the ID of the current path node.
|
||||
switch ($gdcontent->mimeType) {
|
||||
case 'application/vnd.google-apps.folder':
|
||||
return new folder_node($gdcontent, $path);
|
||||
default:
|
||||
return new file_node($gdcontent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function to perform an API call and also catch and handle potential exceptions.
|
||||
*
|
||||
* @param rest $service The rest API object
|
||||
* @param string $api The name of the API call
|
||||
* @param array $params The parameters required by the API call
|
||||
* @return \stdClass The response object
|
||||
* @throws \repository_exception
|
||||
*/
|
||||
public static function request(rest $service, string $api, array $params): ?\stdClass {
|
||||
try {
|
||||
// Retrieving files and folders.
|
||||
$response = $service->call($api, $params);
|
||||
} catch (\Exception $e) {
|
||||
if ($e->getCode() == 403 && strpos($e->getMessage(), 'Access Not Configured') !== false) {
|
||||
// This is raised when the service Drive API has not been enabled on Google APIs control panel.
|
||||
throw new \repository_exception('servicenotenabled', 'repository_googledocs');
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace repository_googledocs\local\browser;
|
||||
|
||||
use repository_googledocs\googledocs_content;
|
||||
use repository_googledocs\helper;
|
||||
|
||||
/**
|
||||
* Utility class for browsing content from or within a specified google drive.
|
||||
*
|
||||
* This class is responsible for generating the content that would be displayed for a specified drive such as
|
||||
* 'my drive' or any existing shared drive. It also supports generating data for paths which are located
|
||||
* within a given drive.
|
||||
*
|
||||
* @package repository_googledocs
|
||||
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class googledocs_drive_content extends googledocs_content {
|
||||
|
||||
/**
|
||||
* Returns all relevant contents based on the given path or search query.
|
||||
*
|
||||
* The method fetches all existing content (files and folders) located in a specific folder under a given drive
|
||||
* through an API call.
|
||||
*
|
||||
* @param string $query The search query
|
||||
* @return array The array containing the contents
|
||||
*/
|
||||
protected function get_contents(string $query): array {
|
||||
|
||||
$id = str_replace("'", "\'", $query);
|
||||
// Define the parameters required by the API call.
|
||||
// Query all files and folders which have not been trashed and located directly in the folder whose ID is $id.
|
||||
$q = "'{$id}' in parents AND trashed = false";
|
||||
// The file fields that should be returned in the response.
|
||||
$fields = 'files(id,name,mimeType,webContentLink,webViewLink,fileExtension,modifiedTime,size,iconLink)';
|
||||
|
||||
$params = [
|
||||
'q' => $q,
|
||||
'fields' => $fields,
|
||||
'spaces' => 'drive',
|
||||
];
|
||||
|
||||
// Check whether there are any shared drives.
|
||||
$response = helper::request($this->service, 'shared_drives_list', []);
|
||||
if (!empty($response->drives)) {
|
||||
// To be able to include content from shared drives, we need to enable 'supportsAllDrives' and
|
||||
// 'includeItemsFromAllDrives'. The Google Drive API requires explicit request for inclusion of content from
|
||||
// shared drives and also a confirmation that the application is designed to handle files on shared drives.
|
||||
$params['supportsAllDrives'] = 'true';
|
||||
$params['includeItemsFromAllDrives'] = 'true';
|
||||
}
|
||||
|
||||
// Request the content through the API call.
|
||||
$response = helper::request($this->service, 'list', $params);
|
||||
|
||||
return $response->files ?? [];
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace repository_googledocs\local\browser;
|
||||
|
||||
use repository_googledocs\googledocs_content;
|
||||
use repository_googledocs\helper;
|
||||
|
||||
/**
|
||||
* Utility class for browsing the content within the googledocs repository root.
|
||||
*
|
||||
* This class is responsible for generating the content that would be displayed in the googledocs repository root.
|
||||
*
|
||||
* @package repository_googledocs
|
||||
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class googledocs_root_content extends googledocs_content {
|
||||
|
||||
/**
|
||||
* Returns all relevant contents based on the given path or search query.
|
||||
*
|
||||
* The method predefines the content which will be displayed in the repository root level. Currently,
|
||||
* only the folders representing 'My drives' and 'Shared drives' will be displayed in the root level.
|
||||
*
|
||||
* @param string $query The search query
|
||||
* @return array The array containing the contents
|
||||
*/
|
||||
protected function get_contents(string $query): array {
|
||||
// Add 'My drive' folder into the displayed contents.
|
||||
$contents = [
|
||||
(object)[
|
||||
'id' => \repository_googledocs::MY_DRIVE_ROOT_ID,
|
||||
'name' => get_string('mydrive', 'repository_googledocs'),
|
||||
'mimeType' => 'application/vnd.google-apps.folder',
|
||||
'modifiedTime' => '',
|
||||
],
|
||||
];
|
||||
|
||||
// If shared drives exists, include 'Shared drives' folder to the displayed contents.
|
||||
$response = helper::request($this->service, 'shared_drives_list', []);
|
||||
|
||||
if (!empty($response->drives)) {
|
||||
$contents[] = (object)[
|
||||
'id' => \repository_googledocs::SHARED_DRIVES_ROOT_ID,
|
||||
'name' => get_string('shareddrives', 'repository_googledocs'),
|
||||
'mimeType' => 'application/vnd.google-apps.folder',
|
||||
'modifiedTime' => '',
|
||||
];
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace repository_googledocs\local\browser;
|
||||
|
||||
use repository_googledocs\googledocs_content;
|
||||
use repository_googledocs\helper;
|
||||
|
||||
/**
|
||||
* Utility class for browsing the content within the googledocs repository shared drives root.
|
||||
*
|
||||
* This class is responsible for generating the content that would be displayed in the googledocs repository
|
||||
* shared drives root.
|
||||
*
|
||||
* @package repository_googledocs
|
||||
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class googledocs_shared_drives_content extends googledocs_content {
|
||||
|
||||
/**
|
||||
* Returns all relevant contents based on the given path or search query.
|
||||
*
|
||||
* The method generates the content which will be displayed in the repository shared drives root level.
|
||||
* All existing shared drives will be fetched through an API call and presented as folders.
|
||||
*
|
||||
* @param string $query The search query
|
||||
* @return array The array containing the contents
|
||||
*/
|
||||
protected function get_contents(string $query): array {
|
||||
// Make an API request to get all existing shared drives.
|
||||
$response = helper::request($this->service, 'shared_drives_list', []);
|
||||
// If shared drives exist, create folder for each shared drive.
|
||||
if ($shareddrives = $response->drives) {
|
||||
return array_map(function($shareddrive) {
|
||||
return (object)[
|
||||
'id' => $shareddrive->id,
|
||||
'name' => $shareddrive->name,
|
||||
'mimeType' => 'application/vnd.google-apps.folder',
|
||||
'modifiedTime' => '',
|
||||
];
|
||||
}, $shareddrives);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
200
repository/googledocs/classes/local/node/file_node.php
Normal file
200
repository/googledocs/classes/local/node/file_node.php
Normal file
@ -0,0 +1,200 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace repository_googledocs\local\node;
|
||||
|
||||
/**
|
||||
* Class used to represent a file node in the googledocs repository.
|
||||
*
|
||||
* @package repository_googledocs
|
||||
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class file_node implements node {
|
||||
|
||||
/** @var string The ID of the file node. */
|
||||
private $id;
|
||||
|
||||
/** @var string|null The title of the file node. */
|
||||
private $title;
|
||||
|
||||
/** @var string|null The file's export format. */
|
||||
private $exportformat;
|
||||
|
||||
/** @var string The external link to the file. */
|
||||
private $link;
|
||||
|
||||
/** @var string The timestamp representing the last modified date. */
|
||||
private $modified;
|
||||
|
||||
/** @var string|null The size of the file. */
|
||||
private $size;
|
||||
|
||||
/** @var string The thumbnail of the file. */
|
||||
private $thumbnail;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \stdClass $gdfile The Google Drive file object
|
||||
*/
|
||||
public function __construct(\stdClass $gdfile) {
|
||||
$this->id = $gdfile->id;
|
||||
$this->title = $this->generate_file_title($gdfile);
|
||||
$this->exportformat = $this->generate_file_export_format($gdfile);
|
||||
$this->link = $this->generate_file_link($gdfile);
|
||||
$this->modified = ($gdfile->modifiedTime) ? strtotime($gdfile->modifiedTime) : '';
|
||||
$this->size = !empty($gdfile->size) ? $gdfile->size : null;
|
||||
// Use iconLink as a file thumbnail if set, otherwise use the default icon depending on the file type.
|
||||
// Note: The Google Drive API can return a link to a preview thumbnail of the file (via thumbnailLink).
|
||||
// However, in many cases the Google Drive files are not public and an authorized request is required
|
||||
// to get the thumbnail which we currently do not support. Therefore, to avoid displaying broken
|
||||
// thumbnail images in the repository, the icon of the Google Drive file is being used as a thumbnail
|
||||
// instead as it does not require an authorized request.
|
||||
// Currently, the returned file icon link points to the 16px version of the icon by default which would result
|
||||
// in displaying 16px file thumbnails in the repository. To avoid this, the link can be slightly modified in
|
||||
// order to get a larger version of the icon as there isn't an option to request this through the API call.
|
||||
$this->thumbnail = !empty($gdfile->iconLink) ? str_replace('/16/', '/64/', $gdfile->iconLink) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a repository file array.
|
||||
*
|
||||
* This method returns an array which structure is compatible to represent a file node in the repository.
|
||||
*
|
||||
* @return array|null The node array or null if the node could not be created
|
||||
*/
|
||||
public function create_node_array(): ?array {
|
||||
// Cannot create the file node if the file title was not generated or the export format.
|
||||
// This means that the current file type is invalid or unknown.
|
||||
if (!$this->title || !$this->exportformat) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'title' => $this->title,
|
||||
'source' => json_encode(
|
||||
[
|
||||
'id' => $this->id,
|
||||
'name' => $this->title,
|
||||
'link' => $this->link,
|
||||
'exportformat' => $this->exportformat,
|
||||
]
|
||||
),
|
||||
'date' => $this->modified,
|
||||
'size' => $this->size,
|
||||
'thumbnail' => $this->thumbnail,
|
||||
'thumbnail_height' => 64,
|
||||
'thumbnail_width' => 64,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and returns the title for the file node depending on the type of the Google drive file.
|
||||
*
|
||||
* @param \stdClass $gdfile The Google Drive file object
|
||||
* @return string The file title
|
||||
*/
|
||||
private function generate_file_title(\stdClass $gdfile): ?string {
|
||||
// Determine the file type through the file extension.
|
||||
if (isset($gdfile->fileExtension)) { // The file is a regular file.
|
||||
return $gdfile->name;
|
||||
} else { // The file is probably a Google Doc file.
|
||||
// We need to generate the name by appending the proper google doc extension.
|
||||
$type = str_replace('application/vnd.google-apps.', '', $gdfile->mimeType);
|
||||
|
||||
if ($type === 'document') {
|
||||
return "{$gdfile->name}.gdoc";
|
||||
}
|
||||
if ($type === 'presentation') {
|
||||
return "{$gdfile->name}.gslides";
|
||||
}
|
||||
if ($type === 'spreadsheet') {
|
||||
return "{$gdfile->name}.gsheet";
|
||||
}
|
||||
if ($type === 'drawing') {
|
||||
$config = get_config('googledocs');
|
||||
$ext = $config->drawingformat;
|
||||
return "{$gdfile->name}.{$ext}";
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and returns the file export format depending on the type of the Google drive file.
|
||||
*
|
||||
* @param \stdClass $gdfile The Google Drive file object
|
||||
* @return string The file export format
|
||||
*/
|
||||
private function generate_file_export_format(\stdClass $gdfile): ?string {
|
||||
// Determine the file type through the file extension.
|
||||
if (isset($gdfile->fileExtension)) { // The file is a regular file.
|
||||
// The file has an extension, therefore we can download it.
|
||||
return 'download';
|
||||
} else {
|
||||
// The file is probably a Google Doc file, we get the corresponding export link.
|
||||
$type = str_replace('application/vnd.google-apps.', '', $gdfile->mimeType);
|
||||
$types = get_mimetypes_array();
|
||||
$config = get_config('googledocs');
|
||||
|
||||
if ($type === 'document' && !empty($config->documentformat)) {
|
||||
$ext = $config->documentformat;
|
||||
if ($ext === 'rtf') {
|
||||
// Moodle user 'text/rtf' as the MIME type for RTF files.
|
||||
// Google uses 'application/rtf' for the same type of file.
|
||||
// See https://developers.google.com/drive/v3/web/manage-downloads.
|
||||
return 'application/rtf';
|
||||
} else {
|
||||
return $types[$ext]['type'];
|
||||
}
|
||||
}
|
||||
if ($type === 'presentation' && !empty($config->presentationformat)) {
|
||||
$ext = $config->presentationformat;
|
||||
return $types[$ext]['type'];
|
||||
}
|
||||
if ($type === 'spreadsheet' && !empty($config->spreadsheetformat)) {
|
||||
$ext = $config->spreadsheetformat;
|
||||
return $types[$ext]['type'];
|
||||
}
|
||||
if ($type === 'drawing' && !empty($config->drawingformat)) {
|
||||
$ext = $config->drawingformat;
|
||||
return $types[$ext]['type'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and returns the external link to the file.
|
||||
*
|
||||
* @param \stdClass $gdfile The Google Drive file object
|
||||
* @return string The link to the file
|
||||
*/
|
||||
private function generate_file_link(\stdClass $gdfile): string {
|
||||
// If the google drive file has webViewLink set, use it as an external link.
|
||||
$link = !empty($gdfile->webViewLink) ? $gdfile->webViewLink : '';
|
||||
// Otherwise, use webContentLink if set or leave the external link empty.
|
||||
if (empty($link) && !empty($gdfile->webContentLink)) {
|
||||
$link = $gdfile->webContentLink;
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
}
|
76
repository/googledocs/classes/local/node/folder_node.php
Normal file
76
repository/googledocs/classes/local/node/folder_node.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace repository_googledocs\local\node;
|
||||
|
||||
use repository_googledocs\helper;
|
||||
|
||||
/**
|
||||
* Class used to represent a folder node in the googledocs repository.
|
||||
*
|
||||
* @package repository_googledocs
|
||||
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class folder_node implements node {
|
||||
|
||||
/** @var string The ID of the folder node. */
|
||||
private $id;
|
||||
|
||||
/** @var string The title of the folder node. */
|
||||
private $title;
|
||||
|
||||
/** @var bool The timestamp representing the last modified date. */
|
||||
private $modified;
|
||||
|
||||
/** @var string The path of the folder node. */
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \stdClass $gdfolder The Google Drive folder object
|
||||
* @param string $path The path of the folder node
|
||||
*/
|
||||
public function __construct(\stdClass $gdfolder, string $path) {
|
||||
$this->id = $gdfolder->id;
|
||||
$this->title = $gdfolder->name;
|
||||
$this->modified = $gdfolder->modifiedTime ? strtotime($gdfolder->modifiedTime) : '';
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a repository folder array.
|
||||
*
|
||||
* This method returns an array which structure is compatible to represent a folder node in the repository.
|
||||
*
|
||||
* @return array|null The node array or null if the node could not be created
|
||||
*/
|
||||
public function create_node_array(): ?array {
|
||||
global $OUTPUT;
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'title' => $this->title,
|
||||
'path' => helper::build_node_path($this->id, $this->title, $this->path),
|
||||
'date' => $this->modified,
|
||||
'thumbnail' => $OUTPUT->image_url(file_folder_icon(64))->out(false),
|
||||
'thumbnail_height' => 64,
|
||||
'thumbnail_width' => 64,
|
||||
'children' => [],
|
||||
];
|
||||
}
|
||||
}
|
37
repository/googledocs/classes/local/node/node.php
Normal file
37
repository/googledocs/classes/local/node/node.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace repository_googledocs\local\node;
|
||||
|
||||
/**
|
||||
* The googledocs repository content node interface.
|
||||
*
|
||||
* @package repository_googledocs
|
||||
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
interface node {
|
||||
|
||||
/**
|
||||
* Create a repository node array.
|
||||
*
|
||||
* This method returns an array which structure is compatible to represent a content node (file/folder)
|
||||
* in the repository.
|
||||
*
|
||||
* @return array|null The node array or null if the node could not be created
|
||||
*/
|
||||
public function create_node_array(): ?array;
|
||||
}
|
@ -27,14 +27,16 @@ $string['docsformat'] = 'Default document import format';
|
||||
$string['drawingformat'] = 'Default drawing import format';
|
||||
$string['googledocs:view'] = 'View Google Drive repository';
|
||||
$string['importformat'] = 'Configure the default import formats from Google';
|
||||
$string['mydrive'] = 'My Drive';
|
||||
$string['pluginname'] = 'Google Drive';
|
||||
$string['presentationformat'] = 'Default presentation import format';
|
||||
$string['shareddrives'] = 'Shared Drives';
|
||||
$string['spreadsheetformat'] = 'Default spreadsheet import format';
|
||||
$string['issuer'] = 'OAuth 2 service';
|
||||
$string['issuer_help'] = 'Select the OAuth 2 service that is configured to talk to the Google Drive API. If the service does not exist yet, you will need to create it.';
|
||||
$string['servicenotenabled'] = 'Access not configured. Make sure the service \'Drive API\' is enabled.';
|
||||
$string['oauth2serviceslink'] = '<a href="{$a}" title="Link to OAuth 2 services configuration">OAuth 2 services configuration</a>';
|
||||
$string['searchfor'] = 'Search for {$a}';
|
||||
$string['searchfor'] = 'Search results for:';
|
||||
$string['internal'] = 'Internal (files stored in Moodle)';
|
||||
$string['external'] = 'External (only links stored in Moodle)';
|
||||
$string['both'] = 'Internal and external';
|
||||
|
@ -28,6 +28,9 @@ defined('MOODLE_INTERNAL') || die();
|
||||
require_once($CFG->dirroot . '/repository/lib.php');
|
||||
require_once($CFG->libdir . '/filebrowser/file_browser.php');
|
||||
|
||||
use repository_googledocs\helper;
|
||||
use repository_googledocs\googledocs_content_search;
|
||||
|
||||
/**
|
||||
* Google Docs Plugin
|
||||
*
|
||||
@ -55,6 +58,18 @@ class repository_googledocs extends repository {
|
||||
*/
|
||||
const SCOPES = 'https://www.googleapis.com/auth/drive';
|
||||
|
||||
/** @var string Defines the path node identifier for the repository root. */
|
||||
const REPOSITORY_ROOT_ID = 'repository_root';
|
||||
|
||||
/** @var string Defines the path node identifier for the my drive root. */
|
||||
const MY_DRIVE_ROOT_ID = 'root';
|
||||
|
||||
/** @var string Defines the path node identifier for the shared drives root. */
|
||||
const SHARED_DRIVES_ROOT_ID = 'shared_drives_root';
|
||||
|
||||
/** @var string Defines the path node identifier for the content search root. */
|
||||
const SEARCH_ROOT_ID = 'search';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@ -234,38 +249,48 @@ class repository_googledocs extends repository {
|
||||
*/
|
||||
public function get_listing($path='', $page = '') {
|
||||
if (empty($path)) {
|
||||
$path = $this->build_node_path('root', get_string('pluginname', 'repository_googledocs'));
|
||||
$pluginname = get_string('pluginname', 'repository_googledocs');
|
||||
$path = helper::build_node_path('repository_root', $pluginname);
|
||||
}
|
||||
|
||||
if (!$this->issuer->get('enabled')) {
|
||||
// Empty list of files for disabled repository.
|
||||
return ['dynload' => false, 'list' => [], 'nologin' => true];
|
||||
return [
|
||||
'dynload' => false,
|
||||
'list' => [],
|
||||
'nologin' => true,
|
||||
];
|
||||
}
|
||||
|
||||
// We analyse the path to extract what to browse.
|
||||
$trail = explode('/', $path);
|
||||
$uri = array_pop($trail);
|
||||
list($id, $name) = $this->explode_node_path($uri);
|
||||
list($id, $name) = helper::explode_node_path($uri);
|
||||
$service = new repository_googledocs\rest($this->get_user_oauth_client());
|
||||
|
||||
// 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 set up by Google, or a parent (folder) ID.
|
||||
if ($id === 'search') {
|
||||
return $this->search($name);
|
||||
// Define the content class object and query which will be used to get the contents for this path.
|
||||
if ($id === self::SEARCH_ROOT_ID) {
|
||||
// The special keyword 'search' is the ID of the node. This is possible as we can set up a breadcrumb in
|
||||
// the search results. Therefore, we should use the content search object to get the results from the
|
||||
// previously performed search.
|
||||
$contentobj = new googledocs_content_search($service, $path);
|
||||
// We need to deconstruct the node name in order to obtain the search term and use it as a query.
|
||||
$query = str_replace(get_string('searchfor', 'repository_googledocs'), '', $name);
|
||||
$query = trim(str_replace("'", "", $query));
|
||||
} else {
|
||||
// Otherwise, return and use the appropriate (based on the path) content browser object.
|
||||
$contentobj = helper::get_browser($service, $path);
|
||||
// Use the node ID as a query.
|
||||
$query = $id;
|
||||
}
|
||||
|
||||
// Query the Drive.
|
||||
$q = "'" . str_replace("'", "\'", $id) . "' in parents";
|
||||
$q .= ' AND trashed = false';
|
||||
$results = $this->query($q, $path);
|
||||
|
||||
$ret = array();
|
||||
$ret['dynload'] = true;
|
||||
$ret['defaultreturntype'] = $this->default_returntype();
|
||||
$ret['path'] = $this->build_breadcrumb($path);
|
||||
$ret['list'] = $results;
|
||||
$ret['manage'] = 'https://drive.google.com/';
|
||||
|
||||
return $ret;
|
||||
return [
|
||||
'dynload' => true,
|
||||
'defaultreturntype' => $this->default_returntype(),
|
||||
'path' => $contentobj->get_navigation(),
|
||||
'list' => $contentobj->get_content_nodes($query, [$this, 'filter']),
|
||||
'manage' => 'https://drive.google.com/',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,21 +301,25 @@ class repository_googledocs extends repository {
|
||||
* @return array of results.
|
||||
*/
|
||||
public function search($searchtext, $page = 0) {
|
||||
$path = $this->build_node_path('root', get_string('pluginname', 'repository_googledocs'));
|
||||
$str = get_string('searchfor', 'repository_googledocs', $searchtext);
|
||||
$path = $this->build_node_path('search', $str, $path);
|
||||
// Construct the path to the repository root.
|
||||
$pluginname = get_string('pluginname', 'repository_googledocs');
|
||||
$rootpath = helper::build_node_path(self::REPOSITORY_ROOT_ID, $pluginname);
|
||||
// Construct the path to the search results node.
|
||||
// Currently, when constructing the search node name, the search term is concatenated to the lang string.
|
||||
// This was done deliberately so that we can easily and accurately obtain the search term from the search node
|
||||
// name later when navigating to the search results through the breadcrumb navigation.
|
||||
$name = get_string('searchfor', 'repository_googledocs') . " '{$searchtext}'";
|
||||
$path = helper::build_node_path(self::SEARCH_ROOT_ID, $name, $rootpath);
|
||||
|
||||
// Query the Drive.
|
||||
$q = "fullText contains '" . str_replace("'", "\'", $searchtext) . "'";
|
||||
$q .= ' AND trashed = false';
|
||||
$results = $this->query($q, $path);
|
||||
$service = new repository_googledocs\rest($this->get_user_oauth_client());
|
||||
$searchobj = new googledocs_content_search($service, $path);
|
||||
|
||||
$ret = array();
|
||||
$ret['dynload'] = true;
|
||||
$ret['path'] = $this->build_breadcrumb($path);
|
||||
$ret['list'] = $results;
|
||||
$ret['manage'] = 'https://drive.google.com/';
|
||||
return $ret;
|
||||
return [
|
||||
'dynload' => true,
|
||||
'path' => $searchobj->get_navigation(),
|
||||
'list' => $searchobj->get_content_nodes($searchtext, [$this, 'filter']),
|
||||
'manage' => 'https://drive.google.com/',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,6 +25,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2021052500; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2021052501; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2021052500; // Requires this Moodle version.
|
||||
$plugin->component = 'repository_googledocs'; // Full name of the plugin (used for diagnostics).
|
||||
|
@ -6,6 +6,7 @@ http://docs.moodle.org/dev/Repository_API
|
||||
=== 3.11 ===
|
||||
* The Google Drive repository now includes a new rest API function 'shared_drives_list', which can be used to fetch
|
||||
a list of existing shared drives.
|
||||
* The Google Drive repository now supports browsing and searching for content from shared drives.
|
||||
|
||||
=== 3.4 ===
|
||||
Repositories should no longer directly call file_system#add_file_to_pool or file_system#add_string_to_pool
|
||||
|
Loading…
x
Reference in New Issue
Block a user