|    1:  | <?php | 
|    2:  |  | 
|    3:  |  | 
|    4:  |  | 
|    5:  |  | 
|    6:  |  | 
|    7:  |  | 
|    8:  | namespace Aurora\Modules\OfficeDocumentEditor; | 
|    9:  |  | 
|   10:  | use Afterlogic\DAV\FS\File; | 
|   11:  | use Afterlogic\DAV\FS\Directory; | 
|   12:  | use Afterlogic\DAV\FS\Permission; | 
|   13:  | use Aurora\Api; | 
|   14:  | use Afterlogic\DAV\Server; | 
|   15:  | use Aurora\System\Application; | 
|   16:  | use Aurora\Modules\Core\Module as CoreModule; | 
|   17:  | use Aurora\Modules\Files\Module as FilesModule; | 
|   18:  | use Aurora\Modules\Files\Classes\FileItem; | 
|   19:  | use Aurora\Modules\OfficeDocumentEditor\Exceptions\Exception; | 
|   20:  | use Aurora\System\Enums\FileStorageType; | 
|   21:  |  | 
|   22:  | use function Sabre\Uri\split; | 
|   23:  |  | 
|   24:  |  | 
|   25:  |  | 
|   26:  |  | 
|   27:  |  | 
|   28:  |  | 
|   29:  |  | 
|   30:  |  | 
|   31:  |  | 
|   32:  |  | 
|   33:  | class Module extends \Aurora\System\Module\AbstractModule | 
|   34:  | { | 
|   35:  |     public $ExtsSpreadsheet = [ | 
|   36:  |         ".xls", | 
|   37:  |         ".xlsx", | 
|   38:  |         ".xlsm", | 
|   39:  |         ".xlt", | 
|   40:  |         ".xltx", | 
|   41:  |         ".xltm", | 
|   42:  |         ".ods", | 
|   43:  |         ".fods", | 
|   44:  |         ".ots", | 
|   45:  |         ".csv" | 
|   46:  |     ]; | 
|   47:  |     public $ExtsPresentation = [ | 
|   48:  |         ".pps", | 
|   49:  |         ".ppsx", | 
|   50:  |         ".ppsm", | 
|   51:  |         ".ppt", | 
|   52:  |         ".pptx", | 
|   53:  |         ".pptm", | 
|   54:  |         ".pot", | 
|   55:  |         ".potx", | 
|   56:  |         ".potm", | 
|   57:  |         ".odp", | 
|   58:  |         ".fodp", | 
|   59:  |         ".otp" | 
|   60:  |     ]; | 
|   61:  |     public $ExtsDocument = [ | 
|   62:  |         ".doc", | 
|   63:  |         ".docx", | 
|   64:  |         ".docm", | 
|   65:  |         ".dot", | 
|   66:  |         ".dotx", | 
|   67:  |         ".dotm", | 
|   68:  |         ".odt", | 
|   69:  |         ".fodt", | 
|   70:  |         ".ott", | 
|   71:  |         ".rtf", | 
|   72:  |         ".txt", | 
|   73:  |         ".html", | 
|   74:  |         ".htm", | 
|   75:  |         ".mht", | 
|   76:  |         ".pdf", | 
|   77:  |         ".djvu", | 
|   78:  |         ".fb2", | 
|   79:  |         ".epub", | 
|   80:  |         ".xps" | 
|   81:  |     ]; | 
|   82:  |  | 
|   83:  |  | 
|   84:  |      | 
|   85:  |  | 
|   86:  |  | 
|   87:  |  | 
|   88:  |  | 
|   89:  |     public function init() | 
|   90:  |     { | 
|   91:  |         $this->aErrors = [ | 
|   92:  |             Enums\ErrorCodes::ExtensionCannotBeConverted => $this->i18N('ERROR_EXTENSION_CANNOT_BE_CONVERTED') | 
|   93:  |         ]; | 
|   94:  |  | 
|   95:  |         $this->AddEntries([ | 
|   96:  |             'editor' => 'EntryEditor', | 
|   97:  |             'ode-callback' => 'EntryCallback' | 
|   98:  |         ]); | 
|   99:  |  | 
|  100:  |         $this->subscribeEvent('System::RunEntry::before', [$this, 'onBeforeFileViewEntry'], 10); | 
|  101:  |         $this->subscribeEvent('Files::GetFile', [$this, 'onGetFile'], 10); | 
|  102:  |         $this->subscribeEvent('Files::GetItems', [$this, 'onGetItems'], 20000); | 
|  103:  |         $this->subscribeEvent('Files::GetFileInfo::after', [$this, 'onAfterGetFileInfo'], 20000); | 
|  104:  |         $this->subscribeEvent('AddToContentSecurityPolicyDefault', [$this, 'onAddToContentSecurityPolicyDefault']); | 
|  105:  |     } | 
|  106:  |  | 
|  107:  |      | 
|  108:  |  | 
|  109:  |  | 
|  110:  |     public static function getInstance() | 
|  111:  |     { | 
|  112:  |         return parent::getInstance(); | 
|  113:  |     } | 
|  114:  |  | 
|  115:  |      | 
|  116:  |  | 
|  117:  |  | 
|  118:  |     public static function Decorator() | 
|  119:  |     { | 
|  120:  |         return parent::Decorator(); | 
|  121:  |     } | 
|  122:  |  | 
|  123:  |      | 
|  124:  |  | 
|  125:  |  | 
|  126:  |     public function getModuleSettings() | 
|  127:  |     { | 
|  128:  |         return $this->oModuleSettings; | 
|  129:  |     } | 
|  130:  |  | 
|  131:  |     public function GetSettings() | 
|  132:  |     { | 
|  133:  |         Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); | 
|  134:  |  | 
|  135:  |         return [ | 
|  136:  |             'ExtensionsToView' => $this->getOfficeExtensions() | 
|  137:  |         ]; | 
|  138:  |     } | 
|  139:  |  | 
|  140:  |     protected function getExtensionsToView() | 
|  141:  |     { | 
|  142:  |         return $this->oModuleSettings->ExtensionsToView; | 
|  143:  |     } | 
|  144:  |  | 
|  145:  |     protected function getExtensionsToConvert() | 
|  146:  |     { | 
|  147:  |         return $this->oModuleSettings->ExtensionsToConvert; | 
|  148:  |     } | 
|  149:  |  | 
|  150:  |     protected function getExtensionsToEdit() | 
|  151:  |     { | 
|  152:  |         return $this->oModuleSettings->ExtensionsToEdit; | 
|  153:  |     } | 
|  154:  |  | 
|  155:  |     protected function getOfficeExtensions() | 
|  156:  |     { | 
|  157:  |         return array_merge( | 
|  158:  |             $this->getExtensionsToView(), | 
|  159:  |             $this->getExtensionsToEdit(), | 
|  160:  |             array_keys($this->getExtensionsToConvert()) | 
|  161:  |         ); | 
|  162:  |     } | 
|  163:  |  | 
|  164:  |     protected function getDocumentType($filename) | 
|  165:  |     { | 
|  166:  |         $ext = strtolower('.' . pathinfo($filename, PATHINFO_EXTENSION)); | 
|  167:  |  | 
|  168:  |         if (in_array($ext, $this->ExtsDocument)) { | 
|  169:  |             return "word"; | 
|  170:  |         } | 
|  171:  |         if (in_array($ext, $this->ExtsSpreadsheet)) { | 
|  172:  |             return "cell"; | 
|  173:  |         } | 
|  174:  |         if (in_array($ext, $this->ExtsPresentation)) { | 
|  175:  |             return "slide"; | 
|  176:  |         } | 
|  177:  |         return ""; | 
|  178:  |     } | 
|  179:  |  | 
|  180:  |     protected function isReadOnlyDocument($filename) | 
|  181:  |     { | 
|  182:  |         $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); | 
|  183:  |  | 
|  184:  |         return in_array($ext, $this->getExtensionsToView()); | 
|  185:  |     } | 
|  186:  |  | 
|  187:  |     protected function documentCanBeEdited($filename) | 
|  188:  |     { | 
|  189:  |         $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); | 
|  190:  |  | 
|  191:  |         return in_array($ext, $this->getExtensionsToEdit()); | 
|  192:  |     } | 
|  193:  |  | 
|  194:  |     protected function documentCanBeConverted($sFilename) | 
|  195:  |     { | 
|  196:  |         $ext = strtolower(pathinfo($sFilename, PATHINFO_EXTENSION)); | 
|  197:  |  | 
|  198:  |         return in_array($ext, array_keys($this->getExtensionsToConvert())); | 
|  199:  |     } | 
|  200:  |  | 
|  201:  |      | 
|  202:  |  | 
|  203:  |  | 
|  204:  |  | 
|  205:  |     public function isOfficeDocument($sFileName = '') | 
|  206:  |     { | 
|  207:  |         $sExtensions = implode('|', $this->getOfficeExtensions()); | 
|  208:  |         return !!preg_match('/\.(' . $sExtensions . ')$/', strtolower(trim($sFileName))); | 
|  209:  |     } | 
|  210:  |  | 
|  211:  |     protected function isTrustedRequest() | 
|  212:  |     { | 
|  213:  |         return true;  | 
|  214:  |  | 
|  215:  |         $bResult = false; | 
|  216:  |  | 
|  217:  |         $sTrustedServerHost = $this->oModuleSettings->TrustedServerHost; | 
|  218:  |         if (empty($sTrustedServerHost)) { | 
|  219:  |             $bResult = true; | 
|  220:  |         } else { | 
|  221:  |             if (!empty($_SERVER['HTTP_CLIENT_IP'])) { | 
|  222:  |                 $ip = $_SERVER['HTTP_CLIENT_IP']; | 
|  223:  |             } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { | 
|  224:  |                 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; | 
|  225:  |             } else { | 
|  226:  |                 $ip = $_SERVER['REMOTE_ADDR']; | 
|  227:  |             } | 
|  228:  |  | 
|  229:  |             $bResult = $sTrustedServerHost === $ip; | 
|  230:  |         } | 
|  231:  |  | 
|  232:  |         return $bResult; | 
|  233:  |     } | 
|  234:  |  | 
|  235:  |      | 
|  236:  |  | 
|  237:  |  | 
|  238:  |  | 
|  239:  |  | 
|  240:  |     public function onBeforeFileViewEntry(&$aArguments, &$aResult) | 
|  241:  |     { | 
|  242:  |         $aEntries = [ | 
|  243:  |             'download-file', | 
|  244:  |             'file-cache', | 
|  245:  |             'mail-attachment' | 
|  246:  |         ]; | 
|  247:  |         if (in_array($aArguments['EntryName'], $aEntries)) { | 
|  248:  |             $sEntry = (string) \Aurora\System\Router::getItemByIndex(0, ''); | 
|  249:  |             $sHash = (string) \Aurora\System\Router::getItemByIndex(1, ''); | 
|  250:  |             $sAction = (string) \Aurora\System\Router::getItemByIndex(2, ''); | 
|  251:  |  | 
|  252:  |             $aValues = Api::DecodeKeyValues($sHash); | 
|  253:  |  | 
|  254:  |             $sFileName = isset($aValues['FileName']) ? urldecode($aValues['FileName']) : ''; | 
|  255:  |             if (empty($sFileName)) { | 
|  256:  |                 $sFileName = isset($aValues['Name']) ? urldecode($aValues['Name']) : ''; | 
|  257:  |             } | 
|  258:  |             if ($sAction === 'view' && $this->isOfficeDocument($sFileName) && !isset($aValues[\Aurora\System\Application::AUTH_TOKEN_KEY])) { | 
|  259:  |                 $sViewerUrl = './?editor=' . urlencode($sEntry . '/' . $sHash . '/' . $sAction . '/' . time()); | 
|  260:  |                 \header('Location: ' . $sViewerUrl); | 
|  261:  |             } elseif ($this->isOfficeDocument($sFileName) || $sFileName === 'diff.zip' || $sFileName === 'changes.json') { | 
|  262:  |                 if ($this->isTrustedRequest()) { | 
|  263:  |                     $sAuthToken = $aValues[\Aurora\System\Application::AUTH_TOKEN_KEY] ?? null; | 
|  264:  |                     if (isset($sAuthToken)) { | 
|  265:  |                         Api::setAuthToken($sAuthToken); | 
|  266:  |                         Api::setUserId( | 
|  267:  |                             Api::getAuthenticatedUserId($sAuthToken) | 
|  268:  |                         ); | 
|  269:  |                     } | 
|  270:  |                 } | 
|  271:  |             } | 
|  272:  |         } | 
|  273:  |     } | 
|  274:  |  | 
|  275:  |     public function EntryEditor() | 
|  276:  |     { | 
|  277:  |         $sResult = ''; | 
|  278:  |         $sFullUrl = Application::getBaseUrl(); | 
|  279:  |         $sMode = 'view'; | 
|  280:  |         $fileuri = isset($_GET['editor']) ? $_GET['editor'] : null; | 
|  281:  |         $filename = null; | 
|  282:  |         $sHash = null; | 
|  283:  |         $aHashValues = []; | 
|  284:  |         $docKey = null; | 
|  285:  |         $lastModified = time(); | 
|  286:  |         $aHistory = []; | 
|  287:  |  | 
|  288:  |         $oUser = Api::getAuthenticatedUser(); | 
|  289:  |  | 
|  290:  |         if (isset($fileuri)) { | 
|  291:  |             $fileuri = \urldecode($fileuri); | 
|  292:  |             $aFileuri = \explode('/', $fileuri); | 
|  293:  |             if (isset($aFileuri[1])) { | 
|  294:  |                 $sHash = $aFileuri[1]; | 
|  295:  |                 $aHashValues = Api::DecodeKeyValues($sHash); | 
|  296:  |                 if (!isset($aHashValues[\Aurora\System\Application::AUTH_TOKEN_KEY])) { | 
|  297:  |                     $aHashValues[\Aurora\System\Application::AUTH_TOKEN_KEY] = Api::UserSession()->Set( | 
|  298:  |                         [ | 
|  299:  |                             'token' => 'auth', | 
|  300:  |                             'id' => Api::getAuthenticatedUserId() | 
|  301:  |                         ], | 
|  302:  |                         time(), | 
|  303:  |                         time() + 60 * 5  | 
|  304:  |                     ); | 
|  305:  |                 } | 
|  306:  |             } | 
|  307:  |             $sHash = Api::EncodeKeyValues($aHashValues); | 
|  308:  |             $aFileuri[1] = $sHash; | 
|  309:  |             $fileuri = implode('/', $aFileuri); | 
|  310:  |             $fileuri = $sFullUrl . '?' . $fileuri; | 
|  311:  |         } | 
|  312:  |  | 
|  313:  |         if ($sHash) { | 
|  314:  |             $aHashValues = Api::DecodeKeyValues($sHash); | 
|  315:  |             if (isset($aHashValues['FileName'])) { | 
|  316:  |                 $filename = $aHashValues['FileName']; | 
|  317:  |             } elseif (isset($aHashValues['Name'])) { | 
|  318:  |                 $filename = $aHashValues['Name']; | 
|  319:  |             } | 
|  320:  |             if (isset($aHashValues['Edit'])) { | 
|  321:  |                 $sMode = 'edit'; | 
|  322:  |             } | 
|  323:  |  | 
|  324:  |             $sHash = Api::EncodeKeyValues($aHashValues); | 
|  325:  |             $oFileInfo = null; | 
|  326:  |             try { | 
|  327:  |                 if (isset($aHashValues['UserId'], $aHashValues['Type'], $aHashValues['Path'], $aHashValues['Id'])) { | 
|  328:  |                     $oFileInfo = FilesModule::Decorator()->GetFileInfo( | 
|  329:  |                         $aHashValues['UserId'], | 
|  330:  |                         $aHashValues['Type'], | 
|  331:  |                         $aHashValues['Path'], | 
|  332:  |                         $aHashValues['Id'] | 
|  333:  |                     ); | 
|  334:  |                 } | 
|  335:  |             } catch (\Exception $oEx) { | 
|  336:  |             } | 
|  337:  |  | 
|  338:  |             if ($oFileInfo) { | 
|  339:  |                 $lastModified = $oFileInfo->LastModified; | 
|  340:  |                 $docKey = \md5($oFileInfo->RealPath . $lastModified); | 
|  341:  |                 $oFileInfo->Path = $aHashValues['Path']; | 
|  342:  |                 $SharedWithMeAccess = isset($oFileInfo->ExtendedProps['SharedWithMeAccess']) ? (int) $oFileInfo->ExtendedProps['SharedWithMeAccess'] : null; | 
|  343:  |  | 
|  344:  |                 if (!isset($SharedWithMeAccess) && $oFileInfo->Owner !== $oUser->PublicId) { | 
|  345:  |                     list($sParentPath, $sParentId) = \Sabre\Uri\split($aHashValues['Path']); | 
|  346:  |                     $oParentFileInfo = null; | 
|  347:  |                     try { | 
|  348:  |                         $oParentFileInfo = FilesModule::Decorator()->GetFileInfo( | 
|  349:  |                             $aHashValues['UserId'], | 
|  350:  |                             $aHashValues['Type'], | 
|  351:  |                             $sParentPath, | 
|  352:  |                             $sParentId | 
|  353:  |                         ); | 
|  354:  |                     } catch (\Exception $oEx) { | 
|  355:  |                     } | 
|  356:  |  | 
|  357:  |                     if (isset($oParentFileInfo) && $oParentFileInfo->Owner === $oUser->PublicId) { | 
|  358:  |                         $oFileInfo->Owner = $oParentFileInfo->Owner; | 
|  359:  |                     } | 
|  360:  |                 } | 
|  361:  |  | 
|  362:  |                 $sMode = (isset($SharedWithMeAccess) && ($SharedWithMeAccess === Permission::Write || $SharedWithMeAccess === Permission::Reshare)) || | 
|  363:  |                     (!isset($SharedWithMeAccess) && $oFileInfo->Owner === $oUser->PublicId) || ($oFileInfo->TypeStr === FileStorageType::Corporate) ? $sMode : 'view'; | 
|  364:  |                 $aHistory = $this->getHistory($oFileInfo, $docKey, $fileuri); | 
|  365:  |             } elseif (isset($aHashValues['FileName'])) { | 
|  366:  |                 $docKey = \md5($aHashValues['FileName'] . time()); | 
|  367:  |             } elseif (isset($aHashValues['Name'])) { | 
|  368:  |                 $docKey = \md5($aHashValues['Name'] . time()); | 
|  369:  |             } | 
|  370:  |         } | 
|  371:  |  | 
|  372:  |         $bIsReadOnlyMode = ($sMode === 'view') ? true : false; | 
|  373:  |  | 
|  374:  |         $filetype = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); | 
|  375:  |         $lang = 'en'; | 
|  376:  |         $mode = $bIsReadOnlyMode || $this->isReadOnlyDocument($filename) ? 'view' : 'edit'; | 
|  377:  |         $fileuriUser = ''; | 
|  378:  |  | 
|  379:  |         $serverPath = $this->oModuleSettings->DocumentServerUrl; | 
|  380:  |  | 
|  381:  |         $callbackUrl = $sFullUrl . '?ode-callback/' . $sHash; | 
|  382:  |  | 
|  383:  |         if (isset($fileuri) && $serverPath) { | 
|  384:  |             if ($oUser) { | 
|  385:  |                 $uid = (string) $oUser->Id; | 
|  386:  |                 $uname = !empty($oUser->Name) ? $oUser->Name : $oUser->PublicId; | 
|  387:  |                 $lang = \Aurora\System\Utils::ConvertLanguageNameToShort($oUser->Language); | 
|  388:  |             } | 
|  389:  |  | 
|  390:  |             $config = [ | 
|  391:  |                 "type" => Api::IsMobileApplication() ? "mobile" : 'desktop', | 
|  392:  |                 "documentType" => $this->getDocumentType($filename), | 
|  393:  |                 "document" => [ | 
|  394:  |                     "title" => $filename, | 
|  395:  |                     "url" => $fileuri, | 
|  396:  |                     "fileType" => $filetype, | 
|  397:  |                     "key" => $docKey, | 
|  398:  |                     "info" => [ | 
|  399:  |                         "owner" => $uname, | 
|  400:  |                         "uploaded" => date('d.m.y', $lastModified) | 
|  401:  |                     ], | 
|  402:  |                     "permissions" => [ | 
|  403:  |                         "comment" => !$bIsReadOnlyMode, | 
|  404:  |                         "download" => true, | 
|  405:  |                         "edit" => !$bIsReadOnlyMode, | 
|  406:  |                         "fillForms" => !$bIsReadOnlyMode, | 
|  407:  |                         "modifyFilter" => !$bIsReadOnlyMode, | 
|  408:  |                         "modifyContentControl" => !$bIsReadOnlyMode, | 
|  409:  |                         "review" => !$bIsReadOnlyMode, | 
|  410:  |                         "changeHistory" => !$bIsReadOnlyMode, | 
|  411:  |                         "chat" => !$bIsReadOnlyMode | 
|  412:  |                     ] | 
|  413:  |                 ], | 
|  414:  |                 "editorConfig" => [ | 
|  415:  |                     "actionLink" => empty($_GET["actionLink"]) ? null : json_decode($_GET["actionLink"]), | 
|  416:  |                     "mode" => $mode, | 
|  417:  |                     "lang" => $lang, | 
|  418:  |                     "callbackUrl" => $callbackUrl, | 
|  419:  |                     "user" => [ | 
|  420:  |                         "id" => $uid, | 
|  421:  |                         "name" => $uname | 
|  422:  |                     ], | 
|  423:  |                     "embedded" => [ | 
|  424:  |                         "saveUrl" => $fileuriUser, | 
|  425:  |                         "embedUrl" => $fileuriUser, | 
|  426:  |                         "shareUrl" => $fileuriUser, | 
|  427:  |                         "toolbarDocked" => "top", | 
|  428:  |                     ], | 
|  429:  |                     "customization" => [ | 
|  430:  |                         "comments" => !$bIsReadOnlyMode, | 
|  431:  |                         "about" => false, | 
|  432:  |                         "feedback" => false, | 
|  433:  |                         "goback" => false, | 
|  434:  |                         "forcesave" => true, | 
|  435:  |                          | 
|  436:  |                          | 
|  437:  |                          | 
|  438:  |                     ] | 
|  439:  |                 ] | 
|  440:  |             ]; | 
|  441:  |  | 
|  442:  |             $oJwt = new Classes\JwtManager($this->oModuleSettings->Secret); | 
|  443:  |             if ($oJwt->isJwtEnabled()) { | 
|  444:  |                 $config['token'] = $oJwt->jwtEncode($config); | 
|  445:  |             } | 
|  446:  |  | 
|  447:  |             $sResult = \file_get_contents($this->GetPath() . '/templates/Editor.html'); | 
|  448:  |  | 
|  449:  |             $iUserId = Api::getAuthenticatedUserId(); | 
|  450:  |             if (0 < $iUserId) { | 
|  451:  |                 $sResult = strtr($sResult, [ | 
|  452:  |                     '{{DOC_SERV_API_URL}}' => $serverPath . '/web-apps/apps/api/documents/api.js', | 
|  453:  |                     '{{CONFIG}}' => \json_encode($config), | 
|  454:  |                     '{{HISTORY}}' => isset($aHistory[0]) ? \json_encode($aHistory[0]) : 'false', | 
|  455:  |                     '{{HISTORY_DATA}}' => isset($aHistory[1]) ? \json_encode($aHistory[1]) : 'false' | 
|  456:  |                 ]); | 
|  457:  |                 \Aurora\Modules\CoreWebclient\Module::Decorator()->SetHtmlOutputHeaders(); | 
|  458:  |                 @header('Cache-Control: no-cache, no-store, must-revalidate', true); | 
|  459:  |                 @header('Pragma: no-cache', true); | 
|  460:  |                 @header('Expires: 0', true); | 
|  461:  |             } else { | 
|  462:  |                 Api::Location('./'); | 
|  463:  |             } | 
|  464:  |         } | 
|  465:  |  | 
|  466:  |         return $sResult; | 
|  467:  |     } | 
|  468:  |  | 
|  469:  |     public function CreateBlankDocument($Type, $Path, $FileName) | 
|  470:  |     { | 
|  471:  |         $mResult = false; | 
|  472:  |         $ext = strtolower(pathinfo($FileName, PATHINFO_EXTENSION)); | 
|  473:  |         $sFilePath = $this->GetPath() . "/data/new." . $ext; | 
|  474:  |         if (file_exists($sFilePath)) { | 
|  475:  |             $rData = \fopen($sFilePath, "r"); | 
|  476:  |             $FileName = FilesModule::Decorator()->GetNonExistentFileName( | 
|  477:  |                 Api::getAuthenticatedUserId(), | 
|  478:  |                 $Type, | 
|  479:  |                 $Path, | 
|  480:  |                 $FileName | 
|  481:  |             ); | 
|  482:  |             $mResult = $this->createFile( | 
|  483:  |                 Api::getAuthenticatedUserId(), | 
|  484:  |                 $Type, | 
|  485:  |                 $Path, | 
|  486:  |                 $FileName, | 
|  487:  |                 $rData | 
|  488:  |             ); | 
|  489:  |             \fclose($rData); | 
|  490:  |  | 
|  491:  |             if ($mResult) { | 
|  492:  |                 $mResult = FilesModule::Decorator()->GetFileInfo( | 
|  493:  |                     Api::getAuthenticatedUserId(), | 
|  494:  |                     $Type, | 
|  495:  |                     $Path, | 
|  496:  |                     $FileName | 
|  497:  |                 ); | 
|  498:  |             } | 
|  499:  |         } | 
|  500:  |         return $mResult; | 
|  501:  |     } | 
|  502:  |  | 
|  503:  |     public function ConvertDocument($Type, $Path, $FileName) | 
|  504:  |     { | 
|  505:  |         $mResult = false; | 
|  506:  |         $aExtensions = $this->getExtensionsToConvert(); | 
|  507:  |         $sExtension = pathinfo($FileName, PATHINFO_EXTENSION); | 
|  508:  |         $sNewExtension = isset($aExtensions[$sExtension]) ? $aExtensions[$sExtension] : null; | 
|  509:  |         if ($sNewExtension === null) { | 
|  510:  |             throw new Exceptions\Exception(Enums\ErrorCodes::ExtensionCannotBeConverted); | 
|  511:  |         } else { | 
|  512:  |             $mResult = self::Decorator()->ConvertDocumentToFormat($Type, $Path, $FileName, $sNewExtension); | 
|  513:  |         } | 
|  514:  |  | 
|  515:  |         return $mResult; | 
|  516:  |     } | 
|  517:  |  | 
|  518:  |     public function ConvertDocumentToFormat($Type, $Path, $FileName, $ToExtension) | 
|  519:  |     { | 
|  520:  |         $mResult = false; | 
|  521:  |         $oFileInfo = FilesModule::Decorator()->GetFileInfo( | 
|  522:  |             Api::getAuthenticatedUserId(), | 
|  523:  |             $Type, | 
|  524:  |             $Path, | 
|  525:  |             $FileName | 
|  526:  |         ); | 
|  527:  |         if ($oFileInfo instanceof  FileItem) { | 
|  528:  |             $sConvertedDocumentUri = null; | 
|  529:  |             $aPathParts = pathinfo($FileName); | 
|  530:  |             $sFromExtension = $aPathParts['extension']; | 
|  531:  |             $sFileNameWOExt = $aPathParts['filename']; | 
|  532:  |             $sDocumentUri = ''; | 
|  533:  |  | 
|  534:  |             if (isset($oFileInfo->Actions['download']['url'])) { | 
|  535:  |                 $sDownloadUrl = $oFileInfo->Actions['download']['url']; | 
|  536:  |                 $aUrlParts = \explode('/', $sDownloadUrl); | 
|  537:  |                 if (isset($aUrlParts[1])) { | 
|  538:  |                     $aUrlParts[1] = $this->GetFileTempHash($aUrlParts[1]); | 
|  539:  |                     $sDocumentUri = Application::getBaseUrl() . \implode('/', $aUrlParts); | 
|  540:  |                     $this->GetConvertedUri( | 
|  541:  |                         $sDocumentUri, | 
|  542:  |                         $sFromExtension, | 
|  543:  |                         $ToExtension, | 
|  544:  |                         '', | 
|  545:  |                         false, | 
|  546:  |                         $sConvertedDocumentUri | 
|  547:  |                     ); | 
|  548:  |                     if (!empty($sConvertedDocumentUri)) { | 
|  549:  |                         $rData = \file_get_contents($sConvertedDocumentUri); | 
|  550:  |                         if ($rData !== false) { | 
|  551:  |                             $sNewFileName = FilesModule::Decorator()->GetNonExistentFileName( | 
|  552:  |                                 Api::getAuthenticatedUserId(), | 
|  553:  |                                 $Type, | 
|  554:  |                                 $Path, | 
|  555:  |                                 $sFileNameWOExt . '.' . $ToExtension | 
|  556:  |                             ); | 
|  557:  |                             $mResult = $this->createFile( | 
|  558:  |                                 Api::getAuthenticatedUserId(), | 
|  559:  |                                 $Type, | 
|  560:  |                                 $Path, | 
|  561:  |                                 $sNewFileName, | 
|  562:  |                                 $rData | 
|  563:  |                             ); | 
|  564:  |                             if ($mResult) { | 
|  565:  |                                 $mResult = FilesModule::Decorator()->GetFileInfo( | 
|  566:  |                                     Api::getAuthenticatedUserId(), | 
|  567:  |                                     $Type, | 
|  568:  |                                     $Path, | 
|  569:  |                                     $sNewFileName | 
|  570:  |                                 ); | 
|  571:  |                             } | 
|  572:  |                         } | 
|  573:  |                     } | 
|  574:  |                 } | 
|  575:  |             } | 
|  576:  |         } | 
|  577:  |  | 
|  578:  |         return $mResult; | 
|  579:  |     } | 
|  580:  |  | 
|  581:  |     protected function GetFileTempHash($sHash) | 
|  582:  |     { | 
|  583:  |         $aValues = Api::DecodeKeyValues($sHash); | 
|  584:  |  | 
|  585:  |         $sFileName = isset($aValues['FileName']) ? urldecode($aValues['FileName']) : ''; | 
|  586:  |         if (empty($sFileName)) { | 
|  587:  |             $sFileName = isset($aValues['Name']) ? urldecode($aValues['Name']) : ''; | 
|  588:  |         } | 
|  589:  |         $aValues[\Aurora\System\Application::AUTH_TOKEN_KEY] = Api::UserSession()->Set( | 
|  590:  |             [ | 
|  591:  |                 'token' => 'auth', | 
|  592:  |                 'id' => Api::getAuthenticatedUserId() | 
|  593:  |             ], | 
|  594:  |             time(), | 
|  595:  |             time() + 60 * 5  | 
|  596:  |         ); | 
|  597:  |  | 
|  598:  |         return Api::EncodeKeyValues($aValues); | 
|  599:  |     } | 
|  600:  |  | 
|  601:  |     protected function GetConvertedUri($document_uri, $from_extension, $to_extension, $document_revision_id, $is_async, &$converted_document_uri) | 
|  602:  |     { | 
|  603:  |         $converted_document_uri = ""; | 
|  604:  |         $responceFromConvertService = $this->SendRequestToConvertService( | 
|  605:  |             $document_uri, | 
|  606:  |             $from_extension, | 
|  607:  |             $to_extension, | 
|  608:  |             $document_revision_id, | 
|  609:  |             $is_async | 
|  610:  |         ); | 
|  611:  |         $json = \json_decode($responceFromConvertService, true); | 
|  612:  |  | 
|  613:  |         $errorElement = isset($json["error"]) ? $json["error"] : null; | 
|  614:  |         if ($errorElement != null && $errorElement != "") { | 
|  615:  |             $this->ProcessConvServResponceError($errorElement); | 
|  616:  |         } | 
|  617:  |  | 
|  618:  |         $isEndConvert = $json["endConvert"]; | 
|  619:  |         $percent = $json["percent"]; | 
|  620:  |  | 
|  621:  |         if ($isEndConvert != null && $isEndConvert == true) { | 
|  622:  |             $converted_document_uri = $json["fileUrl"]; | 
|  623:  |             $percent = 100; | 
|  624:  |         } elseif ($percent >= 100) { | 
|  625:  |             $percent = 99; | 
|  626:  |         } | 
|  627:  |  | 
|  628:  |         return $percent; | 
|  629:  |     } | 
|  630:  |  | 
|  631:  |     protected function SendRequestToConvertService($document_uri, $from_extension, $to_extension, $document_revision_id, $is_async) | 
|  632:  |     { | 
|  633:  |         $title = basename($document_uri); | 
|  634:  |         $urlToConverter = ''; | 
|  635:  |  | 
|  636:  |         if (empty($title)) { | 
|  637:  |             $title = \Sabre\DAV\UUIDUtil::getUUID(); | 
|  638:  |         } | 
|  639:  |  | 
|  640:  |         if (empty($document_revision_id)) { | 
|  641:  |             $document_revision_id = $document_uri; | 
|  642:  |         } | 
|  643:  |  | 
|  644:  |         $document_revision_id = $this->GenerateRevisionId($document_revision_id); | 
|  645:  |  | 
|  646:  |         $serverPath = $this->oModuleSettings->DocumentServerUrl; | 
|  647:  |         if ($serverPath !== null) { | 
|  648:  |             $urlToConverter = $serverPath . '/ConvertService.ashx'; | 
|  649:  |         } | 
|  650:  |  | 
|  651:  |         $arr = [ | 
|  652:  |             "async" => $is_async, | 
|  653:  |             "url" => $document_uri, | 
|  654:  |             "outputtype" => trim($to_extension, '.'), | 
|  655:  |             "filetype" => trim($from_extension, '.'), | 
|  656:  |             "title" => $title, | 
|  657:  |             "key" => $document_revision_id | 
|  658:  |         ]; | 
|  659:  |  | 
|  660:  |         $headerToken = ""; | 
|  661:  |  | 
|  662:  |         $oJwt = new Classes\JwtManager($this->oModuleSettings->Secret); | 
|  663:  |         if ($oJwt->isJwtEnabled()) { | 
|  664:  |             $headerToken = $oJwt->jwtEncode([ "payload" => $arr ]); | 
|  665:  |             $arr["token"] = $oJwt->jwtEncode($arr); | 
|  666:  |         } | 
|  667:  |  | 
|  668:  |         $opts = [ | 
|  669:  |             'http' => [ | 
|  670:  |                 'method'  => 'POST', | 
|  671:  |                 'timeout' => '120000', | 
|  672:  |                 'header' => "Content-type: application/json\r\n" . | 
|  673:  |                             "Accept: application/json\r\n" . | 
|  674:  |                             (empty($headerToken) ? "" : "Authorization: $headerToken\r\n"), | 
|  675:  |                 'content' => \json_encode($arr) | 
|  676:  |             ] | 
|  677:  |         ]; | 
|  678:  |  | 
|  679:  |         if (substr($urlToConverter, 0, strlen("https")) === "https") { | 
|  680:  |             $opts['ssl'] = ['verify_peer' => false]; | 
|  681:  |         } | 
|  682:  |  | 
|  683:  |         $context  = stream_context_create($opts); | 
|  684:  |         $response_data = file_get_contents($urlToConverter, false, $context); | 
|  685:  |  | 
|  686:  |         return $response_data; | 
|  687:  |     } | 
|  688:  |  | 
|  689:  |     protected function ProcessConvServResponceError($errorCode) | 
|  690:  |     { | 
|  691:  |         $errorMessageTemplate = "Error occurred in the document service: "; | 
|  692:  |         $errorMessage = ''; | 
|  693:  |  | 
|  694:  |         switch ($errorCode) { | 
|  695:  |             case -8: | 
|  696:  |                 $errorMessage = $errorMessageTemplate . "Error document VKey"; | 
|  697:  |                 break; | 
|  698:  |             case -7: | 
|  699:  |                 $errorMessage = $errorMessageTemplate . "Error document request"; | 
|  700:  |                 break; | 
|  701:  |             case -6: | 
|  702:  |                 $errorMessage = $errorMessageTemplate . "Error database"; | 
|  703:  |                 break; | 
|  704:  |             case -5: | 
|  705:  |                 $errorMessage = $errorMessageTemplate . "Error unexpected guid"; | 
|  706:  |                 break; | 
|  707:  |             case -4: | 
|  708:  |                 $errorMessage = $errorMessageTemplate . "Error download error"; | 
|  709:  |                 break; | 
|  710:  |             case -3: | 
|  711:  |                 $errorMessage = $errorMessageTemplate . "Error convertation error"; | 
|  712:  |                 break; | 
|  713:  |             case -2: | 
|  714:  |                 $errorMessage = $errorMessageTemplate . "Error convertation timeout"; | 
|  715:  |                 break; | 
|  716:  |             case -1: | 
|  717:  |                 $errorMessage = $errorMessageTemplate . "Error convertation unknown"; | 
|  718:  |                 break; | 
|  719:  |             case 0: | 
|  720:  |                 break; | 
|  721:  |             default: | 
|  722:  |                 $errorMessage = $errorMessageTemplate . "ErrorCode = " . $errorCode; | 
|  723:  |                 break; | 
|  724:  |         } | 
|  725:  |  | 
|  726:  |         throw new Exception($errorMessage); | 
|  727:  |     } | 
|  728:  |  | 
|  729:  |     protected function GenerateRevisionId($expected_key) | 
|  730:  |     { | 
|  731:  |         if (strlen($expected_key) > 20) { | 
|  732:  |             $expected_key = crc32($expected_key); | 
|  733:  |         } | 
|  734:  |         $key = preg_replace("[^0-9-.a-zA-Z_=]", "_", $expected_key); | 
|  735:  |         $key = substr($key, 0, min(array(strlen($key), 20))); | 
|  736:  |         return $key; | 
|  737:  |     } | 
|  738:  |  | 
|  739:  |     public function EntryCallback() | 
|  740:  |     { | 
|  741:  |         $result = ["error" => 0]; | 
|  742:  |  | 
|  743:  |         if (($body_stream = file_get_contents("php://input")) === false) { | 
|  744:  |             $result["error"] = "Bad Request"; | 
|  745:  |         } else { | 
|  746:  |             $data = json_decode($body_stream, true); | 
|  747:  |  | 
|  748:  |             if (isset($data['token'])) { | 
|  749:  |                 Api::AddSecret($data['token']); | 
|  750:  |             } | 
|  751:  |             Api::Log($body_stream); | 
|  752:  |  | 
|  753:  |             $oJwt = new Classes\JwtManager($this->oModuleSettings->Secret); | 
|  754:  |             if ($oJwt->isJwtEnabled()) { | 
|  755:  |                 $inHeader = false; | 
|  756:  |                 $token = ""; | 
|  757:  |                 if (!empty($data["token"])) { | 
|  758:  |                     $token = $oJwt->jwtDecode($data["token"]); | 
|  759:  |                 } elseif (!empty($_SERVER['HTTP_AUTHORIZATION'])) { | 
|  760:  |                     $token = $oJwt->jwtDecode(substr($_SERVER['HTTP_AUTHORIZATION'], strlen("Bearer "))); | 
|  761:  |                     $inHeader = true; | 
|  762:  |                 } else { | 
|  763:  |                     $result["error"] = "Expected JWT"; | 
|  764:  |                 } | 
|  765:  |                 if (empty($token)) { | 
|  766:  |                     $result["error"] = "Invalid JWT signature"; | 
|  767:  |                 } else { | 
|  768:  |                     $data = json_decode($token, true); | 
|  769:  |                     if ($inHeader) { | 
|  770:  |                         $data = $data["payload"]; | 
|  771:  |                     } | 
|  772:  |                 } | 
|  773:  |             } | 
|  774:  |  | 
|  775:  |             if ($data["status"] == 2 || $data["status"] == 6) { | 
|  776:  |                 $sHash = (string) \Aurora\System\Router::getItemByIndex(1, ''); | 
|  777:  |                 if (!empty($sHash)) { | 
|  778:  |                     $aHashValues = Api::DecodeKeyValues($sHash); | 
|  779:  |  | 
|  780:  |                     $prevState = Api::skipCheckUserRole(true); | 
|  781:  |                     $oFileInfo = FilesModule::Decorator()->GetFileInfo( | 
|  782:  |                         $aHashValues['UserId'], | 
|  783:  |                         $aHashValues['Type'], | 
|  784:  |                         $aHashValues['Path'], | 
|  785:  |                         $aHashValues['Name'] | 
|  786:  |                     ); | 
|  787:  |                     if ($oFileInfo instanceof FileItem && $this->isOfficeDocument($oFileInfo->Name)) { | 
|  788:  |                         if ((isset($oFileInfo->ExtendedProps['SharedWithMeAccess']) && (int) $oFileInfo->ExtendedProps['SharedWithMeAccess'] === \Afterlogic\DAV\FS\Permission::Write) || !isset($oFileInfo->ExtendedProps['SharedWithMeAccess']) | 
|  789:  |                             && !$this->isReadOnlyDocument($oFileInfo->Name)) { | 
|  790:  |                             $rData = \file_get_contents($data["url"]); | 
|  791:  |                             if ($rData !== false) { | 
|  792:  |                                 if ($this->oModuleSettings->EnableHistory && $data["status"] == 2) { | 
|  793:  |                                     if ($this->isTrustedRequest()) { | 
|  794:  |                                         $iUserId = isset($aHashValues['UserId']) ? $aHashValues['UserId'] : null; | 
|  795:  |                                         if (isset($iUserId)) { | 
|  796:  |                                             Server::setUser(Api::getUserPublicIdById($iUserId)); | 
|  797:  |                                         } | 
|  798:  |                                     } | 
|  799:  |                                     $histDir = $this->getHistoryDir($oFileInfo, true); | 
|  800:  |                                     if ($histDir) { | 
|  801:  |                                         $curVer = $histDir->getFileVersion(); | 
|  802:  |                                         $verDir = $histDir->getVersionDir($curVer + 1, true); | 
|  803:  |                                         $ext = strtolower(pathinfo($oFileInfo->Name, PATHINFO_EXTENSION)); | 
|  804:  |  | 
|  805:  |                                         list(, $sOwnerUserPublicId) = split($verDir->getOwner()); | 
|  806:  |                                         $iOwnerUserId = Api::getUserIdByPublicId($sOwnerUserPublicId); | 
|  807:  |  | 
|  808:  |                                         if (!isset($oFileInfo->ExtendedProps['Created']) && $curVer == 0) { | 
|  809:  |                                             FilesModule::Decorator()->UpdateExtendedProps( | 
|  810:  |                                                 $iOwnerUserId, | 
|  811:  |                                                 $aHashValues['Type'], | 
|  812:  |                                                 $oFileInfo->Path, | 
|  813:  |                                                 $oFileInfo->Name, | 
|  814:  |                                                 [ | 
|  815:  |                                                     'Created' => $oFileInfo->LastModified | 
|  816:  |                                                 ] | 
|  817:  |                                             ); | 
|  818:  |                                         } | 
|  819:  |  | 
|  820:  |                                         $fileContent = FilesModule::Decorator()->GetFileContent( | 
|  821:  |                                             $aHashValues['UserId'], | 
|  822:  |                                             $aHashValues['Type'], | 
|  823:  |                                             $aHashValues['Path'], | 
|  824:  |                                             $aHashValues['Name'] | 
|  825:  |                                         ); | 
|  826:  |                                         $verDir->createFile('prev.' . $ext, $fileContent); | 
|  827:  |                                         $prevChild = $verDir->getChild('prev.' . $ext); | 
|  828:  |                                         if ($prevChild) { | 
|  829:  |                                             $mExtendedProps = $prevChild->getProperty('ExtendedProps'); | 
|  830:  |                                             $aExtendedProps = is_array($mExtendedProps) ? $mExtendedProps : []; | 
|  831:  |                                             $aExtendedProps['Created'] = $oFileInfo->LastModified; | 
|  832:  |  | 
|  833:  |                                             $prevChild->setProperty('ExtendedProps', $aExtendedProps); | 
|  834:  |                                         } | 
|  835:  |                                         $this->updateHistory($verDir, $data); | 
|  836:  |                                     } | 
|  837:  |                                 } | 
|  838:  |  | 
|  839:  |                                 $this->createFile( | 
|  840:  |                                     $aHashValues['UserId'], | 
|  841:  |                                     $aHashValues['Type'], | 
|  842:  |                                     $aHashValues['Path'], | 
|  843:  |                                     $aHashValues['Name'], | 
|  844:  |                                     $rData | 
|  845:  |                                 ); | 
|  846:  |                             } | 
|  847:  |                         } | 
|  848:  |                     } | 
|  849:  |                     Api::skipCheckUserRole($prevState); | 
|  850:  |                 } | 
|  851:  |             } | 
|  852:  |         } | 
|  853:  |         return json_encode($result); | 
|  854:  |     } | 
|  855:  |  | 
|  856:  |      | 
|  857:  |  | 
|  858:  |  | 
|  859:  |  | 
|  860:  |  | 
|  861:  |     public function onGetFile(&$aArguments, &$aResult) | 
|  862:  |     { | 
|  863:  |         if ($this->isOfficeDocument($aArguments['Name'])) { | 
|  864:  |             $aArguments['NoRedirect'] = true; | 
|  865:  |         } | 
|  866:  |     } | 
|  867:  |  | 
|  868:  |      | 
|  869:  |  | 
|  870:  |  | 
|  871:  |  | 
|  872:  |  | 
|  873:  |  | 
|  874:  |  | 
|  875:  |     public function onGetItems($aArgs, &$mResult) | 
|  876:  |     { | 
|  877:  |         if (is_array($mResult)) { | 
|  878:  |             foreach ($mResult as $oItem) { | 
|  879:  |                 if ($oItem instanceof FileItem && $this->isOfficeDocument($oItem->Name)) { | 
|  880:  |                     $bEncrypted = isset($oItem->ExtendedProps['InitializationVector']); | 
|  881:  |                     $bAccessSet = isset($oItem->ExtendedProps['SharedWithMeAccess']); | 
|  882:  |                     $bHasWriteAccess = !$bAccessSet || ($bAccessSet && ((int) $oItem->ExtendedProps['SharedWithMeAccess'] === \Afterlogic\DAV\FS\Permission::Write || (int) $oItem->ExtendedProps['SharedWithMeAccess'] === \Afterlogic\DAV\FS\Permission::Reshare)); | 
|  883:  |                     if (!$bEncrypted && $bHasWriteAccess) { | 
|  884:  |                         if ($this->documentCanBeConverted($oItem->Name)) { | 
|  885:  |                             $oItem->UnshiftAction([ | 
|  886:  |                                 'convert' => [ | 
|  887:  |                                     'url' => '' | 
|  888:  |                                 ] | 
|  889:  |                             ]); | 
|  890:  |                         } elseif ($this->documentCanBeEdited($oItem->Name)) { | 
|  891:  |                             $sHash = $oItem->getHash(); | 
|  892:  |                             $aHashValues = Api::DecodeKeyValues($sHash); | 
|  893:  |                             $aHashValues['Edit'] = true; | 
|  894:  |                             $sHash = Api::EncodeKeyValues($aHashValues); | 
|  895:  |                             $oItem->UnshiftAction([ | 
|  896:  |                                 'edit' => [ | 
|  897:  |                                     'url' => '?download-file/' . $sHash . '/view' | 
|  898:  |                                 ] | 
|  899:  |                             ]); | 
|  900:  |                         } | 
|  901:  |                     } | 
|  902:  |                 } | 
|  903:  |             } | 
|  904:  |         } | 
|  905:  |     } | 
|  906:  |  | 
|  907:  |     public function onAfterGetFileInfo($aArgs, &$mResult) | 
|  908:  |     { | 
|  909:  |         if ($mResult) { | 
|  910:  |             if ($mResult instanceof FileItem && $this->isOfficeDocument($mResult->Name)) { | 
|  911:  |                 if ((isset($mResult->ExtendedProps['SharedWithMeAccess']) && ((int) $mResult->ExtendedProps['SharedWithMeAccess'] === \Afterlogic\DAV\FS\Permission::Write || (int) $mResult->ExtendedProps['SharedWithMeAccess'] === \Afterlogic\DAV\FS\Permission::Reshare)) || !isset($mResult->ExtendedProps['SharedWithMeAccess']) | 
|  912:  |                     && !$this->isReadOnlyDocument($mResult->Name)) { | 
|  913:  |                     $sHash = $mResult->getHash(); | 
|  914:  |                     $aHashValues = Api::DecodeKeyValues($sHash); | 
|  915:  |                     $aHashValues['Edit'] = true; | 
|  916:  |                     $sHash = Api::EncodeKeyValues($aHashValues); | 
|  917:  |                     $mResult->UnshiftAction([ | 
|  918:  |                         'edit' => [ | 
|  919:  |                             'url' => '?download-file/' . $sHash . '/view' | 
|  920:  |                         ] | 
|  921:  |                     ]); | 
|  922:  |                 } | 
|  923:  |             } | 
|  924:  |         } | 
|  925:  |     } | 
|  926:  |  | 
|  927:  |     public function onAddToContentSecurityPolicyDefault($aArgs, &$aAddDefault) | 
|  928:  |     { | 
|  929:  |         $sUrl = $this->oModuleSettings->DocumentServerUrl; | 
|  930:  |         if (!empty($sUrl)) { | 
|  931:  |             $aAddDefault[] = $sUrl; | 
|  932:  |         } | 
|  933:  |     } | 
|  934:  |  | 
|  935:  |  | 
|  936:  |      | 
|  937:  |     public function RestoreFromHistory($Url, $Version) {} | 
|  938:  |  | 
|  939:  |     protected function getHistoryDir($oFileInfo, $bCreateIfNotExists = false) | 
|  940:  |     { | 
|  941:  |         $oHistNode = false; | 
|  942:  |  | 
|  943:  |         $sType = $oFileInfo->TypeStr; | 
|  944:  |         $sPath = $oFileInfo->Path; | 
|  945:  |         $sName = $oFileInfo->Name; | 
|  946:  |         $sOwner = $oFileInfo->Owner; | 
|  947:  |  | 
|  948:  |          | 
|  949:  |  | 
|  950:  |  | 
|  951:  |         $oNode = Server::getNodeForPath('files/' . $sType . $sPath . '/' . $sName, $sOwner); | 
|  952:  |         if ($oNode instanceof File) { | 
|  953:  |             $oHistNode = $oNode->getHistoryDirectory(); | 
|  954:  |             if (!$oHistNode && $bCreateIfNotExists) { | 
|  955:  |                 $oParentNode = Server::getNodeForPath('files/' . $sType . $sPath, $sOwner); | 
|  956:  |                 if ($oParentNode instanceof Directory) { | 
|  957:  |                     $oParentNode->createDirectory($sName . '.hist'); | 
|  958:  |                 } | 
|  959:  |                 $oHistNode = $oNode->getHistoryDirectory(); | 
|  960:  |             } | 
|  961:  |         } | 
|  962:  |  | 
|  963:  |         return $oHistNode; | 
|  964:  |     } | 
|  965:  |  | 
|  966:  |     protected function getHistory($oFileInfo, $docKey, $sUrl) | 
|  967:  |     { | 
|  968:  |         $result = []; | 
|  969:  |         $curVer = 0; | 
|  970:  |         $histDir = $this->getHistoryDir($oFileInfo); | 
|  971:  |         if ($histDir && method_exists($histDir, 'getFileVersion')) { | 
|  972:  |             $curVer = $histDir->getFileVersion(); | 
|  973:  |         } | 
|  974:  |  | 
|  975:  |         if ($curVer > 0) { | 
|  976:  |             $hist = []; | 
|  977:  |             $histData = []; | 
|  978:  |             $oUser = CoreModule::getInstance()->GetUserByPublicId($oFileInfo->Owner); | 
|  979:  |  | 
|  980:  |             for ($i = 0; $i <= $curVer; $i++) { | 
|  981:  |                 $obj = []; | 
|  982:  |                 $dataObj = []; | 
|  983:  |  | 
|  984:  |                 $verDir = $histDir->getVersionDir($i + 1); | 
|  985:  |  | 
|  986:  |                 $key = false; | 
|  987:  |  | 
|  988:  |                 if ($i == $curVer) { | 
|  989:  |                     $key = $docKey; | 
|  990:  |                 } else { | 
|  991:  |                     if ($verDir && $verDir->childExists('key.txt')) { | 
|  992:  |                         $oKeyFile = $verDir->getChild('key.txt'); | 
|  993:  |                         if ($oKeyFile instanceof \Afterlogic\DAV\FS\File) { | 
|  994:  |                             $mKeyData = $oKeyFile->get(false); | 
|  995:  |                             if (is_resource($mKeyData)) { | 
|  996:  |                                 $key = \stream_get_contents($mKeyData); | 
|  997:  |                             } | 
|  998:  |                         } | 
|  999:  |                     } else { | 
| 1000:  |                         $key = false; | 
| 1001:  |                     } | 
| 1002:  |                 } | 
| 1003:  |  | 
| 1004:  |                 if ($key === false) { | 
| 1005:  |                     continue; | 
| 1006:  |                 } | 
| 1007:  |                 $obj["key"] = $key; | 
| 1008:  |                 $obj["version"] = $i + 1; | 
| 1009:  |  | 
| 1010:  |                 if ($i === 0) { | 
| 1011:  |                     $ext = strtolower(pathinfo($oFileInfo->Name, PATHINFO_EXTENSION)); | 
| 1012:  |                     list(, $sUserPublicId) = split($verDir->getOwner()); | 
| 1013:  |  | 
| 1014:  |                     $prevDoc = $verDir->getChild('prev.' . $ext); | 
| 1015:  |                     $aPrevFileExtendedProps = []; | 
| 1016:  |                     if ($prevDoc) { | 
| 1017:  |                         $aPrevFileExtendedProps = $prevDoc->getProperty('ExtendedProps'); | 
| 1018:  |                     } | 
| 1019:  |  | 
| 1020:  |                     if (isset($aPrevFileExtendedProps['Created'])) { | 
| 1021:  |                         $obj["created"] =  $this->convetToUserTime( | 
| 1022:  |                             $oUser, | 
| 1023:  |                             date("Y-m-d H:i:s", $aPrevFileExtendedProps['Created']) | 
| 1024:  |                         ); | 
| 1025:  |                     } else { | 
| 1026:  |                         $obj["created"] = ''; | 
| 1027:  |                     } | 
| 1028:  |                     $obj["user"] = [ | 
| 1029:  |                         "id" => (string) $oUser->Id, | 
| 1030:  |                         "name" => $oFileInfo->Owner | 
| 1031:  |                     ]; | 
| 1032:  |                 } | 
| 1033:  |  | 
| 1034:  |                 if ($i != $curVer) { | 
| 1035:  |                     $ext = strtolower(pathinfo($oFileInfo->Name, PATHINFO_EXTENSION)); | 
| 1036:  |  | 
| 1037:  |                     $sUrl = $this->getDownloadUrl( | 
| 1038:  |                          | 
| 1039:  |                         Api::getUserIdByPublicId($verDir->getUser()), | 
| 1040:  |                          | 
| 1041:  |                         $verDir->getStorage(), | 
| 1042:  |                         $verDir->getRelativePath() . '/' . $verDir->getName(), | 
| 1043:  |                         'prev.' . $ext | 
| 1044:  |                     ); | 
| 1045:  |                 } | 
| 1046:  |  | 
| 1047:  |                 $dataObj["key"] = $key; | 
| 1048:  |                 $dataObj["url"] = $sUrl; | 
| 1049:  |                 $dataObj["version"] = $i + 1; | 
| 1050:  |  | 
| 1051:  |                 if ($i > 0) { | 
| 1052:  |                     $changes = false; | 
| 1053:  |                     $verDirPrev = $histDir->getVersionDir($i); | 
| 1054:  |                     if ($verDirPrev && $verDirPrev->childExists('changes.json')) { | 
| 1055:  |                         $oChangesFile = $verDirPrev->getChild('changes.json'); | 
| 1056:  |                         if ($oChangesFile instanceof \Afterlogic\DAV\FS\File) { | 
| 1057:  |                             $mChangesFileData = $oChangesFile->get(false); | 
| 1058:  |                             if (is_resource($mChangesFileData)) { | 
| 1059:  |                                 $changes = \json_decode(\stream_get_contents($mChangesFileData), true); | 
| 1060:  |                             } | 
| 1061:  |                         } | 
| 1062:  |                     } | 
| 1063:  |  | 
| 1064:  |                     if (!$changes) { | 
| 1065:  |                         continue; | 
| 1066:  |                     } | 
| 1067:  |                     $change = $changes["changes"][0]; | 
| 1068:  |  | 
| 1069:  |                     $obj["changes"] = $changes["changes"]; | 
| 1070:  |                     $obj["serverVersion"] = $changes["serverVersion"]; | 
| 1071:  |                     $obj["created"] = $this->convetToUserTime( | 
| 1072:  |                         $oUser, | 
| 1073:  |                         $change["created"] | 
| 1074:  |                     ); | 
| 1075:  |                     $obj["user"] = $change["user"]; | 
| 1076:  |  | 
| 1077:  |                     if (isset($histData[$i])) { | 
| 1078:  |                         $prev = $histData[$i]; | 
| 1079:  |                         $dataObj["previous"] = [ | 
| 1080:  |                             "key" => $prev["key"], | 
| 1081:  |                             "url" => $prev["url"] | 
| 1082:  |                         ]; | 
| 1083:  |                     } | 
| 1084:  |  | 
| 1085:  |                     $dataObj["changesUrl"] = $this->getDownloadUrl( | 
| 1086:  |                          | 
| 1087:  |                         Api::getUserIdByPublicId($histDir->getUser()), | 
| 1088:  |                          | 
| 1089:  |                         $histDir->getStorage(), | 
| 1090:  |                         $histDir->getVersionDir($i)->getRelativePath() . '/' . $verDirPrev->getName(), | 
| 1091:  |                         'diff.zip' | 
| 1092:  |                     ); | 
| 1093:  |                 } | 
| 1094:  |  | 
| 1095:  |                 array_push($hist, $obj); | 
| 1096:  |                 $oJwt = new Classes\JwtManager($this->oModuleSettings->Secret); | 
| 1097:  |                 if ($oJwt->isJwtEnabled()) { | 
| 1098:  |                     $dataObj['token'] = $oJwt->jwtEncode($dataObj); | 
| 1099:  |                 } | 
| 1100:  |                 $histData[$i + 1] = $dataObj; | 
| 1101:  |             } | 
| 1102:  |  | 
| 1103:  |             array_push( | 
| 1104:  |                 $result, | 
| 1105:  |                 [ | 
| 1106:  |                     "currentVersion" => $curVer + 1, | 
| 1107:  |                     "history" => $hist | 
| 1108:  |                 ], | 
| 1109:  |                 $histData | 
| 1110:  |             ); | 
| 1111:  |         } | 
| 1112:  |  | 
| 1113:  |         return $result; | 
| 1114:  |     } | 
| 1115:  |  | 
| 1116:  |     protected function updateHistory($verDir, $data) | 
| 1117:  |     { | 
| 1118:  |         if (isset($data["changesurl"])) { | 
| 1119:  |             $rChangesData = \file_get_contents($data["changesurl"]); | 
| 1120:  |             if ($rChangesData !== false) { | 
| 1121:  |                 $verDir->createFile('diff.zip', $rChangesData); | 
| 1122:  |             } | 
| 1123:  |         } | 
| 1124:  |         $histData = isset($data["changeshistory"]) ? $data["changeshistory"] : ''; | 
| 1125:  |         if (empty($histData)) { | 
| 1126:  |             $histData = json_encode($data["history"], JSON_PRETTY_PRINT); | 
| 1127:  |         } | 
| 1128:  |         if (!empty($histData)) { | 
| 1129:  |             $verDir->createFile('changes.json', $histData); | 
| 1130:  |         } | 
| 1131:  |         $verDir->createFile('key.txt', $data['key']); | 
| 1132:  |     } | 
| 1133:  |  | 
| 1134:  |     protected function createFile($iUserId, $sType, $sPath, $sFileName, $mData) | 
| 1135:  |     { | 
| 1136:  |         Api::Log(self::GetName() . '::writeFile'); | 
| 1137:  |  | 
| 1138:  |         $mResult = false; | 
| 1139:  |         $aArgs = [ | 
| 1140:  |             'UserId' => $iUserId, | 
| 1141:  |             'Type' => $sType, | 
| 1142:  |             'Path' => $sPath, | 
| 1143:  |             'Name' => $sFileName, | 
| 1144:  |             'Data' => $mData, | 
| 1145:  |             'Overwrite' => true, | 
| 1146:  |             'RangeType' => 0, | 
| 1147:  |             'Offset' => 0, | 
| 1148:  |             'ExtendedProps' => [] | 
| 1149:  |         ]; | 
| 1150:  |  | 
| 1151:  |         $this->broadcastEvent( | 
| 1152:  |             'Files::CreateFile', | 
| 1153:  |             $aArgs, | 
| 1154:  |             $mResult | 
| 1155:  |         ); | 
| 1156:  |  | 
| 1157:  |         return $mResult; | 
| 1158:  |     } | 
| 1159:  |  | 
| 1160:  |     protected function getDownloadUrl($iUserId, $sType, $sPath, $sName) | 
| 1161:  |     { | 
| 1162:  |         $sHash = Api::EncodeKeyValues([ | 
| 1163:  |             'UserId' =>  $iUserId, | 
| 1164:  |             'Id' => $sName, | 
| 1165:  |             'Type' => $sType, | 
| 1166:  |             'Path' => $sPath, | 
| 1167:  |             'Name' => $sName, | 
| 1168:  |             'FileName' => $sName, | 
| 1169:  |             \Aurora\System\Application::AUTH_TOKEN_KEY => Api::UserSession()->Set([ | 
| 1170:  |                 'token' => 'auth', | 
| 1171:  |                 'id' => $iUserId, | 
| 1172:  |                 't' => time(), | 
| 1173:  |             ]) | 
| 1174:  |         ]); | 
| 1175:  |  | 
| 1176:  |         return Application::getBaseUrl() . '?download-file/' . $sHash; | 
| 1177:  |     } | 
| 1178:  |  | 
| 1179:  |     protected function convetToUserTime($oUser, $sTime) | 
| 1180:  |     { | 
| 1181:  |         $dt = \DateTime::createFromFormat( | 
| 1182:  |             'Y-m-d H:i:s', | 
| 1183:  |             $sTime, | 
| 1184:  |             new \DateTimeZone('UTC') | 
| 1185:  |         ); | 
| 1186:  |         if (!empty($oUser->DefaultTimeZone)) { | 
| 1187:  |             $dt->setTimezone(new \DateTimeZone($oUser->DefaultTimeZone)); | 
| 1188:  |         } | 
| 1189:  |  | 
| 1190:  |         return $dt->format("Y-m-d H:i:s"); | 
| 1191:  |     } | 
| 1192:  | } | 
| 1193:  |  |