[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * This plugin is used to access Google Drive. 19 * 20 * @since Moodle 2.0 21 * @package repository_googledocs 22 * @copyright 2009 Dan Poltawski <talktodan@gmail.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 require_once($CFG->dirroot . '/repository/lib.php'); 29 require_once($CFG->libdir . '/google/lib.php'); 30 31 /** 32 * Google Docs Plugin 33 * 34 * @since Moodle 2.0 35 * @package repository_googledocs 36 * @copyright 2009 Dan Poltawski <talktodan@gmail.com> 37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 */ 39 class repository_googledocs extends repository { 40 41 /** 42 * Google Client. 43 * @var Google_Client 44 */ 45 private $client = null; 46 47 /** 48 * Google Drive Service. 49 * @var Google_Drive_Service 50 */ 51 private $service = null; 52 53 /** 54 * Session key to store the accesstoken. 55 * @var string 56 */ 57 const SESSIONKEY = 'googledrive_accesstoken'; 58 59 /** 60 * URI to the callback file for OAuth. 61 * @var string 62 */ 63 const CALLBACKURL = '/admin/oauth2callback.php'; 64 65 /** 66 * Constructor. 67 * 68 * @param int $repositoryid repository instance id. 69 * @param int|stdClass $context a context id or context object. 70 * @param array $options repository options. 71 * @param int $readonly indicate this repo is readonly or not. 72 * @return void 73 */ 74 public function __construct($repositoryid, $context = SYSCONTEXTID, $options = array(), $readonly = 0) { 75 parent::__construct($repositoryid, $context, $options, $readonly = 0); 76 77 $callbackurl = new moodle_url(self::CALLBACKURL); 78 79 $this->client = get_google_client(); 80 $this->client->setClientId(get_config('googledocs', 'clientid')); 81 $this->client->setClientSecret(get_config('googledocs', 'secret')); 82 $this->client->setScopes(array(Google_Service_Drive::DRIVE_READONLY)); 83 $this->client->setRedirectUri($callbackurl->out(false)); 84 $this->service = new Google_Service_Drive($this->client); 85 86 $this->check_login(); 87 } 88 89 /** 90 * Returns the access token if any. 91 * 92 * @return string|null access token. 93 */ 94 protected function get_access_token() { 95 global $SESSION; 96 if (isset($SESSION->{self::SESSIONKEY})) { 97 return $SESSION->{self::SESSIONKEY}; 98 } 99 return null; 100 } 101 102 /** 103 * Store the access token in the session. 104 * 105 * @param string $token token to store. 106 * @return void 107 */ 108 protected function store_access_token($token) { 109 global $SESSION; 110 $SESSION->{self::SESSIONKEY} = $token; 111 } 112 113 /** 114 * Callback method during authentication. 115 * 116 * @return void 117 */ 118 public function callback() { 119 if ($code = optional_param('oauth2code', null, PARAM_RAW)) { 120 $this->client->authenticate($code); 121 $this->store_access_token($this->client->getAccessToken()); 122 } 123 } 124 125 /** 126 * Checks whether the user is authenticate or not. 127 * 128 * @return bool true when logged in. 129 */ 130 public function check_login() { 131 if ($token = $this->get_access_token()) { 132 $this->client->setAccessToken($token); 133 return true; 134 } 135 return false; 136 } 137 138 /** 139 * Print or return the login form. 140 * 141 * @return void|array for ajax. 142 */ 143 public function print_login() { 144 $returnurl = new moodle_url('/repository/repository_callback.php'); 145 $returnurl->param('callback', 'yes'); 146 $returnurl->param('repo_id', $this->id); 147 $returnurl->param('sesskey', sesskey()); 148 149 $url = new moodle_url($this->client->createAuthUrl()); 150 $url->param('state', $returnurl->out_as_local_url(false)); 151 if ($this->options['ajax']) { 152 $popup = new stdClass(); 153 $popup->type = 'popup'; 154 $popup->url = $url->out(false); 155 return array('login' => array($popup)); 156 } else { 157 echo '<a target="_blank" href="'.$url->out(false).'">'.get_string('login', 'repository').'</a>'; 158 } 159 } 160 161 /** 162 * Build the breadcrumb from a path. 163 * 164 * @param string $path to create a breadcrumb from. 165 * @return array containing name and path of each crumb. 166 */ 167 protected function build_breadcrumb($path) { 168 $bread = explode('/', $path); 169 $crumbtrail = ''; 170 foreach ($bread as $crumb) { 171 list($id, $name) = $this->explode_node_path($crumb); 172 $name = empty($name) ? $id : $name; 173 $breadcrumb[] = array( 174 'name' => $name, 175 'path' => $this->build_node_path($id, $name, $crumbtrail) 176 ); 177 $tmp = end($breadcrumb); 178 $crumbtrail = $tmp['path']; 179 } 180 return $breadcrumb; 181 } 182 183 /** 184 * Generates a safe path to a node. 185 * 186 * Typically, a node will be id|Name of the node. 187 * 188 * @param string $id of the node. 189 * @param string $name of the node, will be URL encoded. 190 * @param string $root to append the node on, must be a result of this function. 191 * @return string path to the node. 192 */ 193 protected function build_node_path($id, $name = '', $root = '') { 194 $path = $id; 195 if (!empty($name)) { 196 $path .= '|' . urlencode($name); 197 } 198 if (!empty($root)) { 199 $path = trim($root, '/') . '/' . $path; 200 } 201 return $path; 202 } 203 204 /** 205 * Returns information about a node in a path. 206 * 207 * @see self::build_node_path() 208 * @param string $node to extrat information from. 209 * @return array about the node. 210 */ 211 protected function explode_node_path($node) { 212 if (strpos($node, '|') !== false) { 213 list($id, $name) = explode('|', $node, 2); 214 $name = urldecode($name); 215 } else { 216 $id = $node; 217 $name = ''; 218 } 219 $id = urldecode($id); 220 return array( 221 0 => $id, 222 1 => $name, 223 'id' => $id, 224 'name' => $name 225 ); 226 } 227 228 229 /** 230 * List the files and folders. 231 * 232 * @param string $path path to browse. 233 * @param string $page page to browse. 234 * @return array of result. 235 */ 236 public function get_listing($path='', $page = '') { 237 if (empty($path)) { 238 $path = $this->build_node_path('root', get_string('pluginname', 'repository_googledocs')); 239 } 240 241 // We analyse the path to extract what to browse. 242 $trail = explode('/', $path); 243 $uri = array_pop($trail); 244 list($id, $name) = $this->explode_node_path($uri); 245 246 // Handle the special keyword 'search', which we defined in self::search() so that 247 // we could set up a breadcrumb in the search results. In any other case ID would be 248 // 'root' which is a special keyword set up by Google, or a parent (folder) ID. 249 if ($id === 'search') { 250 return $this->search($name); 251 } 252 253 // Query the Drive. 254 $q = "'" . str_replace("'", "\'", $id) . "' in parents"; 255 $q .= ' AND trashed = false'; 256 $results = $this->query($q, $path); 257 258 $ret = array(); 259 $ret['dynload'] = true; 260 $ret['path'] = $this->build_breadcrumb($path); 261 $ret['list'] = $results; 262 return $ret; 263 } 264 265 /** 266 * Search throughout the Google Drive. 267 * 268 * @param string $search_text text to search for. 269 * @param int $page search page. 270 * @return array of results. 271 */ 272 public function search($search_text, $page = 0) { 273 $path = $this->build_node_path('root', get_string('pluginname', 'repository_googledocs')); 274 $path = $this->build_node_path('search', $search_text, $path); 275 276 // Query the Drive. 277 $q = "fullText contains '" . str_replace("'", "\'", $search_text) . "'"; 278 $q .= ' AND trashed = false'; 279 $results = $this->query($q, $path); 280 281 $ret = array(); 282 $ret['dynload'] = true; 283 $ret['path'] = $this->build_breadcrumb($path); 284 $ret['list'] = $results; 285 return $ret; 286 } 287 288 /** 289 * Query Google Drive for files and folders using a search query. 290 * 291 * Documentation about the query format can be found here: 292 * https://developers.google.com/drive/search-parameters 293 * 294 * This returns a list of files and folders with their details as they should be 295 * formatted and returned by functions such as get_listing() or search(). 296 * 297 * @param string $q search query as expected by the Google API. 298 * @param string $path parent path of the current files, will not be used for the query. 299 * @param int $page page. 300 * @return array of files and folders. 301 */ 302 protected function query($q, $path = null, $page = 0) { 303 global $OUTPUT; 304 305 $files = array(); 306 $folders = array(); 307 $fields = "items(id,title,mimeType,downloadUrl,fileExtension,exportLinks,modifiedDate,fileSize,thumbnailLink)"; 308 $params = array('q' => $q, 'fields' => $fields); 309 310 try { 311 // Retrieving files and folders. 312 $response = $this->service->files->listFiles($params); 313 } catch (Google_Service_Exception $e) { 314 if ($e->getCode() == 403 && strpos($e->getMessage(), 'Access Not Configured') !== false) { 315 // This is raised when the service Drive API has not been enabled on Google APIs control panel. 316 throw new repository_exception('servicenotenabled', 'repository_googledocs'); 317 } else { 318 throw $e; 319 } 320 } 321 322 $items = isset($response['items']) ? $response['items'] : array(); 323 foreach ($items as $item) { 324 if ($item['mimeType'] == 'application/vnd.google-apps.folder') { 325 // This is a folder. 326 $folders[$item['title'] . $item['id']] = array( 327 'title' => $item['title'], 328 'path' => $this->build_node_path($item['id'], $item['title'], $path), 329 'date' => strtotime($item['modifiedDate']), 330 'thumbnail' => $OUTPUT->pix_url(file_folder_icon(64))->out(false), 331 'thumbnail_height' => 64, 332 'thumbnail_width' => 64, 333 'children' => array() 334 ); 335 } else { 336 // This is a file. 337 if (isset($item['fileExtension'])) { 338 // The file has an extension, therefore there is a download link. 339 $title = $item['title']; 340 $source = $item['downloadUrl']; 341 } else { 342 // The file is probably a Google Doc file, we get the corresponding export link. 343 // This should be improved by allowing the user to select the type of export they'd like. 344 $type = str_replace('application/vnd.google-apps.', '', $item['mimeType']); 345 $title = ''; 346 $exportType = ''; 347 switch ($type){ 348 case 'document': 349 $title = $item['title'] . '.rtf'; 350 $exportType = 'application/rtf'; 351 break; 352 case 'presentation': 353 $title = $item['title'] . '.pptx'; 354 $exportType = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'; 355 break; 356 case 'spreadsheet': 357 $title = $item['title'] . '.xlsx'; 358 $exportType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; 359 break; 360 } 361 // Skips invalid/unknown types. 362 if (empty($title) || !isset($item['exportLinks'][$exportType])) { 363 continue; 364 } 365 $source = $item['exportLinks'][$exportType]; 366 } 367 // Adds the file to the file list. Using the itemId along with the title as key 368 // of the array because Google Drive allows files with identical names. 369 $files[$title . $item['id']] = array( 370 'title' => $title, 371 'source' => $source, 372 'date' => strtotime($item['modifiedDate']), 373 'size' => isset($item['fileSize']) ? $item['fileSize'] : null, 374 'thumbnail' => $OUTPUT->pix_url(file_extension_icon($title, 64))->out(false), 375 'thumbnail_height' => 64, 376 'thumbnail_width' => 64, 377 // Do not use real thumbnails as they wouldn't work if the user disabled 3rd party 378 // plugins in his browser, or if they're not logged in their Google account. 379 ); 380 381 // Sometimes the real thumbnails can't be displayed, for example if 3rd party cookies are disabled 382 // or if the user is not logged in Google anymore. But this restriction does not seem to be applied 383 // to a small subset of files. 384 $extension = strtolower(pathinfo($title, PATHINFO_EXTENSION)); 385 if (isset($item['thumbnailLink']) && in_array($extension, array('jpg', 'png', 'txt', 'pdf'))) { 386 $files[$title . $item['id']]['realthumbnail'] = $item['thumbnailLink']; 387 } 388 } 389 } 390 391 // Filter and order the results. 392 $files = array_filter($files, array($this, 'filter')); 393 core_collator::ksort($files, core_collator::SORT_NATURAL); 394 core_collator::ksort($folders, core_collator::SORT_NATURAL); 395 return array_merge(array_values($folders), array_values($files)); 396 } 397 398 /** 399 * Logout. 400 * 401 * @return string 402 */ 403 public function logout() { 404 $this->store_access_token(null); 405 return parent::logout(); 406 } 407 408 /** 409 * Get a file. 410 * 411 * @param string $reference reference of the file. 412 * @param string $file name to save the file to. 413 * @return string JSON encoded array of information about the file. 414 */ 415 public function get_file($reference, $filename = '') { 416 global $CFG; 417 418 $auth = $this->client->getAuth(); 419 $request = $auth->authenticatedRequest(new Google_Http_Request($reference)); 420 if ($request->getResponseHttpCode() == 200) { 421 $path = $this->prepare_file($filename); 422 $content = $request->getResponseBody(); 423 if (file_put_contents($path, $content) !== false) { 424 @chmod($path, $CFG->filepermissions); 425 return array( 426 'path' => $path, 427 'url' => $reference 428 ); 429 } 430 } 431 throw new repository_exception('cannotdownload', 'repository'); 432 } 433 434 /** 435 * Prepare file reference information. 436 * 437 * We are using this method to clean up the source to make sure that it 438 * is a valid source. 439 * 440 * @param string $source of the file. 441 * @return string file reference. 442 */ 443 public function get_file_reference($source) { 444 return clean_param($source, PARAM_URL); 445 } 446 447 /** 448 * What kind of files will be in this repository? 449 * 450 * @return array return '*' means this repository support any files, otherwise 451 * return mimetypes of files, it can be an array 452 */ 453 public function supported_filetypes() { 454 return '*'; 455 } 456 457 /** 458 * Tells how the file can be picked from this repository. 459 * 460 * Maximum value is FILE_INTERNAL | FILE_EXTERNAL | FILE_REFERENCE. 461 * 462 * @return int 463 */ 464 public function supported_returntypes() { 465 return FILE_INTERNAL; 466 } 467 468 /** 469 * Return names of the general options. 470 * By default: no general option name. 471 * 472 * @return array 473 */ 474 public static function get_type_option_names() { 475 return array('clientid', 'secret', 'pluginname'); 476 } 477 478 /** 479 * Edit/Create Admin Settings Moodle form. 480 * 481 * @param moodleform $mform Moodle form (passed by reference). 482 * @param string $classname repository class name. 483 */ 484 public static function type_config_form($mform, $classname = 'repository') { 485 486 $callbackurl = new moodle_url(self::CALLBACKURL); 487 488 $a = new stdClass; 489 $a->docsurl = get_docs_url('Google_OAuth_2.0_setup'); 490 $a->callbackurl = $callbackurl->out(false); 491 492 $mform->addElement('static', null, '', get_string('oauthinfo', 'repository_googledocs', $a)); 493 494 parent::type_config_form($mform); 495 $mform->addElement('text', 'clientid', get_string('clientid', 'repository_googledocs')); 496 $mform->setType('clientid', PARAM_RAW_TRIMMED); 497 $mform->addElement('text', 'secret', get_string('secret', 'repository_googledocs')); 498 $mform->setType('secret', PARAM_RAW_TRIMMED); 499 500 $strrequired = get_string('required'); 501 $mform->addRule('clientid', $strrequired, 'required', null, 'client'); 502 $mform->addRule('secret', $strrequired, 'required', null, 'client'); 503 } 504 } 505 // Icon from: http://www.iconspedia.com/icon/google-2706.html.
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |