/home/ivoiecob/email.hirewise-va.com/modules/OfficeDocumentEditor/Module.php
<?php
/**
* This code is licensed under AGPLv3 license or Afterlogic Software License
* if commercial version of the product was purchased.
* For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
*/
namespace Aurora\Modules\OfficeDocumentEditor;
use Afterlogic\DAV\FS\File;
use Afterlogic\DAV\FS\Directory;
use Afterlogic\DAV\FS\Permission;
use Aurora\Api;
use Afterlogic\DAV\Server;
use Aurora\System\Application;
use Aurora\Modules\Core\Module as CoreModule;
use Aurora\Modules\Files\Module as FilesModule;
use Aurora\Modules\Files\Classes\FileItem;
use Aurora\Modules\OfficeDocumentEditor\Exceptions\Exception;
use Aurora\System\Enums\FileStorageType;
use function Sabre\Uri\split;
/**
* @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
* @license https://afterlogic.com/products/common-licensing Afterlogic Software License
* @copyright Copyright (c) 2023, Afterlogic Corp.
*
* @property Settings $oModuleSettings
*
* @package Modules
*/
class Module extends \Aurora\System\Module\AbstractModule
{
public $ExtsSpreadsheet = [
".xls",
".xlsx",
".xlsm",
".xlt",
".xltx",
".xltm",
".ods",
".fods",
".ots",
".csv"
];
public $ExtsPresentation = [
".pps",
".ppsx",
".ppsm",
".ppt",
".pptx",
".pptm",
".pot",
".potx",
".potm",
".odp",
".fodp",
".otp"
];
public $ExtsDocument = [
".doc",
".docx",
".docm",
".dot",
".dotx",
".dotm",
".odt",
".fodt",
".ott",
".rtf",
".txt",
".html",
".htm",
".mht",
".pdf",
".djvu",
".fb2",
".epub",
".xps"
];
/**
* Initializes module.
*
* @ignore
*/
public function init()
{
$this->aErrors = [
Enums\ErrorCodes::ExtensionCannotBeConverted => $this->i18N('ERROR_EXTENSION_CANNOT_BE_CONVERTED')
];
$this->AddEntries([
'editor' => 'EntryEditor',
'ode-callback' => 'EntryCallback'
]);
$this->subscribeEvent('System::RunEntry::before', [$this, 'onBeforeFileViewEntry'], 10);
$this->subscribeEvent('Files::GetFile', [$this, 'onGetFile'], 10);
$this->subscribeEvent('Files::GetItems', [$this, 'onGetItems'], 20000);
$this->subscribeEvent('Files::GetFileInfo::after', [$this, 'onAfterGetFileInfo'], 20000);
$this->subscribeEvent('AddToContentSecurityPolicyDefault', [$this, 'onAddToContentSecurityPolicyDefault']);
}
/**
* @return Module
*/
public static function getInstance()
{
return parent::getInstance();
}
/**
* @return Module
*/
public static function Decorator()
{
return parent::Decorator();
}
/**
* @return Settings
*/
public function getModuleSettings()
{
return $this->oModuleSettings;
}
public function GetSettings()
{
Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
return [
'ExtensionsToView' => $this->getOfficeExtensions()
];
}
protected function getExtensionsToView()
{
return $this->oModuleSettings->ExtensionsToView;
}
protected function getExtensionsToConvert()
{
return $this->oModuleSettings->ExtensionsToConvert;
}
protected function getExtensionsToEdit()
{
return $this->oModuleSettings->ExtensionsToEdit;
}
protected function getOfficeExtensions()
{
return array_merge(
$this->getExtensionsToView(),
$this->getExtensionsToEdit(),
array_keys($this->getExtensionsToConvert())
);
}
protected function getDocumentType($filename)
{
$ext = strtolower('.' . pathinfo($filename, PATHINFO_EXTENSION));
if (in_array($ext, $this->ExtsDocument)) {
return "word";
}
if (in_array($ext, $this->ExtsSpreadsheet)) {
return "cell";
}
if (in_array($ext, $this->ExtsPresentation)) {
return "slide";
}
return "";
}
protected function isReadOnlyDocument($filename)
{
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
return in_array($ext, $this->getExtensionsToView());
}
protected function documentCanBeEdited($filename)
{
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
return in_array($ext, $this->getExtensionsToEdit());
}
protected function documentCanBeConverted($sFilename)
{
$ext = strtolower(pathinfo($sFilename, PATHINFO_EXTENSION));
return in_array($ext, array_keys($this->getExtensionsToConvert()));
}
/**
* @param string $sFileName = ''
* @return bool
*/
public function isOfficeDocument($sFileName = '')
{
$sExtensions = implode('|', $this->getOfficeExtensions());
return !!preg_match('/\.(' . $sExtensions . ')$/', strtolower(trim($sFileName)));
}
protected function isTrustedRequest()
{
return true; // TODO: find another way to protect dowmload url
$bResult = false;
$sTrustedServerHost = $this->oModuleSettings->TrustedServerHost;
if (empty($sTrustedServerHost)) {
$bResult = true;
} else {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
$bResult = $sTrustedServerHost === $ip;
}
return $bResult;
}
/**
*
* @param array $aArguments
* @param array $aResult
*/
public function onBeforeFileViewEntry(&$aArguments, &$aResult)
{
$aEntries = [
'download-file',
'file-cache',
'mail-attachment'
];
if (in_array($aArguments['EntryName'], $aEntries)) {
$sEntry = (string) \Aurora\System\Router::getItemByIndex(0, '');
$sHash = (string) \Aurora\System\Router::getItemByIndex(1, '');
$sAction = (string) \Aurora\System\Router::getItemByIndex(2, '');
$aValues = Api::DecodeKeyValues($sHash);
$sFileName = isset($aValues['FileName']) ? urldecode($aValues['FileName']) : '';
if (empty($sFileName)) {
$sFileName = isset($aValues['Name']) ? urldecode($aValues['Name']) : '';
}
if ($sAction === 'view' && $this->isOfficeDocument($sFileName) && !isset($aValues[\Aurora\System\Application::AUTH_TOKEN_KEY])) {
$sViewerUrl = './?editor=' . urlencode($sEntry . '/' . $sHash . '/' . $sAction . '/' . time());
\header('Location: ' . $sViewerUrl);
} elseif ($this->isOfficeDocument($sFileName) || $sFileName === 'diff.zip' || $sFileName === 'changes.json') {
if ($this->isTrustedRequest()) {
$sAuthToken = $aValues[\Aurora\System\Application::AUTH_TOKEN_KEY] ?? null;
if (isset($sAuthToken)) {
Api::setAuthToken($sAuthToken);
Api::setUserId(
Api::getAuthenticatedUserId($sAuthToken)
);
}
}
}
}
}
public function EntryEditor()
{
$sResult = '';
$sFullUrl = Application::getBaseUrl();
$sMode = 'view';
$fileuri = isset($_GET['editor']) ? $_GET['editor'] : null;
$filename = null;
$sHash = null;
$aHashValues = [];
$docKey = null;
$lastModified = time();
$aHistory = [];
$oUser = Api::getAuthenticatedUser();
if (isset($fileuri)) {
$fileuri = \urldecode($fileuri);
$aFileuri = \explode('/', $fileuri);
if (isset($aFileuri[1])) {
$sHash = $aFileuri[1];
$aHashValues = Api::DecodeKeyValues($sHash);
if (!isset($aHashValues[\Aurora\System\Application::AUTH_TOKEN_KEY])) {
$aHashValues[\Aurora\System\Application::AUTH_TOKEN_KEY] = Api::UserSession()->Set(
[
'token' => 'auth',
'id' => Api::getAuthenticatedUserId()
],
time(),
time() + 60 * 5 // 5 min
);
}
}
$sHash = Api::EncodeKeyValues($aHashValues);
$aFileuri[1] = $sHash;
$fileuri = implode('/', $aFileuri);
$fileuri = $sFullUrl . '?' . $fileuri;
}
if ($sHash) {
$aHashValues = Api::DecodeKeyValues($sHash);
if (isset($aHashValues['FileName'])) {
$filename = $aHashValues['FileName'];
} elseif (isset($aHashValues['Name'])) {
$filename = $aHashValues['Name'];
}
if (isset($aHashValues['Edit'])) {
$sMode = 'edit';
}
$sHash = Api::EncodeKeyValues($aHashValues);
$oFileInfo = null;
try {
if (isset($aHashValues['UserId'], $aHashValues['Type'], $aHashValues['Path'], $aHashValues['Id'])) {
$oFileInfo = FilesModule::Decorator()->GetFileInfo(
$aHashValues['UserId'],
$aHashValues['Type'],
$aHashValues['Path'],
$aHashValues['Id']
);
}
} catch (\Exception $oEx) {
}
if ($oFileInfo) {
$lastModified = $oFileInfo->LastModified;
$docKey = \md5($oFileInfo->RealPath . $lastModified);
$oFileInfo->Path = $aHashValues['Path'];
$SharedWithMeAccess = isset($oFileInfo->ExtendedProps['SharedWithMeAccess']) ? (int) $oFileInfo->ExtendedProps['SharedWithMeAccess'] : null;
if (!isset($SharedWithMeAccess) && $oFileInfo->Owner !== $oUser->PublicId) {
list($sParentPath, $sParentId) = \Sabre\Uri\split($aHashValues['Path']);
$oParentFileInfo = null;
try {
$oParentFileInfo = FilesModule::Decorator()->GetFileInfo(
$aHashValues['UserId'],
$aHashValues['Type'],
$sParentPath,
$sParentId
);
} catch (\Exception $oEx) {
}
if (isset($oParentFileInfo) && $oParentFileInfo->Owner === $oUser->PublicId) {
$oFileInfo->Owner = $oParentFileInfo->Owner;
}
}
$sMode = (isset($SharedWithMeAccess) && ($SharedWithMeAccess === Permission::Write || $SharedWithMeAccess === Permission::Reshare)) ||
(!isset($SharedWithMeAccess) && $oFileInfo->Owner === $oUser->PublicId) || ($oFileInfo->TypeStr === FileStorageType::Corporate) ? $sMode : 'view';
$aHistory = $this->getHistory($oFileInfo, $docKey, $fileuri);
} elseif (isset($aHashValues['FileName'])) {
$docKey = \md5($aHashValues['FileName'] . time());
} elseif (isset($aHashValues['Name'])) {
$docKey = \md5($aHashValues['Name'] . time());
}
}
$bIsReadOnlyMode = ($sMode === 'view') ? true : false;
$filetype = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$lang = 'en';
$mode = $bIsReadOnlyMode || $this->isReadOnlyDocument($filename) ? 'view' : 'edit';
$fileuriUser = '';
$serverPath = $this->oModuleSettings->DocumentServerUrl;
$callbackUrl = $sFullUrl . '?ode-callback/' . $sHash;
if (isset($fileuri) && $serverPath) {
if ($oUser) {
$uid = (string) $oUser->Id;
$uname = !empty($oUser->Name) ? $oUser->Name : $oUser->PublicId;
$lang = \Aurora\System\Utils::ConvertLanguageNameToShort($oUser->Language);
}
$config = [
"type" => Api::IsMobileApplication() ? "mobile" : 'desktop',
"documentType" => $this->getDocumentType($filename),
"document" => [
"title" => $filename,
"url" => $fileuri,
"fileType" => $filetype,
"key" => $docKey,
"info" => [
"owner" => $uname,
"uploaded" => date('d.m.y', $lastModified)
],
"permissions" => [
"comment" => !$bIsReadOnlyMode,
"download" => true,
"edit" => !$bIsReadOnlyMode,
"fillForms" => !$bIsReadOnlyMode,
"modifyFilter" => !$bIsReadOnlyMode,
"modifyContentControl" => !$bIsReadOnlyMode,
"review" => !$bIsReadOnlyMode,
"changeHistory" => !$bIsReadOnlyMode,
"chat" => !$bIsReadOnlyMode
]
],
"editorConfig" => [
"actionLink" => empty($_GET["actionLink"]) ? null : json_decode($_GET["actionLink"]),
"mode" => $mode,
"lang" => $lang,
"callbackUrl" => $callbackUrl,
"user" => [
"id" => $uid,
"name" => $uname
],
"embedded" => [
"saveUrl" => $fileuriUser,
"embedUrl" => $fileuriUser,
"shareUrl" => $fileuriUser,
"toolbarDocked" => "top",
],
"customization" => [
"comments" => !$bIsReadOnlyMode,
"about" => false,
"feedback" => false,
"goback" => false,
"forcesave" => true,
// "logo"=> [
// "image"=> $sFullUrl . 'static/styles/images/logo.png',
// ],
]
]
];
$oJwt = new Classes\JwtManager($this->oModuleSettings->Secret);
if ($oJwt->isJwtEnabled()) {
$config['token'] = $oJwt->jwtEncode($config);
}
$sResult = \file_get_contents($this->GetPath() . '/templates/Editor.html');
$iUserId = Api::getAuthenticatedUserId();
if (0 < $iUserId) {
$sResult = strtr($sResult, [
'{{DOC_SERV_API_URL}}' => $serverPath . '/web-apps/apps/api/documents/api.js',
'{{CONFIG}}' => \json_encode($config),
'{{HISTORY}}' => isset($aHistory[0]) ? \json_encode($aHistory[0]) : 'false',
'{{HISTORY_DATA}}' => isset($aHistory[1]) ? \json_encode($aHistory[1]) : 'false'
]);
\Aurora\Modules\CoreWebclient\Module::Decorator()->SetHtmlOutputHeaders();
@header('Cache-Control: no-cache, no-store, must-revalidate', true);
@header('Pragma: no-cache', true);
@header('Expires: 0', true);
} else {
Api::Location('./');
}
}
return $sResult;
}
public function CreateBlankDocument($Type, $Path, $FileName)
{
$mResult = false;
$ext = strtolower(pathinfo($FileName, PATHINFO_EXTENSION));
$sFilePath = $this->GetPath() . "/data/new." . $ext;
if (file_exists($sFilePath)) {
$rData = \fopen($sFilePath, "r");
$FileName = FilesModule::Decorator()->GetNonExistentFileName(
Api::getAuthenticatedUserId(),
$Type,
$Path,
$FileName
);
$mResult = $this->createFile(
Api::getAuthenticatedUserId(),
$Type,
$Path,
$FileName,
$rData
);
\fclose($rData);
if ($mResult) {
$mResult = FilesModule::Decorator()->GetFileInfo(
Api::getAuthenticatedUserId(),
$Type,
$Path,
$FileName
);
}
}
return $mResult;
}
public function ConvertDocument($Type, $Path, $FileName)
{
$mResult = false;
$aExtensions = $this->getExtensionsToConvert();
$sExtension = pathinfo($FileName, PATHINFO_EXTENSION);
$sNewExtension = isset($aExtensions[$sExtension]) ? $aExtensions[$sExtension] : null;
if ($sNewExtension === null) {
throw new Exceptions\Exception(Enums\ErrorCodes::ExtensionCannotBeConverted);
} else {
$mResult = self::Decorator()->ConvertDocumentToFormat($Type, $Path, $FileName, $sNewExtension);
}
return $mResult;
}
public function ConvertDocumentToFormat($Type, $Path, $FileName, $ToExtension)
{
$mResult = false;
$oFileInfo = FilesModule::Decorator()->GetFileInfo(
Api::getAuthenticatedUserId(),
$Type,
$Path,
$FileName
);
if ($oFileInfo instanceof FileItem) {
$sConvertedDocumentUri = null;
$aPathParts = pathinfo($FileName);
$sFromExtension = $aPathParts['extension'];
$sFileNameWOExt = $aPathParts['filename'];
$sDocumentUri = '';
if (isset($oFileInfo->Actions['download']['url'])) {
$sDownloadUrl = $oFileInfo->Actions['download']['url'];
$aUrlParts = \explode('/', $sDownloadUrl);
if (isset($aUrlParts[1])) {
$aUrlParts[1] = $this->GetFileTempHash($aUrlParts[1]);
$sDocumentUri = Application::getBaseUrl() . \implode('/', $aUrlParts);
$this->GetConvertedUri(
$sDocumentUri,
$sFromExtension,
$ToExtension,
'',
false,
$sConvertedDocumentUri
);
if (!empty($sConvertedDocumentUri)) {
$rData = \file_get_contents($sConvertedDocumentUri);
if ($rData !== false) {
$sNewFileName = FilesModule::Decorator()->GetNonExistentFileName(
Api::getAuthenticatedUserId(),
$Type,
$Path,
$sFileNameWOExt . '.' . $ToExtension
);
$mResult = $this->createFile(
Api::getAuthenticatedUserId(),
$Type,
$Path,
$sNewFileName,
$rData
);
if ($mResult) {
$mResult = FilesModule::Decorator()->GetFileInfo(
Api::getAuthenticatedUserId(),
$Type,
$Path,
$sNewFileName
);
}
}
}
}
}
}
return $mResult;
}
protected function GetFileTempHash($sHash)
{
$aValues = Api::DecodeKeyValues($sHash);
$sFileName = isset($aValues['FileName']) ? urldecode($aValues['FileName']) : '';
if (empty($sFileName)) {
$sFileName = isset($aValues['Name']) ? urldecode($aValues['Name']) : '';
}
$aValues[\Aurora\System\Application::AUTH_TOKEN_KEY] = Api::UserSession()->Set(
[
'token' => 'auth',
'id' => Api::getAuthenticatedUserId()
],
time(),
time() + 60 * 5 // 5 min
);
return Api::EncodeKeyValues($aValues);
}
protected function GetConvertedUri($document_uri, $from_extension, $to_extension, $document_revision_id, $is_async, &$converted_document_uri)
{
$converted_document_uri = "";
$responceFromConvertService = $this->SendRequestToConvertService(
$document_uri,
$from_extension,
$to_extension,
$document_revision_id,
$is_async
);
$json = \json_decode($responceFromConvertService, true);
$errorElement = isset($json["error"]) ? $json["error"] : null;
if ($errorElement != null && $errorElement != "") {
$this->ProcessConvServResponceError($errorElement);
}
$isEndConvert = $json["endConvert"];
$percent = $json["percent"];
if ($isEndConvert != null && $isEndConvert == true) {
$converted_document_uri = $json["fileUrl"];
$percent = 100;
} elseif ($percent >= 100) {
$percent = 99;
}
return $percent;
}
protected function SendRequestToConvertService($document_uri, $from_extension, $to_extension, $document_revision_id, $is_async)
{
$title = basename($document_uri);
$urlToConverter = '';
if (empty($title)) {
$title = \Sabre\DAV\UUIDUtil::getUUID();
}
if (empty($document_revision_id)) {
$document_revision_id = $document_uri;
}
$document_revision_id = $this->GenerateRevisionId($document_revision_id);
$serverPath = $this->oModuleSettings->DocumentServerUrl;
if ($serverPath !== null) {
$urlToConverter = $serverPath . '/ConvertService.ashx';
}
$arr = [
"async" => $is_async,
"url" => $document_uri,
"outputtype" => trim($to_extension, '.'),
"filetype" => trim($from_extension, '.'),
"title" => $title,
"key" => $document_revision_id
];
$headerToken = "";
$oJwt = new Classes\JwtManager($this->oModuleSettings->Secret);
if ($oJwt->isJwtEnabled()) {
$headerToken = $oJwt->jwtEncode([ "payload" => $arr ]);
$arr["token"] = $oJwt->jwtEncode($arr);
}
$opts = [
'http' => [
'method' => 'POST',
'timeout' => '120000',
'header' => "Content-type: application/json\r\n" .
"Accept: application/json\r\n" .
(empty($headerToken) ? "" : "Authorization: $headerToken\r\n"),
'content' => \json_encode($arr)
]
];
if (substr($urlToConverter, 0, strlen("https")) === "https") {
$opts['ssl'] = ['verify_peer' => false];
}
$context = stream_context_create($opts);
$response_data = file_get_contents($urlToConverter, false, $context);
return $response_data;
}
protected function ProcessConvServResponceError($errorCode)
{
$errorMessageTemplate = "Error occurred in the document service: ";
$errorMessage = '';
switch ($errorCode) {
case -8:
$errorMessage = $errorMessageTemplate . "Error document VKey";
break;
case -7:
$errorMessage = $errorMessageTemplate . "Error document request";
break;
case -6:
$errorMessage = $errorMessageTemplate . "Error database";
break;
case -5:
$errorMessage = $errorMessageTemplate . "Error unexpected guid";
break;
case -4:
$errorMessage = $errorMessageTemplate . "Error download error";
break;
case -3:
$errorMessage = $errorMessageTemplate . "Error convertation error";
break;
case -2:
$errorMessage = $errorMessageTemplate . "Error convertation timeout";
break;
case -1:
$errorMessage = $errorMessageTemplate . "Error convertation unknown";
break;
case 0:
break;
default:
$errorMessage = $errorMessageTemplate . "ErrorCode = " . $errorCode;
break;
}
throw new Exception($errorMessage);
}
protected function GenerateRevisionId($expected_key)
{
if (strlen($expected_key) > 20) {
$expected_key = crc32($expected_key);
}
$key = preg_replace("[^0-9-.a-zA-Z_=]", "_", $expected_key);
$key = substr($key, 0, min(array(strlen($key), 20)));
return $key;
}
public function EntryCallback()
{
$result = ["error" => 0];
if (($body_stream = file_get_contents("php://input")) === false) {
$result["error"] = "Bad Request";
} else {
$data = json_decode($body_stream, true);
if (isset($data['token'])) {
Api::AddSecret($data['token']);
}
Api::Log($body_stream);
$oJwt = new Classes\JwtManager($this->oModuleSettings->Secret);
if ($oJwt->isJwtEnabled()) {
$inHeader = false;
$token = "";
if (!empty($data["token"])) {
$token = $oJwt->jwtDecode($data["token"]);
} elseif (!empty($_SERVER['HTTP_AUTHORIZATION'])) {
$token = $oJwt->jwtDecode(substr($_SERVER['HTTP_AUTHORIZATION'], strlen("Bearer ")));
$inHeader = true;
} else {
$result["error"] = "Expected JWT";
}
if (empty($token)) {
$result["error"] = "Invalid JWT signature";
} else {
$data = json_decode($token, true);
if ($inHeader) {
$data = $data["payload"];
}
}
}
if ($data["status"] == 2 || $data["status"] == 6) {
$sHash = (string) \Aurora\System\Router::getItemByIndex(1, '');
if (!empty($sHash)) {
$aHashValues = Api::DecodeKeyValues($sHash);
$prevState = Api::skipCheckUserRole(true);
$oFileInfo = FilesModule::Decorator()->GetFileInfo(
$aHashValues['UserId'],
$aHashValues['Type'],
$aHashValues['Path'],
$aHashValues['Name']
);
if ($oFileInfo instanceof FileItem && $this->isOfficeDocument($oFileInfo->Name)) {
if ((isset($oFileInfo->ExtendedProps['SharedWithMeAccess']) && (int) $oFileInfo->ExtendedProps['SharedWithMeAccess'] === \Afterlogic\DAV\FS\Permission::Write) || !isset($oFileInfo->ExtendedProps['SharedWithMeAccess'])
&& !$this->isReadOnlyDocument($oFileInfo->Name)) {
$rData = \file_get_contents($data["url"]);
if ($rData !== false) {
if ($this->oModuleSettings->EnableHistory && $data["status"] == 2) {
if ($this->isTrustedRequest()) {
$iUserId = isset($aHashValues['UserId']) ? $aHashValues['UserId'] : null;
if (isset($iUserId)) {
Server::setUser(Api::getUserPublicIdById($iUserId));
}
}
$histDir = $this->getHistoryDir($oFileInfo, true);
if ($histDir) {
$curVer = $histDir->getFileVersion();
$verDir = $histDir->getVersionDir($curVer + 1, true);
$ext = strtolower(pathinfo($oFileInfo->Name, PATHINFO_EXTENSION));
list(, $sOwnerUserPublicId) = split($verDir->getOwner());
$iOwnerUserId = Api::getUserIdByPublicId($sOwnerUserPublicId);
if (!isset($oFileInfo->ExtendedProps['Created']) && $curVer == 0) {
FilesModule::Decorator()->UpdateExtendedProps(
$iOwnerUserId,
$aHashValues['Type'],
$oFileInfo->Path,
$oFileInfo->Name,
[
'Created' => $oFileInfo->LastModified
]
);
}
$fileContent = FilesModule::Decorator()->GetFileContent(
$aHashValues['UserId'],
$aHashValues['Type'],
$aHashValues['Path'],
$aHashValues['Name']
);
$verDir->createFile('prev.' . $ext, $fileContent);
$prevChild = $verDir->getChild('prev.' . $ext);
if ($prevChild) {
$mExtendedProps = $prevChild->getProperty('ExtendedProps');
$aExtendedProps = is_array($mExtendedProps) ? $mExtendedProps : [];
$aExtendedProps['Created'] = $oFileInfo->LastModified;
$prevChild->setProperty('ExtendedProps', $aExtendedProps);
}
$this->updateHistory($verDir, $data);
}
}
$this->createFile(
$aHashValues['UserId'],
$aHashValues['Type'],
$aHashValues['Path'],
$aHashValues['Name'],
$rData
);
}
}
}
Api::skipCheckUserRole($prevState);
}
}
}
return json_encode($result);
}
/**
*
* @param array $aArguments
* @param array $aResult
*/
public function onGetFile(&$aArguments, &$aResult)
{
if ($this->isOfficeDocument($aArguments['Name'])) {
$aArguments['NoRedirect'] = true;
}
}
/**
* Writes to $aData variable list of DropBox files if $aData['Type'] is DropBox account type.
*
* @ignore
* @param array $aArgs
* @param mixed $mResult
*/
public function onGetItems($aArgs, &$mResult)
{
if (is_array($mResult)) {
foreach ($mResult as $oItem) {
if ($oItem instanceof FileItem && $this->isOfficeDocument($oItem->Name)) {
$bEncrypted = isset($oItem->ExtendedProps['InitializationVector']);
$bAccessSet = isset($oItem->ExtendedProps['SharedWithMeAccess']);
$bHasWriteAccess = !$bAccessSet || ($bAccessSet && ((int) $oItem->ExtendedProps['SharedWithMeAccess'] === \Afterlogic\DAV\FS\Permission::Write || (int) $oItem->ExtendedProps['SharedWithMeAccess'] === \Afterlogic\DAV\FS\Permission::Reshare));
if (!$bEncrypted && $bHasWriteAccess) {
if ($this->documentCanBeConverted($oItem->Name)) {
$oItem->UnshiftAction([
'convert' => [
'url' => ''
]
]);
} elseif ($this->documentCanBeEdited($oItem->Name)) {
$sHash = $oItem->getHash();
$aHashValues = Api::DecodeKeyValues($sHash);
$aHashValues['Edit'] = true;
$sHash = Api::EncodeKeyValues($aHashValues);
$oItem->UnshiftAction([
'edit' => [
'url' => '?download-file/' . $sHash . '/view'
]
]);
}
}
}
}
}
}
public function onAfterGetFileInfo($aArgs, &$mResult)
{
if ($mResult) {
if ($mResult instanceof FileItem && $this->isOfficeDocument($mResult->Name)) {
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'])
&& !$this->isReadOnlyDocument($mResult->Name)) {
$sHash = $mResult->getHash();
$aHashValues = Api::DecodeKeyValues($sHash);
$aHashValues['Edit'] = true;
$sHash = Api::EncodeKeyValues($aHashValues);
$mResult->UnshiftAction([
'edit' => [
'url' => '?download-file/' . $sHash . '/view'
]
]);
}
}
}
}
public function onAddToContentSecurityPolicyDefault($aArgs, &$aAddDefault)
{
$sUrl = $this->oModuleSettings->DocumentServerUrl;
if (!empty($sUrl)) {
$aAddDefault[] = $sUrl;
}
}
// History
public function RestoreFromHistory($Url, $Version) {}
protected function getHistoryDir($oFileInfo, $bCreateIfNotExists = false)
{
$oHistNode = false;
$sType = $oFileInfo->TypeStr;
$sPath = $oFileInfo->Path;
$sName = $oFileInfo->Name;
$sOwner = $oFileInfo->Owner;
/**
* @var \Afterlogic\DAV\FS\Directory $oNode
*/
$oNode = Server::getNodeForPath('files/' . $sType . $sPath . '/' . $sName, $sOwner);
if ($oNode instanceof File) {
$oHistNode = $oNode->getHistoryDirectory();
if (!$oHistNode && $bCreateIfNotExists) {
$oParentNode = Server::getNodeForPath('files/' . $sType . $sPath, $sOwner);
if ($oParentNode instanceof Directory) {
$oParentNode->createDirectory($sName . '.hist');
}
$oHistNode = $oNode->getHistoryDirectory();
}
}
return $oHistNode;
}
protected function getHistory($oFileInfo, $docKey, $sUrl)
{
$result = [];
$curVer = 0;
$histDir = $this->getHistoryDir($oFileInfo);
if ($histDir && method_exists($histDir, 'getFileVersion')) {
$curVer = $histDir->getFileVersion();
}
if ($curVer > 0) {
$hist = [];
$histData = [];
$oUser = CoreModule::getInstance()->GetUserByPublicId($oFileInfo->Owner);
for ($i = 0; $i <= $curVer; $i++) {
$obj = [];
$dataObj = [];
$verDir = $histDir->getVersionDir($i + 1);
$key = false;
if ($i == $curVer) {
$key = $docKey;
} else {
if ($verDir && $verDir->childExists('key.txt')) {
$oKeyFile = $verDir->getChild('key.txt');
if ($oKeyFile instanceof \Afterlogic\DAV\FS\File) {
$mKeyData = $oKeyFile->get();
if (is_resource($mKeyData)) {
$key = \stream_get_contents($mKeyData);
}
}
} else {
$key = false;
}
}
if ($key === false) {
continue;
}
$obj["key"] = $key;
$obj["version"] = $i + 1;
if ($i === 0) {
$ext = strtolower(pathinfo($oFileInfo->Name, PATHINFO_EXTENSION));
list(, $sUserPublicId) = split($verDir->getOwner());
$prevDoc = $verDir->getChild('prev.' . $ext);
$aPrevFileExtendedProps = [];
if ($prevDoc) {
$aPrevFileExtendedProps = $prevDoc->getProperty('ExtendedProps');
}
if (isset($aPrevFileExtendedProps['Created'])) {
$obj["created"] = $this->convetToUserTime(
$oUser,
date("Y-m-d H:i:s", $aPrevFileExtendedProps['Created'])
);
} else {
$obj["created"] = '';
}
$obj["user"] = [
"id" => (string) $oUser->Id,
"name" => $oFileInfo->Owner
];
}
if ($i != $curVer) {
$ext = strtolower(pathinfo($oFileInfo->Name, PATHINFO_EXTENSION));
$sUrl = $this->getDownloadUrl(
//Api::getUserIdByPublicId($sUserPublicId),
Api::getUserIdByPublicId($verDir->getUser()),
// $oFileInfo->TypeStr,
$verDir->getStorage(),
$verDir->getRelativePath() . '/' . $verDir->getName(),
'prev.' . $ext
);
}
$dataObj["key"] = $key;
$dataObj["url"] = $sUrl;
$dataObj["version"] = $i + 1;
if ($i > 0) {
$changes = false;
$verDirPrev = $histDir->getVersionDir($i);
if ($verDirPrev && $verDirPrev->childExists('changes.json')) {
$oChangesFile = $verDirPrev->getChild('changes.json');
if ($oChangesFile instanceof \Afterlogic\DAV\FS\File) {
$mChangesFileData = $oChangesFile->get();
if (is_resource($mChangesFileData)) {
$changes = \json_decode(\stream_get_contents($mChangesFileData), true);
}
}
}
if (!$changes) {
continue;
}
$change = $changes["changes"][0];
$obj["changes"] = $changes["changes"];
$obj["serverVersion"] = $changes["serverVersion"];
$obj["created"] = $this->convetToUserTime(
$oUser,
$change["created"]
);
$obj["user"] = $change["user"];
if (isset($histData[$i])) {
$prev = $histData[$i];
$dataObj["previous"] = [
"key" => $prev["key"],
"url" => $prev["url"]
];
}
$dataObj["changesUrl"] = $this->getDownloadUrl(
//Api::getUserIdByPublicId($sUserPublicId),
Api::getUserIdByPublicId($histDir->getUser()),
//$oFileInfo->TypeStr,
$histDir->getStorage(),
$histDir->getVersionDir($i)->getRelativePath() . '/' . $verDirPrev->getName(),
'diff.zip'
);
}
array_push($hist, $obj);
$oJwt = new Classes\JwtManager($this->oModuleSettings->Secret);
if ($oJwt->isJwtEnabled()) {
$dataObj['token'] = $oJwt->jwtEncode($dataObj);
}
$histData[$i + 1] = $dataObj;
}
array_push(
$result,
[
"currentVersion" => $curVer + 1,
"history" => $hist
],
$histData
);
}
return $result;
}
protected function updateHistory($verDir, $data)
{
if (isset($data["changesurl"])) {
$rChangesData = \file_get_contents($data["changesurl"]);
if ($rChangesData !== false) {
$verDir->createFile('diff.zip', $rChangesData);
}
}
$histData = isset($data["changeshistory"]) ? $data["changeshistory"] : '';
if (empty($histData)) {
$histData = json_encode($data["history"], JSON_PRETTY_PRINT);
}
if (!empty($histData)) {
$verDir->createFile('changes.json', $histData);
}
$verDir->createFile('key.txt', $data['key']);
}
protected function createFile($iUserId, $sType, $sPath, $sFileName, $mData)
{
Api::Log(self::GetName() . '::writeFile');
$mResult = false;
$aArgs = [
'UserId' => $iUserId,
'Type' => $sType,
'Path' => $sPath,
'Name' => $sFileName,
'Data' => $mData,
'Overwrite' => true,
'RangeType' => 0,
'Offset' => 0,
'ExtendedProps' => []
];
$this->broadcastEvent(
'Files::CreateFile',
$aArgs,
$mResult
);
return $mResult;
}
protected function getDownloadUrl($iUserId, $sType, $sPath, $sName)
{
$sHash = Api::EncodeKeyValues([
'UserId' => $iUserId,
'Id' => $sName,
'Type' => $sType,
'Path' => $sPath,
'Name' => $sName,
'FileName' => $sName,
\Aurora\System\Application::AUTH_TOKEN_KEY => Api::UserSession()->Set([
'token' => 'auth',
'id' => $iUserId,
't' => time(),
])
]);
return Application::getBaseUrl() . '?download-file/' . $sHash;
}
protected function convetToUserTime($oUser, $sTime)
{
$dt = \DateTime::createFromFormat(
'Y-m-d H:i:s',
$sTime,
new \DateTimeZone('UTC')
);
if (!empty($oUser->DefaultTimeZone)) {
$dt->setTimezone(new \DateTimeZone($oUser->DefaultTimeZone));
}
return $dt->format("Y-m-d H:i:s");
}
}