[ Index ]

PHP Cross Reference of moodle-2.8

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 <[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.


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1