Passed
Pull Request — master (#34)
by
unknown
25:42 queued 10:38
created

PutFile   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 94
c 1
b 0
f 0
dl 0
loc 175
rs 10
wmc 12

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
C __invoke() 0 144 11
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ChampsLibres\WopiBundle\Service\Wopi;
6
7
use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface;
8
use ChampsLibres\WopiLib\Contract\Service\WopiInterface;
9
use DateTimeImmutable;
10
use DateTimeInterface;
11
use Psr\Http\Message\RequestInterface;
12
use Psr\Http\Message\ResponseFactoryInterface;
13
use Psr\Http\Message\ResponseInterface;
14
use Psr\Http\Message\StreamFactoryInterface;
15
use Psr\Log\LoggerInterface;
16
use RuntimeException;
17
18
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
19
20
use function strlen;
21
22
class PutFile
23
{
24
    private const LOG_PREFIX = '[wopi][wopi/PutFile] ';
25
26
    private DocumentManagerInterface $documentManager;
27
28
    private LoggerInterface $logger;
29
30
    private ResponseFactoryInterface $responseFactory;
31
32
    private StreamFactoryInterface $streamFactory;
33
34
    /**
35
     * @var 'version'|'timestamp'
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'version'|'timestamp' at position 0 could not be parsed: Unknown type name ''version'' at position 0 in 'version'|'timestamp'.
Loading history...
36
     */
37
    private string $versionManagement;
38
39
    public function __construct(
40
        DocumentManagerInterface $documentManager,
41
        LoggerInterface $logger,
42
        ParameterBagInterface $parameterBag,
43
        ResponseFactoryInterface $responseFactory,
44
        StreamFactoryInterface $streamFactory
45
    ) {
46
        $this->documentManager = $documentManager;
47
        $this->logger = $logger;
48
        $this->responseFactory = $responseFactory;
49
        $this->streamFactory = $streamFactory;
50
        $this->versionManagement = $parameterBag->get('wopi')['version_management'];
51
    }
52
53
    public function __invoke(
54
        string $fileId,
55
        string $accessToken,
56
        string $xWopiLock,
57
        string $xWopiEditors,
58
        RequestInterface $request
59
    ): ResponseInterface {
60
        $this->logger->debug(self::LOG_PREFIX . 'put file', ['fileId' => $fileId]);
61
        $document = $this->documentManager->findByDocumentId($fileId);
62
63
        if (null === $document) {
64
            return $this->responseFactory->createResponse(404)
65
                ->withBody($this->streamFactory->createStream((string) json_encode([
66
                    'message' => "Document with id {$fileId} not found",
67
                ])));
68
        }
69
70
        $version = $this->documentManager->getVersion($document);
71
72
        // File is unlocked
73
        if (false === $this->documentManager->hasLock($document)) {
74
            if (0 !== $this->documentManager->getSize($document)) {
75
                $this->logger->error(self::LOG_PREFIX . 'file unlocked', ['fileId' => $fileId]);
76
77
                return $this
78
                    ->responseFactory
79
                    ->createResponse(409)
80
                    ->withHeader(
81
                        WopiInterface::HEADER_ITEM_VERSION,
82
                        sprintf('v%s', $version)
83
                    );
84
            }
85
        }
86
87
        // File is locked
88
        if (
89
            $this->documentManager->hasLock($document)
90
            && $xWopiLock !== $currentLock = $this->documentManager->getLock($document)
91
        ) {
92
            $this->logger->error(self::LOG_PREFIX . 'file locked and lock does not match', ['fileId' => $fileId]);
93
94
            return $this
95
                ->responseFactory
96
                ->createResponse(409)
97
                ->withHeader(
98
                    WopiInterface::HEADER_LOCK,
99
                    $currentLock
100
                )
101
                ->withHeader(
102
                    WopiInterface::HEADER_ITEM_VERSION,
103
                    sprintf('v%s', $version)
104
                )
105
                ->withBody(
106
                    $this->streamFactory->createStream((string) json_encode([
107
                        'message' => 'File locked',
108
                    ]))
109
                );
110
        }
111
112
        // for collabora online editor, check timestamp if present
113
        if ($request->hasHeader('x-lool-wopi-timestamp')) {
114
            $date = DateTimeImmutable::createFromFormat(
115
                DateTimeImmutable::ATOM,
116
                $request->getHeader('x-lool-wopi-timestamp')[0]
117
            );
118
119
            if (false === $date) {
120
                $errors = DateTimeImmutable::getLastErrors();
121
122
                if (false === $errors) {
123
                    throw new RuntimeException('Could not find error on DateTimeImmutable parsing');
124
                }
125
126
                $e = array_merge($errors['warnings'], $errors['errors']);
127
128
                $this->logger->error(self::LOG_PREFIX . 'Error parsing date', ['fileId' => $fileId,
129
                    'date' => $request->getHeader('x-lool-wopi-timestamp')[0], 'errors' => $e]);
130
131
                throw new RuntimeException('Error parsing date: ' . implode(', ', array_values($e)));
132
            }
133
134
            if ($this->documentManager->getLastModifiedDate($document) > $date) {
135
                $this->logger->error(self::LOG_PREFIX . 'File has more recent modified date', ['fileId' => $fileId]);
136
137
                return $this
138
                    ->responseFactory
139
                    ->createResponse(409)
140
                    ->withHeader(
141
                        WopiInterface::HEADER_LOCK,
142
                        $currentLock
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $currentLock does not seem to be defined for all execution paths leading up to this point.
Loading history...
143
                    )
144
                    ->withHeader(
145
                        WopiInterface::HEADER_ITEM_VERSION,
146
                        sprintf('v%s', $version)
147
                    )
148
                    ->withBody(
149
                        $this->streamFactory->createStream(
150
                            (string) json_encode(
151
                                [
152
                                    'LOOLStatusCode' => 1010,
153
                                    'COOLStatusCode' => 1010,
154
                                ]
155
                            )
156
                        )
157
                    );
158
            }
159
        }
160
161
        $body = (string) $request->getBody();
162
        $this->documentManager->write(
163
            $document,
164
            [
165
                'content' => $body,
166
                'size' => (string) strlen($body),
167
            ]
168
        );
169
        $version = $this->documentManager->getVersion($document);
170
171
        $response = $this
172
            ->responseFactory
173
            ->createResponse()
174
            ->withHeader(
175
                WopiInterface::HEADER_LOCK,
176
                $xWopiLock
177
            )
178
            ->withHeader(
179
                WopiInterface::HEADER_ITEM_VERSION,
180
                sprintf('v%s', $version)
181
            );
182
183
        if ('timestamp' === $this->versionManagement) {
184
            return $response
185
                ->withBody(
186
                    $this->streamFactory->createStream(
187
                        (string) json_encode([
188
                            'LastModifiedTime' => $this->documentManager->getLastModifiedDate($document)->format(DateTimeInterface::ATOM),
189
                        ])
190
                    )
191
                );
192
        }
193
194
        $this->logger->info(self::LOG_PREFIX . 'file saved', ['fileId' => $fileId]);
195
196
        return $response;
197
    }
198
}
199