mirrored from git://git.moodle.org/moodle.git
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MDL-61768 repository_googledocs: Support shared drives
Enables the Google Drive repository to support browsing and searching for content from the existing shared drives.
- Loading branch information
Mihail Geshoski
committed
Apr 15, 2021
1 parent
2950a9a
commit bded723
Showing
13 changed files
with
915 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 changes: 67 additions & 0 deletions
67
repository/googledocs/classes/googledocs_content_search.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ?? []; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
Oops, something went wrong.