Completed
Push — master ( 4703e1...97bad7 )
by
unknown
11:22
created

FileManager::buildFileDocument()   C

Complexity

Conditions 14
Paths 72

Size

Total Lines 42
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 210

Importance

Changes 0
Metric Value
dl 0
loc 42
ccs 0
cts 33
cp 0
rs 5.0864
c 0
b 0
f 0
cc 14
eloc 26
nc 72
nop 3
crap 210

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 = $metadata->getHash() ? $metadata->getHash() :
221
                hash('sha256', file_get_contents($file->getRealPath()));
222
            $metadata->setHash($hash);
223
            $metadata->setMime($file->getMimeType());
224
            $metadata->setSize($file->getSize());
225
            if (!$metadata->getFilename()) {
226
                $fileName = $file->getClientOriginalName() ? $file->getClientOriginalName() : $file->getFilename();
227
                $fileName = preg_replace("/[^a-zA-Z0-9.]/", "-", $fileName);
228
                $metadata->setFilename($fileName);
229
            }
230
        } elseif ($original && ($originalMetadata = $original->getMetadata())) {
231
            if (!$metadata->getFilename()) {
232
                $metadata->setFilename($originalMetadata->getFilename());
233
            }
234
            $metadata->setHash($originalMetadata->getHash());
235
            $metadata->setMime($originalMetadata->getMime());
236
            $metadata->setSize($originalMetadata->getSize());
237
        }
238
239
        if (!$original || !$metadata->getCreatedate()) {
240
            $metadata->setCreatedate($now);
241
        }
242
        $metadata->setModificationdate($now);
243
244
        $document->setMetadata($metadata);
245
246
        return $document;
247
    }
248
249
    /**
250
     * Simple validation for post/put request
251
     *
252
     * @param DocumentFile $document  File document
253
     * @param string       $requestId Request ID
254
     * @return bool
255
     */
256
    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...
257
    {
258
        if (!$requestId && !$document->getId()) {
259
            return true;
260
        }
261
        if ($requestId === $document->getId()) {
262
            return true;
263
        }
264
        return false;
265
    }
266
267
    /**
268
     * Simple delete item from file system
269
     *
270
     * @param string $id ID of file to be deleted
271
     *
272
     * @return void
273
     */
274
    public function remove($id)
275
    {
276
        if ($this->fileSystem->has($id)) {
277
            $this->fileSystem->delete($id);
278
        }
279
    }
280
281
    /**
282
     * Set global uploaded file.
283
     * Only ONE file allowed per upload.
284
     *
285
     * @param Request $request service request
286
     * @return UploadedFile if file was uploaded
287
     * @throws InvalidArgumentException
288
     */
289
    private function getUploadedFileFromRequest(Request $request)
290
    {
291
        $file = false;
292
293
        if ($request->files instanceof FileBag && $request->files->count() > 0) {
294
            if ($request->files->count() > 1) {
295
                throw new InvalidArgumentException('Only 1 file upload per requests allowed.');
296
            }
297
            $files = $request->files->all();
298
            $file = reset($files);
299
            if ($this->allowedMimeTypes && !in_array($file->getMimeType(), $this->allowedMimeTypes)) {
300
                throw new InvalidArgumentException('File mime type: '.$file->getMimeType().' is not allowed.');
301
            }
302
        }
303
304
        return $file;
305
    }
306
}
307