[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/repository/googledocs/ -> lib.php (source)

   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.


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1