1: <?php
2: /**
3: * This code is licensed under AGPLv3 license or Afterlogic Software License
4: * if commercial version of the product was purchased.
5: * For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
6: */
7:
8: namespace Aurora\Modules\FilesZipFolder;
9:
10: /**
11: * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
12: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
13: * @copyright Copyright (c) 2023, Afterlogic Corp.
14: *
15: * @property Settings $oModuleSettings
16: *
17: * @package Modules
18: */
19: class Module extends \Aurora\System\Module\AbstractModule
20: {
21: /**
22: * @return Module
23: */
24: public static function getInstance()
25: {
26: return parent::getInstance();
27: }
28:
29: /**
30: * @return Module
31: */
32: public static function Decorator()
33: {
34: return parent::Decorator();
35: }
36:
37: /**
38: * @return Settings
39: */
40: public function getModuleSettings()
41: {
42: return $this->oModuleSettings;
43: }
44:
45: /***** private functions *****/
46: /**
47: * Initializes FilesZipFolder Module.
48: *
49: * @ignore
50: */
51: public function init()
52: {
53: $this->subscribeEvent('Files::GetFile', array($this, 'onGetFile'), 50);
54: // $this->subscribeEvent('Files::GetItems::before', array($this, 'onBeforeGetItems'), 500);
55: $this->subscribeEvent('Files::GetItems', array($this, 'onGetItems'), 50);
56: $this->subscribeEvent('Files::CreateFolder::before', array($this, 'onBeforeCreateFolder'), 50);
57: $this->subscribeEvent('Files::CreateFile', array($this, 'onCreateFile'), 50);
58: $this->subscribeEvent('Files::Delete::after', array($this, 'onAfterDelete'), 50);
59: $this->subscribeEvent('Files::Rename::after', array($this, 'onAfterRename'), 50);
60: $this->subscribeEvent('Files::Move::before', array($this, 'onBeforeMove'), 50);
61: $this->subscribeEvent('Files::Copy::before', array($this, 'onBeforeCopy'), 50);
62: $this->subscribeEvent('Files::GetFileInfo::after', array($this, 'onAfterGetFileInfo'), 500);
63: $this->subscribeEvent('Files::PopulateFileItem::after', array($this, 'onAfterPopulateFileItem'));
64: }
65:
66: /**
67: * Returns directory name for the specified path.
68: *
69: * @param string $sPath Path to the file.
70: * @return string
71: */
72: protected function getDirName($sPath)
73: {
74: $sPath = \dirname($sPath);
75: return \str_replace(DIRECTORY_SEPARATOR, '/', $sPath);
76: }
77:
78: /**
79: * Returns base name for the specified path.
80: *
81: * @param string $sPath Path to the file.
82: * @return string
83: */
84: protected function getBaseName($sPath)
85: {
86: $aPath = \explode('/', $sPath);
87: return \end($aPath);
88: }
89:
90: /**
91: * Populates file info.
92: *
93: * @param string $sType Service type.
94: * @param \Kunnu\Dropbox\Dropbox $oClient DropBox client.
95: * @param array $aData Array contains information about file.
96: * @return \Aurora\Modules\Files\Classes\FileItem|false
97: */
98: protected function populateFileInfo($sType, $oClient, $aData)
99: {
100: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
101:
102: $mResult = false;
103: if ($aData && \is_array($aData)) {
104: $sPath = \ltrim($this->getDirName($aData['path']), '/');
105:
106: // $oSocial = $this->GetSocial($oAccount);
107: $mResult /*@var $mResult \Aurora\Modules\Files\Classes\FileItem */ = new \Aurora\Modules\Files\Classes\FileItem();
108: // $mResult->IsExternal = true;
109: $mResult->TypeStr = $sType;
110: $mResult->IsFolder = $aData['is_dir'];
111: $mResult->Id = $this->getBaseName($aData['path']);
112: $mResult->Name = $mResult->Id;
113: $mResult->Path = !empty($sPath) ? '/' . $sPath : $sPath;
114: $mResult->Size = $aData['bytes'];
115: // $bResult->Owner = $oSocial->Name;
116: $dt = \DateTime::createFromFormat("D, d M Y H:i:s T", $aData['modified']);
117: $mResult->LastModified = \date_timestamp_get($dt);
118: $mResult->Shared = isset($aData['shared']) ? $aData['shared'] : false;
119: $mResult->FullPath = $mResult->Name !== '' ? $mResult->Path . '/' . $mResult->Name : $mResult->Path ;
120:
121: if (!$mResult->IsFolder && $aData['thumb_exists']) {
122: $mResult->Thumb = true;
123: }
124: }
125: return $mResult;
126: }
127:
128: public function getItemHash($oItem)
129: {
130: return \Aurora\System\Api::EncodeKeyValues(array(
131: 'UserId' => \Aurora\System\Api::getAuthenticatedUserId(),
132: 'Type' => $oItem->TypeStr,
133: 'Path' => $oItem->FullPath,
134: 'Name' => $oItem->Name
135: ));
136: }
137:
138: /**
139: * Writes to the $mResult variable open file source if $sType is DropBox account type.
140: *
141: * @ignore
142: * @param array $aArgs
143: * @param mixed $mResult
144: */
145: public function onGetFile($aArgs, &$mResult)
146: {
147: $sIndex = null;
148: $sPath = $aArgs['Path'];
149: if (\strpos($sPath, '$ZIP:')) {
150: list($sPath, $sIndex) = \explode('$ZIP:', $sPath);
151: }
152: $aPathInfo = \pathinfo($sPath);
153: if (isset($aPathInfo['extension']) && \strtolower($aPathInfo['extension']) === 'zip' && $sIndex != null) {
154: $aArgs['Id'] = \basename($sPath);
155: $aArgs['Path'] = \dirname($sPath) === '\\' ? '' : \dirname($sPath);
156: $oFileInfo = false;
157: \Aurora\System\Api::GetModuleManager()->broadcastEvent(
158: 'Files',
159: 'GetFileInfo::after',
160: $aArgs,
161: $oFileInfo
162: );
163:
164: if ($oFileInfo && class_exists('ZipArchive')) {
165: $za = new \ZipArchive();
166: $za->open($oFileInfo->RealPath);
167: $mResult = $za->getStream($sIndex);
168: if (\is_resource($mResult)) {
169: $aArgs['Name'] = \basename($sIndex);
170: return true;
171: }
172: }
173: }
174: }
175:
176: /**
177: * Writes to $aData variable list of DropBox files if $aData['Type'] is DropBox account type.
178: *
179: * @ignore
180: * @param array $aArgs Is passed by reference.
181: * @param mixed $mResult Is passed by reference.
182: */
183: public function onGetItems($aArgs, &$mResult)
184: {
185: if (isset($aArgs['Path'])) {
186: $sPath = $aArgs['Path'];
187: $sIndex = '';
188: if (\strpos($sPath, '$ZIP:')) {
189: list($sPath, $sIndex) = \explode('$ZIP:', $sPath);
190: }
191: $aPathInfo = \pathinfo($sPath);
192: if (isset($aPathInfo['extension']) && $aPathInfo['extension'] === 'zip') {
193: $aGetFileInfoArgs = array(
194: 'Id' => \basename($sPath),
195: 'Name' => \basename($sPath),
196: 'Path' => \trim(\dirname($sPath), '\\'),
197: 'UserId' => $aArgs['UserId'],
198: 'Type' => $aArgs['Type']
199: );
200: $oFileInfo = false;
201: \Aurora\System\Api::GetModuleManager()->broadcastEvent(
202: 'Files',
203: 'GetFileInfo::after',
204: $aGetFileInfoArgs,
205: $oFileInfo
206: );
207:
208: if ($oFileInfo && class_exists('ZipArchive')) {
209: $za = new \ZipArchive();
210: $za->open($oFileInfo->RealPath);
211:
212: $mResult = array();
213: $aItems = array();
214: for ($i = 0; $i < $za->numFiles; $i++) {
215: $aStat = $za->statIndex($i);
216: $sStatName = $aStat['name'];
217: if (!empty($sStatName) && !empty($sIndex)) {
218: if (strpos($sStatName, $sIndex) === 0) {
219: $sStatName = \substr($sStatName, \strlen($sIndex));
220: } else {
221: $sStatName = '';
222: }
223: }
224: if (!empty($sStatName)) {
225: $oItem /*@var $oItem \Aurora\Modules\Files\Classes\FileItem */ = new \Aurora\Modules\Files\Classes\FileItem();
226: $oItem->Id = $aStat['name'];
227: $oItem->Path = $sPath;
228: $oItem->TypeStr = $aArgs['Type'];
229: $oItem->FullPath = $oItem->Path . '$ZIP:' . $oItem->Id;
230: if ($aStat['size'] === 0) {
231: $oItem->IsFolder = true;
232: } else {
233: $oItem->Size = $aStat['size'];
234: }
235: $oItem->ContentType = \MailSo\Base\Utils::MimeContentType($oItem->Id);
236:
237: $aPath = \explode('/', $sStatName);
238: $sName = $aPath[0];
239:
240: if (!isset($aItems[$sName])) {
241: $oItem->Name = $sName;
242: $aItems[$sName] = $oItem;
243: }
244:
245: if ($oItem->IsFolder) {
246: $oItem->AddAction([
247: 'list' => []
248: ]);
249: } else {
250: $oItem->AddAction([
251: 'view' => [
252: 'url' => '?download-file/' . $this->getItemHash($oItem) . '/view'
253: ]
254: ]);
255: $oItem->AddAction([
256: 'download' => [
257: 'url' => '?download-file/' . $this->getItemHash($oItem)
258: ]
259: ]);
260:
261: $sMimeType = \MailSo\Base\Utils::MimeContentType($sName);
262: $oSettings = &\Aurora\System\Api::GetSettings();
263: $iThumbnailLimit = ((int) $oSettings->ThumbnailMaxFileSizeMb) * 1024 * 1024;
264: if ($oSettings->AllowThumbnail &&
265: $oItem->Size < $iThumbnailLimit && \Aurora\System\Utils::IsGDImageMimeTypeSuppoted($sMimeType, $sName)) {
266: $oItem->Thumb = true;
267: $oItem->ThumbnailUrl = '?download-file/' . $this->getItemHash($oItem) . '/thumb';
268: }
269: }
270: }
271: }
272: $mResult = \array_values($aItems);
273: }
274: return true;
275: }
276: }
277: }
278:
279: /**
280: * Creates folder if $aData['Type'] is DropBox account type.
281: *
282: * @ignore
283: * @param array $aArgs Is passed by reference.
284: * @param mixed $mResult Is passed by reference.
285: */
286: public function onBeforeCreateFolder($aArgs, &$mResult) {}
287:
288: /**
289: * Creates file if $aData['Type'] is DropBox account type.
290: *
291: * @ignore
292: * @param array $aArgs Is passed by reference.
293: * @param mixed $mResult Is passed by reference.
294: */
295: public function onCreateFile($aArgs, &$mResult) {}
296:
297: /**
298: * Deletes file if $aArgs['Type'] is DropBox account type.
299: *
300: * @ignore
301: * @param array $aArgs Is passed by reference.
302: * @param mixed $mResult Is passed by reference.
303: */
304: public function onAfterDelete($aArgs, &$mResult)
305: {
306: $bResult = false;
307:
308: foreach ($aArgs['Items'] as $aItem) {
309: $sPath = $aItem['Path'];
310: $aPathInfo = \pathinfo($sPath);
311: if (isset($aPathInfo['extension']) && $aPathInfo['extension'] === 'zip') {
312: $sName = $aItem['Name'];
313: $aGetFileInfoArgs = $aArgs;
314: $aGetFileInfoArgs['Name'] = \basename($sPath);
315: $aGetFileInfoArgs['Path'] = \dirname($sPath);
316: $oFileInfo = false;
317: \Aurora\System\Api::GetModuleManager()->broadcastEvent(
318: 'Files',
319: 'GetFileInfo::after',
320: $aGetFileInfoArgs,
321: $oFileInfo
322: );
323: if ($oFileInfo && class_exists('ZipArchive')) {
324: $za = new \ZipArchive();
325: $za->open($oFileInfo->RealPath);
326: $mResult = $za->deleteName($sName);
327: $bResult = $mResult;
328: }
329: }
330: }
331: return $bResult;
332: }
333:
334: /**
335: * Renames file if $aArgs['Type'] is DropBox account type.
336: *
337: * @ignore
338: * @param array $aArgs Is passed by reference.
339: * @param mixed $mResult Is passed by reference.
340: */
341: public function onAfterRename($aArgs, &$mResult)
342: {
343: $sPath = $aArgs['Path'];
344: $aPathInfo = \pathinfo($sPath);
345: if (isset($aPathInfo['extension']) && $aPathInfo['extension'] === 'zip') {
346: $sName = $aArgs['Name'];
347: $sNewName = $aArgs['NewName'];
348: $aArgs['Name'] = \basename($sPath);
349: $aArgs['Path'] = \dirname($sPath);
350: $oFileInfo = false;
351: \Aurora\System\Api::GetModuleManager()->broadcastEvent(
352: 'Files',
353: 'GetFileInfo::after',
354: $aArgs,
355: $oFileInfo
356: );
357: if ($oFileInfo && class_exists('ZipArchive')) {
358: $za = new \ZipArchive();
359: $za->open($oFileInfo->RealPath);
360: $sFileDir = \dirname($sName);
361: if ($sFileDir !== '.') {
362: $sNewFullPath = $sFileDir . $sNewName;
363: } else {
364: $sNewFullPath = $sNewName;
365: }
366: $mResult = $za->renameName($sName, $sNewFullPath);
367: $za->close();
368: }
369: return $mResult;
370: }
371: }
372:
373: /**
374: * Moves file if $aArgs['Type'] is DropBox account type.
375: *
376: * @ignore
377: * @param array $aArgs Is passed by reference.
378: * @param mixed $mResult Is passed by reference.
379: */
380: public function onBeforeMove($aArgs, &$mResult)
381: {
382: $sPath = $aArgs['FromPath'];
383: $aPathInfo = \pathinfo($sPath);
384: if (isset($aPathInfo['extension']) && $aPathInfo['extension'] === 'zip') {
385: $sFileName = $aArgs['Name'];
386: $aArgs['Name'] = \basename($sPath);
387: $aArgs['Path'] = \dirname($sPath);
388: $oFileInfo = false;
389: \Aurora\System\Api::GetModuleManager()->broadcastEvent(
390: 'Files',
391: 'GetFileInfo::after',
392: $aArgs,
393: $oFileInfo
394: );
395: if ($oFileInfo && class_exists('ZipArchive')) {
396: $za = new \ZipArchive();
397: $za->open($oFileInfo->RealPath);
398: }
399: }
400: }
401:
402: /**
403: * Copies file
404: *
405: * @ignore
406: * @param array $aArgs Is passed by reference.
407: * @param mixed $mResult Is passed by reference.
408: */
409: public function onBeforeCopy($aArgs, &$mResult) {}
410:
411: /**
412: * @ignore
413: * @todo not used
414: * @param array $aArgs Is passed by reference.
415: * @param mixed $mResult Is passed by reference.
416: */
417: public function onAfterGetFileInfo($aArgs, &$mResult) {}
418:
419: /**
420: * @ignore
421: * @todo not used
422: * @param object $oItem
423: * @param mixed $mResult Is passed by reference.
424: *
425: * @return boolean
426: */
427: public function onAfterPopulateFileItem($oItem, &$mResult)
428: {
429: if (isset($mResult)) {
430: $aPathInfo = \pathinfo($mResult->Name);
431: if (class_exists('ZipArchive') && isset($aPathInfo['extension']) && $aPathInfo['extension'] === 'zip') {
432: $mResult->UnshiftAction(array(
433: 'list' => array()
434: ));
435: }
436: }
437:
438: return false;
439: }
440: /***** private functions *****/
441: }
442: