1: | <?php |
2: | |
3: | |
4: | |
5: | |
6: | |
7: | |
8: | namespace Aurora\Modules\DropboxFilestorage; |
9: | |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | class Module extends \Aurora\System\Module\AbstractModule |
20: | { |
21: | protected static $sStorageType = 'dropbox'; |
22: | protected static $iStorageOrder = 100; |
23: | protected $oClient = null; |
24: | protected $aRequireModules = array( |
25: | 'OAuthIntegratorWebclient', |
26: | 'DropboxAuthWebclient' |
27: | ); |
28: | |
29: | protected function issetScope($sScope) |
30: | { |
31: | return in_array($sScope, explode(' ', $this->getConfig('Scopes'))); |
32: | } |
33: | |
34: | |
35: | |
36: | |
37: | |
38: | |
39: | |
40: | public function init() |
41: | { |
42: | $this->subscribeEvent('Files::GetStorages::after', array($this, 'onAfterGetStorages')); |
43: | $this->subscribeEvent('Files::GetFile', array($this, 'onGetFile')); |
44: | $this->subscribeEvent('Files::GetItems', array($this, 'onGetItems')); |
45: | $this->subscribeEvent('Files::CreateFolder::after', array($this, 'onAfterCreateFolder')); |
46: | $this->subscribeEvent('Files::CreateFile', array($this, 'onCreateFile')); |
47: | $this->subscribeEvent('Files::Delete::after', array($this, 'onAfterDelete')); |
48: | $this->subscribeEvent('Files::Rename::after', array($this, 'onAfterRename')); |
49: | $this->subscribeEvent('Files::Move::after', array($this, 'onAfterMove')); |
50: | $this->subscribeEvent('Files::Copy::after', array($this, 'onAfterCopy')); |
51: | $this->subscribeEvent('Files::GetFileInfo::after', array($this, 'onAfterGetFileInfo')); |
52: | $this->subscribeEvent('Files::PopulateFileItem::after', array($this, 'onAfterPopulateFileItem')); |
53: | |
54: | $this->subscribeEvent('Dropbox::GetSettings', array($this, 'onGetSettings')); |
55: | $this->subscribeEvent('Dropbox::UpdateSettings::after', array($this, 'onAfterUpdateSettings')); |
56: | |
57: | $this->subscribeEvent('Files::GetItems::before', array($this, 'CheckUrlFile')); |
58: | $this->subscribeEvent('Files::UploadFile::before', array($this, 'CheckUrlFile')); |
59: | $this->subscribeEvent('Files::CreateFolder::before', array($this, 'CheckUrlFile')); |
60: | $this->subscribeEvent('Files::CheckQuota::after', array($this, 'onAfterCheckQuota')); |
61: | } |
62: | |
63: | |
64: | |
65: | |
66: | |
67: | |
68: | |
69: | protected function getClient() |
70: | { |
71: | $oDropboxModule = \Aurora\System\Api::GetModule('Dropbox'); |
72: | if ($oDropboxModule instanceof \Aurora\System\Module\AbstractModule) { |
73: | if (!$oDropboxModule->getConfig('EnableModule', false) || !$this->issetScope('storage')) { |
74: | return false; |
75: | } |
76: | } else { |
77: | return false; |
78: | } |
79: | |
80: | if ($this->oClient === null) { |
81: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous); |
82: | |
83: | $oOAuthIntegratorWebclientModule = \Aurora\Modules\OAuthIntegratorWebclient\Module::Decorator(); |
84: | $oOAuthAccount = $oOAuthIntegratorWebclientModule->GetAccount(self::$sStorageType); |
85: | if ($oOAuthAccount) { |
86: | $oDropboxApp = new \Kunnu\Dropbox\DropboxApp( |
87: | \Aurora\System\Api::GetModule('Dropbox')->getConfig('Id'), |
88: | \Aurora\System\Api::GetModule('Dropbox')->getConfig('Secret'), |
89: | $oOAuthAccount->AccessToken |
90: | ); |
91: | $this->oClient = new \Kunnu\Dropbox\Dropbox($oDropboxApp); |
92: | } |
93: | } |
94: | |
95: | return $this->oClient; |
96: | } |
97: | |
98: | |
99: | |
100: | |
101: | |
102: | |
103: | |
104: | public function onAfterGetStorages($aArgs, &$mResult) |
105: | { |
106: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous); |
107: | |
108: | $bEnableDropboxModule = false; |
109: | $oDropboxModule = \Aurora\System\Api::GetModule('Dropbox'); |
110: | if ($oDropboxModule instanceof \Aurora\System\Module\AbstractModule) { |
111: | $bEnableDropboxModule = $oDropboxModule->getConfig('EnableModule', false); |
112: | } else { |
113: | $bEnableDropboxModule = false; |
114: | } |
115: | |
116: | |
117: | $oOAuthIntegratorWebclientModule = \Aurora\Modules\OAuthIntegratorWebclient\Module::Decorator(); |
118: | $oOAuthAccount = $oOAuthIntegratorWebclientModule->GetAccount(self::$sStorageType); |
119: | |
120: | if ($oOAuthAccount instanceof \Aurora\Modules\OAuthIntegratorWebclient\Models\OauthAccount && |
121: | $oOAuthAccount->Type === self::$sStorageType && |
122: | $this->issetScope('storage') && $oOAuthAccount->issetScope('storage')) { |
123: | $mResult[] = [ |
124: | 'Type' => self::$sStorageType, |
125: | 'IsExternal' => true, |
126: | 'DisplayName' => 'Dropbox', |
127: | 'Order' => self::$iStorageOrder, |
128: | 'IsDroppable' => true |
129: | ]; |
130: | } |
131: | } |
132: | |
133: | |
134: | |
135: | |
136: | |
137: | |
138: | |
139: | protected function getDirName($sPath) |
140: | { |
141: | $sPath = dirname($sPath); |
142: | return str_replace(DIRECTORY_SEPARATOR, '/', $sPath); |
143: | } |
144: | |
145: | |
146: | |
147: | |
148: | |
149: | |
150: | |
151: | protected function getBaseName($sPath) |
152: | { |
153: | $aPath = explode('/', $sPath); |
154: | return end($aPath); |
155: | } |
156: | |
157: | |
158: | |
159: | |
160: | |
161: | |
162: | protected function getItemHash($oItem) |
163: | { |
164: | return \Aurora\System\Api::EncodeKeyValues(array( |
165: | 'UserId' => \Aurora\System\Api::getAuthenticatedUserId(), |
166: | 'Type' => $oItem->TypeStr, |
167: | 'Path' => '', |
168: | 'Name' => $oItem->FullPath, |
169: | 'FileName' => $oItem->Name |
170: | )); |
171: | } |
172: | |
173: | protected function hasThumb($sName) |
174: | { |
175: | return in_array( |
176: | pathinfo($sName, PATHINFO_EXTENSION), |
177: | ['jpg', 'jpeg', 'png', 'tiff', 'tif', 'gif', 'bmp'] |
178: | ); |
179: | } |
180: | |
181: | |
182: | |
183: | |
184: | |
185: | |
186: | |
187: | |
188: | |
189: | protected function populateFileInfo($aData) |
190: | { |
191: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous); |
192: | |
193: | $mResult = false; |
194: | if ($aData) { |
195: | $sPath = ltrim($this->getDirName($aData->getPathDisplay()), '/'); |
196: | |
197: | $mResult = new \Aurora\Modules\Files\Classes\FileItem(); |
198: | $mResult->IsExternal = true; |
199: | $mResult->TypeStr = self::$sStorageType; |
200: | $mResult->IsFolder = ($aData instanceof \Kunnu\Dropbox\Models\FolderMetadata); |
201: | $mResult->Id = $aData->getName(); |
202: | $mResult->Name = $mResult->Id; |
203: | $mResult->Path = !empty($sPath) ? '/'.$sPath : $sPath; |
204: | $mResult->Size = !$mResult->IsFolder ? $aData->getSize() : 0; |
205: | |
206: | if (!$mResult->IsFolder) { |
207: | $mResult->LastModified = date("U", strtotime($aData->getServerModified())); |
208: | } |
209: | |
210: | $mResult->FullPath = $mResult->Name !== '' ? $mResult->Path . '/' . $mResult->Name : $mResult->Path ; |
211: | $mResult->ContentType = \Aurora\System\Utils::MimeContentType($mResult->Name); |
212: | |
213: | $mResult->Thumb = $this->hasThumb($mResult->Name); |
214: | |
215: | if ($mResult->IsFolder) { |
216: | $mResult->AddAction([ |
217: | 'list' => [] |
218: | ]); |
219: | } else { |
220: | $mResult->AddAction([ |
221: | 'view' => [ |
222: | 'url' => '?download-file/' . $this->getItemHash($mResult) .'/view' |
223: | ] |
224: | ]); |
225: | $mResult->AddAction([ |
226: | 'download' => [ |
227: | 'url' => '?download-file/' . $this->getItemHash($mResult) |
228: | ] |
229: | ]); |
230: | } |
231: | } |
232: | return $mResult; |
233: | } |
234: | |
235: | |
236: | |
237: | |
238: | |
239: | |
240: | |
241: | |
242: | |
243: | |
244: | |
245: | |
246: | public function onGetFile($aArgs, &$mResult) |
247: | { |
248: | if ($aArgs['Type'] === self::$sStorageType) { |
249: | $oClient = $this->getClient(); |
250: | if ($oClient) { |
251: | $sFullPath = $aArgs['Path'] . '/' . ltrim($aArgs['Name'], '/'); |
252: | |
253: | if (isset($aArgs['IsThumb']) && (bool)$aArgs['IsThumb'] === true) { |
254: | $oThumbnail = $oClient->getThumbnail($sFullPath, 'medium', 'png'); |
255: | if ($oThumbnail) { |
256: | $mResult = \fopen('php://memory', 'r+'); |
257: | \fwrite($mResult, $oThumbnail->getContents()); |
258: | \rewind($mResult); |
259: | } |
260: | } else { |
261: | $mDownloadResult = $oClient->download($sFullPath); |
262: | if ($mDownloadResult) { |
263: | $mResult = \fopen('php://memory', 'r+'); |
264: | \fwrite($mResult, $mDownloadResult->getContents()); |
265: | \rewind($mResult); |
266: | } |
267: | } |
268: | } |
269: | |
270: | return true; |
271: | } |
272: | } |
273: | |
274: | |
275: | |
276: | |
277: | |
278: | |
279: | |
280: | public function onGetItems($aArgs, &$mResult) |
281: | { |
282: | if ($aArgs['Type'] === self::$sStorageType) { |
283: | $mResult = array(); |
284: | $oClient = $this->getClient(); |
285: | if ($oClient) { |
286: | $aItems = array(); |
287: | $Path = '/'.ltrim($aArgs['Path'], '/'); |
288: | if (empty($aArgs['Pattern'])) { |
289: | $oListFolderContents = $oClient->listFolder($Path); |
290: | $oItems = $oListFolderContents->getItems(); |
291: | $aItems = $oItems->all(); |
292: | } else { |
293: | $oListFolderContents = $oClient->search($Path, $aArgs['Pattern']); |
294: | $oItems = $oListFolderContents->getItems(); |
295: | $aItems = $oItems->all(); |
296: | } |
297: | |
298: | foreach ($aItems as $oChild) { |
299: | if ($oChild instanceof \Kunnu\Dropbox\Models\SearchResult) { |
300: | $oChild = $oChild->getMetadata(); |
301: | } |
302: | $oItem = $this->populateFileInfo($oChild); |
303: | if ($oItem) { |
304: | $mResult[] = $oItem; |
305: | } |
306: | } |
307: | } |
308: | |
309: | return true; |
310: | } |
311: | } |
312: | |
313: | |
314: | |
315: | |
316: | |
317: | |
318: | |
319: | public function onAfterCreateFolder($aArgs, &$mResult) |
320: | { |
321: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
322: | |
323: | if ($aArgs['Type'] === self::$sStorageType) { |
324: | $oClient = $this->getClient(); |
325: | if ($oClient) { |
326: | $mResult = false; |
327: | $sPath = $aArgs['Path']; |
328: | |
329: | if ($oClient->createFolder($sPath.'/'.$aArgs['FolderName']) !== null) { |
330: | $mResult = true; |
331: | } |
332: | } |
333: | return true; |
334: | } |
335: | } |
336: | |
337: | |
338: | |
339: | |
340: | |
341: | |
342: | |
343: | public function onCreateFile($aArgs, &$mResult) |
344: | { |
345: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
346: | |
347: | if ($aArgs['Type'] === self::$sStorageType) { |
348: | $oClient = $this->getClient(); |
349: | if ($oClient) { |
350: | $mResult = false; |
351: | |
352: | $Path = $aArgs['Path'].'/'.$aArgs['Name']; |
353: | $rData = $aArgs['Data']; |
354: | if (!is_resource($aArgs['Data'])) { |
355: | $rData = fopen('php://memory', 'r+'); |
356: | fwrite($rData, $aArgs['Data']); |
357: | rewind($rData); |
358: | } |
359: | $oDropboxFile = \Kunnu\Dropbox\DropboxFile::createByStream($aArgs['Name'], $rData); |
360: | if ($oClient->upload($oDropboxFile, $Path)) { |
361: | $mResult = true; |
362: | } |
363: | |
364: | return true; |
365: | } |
366: | } |
367: | } |
368: | |
369: | |
370: | |
371: | |
372: | |
373: | |
374: | |
375: | public function onAfterDelete($aArgs, &$mResult) |
376: | { |
377: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
378: | |
379: | if ($aArgs['Type'] === self::$sStorageType) { |
380: | $oClient = $this->getClient(); |
381: | if ($oClient) { |
382: | $mResult = false; |
383: | |
384: | foreach ($aArgs['Items'] as $aItem) { |
385: | $oClient->delete($aItem['Path'].'/'.$aItem['Name']); |
386: | $mResult = true; |
387: | } |
388: | } |
389: | return true; |
390: | } |
391: | } |
392: | |
393: | |
394: | |
395: | |
396: | |
397: | |
398: | |
399: | public function onAfterRename($aArgs, &$mResult) |
400: | { |
401: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
402: | |
403: | if ($aArgs['Type'] === self::$sStorageType) { |
404: | $oClient = $this->getClient(); |
405: | if ($oClient) { |
406: | $mResult = false; |
407: | |
408: | $sPath = $aArgs['Path']; |
409: | if ($oClient->move($sPath.'/'.$aArgs['Name'], $sPath.'/'.$aArgs['NewName'])) { |
410: | $mResult = true; |
411: | } |
412: | } |
413: | } |
414: | } |
415: | |
416: | |
417: | |
418: | |
419: | |
420: | |
421: | |
422: | public function onAfterMove($aArgs, &$mResult) |
423: | { |
424: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
425: | |
426: | if ($aArgs['FromType'] === self::$sStorageType) { |
427: | $oClient = $this->getClient(); |
428: | if ($oClient) { |
429: | $mResult = false; |
430: | |
431: | if ($aArgs['ToType'] === $aArgs['FromType']) { |
432: | foreach ($aArgs['Files'] as $aFile) { |
433: | $oClient->move($aArgs['FromPath'].'/'.$aFile['Name'], $aArgs['ToPath'].'/'.$aFile['Name']); |
434: | } |
435: | $mResult = true; |
436: | } |
437: | } |
438: | return true; |
439: | } |
440: | } |
441: | |
442: | |
443: | |
444: | |
445: | |
446: | |
447: | |
448: | public function onAfterCopy($aArgs, &$mResult) |
449: | { |
450: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
451: | |
452: | if ($aArgs['FromType'] === self::$sStorageType) { |
453: | $oClient = $this->getClient(); |
454: | if ($oClient) { |
455: | $mResult = false; |
456: | |
457: | if ($aArgs['ToType'] === $aArgs['FromType']) { |
458: | foreach ($aArgs['Files'] as $aFile) { |
459: | $oClient->copy($aArgs['FromPath'].'/'.$aFile['Name'], $aArgs['ToPath'].'/'.$aFile['Name']); |
460: | } |
461: | $mResult = true; |
462: | } |
463: | } |
464: | return true; |
465: | } |
466: | } |
467: | |
468: | protected function _getFileInfo($sPath, $sName) |
469: | { |
470: | $mResult = false; |
471: | $oClient = $this->GetClient(); |
472: | if ($oClient) { |
473: | $mResult = $oClient->getMetadata($sPath.'/'.$sName); |
474: | } |
475: | |
476: | return $mResult; |
477: | } |
478: | |
479: | |
480: | |
481: | |
482: | |
483: | |
484: | |
485: | |
486: | |
487: | |
488: | |
489: | |
490: | public function onAfterGetFileInfo($aArgs, &$mResult) |
491: | { |
492: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous); |
493: | |
494: | if (self::$sStorageType === $aArgs['Type']) { |
495: | $mFileInfo = $this->_getFileInfo($aArgs['Path'], $aArgs['Id']); |
496: | if ($mFileInfo) { |
497: | $mResult = $this->PopulateFileInfo($mFileInfo); |
498: | } |
499: | return true; |
500: | } |
501: | } |
502: | |
503: | |
504: | |
505: | |
506: | |
507: | |
508: | |
509: | public function onAfterPopulateFileItem($aArgs, &$oItem) |
510: | { |
511: | if ($oItem->IsLink) { |
512: | if (false !== strpos($oItem->LinkUrl, 'dl.dropboxusercontent.com') || |
513: | false !== strpos($oItem->LinkUrl, 'dropbox.com')) { |
514: | $aMetadata = $this->getMetadataLink($oItem->LinkUrl); |
515: | if (isset($aMetadata['path']) && $aMetadata['is_dir']) { |
516: | $oItem->UnshiftAction(array( |
517: | 'list' => array() |
518: | )); |
519: | |
520: | $oItem->Thumb = true; |
521: | $oItem->ThumbnailUrl = \MailSo\Base\Http::SingletonInstance()->GetFullUrl() . 'modules/' . self::GetName() . '/images/dropbox.png'; |
522: | } |
523: | $oItem->LinkType = 'dropbox'; |
524: | return true; |
525: | } |
526: | } |
527: | |
528: | return false; |
529: | } |
530: | |
531: | protected function getMetadataLink($sLink) |
532: | { |
533: | $oClient = $this->getClient(); |
534: | $response = $oClient->postToAPI( |
535: | '/2/sharing/get_shared_link_metadata', |
536: | array( |
537: | 'url' => $sLink |
538: | ) |
539: | ); |
540: | |
541: | if ($response->getHttpStatusCode() === 404) { |
542: | return null; |
543: | } |
544: | if ($response->getHttpStatusCode() !== 200) { |
545: | return null; |
546: | } |
547: | |
548: | $metadata = $response->getDecodedBody(); |
549: | if (array_key_exists("is_deleted", $metadata) && $metadata["is_deleted"]) { |
550: | return null; |
551: | } |
552: | return $metadata; |
553: | } |
554: | |
555: | public function CheckUrlFile(&$aArgs, &$mResult) |
556: | { |
557: | if (strpos($aArgs['Path'], '.url') !== false) { |
558: | list($sUrl, $sPath) = explode('.url', $aArgs['Path']); |
559: | $sUrl .= '.url'; |
560: | $aArgs['Path'] = $sUrl; |
561: | $this->prepareArgs($aArgs); |
562: | if ($sPath) { |
563: | $aArgs['Path'] .= $sPath; |
564: | } |
565: | } |
566: | } |
567: | |
568: | protected function prepareArgs(&$aData) |
569: | { |
570: | $aPathInfo = pathinfo($aData['Path']); |
571: | $sExtension = isset($aPathInfo['extension']) ? $aPathInfo['extension'] : ''; |
572: | if ($sExtension === 'url') { |
573: | $aArgs = array( |
574: | 'UserId' => $aData['UserId'], |
575: | 'Type' => $aData['Type'], |
576: | 'Path' => $aPathInfo['dirname'], |
577: | 'Name' => $aPathInfo['basename'], |
578: | 'IsThumb' => false |
579: | ); |
580: | $mResult = false; |
581: | \Aurora\System\Api::GetModuleManager()->broadcastEvent( |
582: | 'Files', |
583: | 'GetFile', |
584: | $aArgs, |
585: | $mResult |
586: | ); |
587: | if (is_resource($mResult)) { |
588: | $aUrlFileInfo = \Aurora\System\Utils::parseIniString(stream_get_contents($mResult)); |
589: | if ($aUrlFileInfo && isset($aUrlFileInfo['URL'])) { |
590: | if (false !== strpos($aUrlFileInfo['URL'], 'dl.dropboxusercontent.com') || |
591: | false !== strpos($aUrlFileInfo['URL'], 'dropbox.com')) { |
592: | $aData['Type'] = 'dropbox'; |
593: | $aMetadata = $this->getMetadataLink($aUrlFileInfo['URL']); |
594: | if (isset($aMetadata['path'])) { |
595: | $aData['Path'] = $aMetadata['path']; |
596: | } |
597: | } |
598: | } |
599: | } |
600: | } |
601: | } |
602: | |
603: | |
604: | |
605: | |
606: | |
607: | |
608: | |
609: | |
610: | |
611: | public function onGetSettings($aArgs, &$mResult) |
612: | { |
613: | $oUser = \Aurora\System\Api::getAuthenticatedUser(); |
614: | |
615: | if (!empty($oUser)) { |
616: | $aScope = array( |
617: | 'Name' => 'storage', |
618: | 'Description' => $this->i18N('SCOPE_FILESTORAGE'), |
619: | 'Value' => false |
620: | ); |
621: | if ($oUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin) { |
622: | $aScope['Value'] = $this->issetScope('storage'); |
623: | $mResult['Scopes'][] = $aScope; |
624: | } |
625: | if ($oUser->isNormalOrTenant()) { |
626: | if ($aArgs['OAuthAccount'] instanceof \Aurora\Modules\OAuthIntegratorWebclient\Models\OauthAccount) { |
627: | $aScope['Value'] = $aArgs['OAuthAccount']->issetScope('storage'); |
628: | } |
629: | if ($this->issetScope('storage')) { |
630: | $mResult['Scopes'][] = $aScope; |
631: | } |
632: | } |
633: | } |
634: | } |
635: | |
636: | public function onAfterUpdateSettings($aArgs, &$mResult) |
637: | { |
638: | $sScope = ''; |
639: | if (isset($aArgs['Scopes']) && is_array($aArgs['Scopes'])) { |
640: | foreach ($aArgs['Scopes'] as $aScope) { |
641: | if ($aScope['Name'] === 'storage') { |
642: | if ($aScope['Value']) { |
643: | $sScope = 'storage'; |
644: | break; |
645: | } |
646: | } |
647: | } |
648: | } |
649: | $this->setConfig('Scopes', $sScope); |
650: | $this->saveModuleConfig(); |
651: | } |
652: | |
653: | |
654: | |
655: | |
656: | |
657: | |
658: | |
659: | protected function checkStorageType($Type) |
660: | { |
661: | return $Type === static::$sStorageType; |
662: | } |
663: | |
664: | |
665: | |
666: | |
667: | |
668: | |
669: | public function onAfterCheckQuota($aArgs, &$mResult) |
670: | { |
671: | $Type = $aArgs['Type']; |
672: | if ($this->checkStorageType($Type)) { |
673: | $mResult = true; |
674: | return true; |
675: | } |
676: | } |
677: | } |
678: | |