'#^[^/\\?*:|"<>]*[^./\\?*:|"<>]$#',
'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';
}
}