mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-24 11:06:14 +00:00
348 lines
11 KiB
PHP
348 lines
11 KiB
PHP
<?php
|
|
|
|
/**
|
|
* For the full copyright and license information, please view
|
|
* the LICENSE file that was distributed with this source code.
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Chill\WopiBundle\Service\Wopi;
|
|
|
|
use ChampsLibres\AsyncUploaderBundle\TempUrl\TempUrlGeneratorInterface;
|
|
use ChampsLibres\WopiLib\Discovery\WopiDiscoveryInterface;
|
|
use ChampsLibres\WopiLib\WopiInterface;
|
|
use Chill\DocStoreBundle\Repository\StoredObjectRepository;
|
|
use Exception;
|
|
use loophp\psr17\Psr17Interface;
|
|
use Psr\Http\Client\ClientInterface;
|
|
use Psr\Http\Message\RequestInterface;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
use Symfony\Component\Security\Core\Security;
|
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
|
|
|
final class ChillWopi implements WopiInterface
|
|
{
|
|
private Psr17Interface $psr17;
|
|
|
|
private WopiDiscoveryInterface $wopiDiscovery;
|
|
|
|
private StoredObjectRepository $storedObjectRepository;
|
|
|
|
private ClientInterface $httpClient;
|
|
|
|
private TempUrlGeneratorInterface $tempUrlGeneratorInterface;
|
|
|
|
private Security $security;
|
|
|
|
private UserProviderInterface $userProvider;
|
|
|
|
public function __construct(
|
|
Psr17Interface $psr17,
|
|
WopiDiscoveryInterface $wopiDiscovery,
|
|
StoredObjectRepository $storedObjectRepository,
|
|
ClientInterface $httpClient,
|
|
TempUrlGeneratorInterface $tempUrlGeneratorInterface,
|
|
Security $security,
|
|
UserProviderInterface $userProvider
|
|
) {
|
|
$this->psr17 = $psr17;
|
|
$this->wopiDiscovery = $wopiDiscovery;
|
|
$this->storedObjectRepository = $storedObjectRepository;
|
|
$this->httpClient = $httpClient;
|
|
$this->tempUrlGeneratorInterface = $tempUrlGeneratorInterface;
|
|
$this->security = $security;
|
|
$this->userProvider = $userProvider;
|
|
}
|
|
|
|
public function checkFileInfo(
|
|
string $fileId,
|
|
?string $accessToken,
|
|
RequestInterface $request
|
|
): ResponseInterface {
|
|
$user = $this->userProvider->loadUserByUsername($accessToken);
|
|
|
|
$storedObject = $this->storedObjectRepository->findOneBy(['filename' => $fileId]);
|
|
|
|
if (null === $storedObject) {
|
|
throw new Exception(sprintf('Unable to find object named %s', $fileId));
|
|
}
|
|
|
|
$mimeType = $storedObject->getType();
|
|
|
|
if ([] === $this->wopiDiscovery->discoverMimeType($mimeType)) {
|
|
throw new Exception(sprintf('Unable to find mime type %s', $mimeType));
|
|
}
|
|
|
|
return $this
|
|
->psr17
|
|
->createResponse()
|
|
->withHeader('Content-Type', 'application/json')
|
|
->withBody($this->psr17->createStream((string) json_encode(
|
|
[
|
|
'BaseFileName' => $storedObject->getFilename(),
|
|
'OwnerId' => $user->getUsername(),
|
|
'Size' => 0,
|
|
'UserId' => $user->getUsername(),
|
|
// 'Version' => 'v' . uniqid(),
|
|
'ReadOnly' => false,
|
|
'UserCanWrite' => true,
|
|
'UserCanNotWriteRelative' => false,
|
|
'SupportsLocks' => true,
|
|
'UserFriendlyName' => sprintf('User %s', $user->getUsername()),
|
|
'UserExtraInfo' => [],
|
|
'LastModifiedTime' => date('Y-m-d\TH:i:s.u\Z', $storedObject->getCreationDate()->getTimestamp()),
|
|
'CloseButtonClosesWindow' => true,
|
|
'EnableInsertRemoteImage' => true,
|
|
'EnableShare' => true,
|
|
'SupportsUpdate' => true,
|
|
'SupportsRename' => true,
|
|
'DisablePrint' => false,
|
|
'DisableExport' => false,
|
|
'DisableCopy' => false,
|
|
]
|
|
)));
|
|
}
|
|
|
|
public function deleteFile(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
|
{
|
|
return $this->getDebugResponse(__FUNCTION__, $request);
|
|
}
|
|
|
|
public function enumerateAncestors(
|
|
string $fileId,
|
|
?string $accessToken,
|
|
RequestInterface $request
|
|
): ResponseInterface {
|
|
return $this->getDebugResponse(__FUNCTION__, $request);
|
|
}
|
|
|
|
public function getFile(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
|
{
|
|
$storedObject = $this->storedObjectRepository->findOneBy(['filename' => $fileId]);
|
|
|
|
// TODO: Add strict typing in champs-libres/async-uploader-bundle
|
|
/** @var StdClass $object */
|
|
$object = $this->tempUrlGeneratorInterface->generate('GET', $storedObject->getFilename());
|
|
|
|
$response = $this->httpClient->sendRequest($this->psr17->createRequest('GET', $object->url));
|
|
|
|
return $this
|
|
->psr17
|
|
->createResponse()
|
|
->withHeader(
|
|
'Content-Type',
|
|
'application/octet-stream',
|
|
)
|
|
->withHeader(
|
|
'Content-Disposition',
|
|
sprintf('attachment; filename=%s', $storedObject->getFilename())
|
|
)
|
|
->withBody($response->getBody());
|
|
}
|
|
|
|
public function getLock(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
|
{
|
|
$lockFilepath = $this->getLockFilepath($fileId);
|
|
|
|
$lock = '';
|
|
|
|
if ($this->fs->exists($lockFilepath)) {
|
|
$lockData = json_decode(file_get_contents($lockFilepath));
|
|
$lock = $lockData->lock;
|
|
}
|
|
|
|
return $this
|
|
->psr17
|
|
->createResponse()
|
|
->withHeader('X-WOPI-Lock', $lock);
|
|
}
|
|
|
|
public function getShareUrl(
|
|
string $fileId,
|
|
?string $accessToken,
|
|
RequestInterface $request
|
|
): ResponseInterface {
|
|
$data = [
|
|
'ShareUrl' => 'TODO',
|
|
];
|
|
|
|
return $this
|
|
->psr17
|
|
->createResponse()
|
|
->withHeader('Content-Type', 'application/json')
|
|
->withBody($this->psr17->createStream((string) json_encode($data)));
|
|
}
|
|
|
|
public function lock(
|
|
string $fileId,
|
|
?string $accessToken,
|
|
string $xWopiLock,
|
|
RequestInterface $request
|
|
): ResponseInterface {
|
|
$lockFilepath = $this->getLockFilepath($fileId);
|
|
|
|
if ($this->fs->exists($lockFilepath)) {
|
|
$previousLockData = json_decode(file_get_contents($lockFilepath));
|
|
|
|
if ($previousLockData->lock !== $xWopiLock) {
|
|
return $this
|
|
->psr17
|
|
->createResponse(409)
|
|
->withAddedHeader('X-WOPI-Lock', $previousLockData->lock);
|
|
}
|
|
}
|
|
|
|
$data = [
|
|
'lock' => $xWopiLock,
|
|
'time' => microtime(),
|
|
];
|
|
|
|
$this->fs->dumpFile($lockFilepath, json_encode($data));
|
|
|
|
return $this
|
|
->psr17
|
|
->createResponse();
|
|
}
|
|
|
|
public function putFile(
|
|
string $fileId,
|
|
?string $accessToken,
|
|
string $xWopiLock,
|
|
string $xWopiEditors,
|
|
RequestInterface $request
|
|
): ResponseInterface {
|
|
$storedObject = $this->storedObjectRepository->findOneBy(['filename' => $fileId]);
|
|
|
|
// TODO: Add strict typing in champs-libres/async-uploader-bundle
|
|
/** @var StdClass $object */
|
|
$object = $this->tempUrlGeneratorInterface->generate('PUT', $storedObject->getFilename());
|
|
|
|
$response = $this->httpClient->sendRequest($this->psr17->createRequest('PUT', $object->url)->withBody($request->getBody()));
|
|
|
|
if (200 !== $response->getStatusCode())
|
|
{
|
|
throw new Exception('Error in response.');
|
|
}
|
|
|
|
return $this
|
|
->psr17
|
|
->createResponse()
|
|
->withHeader('Content-Type', 'application/json')
|
|
->withAddedHeader('X-WOPI-Lock', $xWopiLock)
|
|
->withBody($this->psr17->createStream((string) json_encode([])));
|
|
}
|
|
|
|
public function putRelativeFile(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
|
{
|
|
$filepath = sprintf(
|
|
'%s/%s',
|
|
$this->filesRepository,
|
|
$request->getHeaderLine('X-WOPI-SuggestedTarget')
|
|
);
|
|
|
|
$return = file_put_contents(
|
|
$filepath,
|
|
(string) $request->getBody()
|
|
);
|
|
|
|
if (false === $return) {
|
|
return $this
|
|
->psr17
|
|
->createResponse(500);
|
|
}
|
|
|
|
return $this
|
|
->psr17
|
|
->createResponse(500); // Not supported yet.
|
|
}
|
|
|
|
public function putUserInfo(string $fileId, ?string $accessToken, RequestInterface $request): ResponseInterface
|
|
{
|
|
return $this->getDebugResponse(__FUNCTION__, $request);
|
|
}
|
|
|
|
public function refreshLock(
|
|
string $fileId,
|
|
?string $accessToken,
|
|
string $xWopiLock,
|
|
RequestInterface $request
|
|
): ResponseInterface {
|
|
return $this->getDebugResponse(__FUNCTION__, $request);
|
|
}
|
|
|
|
public function renameFile(
|
|
string $fileId,
|
|
?string $accessToken,
|
|
string $xWopiLock,
|
|
string $xWopiRequestedName,
|
|
RequestInterface $request
|
|
): ResponseInterface {
|
|
return $this->getDebugResponse(__FUNCTION__, $request);
|
|
}
|
|
|
|
public function unlock(
|
|
string $fileId,
|
|
?string $accessToken,
|
|
string $xWopiLock,
|
|
RequestInterface $request
|
|
): ResponseInterface {
|
|
$lockFilepath = $this->getLockFilepath($fileId);
|
|
|
|
if ($this->fs->exists($lockFilepath)) {
|
|
$previousLockData = json_decode(file_get_contents($lockFilepath));
|
|
|
|
if ($previousLockData->lock !== $xWopiLock) {
|
|
return $this
|
|
->psr17
|
|
->createResponse(409)
|
|
->withAddedHeader('X-WOPI-Lock', $previousLockData->lock);
|
|
}
|
|
}
|
|
|
|
$this->fs->remove($lockFilepath);
|
|
|
|
return $this
|
|
->psr17
|
|
->createResponse()
|
|
->withAddedHeader('X-WOPI-Lock', '');
|
|
}
|
|
|
|
public function unlockAndRelock(
|
|
string $fileId,
|
|
?string $accessToken,
|
|
string $xWopiLock,
|
|
string $xWopiOldLock,
|
|
RequestInterface $request
|
|
): ResponseInterface {
|
|
return $this->getDebugResponse(__FUNCTION__, $request);
|
|
}
|
|
|
|
private function getDebugResponse(string $method, RequestInterface $request): ResponseInterface
|
|
{
|
|
$params = [];
|
|
parse_str($request->getUri()->getQuery(), $params);
|
|
|
|
$data = (string) json_encode(array_merge(
|
|
['method' => $method],
|
|
$params,
|
|
$request->getHeaders()
|
|
));
|
|
|
|
return $this
|
|
->psr17
|
|
->createResponse()
|
|
->withHeader('content', 'application/json')
|
|
->withBody($this->psr17->createStream($data));
|
|
}
|
|
|
|
private function getLockFilepath(string $fileId): string
|
|
{
|
|
return sprintf(
|
|
'%s/%s.lock',
|
|
$this->filesRepository,
|
|
$fileId
|
|
);
|
|
}
|
|
}
|