'#^[^/\\?*:|"<>]*[^./\\?*:|"<>]$#', 'rootCssClass' => 'elfinder-navbar-root-googledrive', 'gdAlias' => '%s@GDrive', 'gdCacheDir' => __DIR__ . '/.tmp', 'gdCachePrefix' => 'gd-', 'gdCacheExpire' => 600 ); $this->options = array_merge($this->options, $opts); } /** * Prepare driver before mount volume. * Return true if volume is ready. * * @return bool **/ protected function init() { if (empty($this->options['icon'])) { $this->options['icon'] = true; } if ($res = parent::init()) { if ($this->options['icon'] === true) { unset($this->options['icon']); } // enable command archive $this->options['useRemoteArchive'] = true; } return $res; } /** * Prepare * Call from elFinder::netmout() before volume->mount() * * @param $options * @return Array * @author Naoki Sawada */ public function netmountPrepare($options) { if (empty($options['client_id']) && defined('ELFINDER_GOOGLEDRIVE_CLIENTID')) { $options['client_id'] = ELFINDER_GOOGLEDRIVE_CLIENTID; } if (empty($options['client_secret']) && defined('ELFINDER_GOOGLEDRIVE_CLIENTSECRET')) { $options['client_secret'] = ELFINDER_GOOGLEDRIVE_CLIENTSECRET; } if (! isset($options['pass'])) { $options['pass'] = ''; } try { $client = new \Google_Client(); $client->setClientId($options['client_id']); $client->setClientSecret($options['client_secret']); if ($options['pass'] === 'reauth') { $options['pass'] = ''; $this->session->set('GoogleDriveAuthParams', [])->set('GoogleDriveTokens', []); } else if ($options['pass'] === 'googledrive') { $options['pass'] = ''; } $options = array_merge($this->session->get('GoogleDriveAuthParams', []), $options); if (! isset($options['access_token'])) { $options['access_token'] = $this->session->get('GoogleDriveTokens', []); $this->session->remove('GoogleDriveTokens'); } $aToken = $options['access_token']; $rootObj = $service = null; if ($aToken) { try { $client->setAccessToken($aToken); if ($client->isAccessTokenExpired()) { $aToken = array_merge($aToken, $client->fetchAccessTokenWithRefreshToken()); $client->setAccessToken($aToken); } $service = new \Google_Service_Drive($client); $rootObj = $service->files->get('root'); $options['access_token'] = $aToken; $this->session->set('GoogleDriveAuthParams', $options); } catch (Exception $e) { $aToken = []; $options['access_token'] = []; if ($options['user'] !== 'init') { $this->session->set('GoogleDriveAuthParams', $options); return array('exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE); } } } if ($options['user'] === 'init') { if (empty($options['url'])) { $options['url'] = elFinder::getConnectorUrl(); } $callback = $options['url'] . '?cmd=netmount&protocol=googledrive&host=1'; $client->setRedirectUri($callback); if (! $aToken && empty($_GET['code'])) { $client->setScopes([ Google_Service_Drive::DRIVE ]); if (! empty($options['offline'])) { $client->setApprovalPrompt('force'); $client->setAccessType('offline'); } $url = $client->createAuthUrl(); $html = ''; $html .= ''; if (empty($options['pass']) && $options['host'] !== '1') { $options['pass'] = 'return'; $this->session->set('GoogleDriveAuthParams', $options); return array('exit' => true, 'body' => $html); } else { $out = array( 'node' => $options['id'], 'json' => '{"protocol": "googledrive", "mode": "makebtn", "body" : "'.str_replace($html, '"', '\\"').'", "error" : "'.elFinder::ERROR_ACCESS_DENIED.'"}', 'bind' => 'netmount' ); return array('exit' => 'callback', 'out' => $out); } } else { if (! empty($_GET['code'])) { $aToken = $client->fetchAccessTokenWithAuthCode($_GET['code']); $options['access_token'] = $aToken; $this->session->set('GoogleDriveTokens', $aToken)->set('GoogleDriveAuthParams', $options); $out = array( 'node' => $options['id'], 'json' => '{"protocol": "googledrive", "mode": "done", "reset": 1}', 'bind' => 'netmount' ); return array('exit' => 'callback', 'out' => $out); } $folders = []; foreach($service->files->listFiles([ 'pageSize' => 1000, 'q' => 'trashed = false and mimeType = "application/vnd.google-apps.folder"' ]) as $f) { $folders[$f->getId()] = $f->getName(); } natcasesort($folders); $folders = ['root' => $rootObj->getName()] + $folders; $folders = json_encode($folders); $json = '{"protocol": "googledrive", "mode": "done", "folders": '.$folders.'}'; $options['pass'] = 'return'; $html = 'Google.com'; $html .= ''; $this->session->set('GoogleDriveAuthParams', $options); return array('exit' => true, 'body' => $html); } } } catch (Exception $e) { $this->session->remove('GoogleDriveAuthParams')->remove('GoogleDriveTokens'); if (empty($options['pass'])) { return array('exit' => true, 'body' => '{msg:'.elFinder::ERROR_ACCESS_DENIED.'}'.' '.$e->getMessage()); } else { return array('exit' => true, 'error' => [elFinder::ERROR_ACCESS_DENIED, $e->getMessage()]); } } if (! $aToken) { return array('exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE); } if ($options['path'] === '/') { $options['path'] = 'root'; } try { $file = $service->files->get($options['path']); $options['alias'] = sprintf($this->options['gdAlias'], $file->getName()); } catch (Google_Service_Exception $e) { $err = json_decode($e->getMessage(), true); if (isset($err['error']) && $err['error']['code'] == 404) { return array('exit' => true, 'error' => [elFinder::ERROR_TRGDIR_NOT_FOUND, $options['path']]); } else { return array('exit' => true, 'error' => $e->getMessage()); } } catch (Exception $e) { return array('exit' => true, 'error' => $e->getMessage()); } foreach(['host', 'user', 'pass', 'id', 'offline'] as $key) { unset($options[$key]); } return $options; } /** * process of on netunmount * Drop table `dropbox` & rm thumbs * * @param $netVolumes * @param $key * @return bool * @internal param array $options */ public function netunmount($netVolumes, $key) { $cache = $this->options['gdCacheDir'] . DIRECTORY_SEPARATOR . $this->options['gdCachePrefix'].$this->netMountKey; if (file_exists($cache) && is_writeable($cache)) { unlink($cache); } if ($tmbs = glob($this->tmbPath . DIRECTORY_SEPARATOR . $this->netMountKey . '*')) { foreach($tmbs as $file) { unlink($file); } } return true; } /** * "Mount" volume. * Return true if volume available for read or write, * false - otherwise * * @param array $opts * @return bool * @author Naoki Sawada */ public function mount(array $opts) { $creds = null; if (isset($opts['access_token'])) { $this->netMountKey = md5(join('-', array('googledrive', $opts['path'], (isset($opts['access_token']['refresh_token'])? $opts['access_token']['refresh_token'] : $opts['access_token']['access_token'])))); } $client = new \Google_Client(); $client->setClientId($opts['client_id']); $client->setClientSecret($opts['client_secret']); if (!empty($opts['access_token'])) { $client->setAccessToken($opts['access_token']); } if ($client->isAccessTokenExpired()) { try { $creds = $client->fetchAccessTokenWithRefreshToken(); } catch (LogicException $e) { $this->session->remove('GoogleDriveAuthParams'); throw $e; } } $service = new \Google_Service_Drive($client); // If path is not set, use the root if (!isset($opts['path']) || $opts['path'] === '') { $opts['path'] = 'root'; } $googleDrive = new GoogleDriveAdapter($service, $opts['path'], [ 'useHasDir' => true ]); $opts['fscache'] = null; if ($this->options['gdCacheDir'] && is_writeable($this->options['gdCacheDir'])) { if ($this->options['gdCacheExpire']) { $opts['fscache'] = new elFinderVolumeFlysystemGoogleDriveCache(new Local($this->options['gdCacheDir']), $this->options['gdCachePrefix'].$this->netMountKey, $this->options['gdCacheExpire']); } } if ($opts['fscache']) { $filesystem = new Filesystem(new CachedAdapter($googleDrive, $opts['fscache'])); } else { $filesystem = new Filesystem($googleDrive); } $opts['driver'] = 'FlysystemExt'; $opts['filesystem'] = $filesystem; $opts['separator'] = '/'; $opts['checkSubfolders'] = true; if (! isset($opts['alias'])) { $opts['alias'] = 'GoogleDrive'; } if ($res = parent::mount($opts)) { // update access_token of session data if ($creds) { $netVolumes = $this->session->get('netvolume'); $netVolumes[$this->netMountKey]['access_token'] = array_merge($netVolumes[$this->netMountKey]['access_token'], $creds); $this->session->set('netvolume', $netVolumes); } } return $res; } /** * @inheritdoc */ protected function tmbname($stat) { return $this->netMountKey.substr(substr($stat['hash'], strlen($this->id)), -38).$stat['ts'].'.png'; } }