Completed
Push — feature/keep-file-info ( acc8ce )
by
unknown
20:26 queued 11:05
created

FileManager::buildFileDocument()   C

Complexity

Conditions 13
Paths 48

Size

Total Lines 41
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 0
Metric Value
dl 0
loc 41
ccs 0
cts 32
cp 0
rs 5.1234
c 0
b 0
f 0
cc 13
eloc 25
nc 48
nop 3
crap 182

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Handles file specific actions
4
 */
5
6
namespace Graviton\FileBundle\Manager;
7
8
use Doctrine\ODM\MongoDB\Id\UuidGenerator;
9
use Gaufrette\File as GaufretteFile;
10
use Gaufrette\Filesystem;
11
use GravitonDyn\FileBundle\Document\File as FileDocument;
12
use GravitonDyn\FileBundle\Document\FileMetadataBase;
13
use GravitonDyn\FileBundle\Document\FileMetadataEmbedded;
14
use Symfony\Component\Form\Exception\InvalidArgumentException;
15
use Symfony\Component\HttpFoundation\Request;
16
use Symfony\Component\HttpFoundation\Response;
17
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
18
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
19
use Symfony\Component\HttpFoundation\FileBag;
20
use Symfony\Component\HttpFoundation\File\UploadedFile;
21
use GravitonDyn\FileBundle\Document\File as DocumentFile;
22
use Doctrine\Bundle\MongoDBBundle\ManagerRegistry;
23
use Doctrine\ODM\MongoDB\DocumentManager;
24
use Symfony\Component\Filesystem\Filesystem as SfFileSystem;
25
use GravitonDyn\FileBundle\Model\File as DocumentModel;
26
27
/**
28
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
29
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
30
 * @link     http://swisscom.ch
31
 */
32
class FileManager
33
{
34
    /**
35
     * @var Filesystem
36
     */
37
    private $fileSystem;
38
39
    /**
40
     * @var DocumentManager
41
     */
42
    private $documentManager;
43
44
    /** @var array allowedMimeTypes Control files to be saved and returned */
45
    private $allowedMimeTypes = [];
46
47
    /**
48
     * FileManager constructor.
49
     *
50
     * @param Filesystem      $fileSystem      file system abstraction layer for s3 and more
51
     * @param ManagerRegistry $managerRegistry MongoDB registry manager
52
     */
53
    public function __construct(
54
        Filesystem $fileSystem,
55
        ManagerRegistry $managerRegistry
56
    ) {
57
        $this->fileSystem = $fileSystem;
58
        $this->documentManager = $managerRegistry->getManager();
59
    }
60
61
    /**
62
     * Configure allowed content types, empty is equal to all
63
     *
64
     * @param array $mimeTypes of Allowed types, application/pdf, image/jpeg...
65
     *
66
     * @return void
67
     */
68
    public function setAllowedMimeTypes(array $mimeTypes)
69
    {
70
        $this->allowedMimeTypes = $mimeTypes;
71
    }
72
73
    /**
74
     * Will update the response object with provided file data
75
     *
76
     * @param Response     $response To building the response on
77
     * @param DocumentFile $file     File document object from DB
78
     *
79
     * @return Response
80
     * @throws InvalidArgumentException if invalid info fetched from fileSystem
81
     */
82
    public function buildGetContentResponse(Response $response, FileDocument $file)
83
    {
84
        /** @var FileMetadataBase $metadata */
85
        $metadata = $file->getMetadata();
86
        if (!$metadata) {
87
            throw new InvalidArgumentException('Loaded file have no valid metadata');
88
        }
89
90
        // We use file's mimeType, just in case none we use DB's.
91
        $mimeType = $this->fileSystem->mimeType($file->getId());
92
        if (!$mimeType) {
93
            $mimeType = $metadata->getMime();
94
        }
95
        if ($this->allowedMimeTypes && !in_array($mimeType, $this->allowedMimeTypes)) {
96
            throw new InvalidArgumentException('File mime type: '.$mimeType.' is not allowed as response.');
97
        }
98
99
        // Read Data
100
        $file = $this->fileSystem->read($file->getId());
101
102
        // Create Response
103
        $disposition = $response->headers->makeDisposition(
104
            ResponseHeaderBag::DISPOSITION_INLINE,
105
            $metadata->getFilename()
106
        );
107
        $response
108
            ->setStatusCode(Response::HTTP_OK)
109
            ->setContent($file);
110
        $response
111
            ->headers->set('Content-Type', $mimeType);
112
        $response
113
            ->headers->set('Content-Disposition', $disposition);
114
        return $response;
115
    }
116
117
118
    /**
119
     * Save or update a file
120
     *
121
     * @param string $id   ID of file
122
     * @param String $data content to save
123
     *
124
     * @return GaufretteFile
125
     *
126
     * @throws BadRequestHttpException
127
     */
128
    public function saveFile($id, $data)
129
    {
130
        if (is_resource($data)) {
131
            throw new BadRequestHttpException('/file does not support storing resources');
132
        }
133
        $file = new GaufretteFile($id, $this->fileSystem);
134
        $file->setContent($data);
135
136
        return $file;
137
    }
138
139
    /**
140
     * @param DocumentFile  $document File Document
141
     * @param Request       $request  Request bag
142
     * @param DocumentModel $model    File Document Model
143
     * @return DocumentFile
144
     */
145
    public function handleSaveRequest(
146
        FileDocument $document,
147
        Request $request,
148
        DocumentModel $model
149
    ) {
150
        $file = $this->getUploadedFileFromRequest($request);
151
        $requestId = $request->get('id', '');
152
        if ($requestId && !$document->getId()) {
153
            $document->setId($requestId);
154
        }
155
156
        $original = $model->find($requestId);
157
        $isNew = $requestId ? !$original : true;
158
159
        // If posted  file document not equal the one to be created or updated, then error
160
        if (!$this->validIdRequest($document, $requestId)) {
161
            throw new InvalidArgumentException('File id and Request id must match.');
162
        }
163
164
        $document = $this->buildFileDocument($document, $file, $original);
165
        if (!$document->getId()) {
166
            $n = new UuidGenerator();
167
            $uuid = (string) $n->generateV4();
168
            $document->setId($uuid);
169
        }
170
171
        // All ok, let's save the file
172
        if ($isNew) {
173
            if (!$file || $file->getSize() == 0) {
174
                throw new InvalidArgumentException('You can not create a new empty file resource. No file received.');
175
            }
176
        }
177
178
        if ($file) {
179
            $this->saveFile($document->getId(), file_get_contents($file->getRealPath()));
180
            $sfFileSys = new SfFileSystem();
181
            $sfFileSys->remove($file->getRealPath());
182
        }
183
184
        if ($isNew) {
185
            $model->insertRecord($document);
186
        } else {
187
            $model->updateRecord($document->getId(), $document);
188
        }
189
190
        // store id of new record so we don't need to re-parse body later when needed
191
        $request->attributes->set('id', $document->getId());
192
193
        return $document;
194
    }
195
196
    /**
197
     * Create the basic needs for a file
198
     *
199
     * @param DocumentFile $document Post or Put file document
200
     * @param UploadedFile $file     To be used in set metadata
201
     * @param DocumentFile $original If there is a original document
202
     *
203
     * @return DocumentFile
204
     * @throws InvalidArgumentException
205
     */
206
    private function buildFileDocument(FileDocument $document, $file, $original)
207
    {
208
        $now = new \DateTime();
209
210
        // If only a file is posted, check if there is a original object and clone it
211
        if ($file && $original && !$document->getMetadata()) {
212
            $document = clone $original;
213
        }
214
215
        // Basic Metadata update
216
        $metadata = $document->getMetadata() ?: new FileMetadataEmbedded();
217
218
        // File related, if no file uploaded we keep original file info.
219
        if ($file) {
220
            $hash = hash('sha256', file_get_contents($file->getRealPath()));
221
            $metadata->setHash($hash);
222
            $metadata->setMime($file->getMimeType());
223
            $metadata->setSize($file->getSize());
224
            if (!$metadata->getFilename()) {
225
                $fileName = $file->getClientOriginalName() ? $file->getClientOriginalName() : $file->getFilename();
226
                $fileName = preg_replace("/[^a-zA-Z0-9.]/", "-", $fileName);
227
                $metadata->setFilename($fileName);
228
            }
229
        } elseif ($original && ($originalMetadata = $original->getMetadata())) {
230
            if (!$metadata->getFilename()) {
231
                $metadata->setFilename($originalMetadata->getFilename());
232
            }
233
            $metadata->setHash($originalMetadata->getHash());
234
            $metadata->setMime($originalMetadata->getMime());
235
            $metadata->setSize($originalMetadata->getSize());
236
        }
237
238
        if (!$original || !$metadata->getCreatedate()) {
239
            $metadata->setCreatedate($now);
240
        }
241
        $metadata->setModificationdate($now);
242
243
        $document->setMetadata($metadata);
244
245
        return $document;
246
    }
247
248
    /**
249
     * Simple validation for post/put request
250
     *
251
     * @param DocumentFile $document  File document
252
     * @param string       $requestId Request ID
253
     * @return bool
254
     */
255
    private function validIdRequest(FileDocument $document, $requestId)
0 ignored issues
show
Coding Style introduced by
function validIdRequest() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
256
    {
257
        if (!$requestId && !$document->getId()) {
258
            return true;
259
        }
260
        if ($requestId === $document->getId()) {
261
            return true;
262
        }
263
        return false;
264
    }
265
266
    /**
267
     * Simple delete item from file system
268
     *
269
     * @param string $id ID of file to be deleted
270
     *
271
     * @return void
272
     */
273
    public function remove($id)
274
    {
275
        if ($this->fileSystem->has($id)) {
276
            $this->fileSystem->delete($id);
277
        }
278
    }
279
280
    /**
281
     * Set global uploaded file.
282
     * Only ONE file allowed per upload.
283
     *
284
     * @param Request $request service request
285
     * @return UploadedFile if file was uploaded
286
     * @throws InvalidArgumentException
287
     */
288
    private function getUploadedFileFromRequest(Request $request)
289
    {
290
        $file = false;
291
292
        if ($request->files instanceof FileBag && $request->files->count() > 0) {
293
            if ($request->files->count() > 1) {
294
                throw new InvalidArgumentException('Only 1 file upload per requests allowed.');
295
            }
296
            $files = $request->files->all();
297
            $file = reset($files);
298
            if ($this->allowedMimeTypes && !in_array($file->getMimeType(), $this->allowedMimeTypes)) {
299
                throw new InvalidArgumentException('File mime type: '.$file->getMimeType().' is not allowed.');
300
            }
301
        }
302
303
        return $file;
304
    }
305
}
306