Merge pull request 'feature/php-laravel-sdk-integration' from feature/php-laravel-sdk-integration into develop

Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/document-server-integration/pulls/25
Reviewed-by: Stepan Mayorov <stepan.mayorov@onlyoffice.com>
Reviewed-by: Alexander Kondratev <aleksandr.kondratyev@onlyoffice.com>
This commit is contained in:
Sergey Linnik
2025-01-31 12:02:28 +00:00
48 changed files with 963 additions and 722 deletions

4
.gitmodules vendored
View File

@ -64,10 +64,6 @@
[submodule "web/documentserver-example/csharp/assets/document-formats"]
path = web/documentserver-example/csharp/assets/document-formats
url = https://github.com/ONLYOFFICE/document-formats
[submodule "web/documentserver-example/php-laravel/public/assets/document-formats"]
path = web/documentserver-example/php-laravel/public/assets/document-formats
url = https://github.com/ONLYOFFICE/document-formats
branch = master
[submodule "web/documentserver-example/php-laravel/public/assets/document-templates"]
path = web/documentserver-example/php-laravel/public/assets/document-templates
url = https://github.com/ONLYOFFICE/document-templates

View File

@ -15,6 +15,7 @@
- php-laravel: create, edit, and submit pdf forms
- php-laravel: show forgotten files on a seperate page
- php-laravel: fetch files
- php-laravel: integrate sdk
- restore by url
- refresh config
- on uploading xml convert to supported type only

View File

@ -1,3 +1,10 @@
# PHP SDK ENV VARIABLES
## COMMAND SERVICE
DOCS_INTEGRATION_SDK_COMMAND_SERVICE_URL=/command
DOCS_INTEGRATION_SDK_CONVERT_SERVICE_URL=/converter
## DOCUMENT STORAGE ENV VARIABLES
DOCUMENT_STORAGE_PUBLIC_URL=http://localhost

View File

@ -18,15 +18,15 @@
namespace App\Helpers\URL;
use App\Services\StorageConfig;
use App\OnlyOffice\Managers\SettingsManager;
class FileURL extends URL
{
public static function download(string $filename, string $address = ''): string
{
$config = app(StorageConfig::class);
$settings = app(SettingsManager::class);
return static::build($config->get('url.private'), 'files/download', [
return static::build($settings->getSetting('url.storage.private'), 'files/download', [
'fileName' => $filename,
'userAddress' => $address,
]);
@ -34,9 +34,9 @@ class FileURL extends URL
public static function changes(string $filename, string $address, int $version): string
{
$config = app(StorageConfig::class);
$settings = app(SettingsManager::class);
return static::build($config->get('url.private'), 'files/versions/changes', [
return static::build($settings->getSetting('url.storage.private'), 'files/versions/changes', [
'filename' => $filename,
'userAddress' => $address,
'version' => $version,
@ -45,9 +45,9 @@ class FileURL extends URL
public static function previous(string $filename, string $address, int $version): string
{
$config = app(StorageConfig::class);
$settings = app(SettingsManager::class);
return static::build($config->get('url.private'), 'files/versions/previous', [
return static::build($settings->getSetting('url.storage.private'), 'files/versions/previous', [
'filename' => $filename,
'userAddress' => $address,
'version' => $version,
@ -56,9 +56,9 @@ class FileURL extends URL
public static function history(string $filename, string $address): string
{
$config = app(StorageConfig::class);
$settings = app(SettingsManager::class);
return static::build($config->get('url.private'), 'files/download', [
return static::build($settings->getSetting('url.storage.private'), 'files/download', [
'fileName' => $filename,
'userAddress' => $address,
]);
@ -66,9 +66,9 @@ class FileURL extends URL
public static function create(string $extension, string $user): string
{
$config = app(StorageConfig::class);
$settings = app(SettingsManager::class);
return static::build($config->get('url.public'), 'editor', [
return static::build($settings->getSetting('url.storage.public'), 'editor', [
'fileExt' => $extension,
'user' => $user,
]);
@ -76,9 +76,9 @@ class FileURL extends URL
public static function callback(string $filename, string $user): string
{
$config = app(StorageConfig::class);
$settings = app(SettingsManager::class);
return static::build($config->get('url.private'), 'editor/track', [
return static::build($settings->getSetting('url.storage.private'), 'editor/track', [
'fileName' => $filename,
'userAddress' => $user,
]);

View File

@ -18,13 +18,13 @@
namespace App\Helpers\URL;
use App\Services\StorageConfig;
use App\OnlyOffice\Managers\SettingsManager;
class TemplateURL extends URL
{
public static function image(string $type)
{
$config = app(StorageConfig::class);
$settings = app(SettingsManager::class);
$name = 'file_docx.svg';
$name = match ($type) {
@ -34,6 +34,6 @@ class TemplateURL extends URL
default => 'file_docx.svg',
};
return static::build($config->get('url.public'), "/images/$name");
return static::build($settings->getSetting('url.storage.public'), "/images/$name");
}
}

View File

@ -5,9 +5,8 @@ namespace App\Http\Controllers\API\Files;
use App\Helpers\Path\PathInfo;
use App\Helpers\URL\URL;
use App\Http\Controllers\Controller;
use App\Services\JWT;
use App\Services\ServerConfig;
use App\Services\StorageConfig;
use App\OnlyOffice\Managers\JWTManager;
use App\OnlyOffice\Managers\SettingsManager;
use App\UseCases\Document\Find\FindDocumentQuery;
use App\UseCases\Document\Find\FindDocumentQueryHandler;
use Exception;
@ -17,10 +16,10 @@ use Illuminate\Support\Str;
class ReferenceController extends Controller
{
public function get(Request $request, StorageConfig $storageConfig, ServerConfig $serverConfig, JWT $jwt)
public function get(Request $request, SettingsManager $settings, JWTManager $jwt)
{
$storagePrivateUrl = $storageConfig->get('url.private');
$storagePublicUrl = $storageConfig->get('url.public');
$storagePrivateUrl = $settings->getSetting('url.storage.private');
$storagePublicUrl = $settings->getSetting('url.storage.public');
$referenceData = $request->input('referenceData');
$link = $request->input('link');
$path = $request->input('path');
@ -94,8 +93,8 @@ class ReferenceController extends Controller
'link' => "$storagePublicUrl/editor?fileID=$filename",
];
if ($serverConfig->get('jwt.enabled')) {
$data['token'] = $jwt->encode($data);
if ($settings->getSetting('jwt.enabled')) {
$data['token'] = $jwt->encode($data, $settings->getSetting('jwt.secret'));
}
return response()->json($data);

View File

@ -19,14 +19,14 @@
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use App\Repositories\FormatRepository;
use App\OnlyOffice\Managers\FormatManager;
class FormatController extends Controller
{
public function index(FormatRepository $formats)
public function index(FormatManager $formats)
{
return response()->json([
'formats' => $formats->all(),
'formats' => $formats->getFormatsList(),
]);
}
}

View File

@ -18,32 +18,20 @@
namespace App\Http\Controllers;
use App\Helpers\Path\Path;
use App\Helpers\Path\PathInfo;
use App\Helpers\URL\FileURL;
use App\Helpers\URL\TemplateURL;
use App\Helpers\URL\URL;
use App\Services\JWT;
use App\Services\ServerConfig;
use App\Services\StorageConfig;
use App\OnlyOffice\Managers\DocumentManager;
use App\OnlyOffice\Managers\JWTManager;
use App\OnlyOffice\Managers\SettingsManager;
use App\OnlyOffice\Services\CallbackService;
use App\UseCases\Common\Http\DownloadFileCommand;
use App\UseCases\Common\Http\DownloadFileRequest;
use App\UseCases\Docs\Command\ForceSaveCommad;
use App\UseCases\Docs\Command\ForceSaveRequest;
use App\UseCases\Docs\Conversion\ConvertCommand;
use App\UseCases\Docs\Conversion\ConvertRequest;
use App\UseCases\Document\Create\CreateDocumentCommand;
use App\UseCases\Document\Create\CreateDocumentFromTemplateCommand;
use App\UseCases\Document\Create\CreateDocumentFromTemplateRequest;
use App\UseCases\Document\Create\CreateDocumentRequest;
use App\UseCases\Document\Find\FindDocumentQuery;
use App\UseCases\Document\Find\FindDocumentQueryHandler;
use App\UseCases\Document\Save\ForceSaveDocumentCommand;
use App\UseCases\Document\Save\ForceSaveDocumentRequest;
use App\UseCases\Document\Save\SaveDocumentCommand;
use App\UseCases\Document\Save\SaveDocumentFormCommand;
use App\UseCases\Document\Save\SaveDocumentFormRequest;
use App\UseCases\Document\Save\SaveDocumentRequest;
use App\UseCases\Editor\Create\CreateConfigCommand;
use App\UseCases\Editor\Create\CreateConfigRequest;
use App\UseCases\File\Find\FileExistsQuery;
@ -55,16 +43,16 @@ use App\UseCases\User\Find\FindUserQueryHandler;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Onlyoffice\DocsIntegrationSdk\Models\Callback;
use Onlyoffice\DocsIntegrationSdk\Models\CallbackDocStatus;
use Onlyoffice\DocsIntegrationSdk\Models\CallbackForceSaveType;
use Onlyoffice\DocsIntegrationSdk\Models\History;
class EditorController extends Controller
{
public function __construct(
private StorageConfig $storageConfig,
private ServerConfig $serverConfig,
) {}
public function __construct(private SettingsManager $settings) {}
public function index(Request $request, JWT $jwt)
public function index(Request $request, JWTManager $jwt)
{
$request->validate([
'fileUrl' => 'required_without_all:fileID,fileExt|string',
@ -86,8 +74,8 @@ class EditorController extends Controller
$lang = $request->cookie('ulang', 'en');
$fileExt = $request->input('fileExt');
$withSample = $request->has('sample') && $request->sample === 'true';
$storagePublicUrl = $this->storageConfig->get('url.public');
$storagePrivateUrl = $this->storageConfig->get('url.private');
$storagePublicUrl = $this->settings->getSetting('url.storage.public');
$storagePrivateUrl = $this->settings->getSetting('url.storage.private');
$user = app(FindUserQueryHandler::class)
->__invoke(new FindUserQuery($userId));
@ -142,7 +130,7 @@ class EditorController extends Controller
$file = app(FindDocumentQueryHandler::class)
->__invoke(new FindDocumentQuery($filename, $request->ip()));
if (! $file['format']->type) {
if (! $file['format']->getType()) {
$message = 'The format '.$file['format']->extension().' has undefined format.';
Log::error($message);
@ -152,10 +140,13 @@ class EditorController extends Controller
]);
}
$downloadUrl = FileURL::download($filename, $request->ip());
$templatesImageUrl = TemplateURL::image($file['format']->type->value);
$createUrl = FileURL::create($file['format']->extension(), $user['id']);
$callbackUrl = FileURL::callback($filename, $request->ip());
$file['user'] = $userId;
$file['address'] = $request->ip();
$fileId = $file['key'];
$documentManager = new DocumentManager($file);
$downloadUrl = $documentManager->getFileUrl($fileId);
$imagesUrl = "$storagePublicUrl/images/";
$mode = $request->action ?? 'edit';
@ -172,10 +163,10 @@ class EditorController extends Controller
lang: $lang,
userAddress: $request->ip(),
serverAddress: $storagePublicUrl,
createUrl: $createUrl,
templatesImageUrl: $templatesImageUrl,
createUrl: $documentManager->getCreateUrl($fileId),
templatesImageUrl: $documentManager->getTemplateImageUrl($fileId),
actionLink: $actionLink,
callbackUrl: $callbackUrl,
callbackUrl: $documentManager->getCallbackUrl($fileId),
imagesUrl: $imagesUrl,
directUrl: $directUrlEnabled ? $downloadUrl : '',
));
@ -227,14 +218,15 @@ class EditorController extends Controller
}
// check if the secret key to generate token exists
if ($this->serverConfig->get('jwt.enabled')) {
$config['token'] = $jwt->encode($config); // encode config into the token
if ($this->settings->getSetting('jwt.enabled')) {
// encode config into the token
$config['token'] = $jwt->encode($config, $this->settings->getSetting('jwt.secret'));
// encode the dataInsertImage object into the token
$dataInsertImage['token'] = $jwt->encode($dataInsertImage);
$dataInsertImage['token'] = $jwt->encode($dataInsertImage, $this->settings->getSetting('jwt.secret'));
// encode the dataDocument object into the token
$dataDocument['token'] = $jwt->encode($dataDocument);
$dataDocument['token'] = $jwt->encode($dataDocument, $this->settings->getSetting('jwt.secret'));
// encode the dataSpreadsheet object into the token
$dataSpreadsheet['token'] = $jwt->encode($dataSpreadsheet);
$dataSpreadsheet['token'] = $jwt->encode($dataSpreadsheet, $this->settings->getSetting('jwt.secret'));
}
$historyLayout = '';
@ -267,8 +259,8 @@ class EditorController extends Controller
$editorConfig = [
'fileName' => $file['filename'],
'docType' => $file['format']->type,
'apiUrl' => $this->serverConfig->get('url.api'),
'docType' => $file['format']->getType(),
'apiUrl' => $this->settings->getSetting('url.api'),
'dataInsertImage' => mb_strimwidth(
json_encode($dataInsertImage),
1,
@ -308,152 +300,36 @@ class EditorController extends Controller
}
$data = $request->input('payload');
$status = $data['status'];
$data['filename'] = $filename;
$data['address'] = $address;
$actions = array_key_exists('actions', $data) ? $data['actions'] : [];
$changesUrl = array_key_exists('changesurl', $data) ? $data['changesurl'] : '';
$formsDataUrl = array_key_exists('formsdataurl', $data) ? $data['formsdataurl'] : '';
$fileType = array_key_exists('filetype', $data) ? $data['filetype'] : '';
$forceSaveType = array_key_exists('forcesavetype', $data) ? new CallbackForceSaveType($data['forcesavetype']) : null;
$history = array_key_exists('history', $data) && $data['history']
? new History($data['history']['serverVersion'], $data['history']['changes'])
: null;
$users = array_key_exists('users', $data) ? $data['users'] : [];
$url = array_key_exists('url', $data) ? $data['url'] : '';
switch ($status) {
case 1:
if ($data['actions'] && $data['actions'][0]['type'] == 0) {
$user = $data['actions'][0]['userid'];
if (array_search($user, $data['users']) === false) {
app(ForceSaveCommad::class)
->__invoke(new ForceSaveRequest(key: $data['key']));
}
}
break;
case 2:
case 3:
$url = $data['url'];
$key = $data['key'];
$user = $data['users'][0];
$changes = null;
$url = Str::replace(URL::origin($url), $this->serverConfig->get('url.private'), $url);
$fileExtension = PathInfo::extension($filename);
$downloadExtension = PathInfo::extension($url);
if ($fileExtension !== $downloadExtension) {
$result = app(ConvertCommand::class)
->__invoke(new ConvertRequest(
filename: $filename,
fileType: $downloadExtension,
outputType: $fileExtension,
url: $url,
password: null,
user: $user,
userAddress: $address,
));
if (array_key_exists('step', $result) || array_key_exists('error', $result)) {
$filename = PathInfo::filename($filename).".$downloadExtension";
} else {
$url = $result['fileUrl'];
}
}
$content = app(DownloadFileCommand::class)
->__invoke(new DownloadFileRequest(url: $url))['content'];
if (array_key_exists('changesurl', $data)) {
$changesUrl = $data['changesurl'];
$changesUrl = Str::replace(URL::origin($changesUrl), $this->serverConfig->get('url.private'), $changesUrl);
$changes = app(DownloadFileCommand::class)
->__invoke(new DownloadFileRequest(url: $changesUrl))['content'];
}
$history = array_key_exists('history', $data) ? $data['history']['changes'] : null;
$serverVersion = array_key_exists('history', $data) ? $data['history']['serverVersion'] : null;
$user = $data['users'][0];
app(SaveDocumentCommand::class)->__invoke(
new SaveDocumentRequest(
Path::join($address, $filename),
$fileExtension,
$key,
$content,
$user,
$serverVersion,
$callback = new Callback(
$actions,
$changesUrl,
$fileType,
$forceSaveType,
$history,
$changes,
)
);
break;
case 6:
case 7:
$isSubmitForm = $data['forcesavetype'] === 3;
if ($isSubmitForm && ! array_key_exists('formsdataurl', $data)) {
abort(500, 'Document editing service did not return formsDataUrl');
}
$url = $data['url'];
$key = $data['key'];
$user = $data['users'][0];
$url = Str::replace(URL::origin($url), $this->serverConfig->get('url.private'), $url);
$fileExtension = PathInfo::extension($filename);
$downloadExtension = PathInfo::extension($url);
if ($fileExtension !== $downloadExtension) {
$result = app(ConvertCommand::class)
->__invoke(new ConvertRequest(
filename: $filename,
fileType: $downloadExtension,
outputType: $fileExtension,
url: $url,
password: null,
user: $user,
userAddress: $address,
));
if (array_key_exists('step', $result) || array_key_exists('error', $result)) {
$filename = PathInfo::filename($filename).".$downloadExtension";
} else {
$url = $result['fileUrl'];
}
}
$content = app(DownloadFileCommand::class)
->__invoke(new DownloadFileRequest(url: $url))['content'];
if ($isSubmitForm) {
$formsDataUrl = $data['formsdataurl'];
$formsDataUrl = Str::replace(
URL::origin($formsDataUrl),
$this->serverConfig->get('url.private'),
$data['key'],
new CallbackDocStatus($data['status']),
$url,
$users,
'',
$formsDataUrl
);
$formData = app(DownloadFileCommand::class)
->__invoke(new DownloadFileRequest(url: $formsDataUrl))['content'];
$callbackService = new CallbackService($this->settings, app(JWTManager::class), $data);
$result = $callbackService->processCallback($callback, $filename);
app(SaveDocumentFormCommand::class)
->__invoke(new SaveDocumentFormRequest(
filename: $filename,
userDirectory: $address,
user: $user,
formContent: $content,
formDataContent: $formData,
));
} else {
app(ForceSaveDocumentCommand::class)
->__invoke(
new ForceSaveDocumentRequest(
filename: $filename,
userDirectory: $address,
content: $content
)
);
}
break;
}
return response()->json([
'error' => 0,
]);
return response()->json($result);
}
}

View File

@ -22,10 +22,9 @@ use App\Helpers\Path\Path;
use App\Helpers\Path\PathInfo;
use App\Helpers\URL\FileURL;
use App\Helpers\URL\URL;
use App\Repositories\FormatRepository;
use App\Services\JWT;
use App\Services\ServerConfig;
use App\Services\StorageConfig;
use App\OnlyOffice\Managers\FormatManager;
use App\OnlyOffice\Managers\JWTManager;
use App\OnlyOffice\Managers\SettingsManager;
use App\UseCases\Common\Http\DownloadFileCommand;
use App\UseCases\Common\Http\DownloadFileRequest;
use App\UseCases\Docs\Command\UpdateMetaCommand;
@ -54,9 +53,8 @@ use Illuminate\Support\Str;
class FileController extends Controller
{
public function __construct(
private ServerConfig $serverConfig,
private StorageConfig $storageConfig,
private FormatRepository $formatRepository,
private SettingsManager $settings,
private FormatManager $formatManager,
) {}
public function index(Request $request)
@ -130,7 +128,7 @@ class FileController extends Controller
$user = $request->input('user', '');
$url = Str::replace(URL::origin($request->url), $this->serverConfig->get('url.private'), $request->url);
$url = Str::replace(URL::origin($request->url), $this->settings->getSetting('url.server.private'), $request->url);
$downloadedFile = app(DownloadFileCommand::class)
->__invoke(new DownloadFileRequest(url: $url));
@ -188,13 +186,13 @@ class FileController extends Controller
], 500);
}
if (empty($this->formatRepository->find($result['fileType'])->actions)) {
if (empty($this->formatManager->find($result['fileType'])->getActions())) {
return response()
->json([
'step' => 100,
'filename' => str_replace(
$this->serverConfig->get('url.private'),
$this->serverConfig->get('url.public'),
$this->settings->getSetting('url.server.private'),
$this->settings->getSetting('url.server.public'),
$result['fileUrl']
),
'error' => 'FileTypeIsNotSupported',
@ -317,7 +315,7 @@ class FileController extends Controller
return response(status: 201);
}
public function config(Request $request, JWT $jwt)
public function config(Request $request, JWTManager $jwt)
{
try {
$request->validate([
@ -361,8 +359,8 @@ class FileController extends Controller
],
];
if ($this->serverConfig->get('jwt.enabled')) {
$config['token'] = $jwt->encode($config);
if ($this->settings->getSetting('jwt.enabled')) {
$config['token'] = $jwt->encode($config, $this->settings->getSetting('jwt.secret'));
}
return response()

View File

@ -19,8 +19,7 @@
namespace App\Http\Controllers;
use App\Helpers\URL\URL;
use App\Services\ServerConfig;
use App\Services\StorageConfig;
use App\OnlyOffice\Managers\SettingsManager;
use App\UseCases\Document\Find\FindAllDocumentsQuery;
use App\UseCases\Document\Find\FindAllDocumentsQueryHandler;
use App\UseCases\Language\Find\FindAllLanguagesQueryHandler;
@ -31,12 +30,12 @@ use Illuminate\Support\Str;
class IndexController extends Controller
{
public function index(Request $request, ServerConfig $serverConfig, StorageConfig $storageConfig)
public function index(Request $request, SettingsManager $settings)
{
$directUrlEnabled = $request->has('directUrl') && $request->directUrl === 'true';
$directUrlArg = 'directUrl='.($directUrlEnabled ? 'true' : 'false');
$preloaderUrl = $serverConfig->get('url.preloader');
$preloaderUrl = $settings->getSetting('url.preloader');
$files = app(FindAllDocumentsQueryHandler::class)
->__invoke(new FindAllDocumentsQuery($request->ip()));
@ -50,7 +49,7 @@ class IndexController extends Controller
foreach ($files as &$file) {
$url = route('files.download', ['fileName' => urlencode($file['filename']), 'dmode' => true]);
$file['url'] = Str::replace(URL::origin($url), $storageConfig->get('url.public'), $url);
$file['url'] = Str::replace(URL::origin($url), $settings->getSetting('url.storage.public'), $url);
}
return view('index', [

View File

@ -2,8 +2,8 @@
namespace App\Http\Middleware;
use App\Services\JWT;
use App\Services\ServerConfig;
use App\OnlyOffice\Managers\JWTManager;
use App\OnlyOffice\Managers\SettingsManager;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
@ -17,18 +17,17 @@ class CheckAndDecodeJWTPayload
*/
public function handle(Request $request, Closure $next): Response
{
$config = app(ServerConfig::class);
$jwt = app(JWT::class);
$jwt = app(JWTManager::class);
$settings = app(SettingsManager::class);
$payload = null;
$embeded = $request->has('dmode');
if ($config->get('jwt.enabled') && $embeded == null && $config->get('jwt.use_for_request')) {
if ($settings->getSetting('jwt.enabled') && $embeded == null && $settings->getSetting('jwt.use_for_request')) {
if ($request->token) {
$payload = $jwt->decode($request->token);
$payload = $jwt->decode($request->token, $settings->getSetting('jwt.secret'));
$payload = json_decode(json_encode($payload), true);
} elseif ($request->hasHeader($config->get('jwt.header'))) {
$payload = $jwt->decode($request->bearerToken());
$payload = json_decode($payload);
} elseif ($request->hasHeader($settings->getSetting('jwt.header'))) {
$payload = $jwt->decode($request->bearerToken(), $settings->getSetting('jwt.secret'));
} else {
abort(499, 'Expected JWT token');
}

View File

@ -2,8 +2,8 @@
namespace App\Http\Middleware;
use App\Services\JWT;
use App\Services\ServerConfig;
use App\OnlyOffice\Managers\JWTManager;
use App\OnlyOffice\Managers\SettingsManager;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
@ -17,13 +17,13 @@ class EnsureJWTTokenIsPresent
*/
public function handle(Request $request, Closure $next): Response
{
$config = app(ServerConfig::class);
$jwt = app(JWT::class);
$jwt = app(JWTManager::class);
$settings = app(SettingsManager::class);
$embeded = $request->has('dmode');
if ($config->get('jwt.enabled') && $embeded == null && $config->get('jwt.use_for_request')) {
if ($request->hasHeader($config->get('jwt.header'))) {
$token = $jwt->decode($request->bearerToken());
if ($settings->getSetting('jwt.enabled') && $embeded == null && $settings->getSetting('jwt.use_for_request')) {
if ($request->hasHeader($settings->getSetting('jwt.header'))) {
$token = $jwt->decode($request->bearerToken(), $settings->getSetting('jwt.secret'));
if (empty($token)) {
abort(498, 'Invalid JWT signature');

View File

@ -18,6 +18,8 @@
namespace App\Models;
use App\OnlyOffice\Models\Format;
class Document
{
public function __construct(

View File

@ -42,7 +42,7 @@ class Editor
'url' => $this->document->url,
'directUrl' => $this->config->directUrl,
],
'documentType' => $this->document->format->type,
'documentType' => $this->document->format->getType(),
'editorConfig' => $this->buildEditorConfig(),
'type' => $this->config->type,
];

View File

@ -18,8 +18,8 @@
namespace App\Models\Editor;
use App\Models\Format;
use App\Models\User;
use App\OnlyOffice\Models\Format;
class EditorPermissions
{
@ -101,12 +101,12 @@ class EditorPermissions
private function editable(): bool
{
return $this->action === 'edit' && $this->format->editable();
return $this->action === 'edit' && $this->format->isEditable();
}
private function fillable(): bool
{
return (($this->action === 'edit' && ! $this->format->editable())
|| $this->action === 'fillForms') && $this->format->fillable();
return (($this->action === 'edit' && ! $this->format->isEditable())
|| $this->action === 'fillForms') && $this->format->isFillable();
}
}

View File

@ -0,0 +1,81 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\OnlyOffice\Managers;
use App\Helpers\URL\FileURL;
use App\Helpers\URL\TemplateURL;
use Onlyoffice\DocsIntegrationSdk\Manager\Document\DocumentManager as OnlyOfficeDocumentManager;
class DocumentManager extends OnlyOfficeDocumentManager
{
private array $file;
public function __construct(array $file, $systemLangCode = 'en')
{
$formats = app(FormatManager::class);
$settings = app(SettingsManager::class);
parent::__construct($settings, $formats, $systemLangCode);
$this->file = $file;
}
public function getDocumentKey(string $fileId, bool $embedded)
{
return $this->file['key'];
}
public function getDocumentName(string $fileId)
{
return $this->file['filename'];
}
public static function getLangMapping()
{
return null;
}
public function getFileUrl(string $fileId)
{
return FileURL::download($this->file['filename'], $this->file['address']);
}
public function getCallbackUrl(string $fileId)
{
return FileURL::callback($this->file['filename'], $this->file['address']);
}
public function getTemplateImageUrl(string $fileId)
{
return TemplateURL::image($this->file['format']->getType());
}
public function getGobackUrl(string $fileId)
{
return $this->file['goback'] !== null ? $this->file['goback'] : '';
}
public function getCreateUrl(string $fileId)
{
return FileURL::create($this->file['format']->extension(), $this->file['user']);
}
public function getFile(): array
{
return $this->file;
}
}

View File

@ -0,0 +1,59 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\OnlyOffice\Managers;
use App\OnlyOffice\Models\Format;
use Onlyoffice\DocsIntegrationSdk\Manager\Formats\FormatsManager;
class FormatManager extends FormatsManager
{
public function __construct($nameAssoc = false)
{
parent::__construct();
$this->createCustomList();
}
private function createCustomList()
{
$newFormats = [];
foreach ($this->formatsList as $format) {
$newFormats[] = new Format(
$format->getName(),
$format->getType(),
$format->getActions(),
$format->getConvert(),
$format->getMimes(),
);
}
$this->formatsList = $newFormats;
}
public function find(string $extension): ?Format
{
foreach ($this->formatsList as $format) {
if ($format->extension() === $extension) {
return $format;
}
}
return null;
}
}

View File

@ -16,22 +16,28 @@
* limitations under the License.
*/
namespace App\Services\Docs\Command;
namespace App\OnlyOffice\Managers;
class ForgottenFileRequest extends CommandRequest
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Onlyoffice\DocsIntegrationSdk\Manager\Security\JwtManager as OnlyOfficeJWTManager;
class JWTManager extends OnlyOfficeJWTManager
{
public function get(string $key): array
public function __construct(SettingsManager $settings)
{
$content = [
'c' => 'getForgotten',
'key' => $key,
];
parent::__construct($settings);
}
$result = $this->send($content, $key);
public function encode($payload, $key, $algorithm = 'HS256')
{
return JWT::encode($payload, $key, $algorithm);
}
return [
'key' => $result['key'],
'url' => $result['url'],
];
public function decode($token, $key, $algorithm = 'HS256')
{
$payload = JWT::decode($token, new Key($key, $algorithm));
return $payload;
}
}

View File

@ -16,12 +16,19 @@
* limitations under the License.
*/
namespace App\Services;
namespace App\OnlyOffice\Managers;
class ServerConfig extends Config
use Exception;
use Onlyoffice\DocsIntegrationSdk\Manager\Settings\SettingsManager as OnlyOfficeSettingsManager;
class SettingsManager extends OnlyOfficeSettingsManager
{
private array $config;
public function __construct()
{
parent::__construct();
$publicServerUrl = rtrim(env('DOCUMENT_SERVER_PUBLIC_URL', 'http://documentserver'), '/');
$privateServerUrl = rtrim(env('DOCUMENT_SERVER_PRIVATE_URL', $publicServerUrl), '/');
$apiUrl = $publicServerUrl.'/'.env('DOCUMENT_SERVER_API_PATH', 'web-apps/apps/api/documents/api.js');
@ -30,8 +37,14 @@ class ServerConfig extends Config
$commandUrl = $privateServerUrl.'/'.env('DOCUMENT_SERVER_COMMAND_PATH', 'command');
$jwtSecret = env('DOCUMENT_SERVER_JWT_SECRET', 'secret');
$jwtUseForRequest = env('DOCUMENT_SERVER_JWT_USE_FOR_REQUEST', true);
$publicStorageUrl = rtrim(env('DOCUMENT_STORAGE_PUBLIC_URL', request()->schemeAndHttpHost()), '/');
$privateStorageUrl = rtrim(env('DOCUMENT_STORAGE_PRIVATE_URL', $publicStorageUrl), '/');
$this->config = [
'documentServerInternalUrl' => $privateServerUrl,
'jwtKey' => $jwtSecret,
'jwtHeader' => env('DOCUMENT_SERVER_JWT_HEADER', 'Authorization'),
'jwtPrefix' => env('DOCUMENT_SERVER_JWT_HEADER', 'Bearer '),
'conversion' => [
'timeout' => env('DOCUMENT_SERVER_CONVERSION_TIMEOUT', 120 * 1000),
'url' => $conversionUrl,
@ -47,11 +60,48 @@ class ServerConfig extends Config
],
'url' => [
'api' => $apiUrl,
'public' => $publicServerUrl,
'private' => $privateServerUrl,
'preloader' => $preloaderUrl,
'command' => $commandUrl,
'server' => [
'public' => $publicServerUrl,
'private' => $privateServerUrl,
],
'storage' => [
'private' => $privateStorageUrl,
'public' => $publicStorageUrl,
],
],
'file' => [
'max_size' => env('DOCUMENT_STORAGE_MAXIMUM_FILE_SIZE', 5 * 1024 * 1024),
],
];
}
public function getServerUrl()
{
return $this->get('url.server.public');
}
public function getSetting($settingName)
{
return $this->get($settingName);
}
public function setSetting($settingName, $value, $createSetting = false) {}
private function get(string $key, mixed $default = null): mixed
{
$keys = explode('.', $key);
$result = $this->config;
try {
foreach ($keys as $key) {
$result = $result[$key];
}
} catch (Exception $e) {
$result = $default;
}
return $result;
}
}

View File

@ -0,0 +1,90 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\OnlyOffice\Miscellaneous;
use App\OnlyOffice\Services\RequestService;
class CommandRequest
{
public static function forceSave(string $key)
{
$requestService = app(RequestService::class);
$data = [
'c' => 'forcesave',
'key' => $key,
];
$result = $requestService->commandRequest('forcesave', $data);
return $result;
}
public static function deleteForgotten(string $key)
{
$requestService = app(RequestService::class);
$data = [
'c' => 'deleteForgotten',
'key' => $key,
];
$result = $requestService->commandRequest('deleteForgotten', $data);
return $result;
}
public static function getForgotten(string $key)
{
$requestService = app(RequestService::class);
$data = [
'c' => 'getForgotten',
'key' => $key,
];
$result = $requestService->commandRequest('getForgotten', $data);
return $result;
}
public static function getForgottenList()
{
$requestService = app(RequestService::class);
$result = $requestService->commandRequest('getForgottenList');
return $result;
}
public static function updateMeta(string $key, array $meta)
{
$requestService = app(RequestService::class);
$data = [
'c' => 'meta',
'key' => $key,
'meta' => $meta,
];
$result = $requestService->commandRequest('meta', $data);
return $result;
}
}

View File

@ -0,0 +1,53 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\OnlyOffice\Miscellaneous;
use App\Exceptions\ConversionError;
use App\Exceptions\ConversionNotComplete;
use App\OnlyOffice\Services\RequestService;
class ConvertRequest
{
public function convert(array $data)
{
$requestService = app(RequestService::class);
$result = $requestService->sendRequestToConvertService(
$data['url'],
$data['filetype'],
$data['outputtype'],
$data['key'],
false,
$data['lang'],
);
if (property_exists($result, 'Error')) {
throw new ConversionError($result->Error);
}
if (! property_exists($result, 'EndConvert')) {
throw new ConversionNotComplete($result->Percent, $result->Filename, $result->FileUrl);
}
return [
'fileType' => $result->FileType,
'fileUrl' => $result->FileUrl,
];
}
}

View File

@ -0,0 +1,55 @@
<?php
//
// (c) Copyright Ascensio System SIA 2024
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
namespace App\OnlyOffice\Models;
use App\Enums\FormatType;
use Onlyoffice\DocsIntegrationSdk\Models\Format as OnlyOfficeFormat;
class Format extends OnlyOfficeFormat
{
public function isAutoConvertable(): bool
{
return in_array('auto-convert', $this->actions);
}
public function extension(): string
{
return $this->name;
}
public function isWord(): bool
{
return $this->type === FormatType::WORD->value;
}
public function isCell(): bool
{
return $this->type === FormatType::CELL->value;
}
public function isSlide(): bool
{
return $this->type === FormatType::SLIDE->value;
}
public function isPDF(): bool
{
return $this->type === FormatType::PDF->value;
}
}

View File

@ -0,0 +1,225 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2024.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\OnlyOffice\Services;
use App\Helpers\Path\Path;
use App\Helpers\Path\PathInfo;
use App\Helpers\URL\URL;
use App\UseCases\Common\Http\DownloadFileCommand;
use App\UseCases\Common\Http\DownloadFileRequest;
use App\UseCases\Docs\Command\ForceSaveCommad;
use App\UseCases\Docs\Command\ForceSaveRequest;
use App\UseCases\Docs\Conversion\ConvertCommand;
use App\UseCases\Docs\Conversion\ConvertRequest;
use App\UseCases\Document\Save\ForceSaveDocumentCommand;
use App\UseCases\Document\Save\ForceSaveDocumentRequest;
use App\UseCases\Document\Save\SaveDocumentCommand;
use App\UseCases\Document\Save\SaveDocumentFormCommand;
use App\UseCases\Document\Save\SaveDocumentFormRequest;
use App\UseCases\Document\Save\SaveDocumentRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Onlyoffice\DocsIntegrationSdk\Models\CallbackForceSaveType;
use Onlyoffice\DocsIntegrationSdk\Service\Callback\CallbackService as OnlyOfficeCallbackService;
class CallbackService extends OnlyOfficeCallbackService
{
private $data;
public function __construct($settingsManager, $jwtManager, $data = [])
{
parent::__construct($settingsManager, $jwtManager);
$this->data = $data;
}
public function processTrackerStatusEditing($callback, string $fileid)
{
return $this->processTrackerStatusEditingAndClosed($callback, $fileid);
}
public function processTrackerStatusMustsave($callback, string $fileid)
{
return $this->processTrackerStatusMustsaveAndCorrupted($callback, $fileid);
}
public function processTrackerStatusCorrupted($callback, string $fileid)
{
return $this->processTrackerStatusMustsaveAndCorrupted($callback, $fileid);
}
public function processTrackerStatusClosed($callback, string $fileid)
{
return $this->processTrackerStatusEditingAndClosed($callback, $fileid);
}
public function processTrackerStatusForcesave($callback, string $fileid)
{
$isSubmitForm = $callback->getForceSaveType()->getValue() === CallbackForceSaveType::SUBMIT_FORM;
if ($isSubmitForm && ! $callback->getFormsDataUrl()) {
Log::error('Document editing service did not return formsDataUrl');
return ['error' => 1];
}
$url = $callback->getUrl();
$key = $callback->getKey();
$user = $callback->getUsers()[0];
$filename = $this->data['filename'];
$address = $this->data['address'];
$url = $this->settingsManager->replaceDocumentServerUrlToInternal($url);
$fileExtension = PathInfo::extension($filename);
$downloadExtension = PathInfo::extension($url);
if ($fileExtension !== $downloadExtension) {
$result = app(ConvertCommand::class)
->__invoke(new ConvertRequest(
filename: $filename,
fileType: $downloadExtension,
outputType: $fileExtension,
url: $url,
password: null,
user: $user,
userAddress: $address,
));
if (array_key_exists('step', $result) || array_key_exists('error', $result)) {
$filename = PathInfo::filename($filename).".$downloadExtension";
} else {
$url = $result['fileUrl'];
}
}
$content = app(DownloadFileCommand::class)
->__invoke(new DownloadFileRequest(url: $url))['content'];
if ($isSubmitForm) {
$formsDataUrl = $this->settingsManager->replaceDocumentServerUrlToInternal($callback->getFormsDataUrl());
$formData = app(DownloadFileCommand::class)
->__invoke(new DownloadFileRequest(url: $formsDataUrl))['content'];
app(SaveDocumentFormCommand::class)
->__invoke(new SaveDocumentFormRequest(
filename: $filename,
userDirectory: $address,
user: $user,
formContent: $content,
formDataContent: $formData,
));
} else {
app(ForceSaveDocumentCommand::class)
->__invoke(
new ForceSaveDocumentRequest(
filename: $filename,
userDirectory: $address,
content: $content
)
);
}
$result['error'] = 0;
return $result;
}
public function processTrackerStatusMustsaveAndCorrupted($callback, string $fileid)
{
$url = $callback->getUrl();
$key = $callback->getKey();
$user = $callback->getUsers()[0];
$changes = null;
$filename = $this->data['filename'];
$address = $this->data['address'];
$url = $this->settingsManager->replaceDocumentServerUrlToInternal($url);
$fileExtension = PathInfo::extension($filename);
$downloadExtension = PathInfo::extension($url);
if ($fileExtension !== $downloadExtension) {
$result = app(ConvertCommand::class)
->__invoke(new ConvertRequest(
filename: $filename,
fileType: $downloadExtension,
outputType: $fileExtension,
url: $url,
password: null,
user: $user,
userAddress: $address,
));
if (array_key_exists('step', $result) || array_key_exists('error', $result)) {
$filename = PathInfo::filename($filename).".$downloadExtension";
} else {
$url = $result['fileUrl'];
}
}
$content = app(DownloadFileCommand::class)
->__invoke(new DownloadFileRequest(url: $url))['content'];
$changesUrl = $callback->getChangesurl();
if ($changesUrl) {
$changesUrl = Str::replace(URL::origin($changesUrl), $this->settingsManager->getSetting('url.server.private'), $changesUrl);
$changes = app(DownloadFileCommand::class)
->__invoke(new DownloadFileRequest(url: $changesUrl))['content'];
}
$historyObject = $callback->getHistory();
$history = $historyObject ? $historyObject->getChanges() : null;
$serverVersion = $historyObject ? $historyObject->getServerVersion() : null;
app(SaveDocumentCommand::class)->__invoke(
new SaveDocumentRequest(
Path::join($address, $filename),
$fileExtension,
$key,
$content,
$user,
$serverVersion,
$history,
$changes,
)
);
return [
'error' => 0,
];
}
public function processTrackerStatusEditingAndClosed($callback, string $fileid)
{
$actions = $callback->getActions();
if ($actions && $actions[0]['type'] == 0) {
$user = $actions[0]['userid'];
if (array_search($user, $callback->getUsers()) === false) {
app(ForceSaveCommad::class)
->__invoke(new ForceSaveRequest(key: $callback->getKey()));
}
}
$result['error'] = 0;
return $result;
}
}

View File

@ -0,0 +1,72 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2024.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\OnlyOffice\Services;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Onlyoffice\DocsIntegrationSdk\Service\Request\HttpClientInterface;
class HttpClient implements HttpClientInterface
{
private $status;
private $body;
public function __construct()
{
$this->status = null;
$this->body = null;
}
/**
* Request to Document Server with turn off verification.
*
* @param string $url - request address
* @param string $method - request method
* @param array $opts - request options
*/
public function request($url, $method = 'GET', $opts = [])
{
$httpClient = new Client(['base_uri' => $url]);
try {
$response = $httpClient->request($method, $url, $opts);
$this->body = $response->getBody()->getContents();
$this->status = $response->getStatusCode();
} catch (RequestException $requestException) {
throw new Exception($requestException->getMessage());
}
}
/**
* Get the status code
*/
public function getStatusCode()
{
return $this->status;
}
/**
* Get the response body.
*/
public function getBody()
{
return $this->body;
}
}

View File

@ -16,27 +16,21 @@
* limitations under the License.
*/
namespace App\Services;
namespace App\OnlyOffice\Services;
use Exception;
use App\OnlyOffice\Managers\JWTManager;
use App\OnlyOffice\Managers\SettingsManager;
use Onlyoffice\DocsIntegrationSdk\Service\Request\RequestService as OnlyOfficeRequestService;
abstract class Config
class RequestService extends OnlyOfficeRequestService
{
protected array $config;
public function get(string $key, mixed $default = null): mixed
public function __construct(SettingsManager $settingsManager, HttpClient $httpClient, JWTManager $jwtManager)
{
$keys = explode('.', $key);
$result = $this->config;
try {
foreach ($keys as $key) {
$result = $result[$key];
}
} catch (Exception $e) {
$result = $default;
parent::__construct($settingsManager, $httpClient, $jwtManager);
}
return $result;
public function getFileUrlForConvert()
{
return '';
}
}

View File

@ -2,7 +2,7 @@
namespace App\Providers;
use App\Repositories\FormatRepository;
use App\OnlyOffice\Managers\FormatManager;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@ -12,10 +12,8 @@ class AppServiceProvider extends ServiceProvider
*/
public function register(): void
{
$this->app->singleton(FormatRepository::class, function () {
$path = public_path('assets/document-formats/onlyoffice-docs-formats.json');
return new FormatRepository($path);
$this->app->singleton(FormatManager::class, function () {
return new FormatManager;
});
}

View File

@ -1,76 +0,0 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\Services\Docs\Command;
use App\Exceptions\CommandServiceError;
use App\Services\JWT;
use App\Services\ServerConfig;
use Exception;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
class CommandRequest
{
private array $headers = [];
public function __construct(private ServerConfig $config, private JWT $jwt) {}
private function withJWTHeader(array $content): void
{
$token = $this->jwt->encode(['payload' => $content]);
$this->headers = [$this->config->get('jwt.header') => "Bearer $token"];
}
public function send(array $content, ?string $key = null): mixed
{
if ($this->config->get('jwt.enabled')) {
$this->withJWTHeader($content);
$content['token'] = $this->jwt->encode($content);
}
$client = Http::withHeaders($this->headers)
->asJson()
->acceptJson();
$url = $this->config->get('url.command');
if (Str::of($url)->isUrl(['https'])
&& ! $this->config->get('ssl_verify')) {
$client = $client->withoutVerifying();
}
if ($key) {
$url = "$url?shardkey=".urlencode($key);
}
$response = $client->post($url, $content);
if (! $response->ok()) {
throw new Exception('Could not execute the command.');
}
$result = $response->json();
if (array_key_exists('error', $result) && $result['error'] !== 0) {
throw new CommandServiceError($result['error']);
}
return $result;
}
}

View File

@ -1,34 +0,0 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\Services\Docs\Command;
class ForceSaveRequest extends CommandRequest
{
public function save(string $key): array
{
$content = [
'c' => 'forcesave',
'key' => $key,
];
$result = $this->send($content, $key);
return $result;
}
}

View File

@ -1,34 +0,0 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\Services\Docs\Command;
class ForgottenDeleteRequest extends CommandRequest
{
public function delete(string $key): string
{
$content = [
'c' => 'deleteForgotten',
'key' => $key,
];
$result = $this->send($content, $key);
return $result['key'];
}
}

View File

@ -1,33 +0,0 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\Services\Docs\Command;
class ForgottenListRequest extends CommandRequest
{
public function get(): array
{
$content = [
'c' => 'getForgottenList',
];
$result = $this->send($content);
return $result['keys'];
}
}

View File

@ -1,84 +0,0 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\Services\Docs\Conversion;
use App\Exceptions\ConversionError;
use App\Exceptions\ConversionNotComplete;
use App\Services\JWT;
use App\Services\ServerConfig;
use Exception;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
class ConversionRequest
{
private array $headers = [];
public function __construct(private ServerConfig $config, private JWT $jwt) {}
private function withJWTHeader(array $content): void
{
$token = $this->jwt->encode(['payload' => $content]);
$this->headers = [$this->config->get('jwt.header') => "Bearer $token"];
}
public function send(array $content, ?string $key = null): mixed
{
if ($this->config->get('jwt.enabled')) {
$this->withJWTHeader($content);
$content['token'] = $this->jwt->encode($content);
}
$client = Http::withHeaders($this->headers)
->timeout($this->config->get('conversion.timeout'))
->asJson()
->acceptJson();
$url = $this->config->get('conversion.url');
if (
Str::of($url)->isUrl(['https'])
&& ! $this->config->get('ssl_verify')
) {
$client = $client->withoutVerifying();
}
if ($key) {
$url = "$url?shardkey=".urlencode($key);
}
$response = $client->post($url, $content);
if (! $response->ok()) {
throw new Exception('Could not convert the file');
}
$result = $response->json();
if (array_key_exists('error', $result)) {
throw new ConversionError($result['error']);
}
if (! $result['endConvert']) {
throw new ConversionNotComplete($result['percent'], $result['filename'], $result['fileUrl']);
}
return $result;
}
}

View File

@ -1,65 +0,0 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\Services;
use Firebase\JWT\JWT as FirebaseJWT;
use Firebase\JWT\Key;
class JWT
{
private string $secret;
private string $algorithm;
public function __construct(ServerConfig $config)
{
$this->secret = $config->get('jwt.secret');
$this->algorithm = $config->get('jwt.algorithm');
}
/**
* Encode a payload object into a token using a secret key
*
* @param array $payload
*/
public function encode(mixed $payload): string
{
return FirebaseJWT::encode($payload, $this->secret, $this->algorithm);
}
/**
* Decode a token into a payload object using a secret key
*
*
* @return string
*/
public function decode(string $token)
{
try {
$payload = FirebaseJWT::decode(
$token,
new Key($this->secret, $this->algorithm),
);
} catch (\UnexpectedValueException $e) {
$payload = '';
}
return $payload;
}
}

View File

@ -1,38 +0,0 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace App\Services;
class StorageConfig extends Config
{
public function __construct()
{
$publicStorageUrl = rtrim(env('DOCUMENT_STORAGE_PUBLIC_URL', request()->schemeAndHttpHost()), '/');
$privateStorageUrl = rtrim(env('DOCUMENT_STORAGE_PRIVATE_URL', $publicStorageUrl), '/');
$this->config = [
'url' => [
'private' => $privateStorageUrl,
'public' => $publicStorageUrl,
],
'file' => [
'max_size' => env('DOCUMENT_STORAGE_MAXIMUM_FILE_SIZE', 5 * 1024 * 1024),
],
];
}
}

View File

@ -18,8 +18,8 @@
namespace App\UseCases\Docs\Command;
use App\Exceptions\CommandServiceError;
use App\Services\Docs\Command\ForceSaveRequest as ForceSave;
use App\OnlyOffice\Miscellaneous\CommandRequest;
use Exception;
use Illuminate\Support\Facades\Log;
class ForceSaveCommad
@ -27,9 +27,9 @@ class ForceSaveCommad
public function __invoke(ForceSaveRequest $request): void
{
try {
app(ForceSave::class)
->save($request->key);
} catch (CommandServiceError $e) {
app(CommandRequest::class)
->forceSave($request->key);
} catch (Exception $e) {
Log::debug($e->getMessage());
}
}

View File

@ -2,21 +2,15 @@
namespace App\UseCases\Docs\Command;
use App\Services\Docs\Command\CommandRequest;
use App\OnlyOffice\Miscellaneous\CommandRequest;
class UpdateMetaCommand
{
public function __invoke(UpdateMetaRequest $request): void
{
$content = [
'c' => 'meta',
'key' => $request->key,
'meta' => [
'title' => $request->title,
],
];
$meta = ['title' => $request->title];
app(CommandRequest::class)
->send($content, $request->key);
->updateMeta($request->key, $meta);
}
}

View File

@ -22,26 +22,20 @@ use App\Exceptions\ConversionError;
use App\Exceptions\ConversionNotComplete;
use App\Helpers\Path\PathInfo;
use App\Helpers\URL\FileURL;
use App\Repositories\FormatRepository;
use App\Services\Docs\Conversion\ConversionRequest;
use App\Services\JWT;
use App\Services\ServerConfig;
use App\OnlyOffice\Managers\FormatManager;
use App\OnlyOffice\Miscellaneous\ConvertRequest as ConvertRequestAdapter;
use Exception;
use Illuminate\Support\Str;
class ConvertCommand
{
public function __construct(
private ServerConfig $serverConfig,
private FormatRepository $formatRepository,
private JWT $jwt,
) {}
public function __construct(private FormatManager $formatManager) {}
public function __invoke(ConvertRequest $request): mixed
{
$format = $this->formatRepository->find($request->fileType);
$format = $this->formatManager->find($request->fileType);
if (! $format->convertible() && $request->outputType == 'ooxml') {
if (! $format->isAutoConvertable() && $request->outputType == 'ooxml') {
throw new Exception("The format $request->fileType is not convertible.");
}
@ -61,8 +55,7 @@ class ConvertCommand
];
try {
$result = app(ConversionRequest::class)
->send($content, $key);
$result = app(ConvertRequestAdapter::class)->convert($content);
$result['filename'] = PathInfo::filename($request->filename).'.'.$result['fileType'];
} catch (ConversionNotComplete $e) {
return [

View File

@ -24,8 +24,8 @@ use App\Helpers\UniqueFilename;
use App\Models\File;
use App\Models\Version;
use App\Models\VersionInfo;
use App\OnlyOffice\Managers\FormatManager;
use App\Repositories\FileRepository;
use App\Repositories\FormatRepository;
use App\Repositories\UserRepository;
use App\Repositories\VersionRepository;
use Illuminate\Support\Str;
@ -35,7 +35,7 @@ class CreateDocumentCommand
{
public function __construct(
private FileRepository $fileRepository,
private FormatRepository $formatRepository,
private FormatManager $formatManager,
private UserRepository $userRepository,
private VersionRepository $versionRepository,
) {}
@ -45,10 +45,10 @@ class CreateDocumentCommand
$filePath = Path::join($request->userDirectory, $request->filename);
$filePath = UniqueFilename::for($filePath);
$format = $this->formatRepository->find($request->fileType);
$format = $this->formatManager->find($request->fileType);
$user = $this->userRepository->find($request->user);
if ($format === null || empty($format->actions)) {
if ($format === null || empty($format->getActions())) {
throw new UnexpectedValueException("The $request->fileType format is not supported");
}

View File

@ -3,15 +3,15 @@
namespace App\UseCases\Document\Find;
use App\Helpers\Path\PathInfo;
use App\OnlyOffice\Managers\FormatManager;
use App\Repositories\FileRepository;
use App\Repositories\FormatRepository;
use App\Repositories\VersionRepository;
class FindAllDocumentsQueryHandler
{
public function __construct(
private FileRepository $fileRepository,
private FormatRepository $formatRepository,
private FormatManager $formatManager,
private VersionRepository $versionRepository,
) {}
@ -27,7 +27,7 @@ class FindAllDocumentsQueryHandler
$result[] = [
'filename' => PathInfo::basename($file->filename),
'version' => $currentVersion,
'format' => $this->formatRepository->find(PathInfo::extension($file->filename)),
'format' => $this->formatManager->find(PathInfo::extension($file->filename)),
'lastModified' => $file->modified,
];
}

View File

@ -4,15 +4,18 @@ namespace App\UseCases\Document\Find;
use App\Helpers\Path\PathInfo;
use App\Helpers\URL\FileURL;
use App\OnlyOffice\Managers\JWTManager;
use App\OnlyOffice\Managers\SettingsManager;
use App\Repositories\UserRepository;
use App\Repositories\VersionRepository;
use App\Services\JWT;
class FindDocumentHistoryQueryHandler
{
public function __construct(
private VersionRepository $versionRepository,
private UserRepository $userRepository,
private SettingsManager $settings,
private JWTManager $jwt,
) {}
public function __invoke(FindDocumentHistoryQuery $request): array
@ -59,7 +62,7 @@ class FindDocumentHistoryQueryHandler
$item['url'] = FileURL::download(
PathInfo::basename($request->filename), $request->userAddress
);
$item['token'] = app(JWT::class)->encode($item);
$item['token'] = $this->jwt->encode($item, $this->settings->getSetting('jwt.secret'));
$history['history'][] = $item;
}

View File

@ -4,9 +4,9 @@ namespace App\UseCases\Document\Find;
use App\Helpers\Path\Path;
use App\Helpers\Path\PathInfo;
use App\OnlyOffice\Managers\FormatManager;
use App\Repositories\FileRepository;
use App\Repositories\ForceSavedFilesRepository;
use App\Repositories\FormatRepository;
use App\Repositories\UserRepository;
use App\Repositories\VersionRepository;
use DomainException;
@ -18,7 +18,7 @@ class FindDocumentQueryHandler
private VersionRepository $versionRepository,
private ForceSavedFilesRepository $forceSavedFilesRepository,
private UserRepository $userRepository,
private FormatRepository $formatRepository,
private FormatManager $formatManager,
) {}
public function __invoke(FindDocumentQuery $request): array
@ -49,7 +49,7 @@ class FindDocumentQueryHandler
'content' => $file->content,
'size' => $file->size,
'mimeType' => $file->mime,
'format' => $this->formatRepository->find(PathInfo::extension($request->filename)),
'format' => $this->formatManager->find(PathInfo::extension($request->filename)),
];
return $document;

View File

@ -21,19 +21,19 @@ namespace App\UseCases\Editor\Create;
use App\Models\Document;
use App\Models\Editor\Editor;
use App\Models\Editor\EditorConfig;
use App\Repositories\FormatRepository;
use App\OnlyOffice\Managers\FormatManager;
use App\Repositories\UserRepository;
class CreateConfigCommand
{
public function __construct(
private UserRepository $userRepository,
private FormatRepository $formatRepository,
private FormatManager $formatManager,
) {}
public function __invoke(CreateConfigRequest $request): array
{
$format = $this->formatRepository->find($request->fileExtension);
$format = $this->formatManager->find($request->fileExtension);
$user = $this->userRepository->find($request->user);
if ($user->goback !== null) {

View File

@ -18,12 +18,13 @@
namespace App\UseCases\Forgotten\Delete;
use App\Services\Docs\Command\ForgottenDeleteRequest;
use App\OnlyOffice\Miscellaneous\CommandRequest;
class DeleteForgottenFileCommand
{
public function __invoke(DeleteForgottenFileRequest $request): void
{
app()->make(ForgottenDeleteRequest::class)->delete($request->key);
app(CommandRequest::class)
->deleteForgotten($request->key);
}
}

View File

@ -20,40 +20,41 @@ namespace App\UseCases\Forgotten\Find;
use App\Helpers\Path\PathInfo;
use App\Helpers\URL\URL;
use App\Repositories\FormatRepository;
use App\Services\Docs\Command\ForgottenFileRequest;
use App\Services\Docs\Command\ForgottenListRequest;
use App\Services\ServerConfig;
use App\OnlyOffice\Managers\FormatManager;
use App\OnlyOffice\Managers\SettingsManager;
use App\OnlyOffice\Miscellaneous\CommandRequest;
use Illuminate\Support\Str;
class FindAllForgottenFilesQueryHandler
{
public function __construct(
private ServerConfig $serverConfig,
private FormatRepository $formatRepository,
private SettingsManager $settings,
private FormatManager $formatManager,
) {}
public function __invoke(FindAllForgottenFilesQuery $query): array
{
$filesList = [];
$commandRequest = app(CommandRequest::class);
$keys = app()->make(ForgottenListRequest::class)->get();
$result = $commandRequest->getForgottenList();
$keys = $result->keys;
foreach ($keys as $key) {
$filesList[] = app()->make(ForgottenFileRequest::class)->get($key);
$filesList[] = $commandRequest->getForgotten($key);
}
$files = [];
foreach ($filesList as $fileItem) {
$url = $fileItem['url'];
$url = Str::replace(URL::origin($url), $this->serverConfig->get('url.public'), $url);
$url = $fileItem->url;
$url = Str::replace(URL::origin($url), $this->settings->getSetting('url.server.public'), $url);
$files[] = [
'key' => $fileItem['key'],
'key' => $fileItem->key,
'filename' => $url,
'url' => $url,
'format' => $this->formatRepository->find(PathInfo::extension($fileItem['url'])),
'format' => $this->formatManager->find(PathInfo::extension($fileItem->url)),
];
}

View File

@ -3,7 +3,8 @@
"php": "^8.2",
"firebase/php-jwt": "^6.10",
"laravel/framework": "^11.0",
"laravel/tinker": "^2.9"
"laravel/tinker": "^2.9",
"onlyoffice/docs-integration-sdk": "^1.1"
},
"require-dev": {
"fakerphp/faker": "^1.23",

View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "15b06064f4351d124f81a5718daddb7c",
"content-hash": "349f218d77cda1f9ef25791dae86f9d4",
"packages": [
{
"name": "brick/math",
@ -2380,6 +2380,59 @@
],
"time": "2024-03-06T16:17:14+00:00"
},
{
"name": "onlyoffice/docs-integration-sdk",
"version": "v1.1.0",
"source": {
"type": "git",
"url": "https://github.com/ONLYOFFICE/docs-integration-sdk-php.git",
"reference": "3715cb022c182551f4bad43f5869c0536260e5ed"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ONLYOFFICE/docs-integration-sdk-php/zipball/3715cb022c182551f4bad43f5869c0536260e5ed",
"reference": "3715cb022c182551f4bad43f5869c0536260e5ed",
"shasum": ""
},
"require": {
"vlucas/phpdotenv": "^5.6"
},
"type": "library",
"autoload": {
"psr-4": {
"Onlyoffice\\DocsIntegrationSdk\\": "src/",
"Onlyoffice\\DocsIntegrationSdk\\Util\\": "src/util/",
"Onlyoffice\\DocsIntegrationSdk\\Models\\": "src/models/",
"Onlyoffice\\DocsIntegrationSdk\\Manager\\Formats\\": "src/manager/formats/",
"Onlyoffice\\DocsIntegrationSdk\\Service\\Request\\": "src/service/request/",
"Onlyoffice\\DocsIntegrationSdk\\Manager\\Document\\": "src/manager/document/",
"Onlyoffice\\DocsIntegrationSdk\\Manager\\Security\\": "src/manager/security/",
"Onlyoffice\\DocsIntegrationSdk\\Manager\\Settings\\": "src/manager/settings/",
"Onlyoffice\\DocsIntegrationSdk\\Service\\DocEditorConfig\\": "src/service/doceditorconfig/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Ascensio System SIA"
}
],
"description": "ONLYOFFICE Docs integration SDK",
"homepage": "https://www.onlyoffice.com",
"keywords": [
"onlyoffice"
],
"support": {
"email": "support@onlyoffice.com",
"forum": "https://forum.onlyoffice.com/",
"issues": "https://github.com/ONLYOFFICE/docs-integration-sdk-php/issues",
"source": "https://github.com/ONLYOFFICE/docs-integration-sdk-php"
},
"time": "2025-01-15T13:52:01+00:00"
},
{
"name": "phpoption/phpoption",
"version": "1.9.2",

View File

@ -85,7 +85,7 @@
@foreach ($files as $file)
<tr class="tableRow" title="{{ $file['key'] }}">
<td>
<a class="stored-edit action-link {{ $file['format']->type }}" href="{{ $file['filename'] }}" target="_blank">
<a class="stored-edit action-link {{ $file['format']->getType() }}" href="{{ $file['filename'] }}" target="_blank">
<span>{{ $file['key'] }}</span>
</a>
</td>

View File

@ -168,11 +168,11 @@
@foreach ($files as $file)
<tr class="tableRow" title="{{ $file['filename'] }} [{{ $file['version'] }}]">
<td class="contentCells">
<a class="stored-edit {{ $file['format']->type }}" href="editor?fileID={{ urlencode($file['filename']) }}&user={{ "$user&$directUrlArg" }}" target="_blank">
<a class="stored-edit {{ $file['format']->getType() }}" href="editor?fileID={{ urlencode($file['filename']) }}&user={{ "$user&$directUrlArg" }}" target="_blank">
<span>{{ $file['filename'] }}</span>
</a>
</td>
@if ($file['format']->editable())
@if ($file['format']->isEditable())
<td class="contentCells contentCells-icon">
<a href="editor?fileID={{ urlencode($file['filename']) }}&user={{ htmlentities($user) . "&$directUrlArg" }}'&action=edit&type=desktop" target="_blank">
<img src="/images/desktop.svg" alt="Open in editor for full size screens" title="Open in editor for full size screens" />
@ -211,7 +211,7 @@
<td class="contentCells contentCells-icon"></td>
<td class="contentCells contentCells-icon"></td>
@endif
@if ($file['format']->fillable())
@if ($file['format']->isFillable())
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift">
<a href="editor?fileID={{ urlencode($file['filename']) }}&user={{ htmlentities($user) . "&$directUrlArg" }}&action=fillForms&type=desktop" target="_blank">
<img src="/images/fill-forms.svg" alt="Open in editor for filling in forms" title="Open in editor for filling in forms" />
@ -220,7 +220,7 @@
@else
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift"></td>
@endif
@elseif ($file['format']->fillable())
@elseif ($file['format']->isFillable())
<td class="contentCells contentCells-icon">
<a href="editor?fileID={{ urlencode($file['filename']) }}&user={{ htmlentities($user) . "&$directUrlArg" }}&action=fillForms&type=desktop" target="_blank">
<img src="/images/mobile-fill-forms.svg" alt="Open in editor for filling in forms for mobile devices" title="Open in editor for filling in forms for mobile devices" />
@ -252,9 +252,9 @@
<img src="/images/embeded.svg" alt="Open in embedded mode" title="Open in embedded mode" />
</a>
</td>
@if ($file['format']->type != null)
@if ($file['format']->getType() != null)
<td class="contentCells contentCells-icon">
<a class="convert-file" data="{{ $file['filename'] }}" data-type="{{ $file['format']->type }}">
<a class="convert-file" data="{{ $file['filename'] }}" data-type="{{ $file['format']->getType() }}">
<img class="icon-action" src="/images/convert.svg" alt="Convert" title="Convert" />
</a>
</td>