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