Newer
Older
<?php
/**
* Copyright Maarch since 2008 under licence GPLv3.
* See LICENCE.txt file at the root folder for more details.
* This file is part of Maarch software.
*/
/**
* @brief Only Office Controller
*
* @author dev@maarch.org
*/
namespace ContentManagement\controllers;
use Attachment\models\AttachmentModel;
use Docserver\models\DocserverModel;
use Docserver\models\DocserverTypeModel;
use Firebase\JWT\JWT;
use Resource\controllers\ResController;
use Resource\controllers\StoreController;
use Resource\models\ResModel;
use Respect\Validation\Validator;
use Slim\Http\Request;
use Slim\Http\Response;
Guillaume Heurtier
committed
use SrcCore\controllers\CoreController;
use SrcCore\controllers\UrlController;
use SrcCore\models\CoreConfigModel;
Guillaume Heurtier
committed
use SrcCore\models\CurlModel;
use Template\models\TemplateModel;
class OnlyOfficeController
{
public static function getConfiguration(Request $request, Response $response)
{
$loadedXml = CoreConfigModel::getXmlLoaded(['path' => 'apps/maarch_entreprise/xml/documentEditorsConfig.xml']);
if (empty($loadedXml) || empty($loadedXml->onlyoffice->enabled) || $loadedXml->onlyoffice->enabled == 'false' || empty($loadedXml->onlyoffice->server_uri)) {
return $response->withJson(['enabled' => false]);
}
$coreUrl = str_replace('rest/', '', UrlController::getCoreUrl());
$configurations = [
'enabled' => true,
'serverUri' => (string)$loadedXml->onlyoffice->server_uri,
'serverPort' => (int)$loadedXml->onlyoffice->server_port,
'serverSsl' => filter_var((string)$loadedXml->onlyoffice->server_ssl, FILTER_VALIDATE_BOOLEAN),
'coreUrl' => $coreUrl
];
return $response->withJson($configurations);
public static function getToken(Request $request, Response $response)
{
$body = $request->getParsedBody();
if (!Validator::notEmpty()->validate($body['config'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body params config is empty']);
}
$loadedXml = CoreConfigModel::getXmlLoaded(['path' => 'apps/maarch_entreprise/xml/documentEditorsConfig.xml']);
if (empty($loadedXml) || empty($loadedXml->onlyoffice->enabled) || $loadedXml->onlyoffice->enabled == 'false' || empty($loadedXml->onlyoffice->server_uri)) {
return $response->withStatus(400)->withJson(['errors' => 'OnlyOffice server is disabled']);
}
$jwt = null;
$serverSecret = (string)$loadedXml->onlyoffice->server_secret;
if (!empty($serverSecret)) {
$header = [
"alg" => "HS256",
"typ" => "JWT"
];
$jwt = JWT::encode($body['config'], $serverSecret, 'HS256', null, $header);
return $response->withJson($jwt);
public static function saveMergedFile(Request $request, Response $response)
{
$body = $request->getParsedBody();
if (!Validator::stringType()->notEmpty()->validate($body['onlyOfficeKey'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body params onlyOfficeKey is empty']);
} elseif (!preg_match('/[A-Za-z0-9]/i', $body['onlyOfficeKey'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body params onlyOfficeKey is forbidden']);
}
if ($body['objectType'] == 'templateCreation') {
$customId = CoreConfigModel::getCustomId();
if (!empty($customId) && is_dir("custom/{$customId}/modules/templates/templates/styles/")) {
$stylesPath = "custom/{$customId}/modules/templates/templates/styles/";
} else {
$stylesPath = 'modules/templates/templates/styles/';
}
if (strpos($body['objectId'], $stylesPath) !== 0 || substr_count($body['objectId'], '.') != 1) {
return $response->withStatus(400)->withJson(['errors' => 'Template path is not valid']);
}
$fileContent = @file_get_contents($path);
if ($fileContent == false) {
return $response->withStatus(400)->withJson(['errors' => 'No content found']);
}
} elseif ($body['objectType'] == 'templateModification') {
$docserver = DocserverModel::getCurrentDocserver(['typeId' => 'TEMPLATES', 'collId' => 'templates', 'select' => ['path_template']]);
$template = TemplateModel::getById(['id' => $body['objectId'], 'select' => ['template_path', 'template_file_name']]);
if (empty($template)) {
return $response->withStatus(400)->withJson(['errors' => 'Template does not exist']);
}
$path = $docserver['path_template'] . str_replace('#', DIRECTORY_SEPARATOR, $template['template_path']) . $template['template_file_name'];
if (!is_file($path)) {
return $response->withStatus(400)->withJson(['errors' => 'Template does not exist on docserver']);
}
$fileContent = file_get_contents($path);
} elseif ($body['objectType'] == 'resourceCreation' || $body['objectType'] == 'attachmentCreation') {
$docserver = DocserverModel::getCurrentDocserver(['typeId' => 'TEMPLATES', 'collId' => 'templates', 'select' => ['path_template']]);
$template = TemplateModel::getById(['id' => $body['objectId'], 'select' => ['template_path', 'template_file_name']]);
if (empty($template)) {
return $response->withStatus(400)->withJson(['errors' => 'Template does not exist']);
}
$path = $docserver['path_template'] . str_replace('#', DIRECTORY_SEPARATOR, $template['template_path']) . $template['template_file_name'];
$dataToMerge = ['userId' => $GLOBALS['id']];
if (!empty($body['data']) && is_array($body['data'])) {
$dataToMerge = array_merge($dataToMerge, $body['data']);
}
$mergedDocument = MergeController::mergeDocument([
'path' => $path,
'data' => $dataToMerge
]);
$fileContent = base64_decode($mergedDocument['encodedDocument']);
} elseif ($body['objectType'] == 'resourceModification') {
if (!ResController::hasRightByResId(['resId' => [$body['objectId']], 'userId' => $GLOBALS['id']])) {
return $response->withStatus(400)->withJson(['errors' => 'Resource out of perimeter']);
}
$resource = ResModel::getById(['resId' => $body['objectId'], 'select' => ['docserver_id', 'path', 'filename', 'fingerprint']]);
if (empty($resource['filename'])) {
return $response->withStatus(400)->withJson(['errors' => 'Resource has no file']);
}
$docserver = DocserverModel::getByDocserverId(['docserverId' => $resource['docserver_id'], 'select' => ['path_template', 'docserver_type_id']]);
$path = $docserver['path_template'] . str_replace('#', DIRECTORY_SEPARATOR, $resource['path']) . $resource['filename'];
$docserverType = DocserverTypeModel::getById(['id' => $docserver['docserver_type_id'], 'select' => ['fingerprint_mode']]);
$fingerprint = StoreController::getFingerPrint(['filePath' => $path, 'mode' => $docserverType['fingerprint_mode']]);
if (empty($resource['fingerprint'])) {
ResModel::update(['set' => ['fingerprint' => $fingerprint], 'where' => ['res_id = ?'], 'data' => [$body['objectId']]]);
$resource['fingerprint'] = $fingerprint;
}
if ($resource['fingerprint'] != $fingerprint) {
return $response->withStatus(400)->withJson(['errors' => 'Fingerprints do not match']);
}
$fileContent = file_get_contents($path);
} elseif ($body['objectType'] == 'attachmentModification') {
$attachment = AttachmentModel::getById(['id' => $body['objectId'], 'select' => ['docserver_id', 'path', 'filename', 'res_id_master', 'fingerprint']]);
if (empty($attachment)) {
return $response->withStatus(400)->withJson(['errors' => 'Attachment does not exist']);
}
if (!ResController::hasRightByResId(['resId' => [$attachment['res_id_master']], 'userId' => $GLOBALS['id']])) {
return $response->withStatus(400)->withJson(['errors' => 'Attachment out of perimeter']);
}
$docserver = DocserverModel::getByDocserverId(['docserverId' => $attachment['docserver_id'], 'select' => ['path_template', 'docserver_type_id']]);
$path = $docserver['path_template'] . str_replace('#', DIRECTORY_SEPARATOR, $attachment['path']) . $attachment['filename'];
$docserverType = DocserverTypeModel::getById(['id' => $docserver['docserver_type_id'], 'select' => ['fingerprint_mode']]);
$fingerprint = StoreController::getFingerPrint(['filePath' => $path, 'mode' => $docserverType['fingerprint_mode']]);
if (empty($attachment['fingerprint'])) {
AttachmentModel::update(['set' => ['fingerprint' => $fingerprint], 'where' => ['res_id = ?'], 'data' => [$body['objectId']]]);
$attachment['fingerprint'] = $fingerprint;
}
if ($attachment['fingerprint'] != $fingerprint) {
return $response->withStatus(400)->withJson(['errors' => 'Fingerprints do not match']);
}
$fileContent = file_get_contents($path);
} elseif ($body['objectType'] == 'encodedResource') {
if (empty($body['format'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body format is empty']);
}
$path = null;
$fileContent = base64_decode($body['objectId']);
$extension = $body['format'];
} else {
return $response->withStatus(400)->withJson(['errors' => 'Query param objectType does not exist']);
}
if (empty($extension)) {
$extension = pathinfo($path, PATHINFO_EXTENSION);
}
$tmpPath = CoreConfigModel::getTmpPath();
$filename = "onlyOffice_{$GLOBALS['id']}_{$body['onlyOfficeKey']}.{$extension}";
$put = file_put_contents($tmpPath . $filename, $fileContent);
if ($put === false) {
return $response->withStatus(400)->withJson(['errors' => 'File put contents failed']);
}
$halfFilename = substr($filename, 11);
return $response->withJson(['filename' => $halfFilename]);
}
public static function getMergedFile(Request $request, Response $response)
{
$queryParams = $request->getQueryParams();
if (!Validator::stringType()->notEmpty()->validate($queryParams['filename'])) {
return $response->withStatus(400)->withJson(['errors' => 'Query params filename is empty']);
} elseif (substr_count($queryParams['filename'], '\\') > 0 || substr_count($queryParams['filename'], '.') != 1) {
return $response->withStatus(400)->withJson(['errors' => 'Query params filename forbidden']);
$tmpPath = CoreConfigModel::getTmpPath();
$filename = "onlyOffice_{$queryParams['filename']}";
$fileContent = file_get_contents($tmpPath . $filename);
if ($fileContent == false) {
return $response->withStatus(400)->withJson(['errors' => 'No content found']);
}
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->buffer($fileContent);
$extension = pathinfo($tmpPath . $filename, PATHINFO_EXTENSION);
$response->write($fileContent);
$response = $response->withAddedHeader('Content-Disposition', "attachment; filename=maarch.{$extension}");
return $response->withHeader('Content-Type', $mimeType);
}
public static function getEncodedFileFromUrl(Request $request, Response $response)
{
$queryParams = $request->getQueryParams();
if (!Validator::stringType()->notEmpty()->validate($queryParams['url'])) {
return $response->withStatus(400)->withJson(['errors' => 'Query params url is empty']);
}
$loadedXml = CoreConfigModel::getXmlLoaded(['path' => 'apps/maarch_entreprise/xml/documentEditorsConfig.xml']);
if (empty($loadedXml) || empty($loadedXml->onlyoffice->enabled) || $loadedXml->onlyoffice->enabled == 'false' || empty($loadedXml->onlyoffice->server_uri)) {
return $response->withStatus(400)->withJson(['errors' => 'Onlyoffice is not enabled']);
}
$checkUrl = str_replace('http://', '', $queryParams['url']);
$checkUrl = str_replace('https://', '', $checkUrl);
$uri = (string)$loadedXml->onlyoffice->server_uri;
$uriPaths = explode('/', $uri, 2);
$masterPath = $uriPaths[0];
$lastPath = !empty($uriPaths[1]) ? rtrim("/{$uriPaths[1]}", '/') : '';
$port = (string)$loadedXml->onlyoffice->server_port;
if (strpos($checkUrl, "{$masterPath}:{$port}{$lastPath}/cache/files/") !== 0 && (($port != 80 && $port != 443) || strpos($checkUrl, "{$masterPath}{$lastPath}/cache/files/") !== 0)) {
return $response->withStatus(400)->withJson(['errors' => 'Query params url is not allowed']);
}
$fileContent = file_get_contents($queryParams['url']);
if ($fileContent == false) {
return $response->withStatus(400)->withJson(['errors' => 'No content found']);
}
return $response->withJson(['encodedFile' => base64_encode($fileContent)]);
}
public static function isAvailable(Request $request, Response $response)
{
$loadedXml = CoreConfigModel::getXmlLoaded(['path' => 'apps/maarch_entreprise/xml/documentEditorsConfig.xml']);
if (empty($loadedXml) || empty($loadedXml->onlyoffice->enabled) || $loadedXml->onlyoffice->enabled == 'false') {
return $response->withStatus(400)->withJson(['errors' => 'Onlyoffice is not enabled', 'lang' => 'onlyOfficeNotEnabled']);
} elseif (empty($loadedXml->onlyoffice->server_uri)) {
return $response->withStatus(400)->withJson(['errors' => 'Onlyoffice server_uri is empty', 'lang' => 'uriIsEmpty']);
} elseif (empty($loadedXml->onlyoffice->server_port)) {
return $response->withStatus(400)->withJson(['errors' => 'Onlyoffice server_port is empty', 'lang' => 'portIsEmpty']);
$uri = (string)$loadedXml->onlyoffice->server_uri;
$port = (string)$loadedXml->onlyoffice->server_port;
Guillaume Heurtier
committed
$isAvailable = DocumentEditorController::isAvailable(['uri' => $uri, 'port' => $port]);
Guillaume Heurtier
committed
if (!empty($isAvailable['errors'])) {
return $response->withStatus(400)->withJson($isAvailable);
}
return $response->withJson(['isAvailable' => $isAvailable]);
}
Guillaume Heurtier
committed
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
public static function canConvert()
{
$loadedXml = CoreConfigModel::getXmlLoaded(['path' => 'apps/maarch_entreprise/xml/documentEditorsConfig.xml']);
if (empty($loadedXml) || empty($loadedXml->onlyoffice->enabled) || $loadedXml->onlyoffice->enabled == 'false') {
return false;
} elseif (empty($loadedXml->onlyoffice->server_uri)) {
return false;
} elseif (empty($loadedXml->onlyoffice->server_port)) {
return false;
}
$uri = (string)$loadedXml->onlyoffice->server_uri;
$port = (string)$loadedXml->onlyoffice->server_port;
$isAvailable = DocumentEditorController::isAvailable(['uri' => $uri, 'port' => $port]);
if (!empty($isAvailable['errors'])) {
return false;
}
return $isAvailable;
}
public static function convert(array $args)
{
$loadedXml = CoreConfigModel::getXmlLoaded(['path' => 'apps/maarch_entreprise/xml/documentEditorsConfig.xml']);
if (empty($loadedXml) || empty($loadedXml->onlyoffice->enabled) || $loadedXml->onlyoffice->enabled == 'false') {
return ['errors' => 'Onlyoffice is not enabled', 'lang' => 'onlyOfficeNotEnabled'];
} elseif (empty($loadedXml->onlyoffice->server_uri)) {
return ['errors' => 'Onlyoffice server_uri is empty', 'lang' => 'uriIsEmpty'];
} elseif (empty($loadedXml->onlyoffice->server_port)) {
return ['errors' => 'Onlyoffice server_port is empty', 'lang' => 'portIsEmpty'];
}
$uri = (string)$loadedXml->onlyoffice->server_uri;
$port = (string)$loadedXml->onlyoffice->server_port;
$tmpPath = CoreConfigModel::getTmpPath();
$docInfo = pathinfo($args['fullFilename']);
$payload = [
'userId' => $GLOBALS['id'],
'fullFilename' => $args['fullFilename']
];
$jwt = JWT::encode($payload, CoreConfigModel::getEncryptKey());
$file = CoreConfigModel::getJsonLoaded(['path' => 'apps/maarch_entreprise/xml/config.json']);
if (empty($file['config']['maarchUrl'])) {
return ['errors' => 'Cannot convert with OnlyOffice : maarchUrl not set in config.json'];
}
$coreUrl = $file['config']['maarchUrl'];
$docUrl = $coreUrl . 'rest/onlyOffice/content?token=' . $jwt;
$response = CurlModel::execSimple([
'url' => $uri . ':' . $port . '/ConvertService.ashx',
'headers' => ['accept: application/json'],
'method' => 'POST',
'body' => json_encode([
'async' => false,
'filetype' => $docInfo['extension'],
'key' => CoreConfigModel::uniqueId(),
'outputtype' => 'pdf',
'title' => $docInfo['filename'] . 'pdf',
'url' => $docUrl
])
]);
if ($response['code'] != 200) {
return ['errors' => 'OnlyOffice conversion failed '];
}
if (!empty($response['response']['error'])) {
return ['errors' => 'OnlyOffice conversion failed : ' . $response['response']['error']];
}
$convertedFile = file_get_contents($response['response']['fileUrl']);
if ($convertedFile === false) {
return ['errors' => 'Cannot get converted document'];
}
$filename = $tmpPath . $docInfo['filename'] . '.pdf';
$saveTmp = file_put_contents($filename, $convertedFile);
if ($saveTmp == false) {
return ['errors' => 'Cannot save converted document'];
}
return true;
}
public function getTmpFile(Request $request, Response $response)
{
$queryParams = $request->getQueryParams();
try {
$jwt = JWT::decode($queryParams['token'], CoreConfigModel::getEncryptKey(), ['HS256']);
} catch (\Exception $e) {
return $response->withStatus(401)->withJson(['errors' => 'Token is invalid']);
}
CoreController::setGlobals(['userId' => $jwt->userId]);
$fileContent = file_get_contents($jwt->fullFilename);
if ($fileContent === false) {
return $response->withStatus(404)->withJson(['errors' => 'Document not found']);
}
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->buffer($fileContent);
$pathInfo = pathinfo($jwt->fullFilename);
$response->write($fileContent);
$response = $response->withAddedHeader('Content-Disposition', "attachment; filename=maarch.{$pathInfo['extension']}");
return $response->withHeader('Content-Type', $mimeType);
}