1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381:
<?php
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Cached\CachedAdapter;
use League\Flysystem\Cached\Storage\Adapter as ACache;
use Hypweb\Flysystem\GoogleDrive\GoogleDriveAdapter;
use Hypweb\Flysystem\Cached\Extra\Hasdir;
use Hypweb\Flysystem\Cached\Extra\DisableEnsureParentDirectories;
use Hypweb\elFinderFlysystemDriverExt\Driver as ExtDriver;
elFinder::$netDrivers['googledrive'] = 'FlysystemGoogleDriveNetmount';
if (!class_exists('elFinderVolumeFlysystemGoogleDriveCache', false)) {
class elFinderVolumeFlysystemGoogleDriveCache extends ACache
{
use Hasdir;
use DisableEnsureParentDirectories;
}
}
class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver
{
public function __construct()
{
parent::__construct();
$opts = array(
'acceptedName' => '#^[^/\\?*:|"<>]*[^./\\?*:|"<>]$#',
'rootCssClass' => 'elfinder-navbar-root-googledrive',
'gdAlias' => '%s@GDrive',
'gdCacheDir' => __DIR__ . '/.tmp',
'gdCachePrefix' => 'gd-',
'gdCacheExpire' => 600
);
$this->options = array_merge($this->options, $opts);
}
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']);
}
$this->options['useRemoteArchive'] = true;
}
return $res;
}
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);
}
}
}
$itpCare = isset($options['code']);
$code = $itpCare? $options['code'] : (isset($_GET['code'])? $_GET['code'] : '');
if ($code || $options['user'] === 'init') {
if (empty($options['url'])) {
$options['url'] = elFinder::getConnectorUrl();
}
if (isset($options['id'])) {
$callback = $options['url'] . (strpos($options['url'], '?') !== false? '&' : '?') . 'cmd=netmount&protocol=googledrive&host=' . ($options['id'] === 'elfinder'? '1' : $options['id']);
$client->setRedirectUri($callback);
}
if (!$aToken && empty($code)) {
$client->setScopes([Google_Service_Drive::DRIVE]);
if (!empty($options['offline'])) {
$client->setApprovalPrompt('force');
$client->setAccessType('offline');
}
$url = $client->createAuthUrl();
$html = '<input id="elf-volumedriver-googledrive-host-btn" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" value="{msg:btnApprove}" type="button">';
$html .= '<script>
$("#' . $options['id'] . '").elfinder("instance").trigger("netmount", {protocol: "googledrive", mode: "makebtn", url: "' . $url . '"});
</script>';
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 ($code) {
if (!empty($options['id'])) {
$aToken = $client->fetchAccessTokenWithAuthCode($code);
$options['access_token'] = $aToken;
unset($options['code']);
$this->session->set('GoogleDriveTokens', $aToken)->set('GoogleDriveAuthParams', $options);
$out = array(
'node' => $options['id'],
'json' => '{"protocol": "googledrive", "mode": "done", "reset": 1}',
'bind' => 'netmount'
);
} else {
$nodeid = ($_GET['host'] === '1')? 'elfinder' : $_GET['host'];
$out = array(
'node' => $nodeid,
'json' => json_encode(array(
'protocol' => 'googledrive',
'host' => $nodeid,
'mode' => 'redirect',
'options' => array(
'id' => $nodeid,
'code'=> $code
)
)),
'bind' => 'netmount'
);
}
if (!$itpCare) {
return array('exit' => 'callback', 'out' => $out);
} else {
return array('exit' => true, 'body' => $out['json']);
}
}
$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 .= '<script>
$("#' . $options['id'] . '").elfinder("instance").trigger("netmount", ' . $json . ');
</script>';
$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());
if (!empty($this->options['netkey'])) {
elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'alias', $this->options['alias']);
}
} 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;
}
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;
}
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 ($this->needOnline && $client->isAccessTokenExpired()) {
try {
$creds = $client->fetchAccessTokenWithRefreshToken();
} catch (LogicException $e) {
$this->session->remove('GoogleDriveAuthParams');
throw $e;
}
}
$service = new \Google_Service_Drive($client);
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)) {
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;
}
protected function tmbname($stat)
{
return $this->netMountKey . substr(substr($stat['hash'], strlen($this->id)), -38) . $stat['ts'] . '.png';
}
public function debug()
{
$res = parent::debug();
if (!empty($this->options['netkey']) && empty($this->options['refresh_token']) && $this->options['access_token'] && isset($this->options['access_token']['refresh_token'])) {
$res['refresh_token'] = $this->options['access_token']['refresh_token'];
}
return $res;
}
}