Completed
Push — feature/EVO-8294-fileUpload ( f76403 )
by
unknown
65:32
created

FileManager::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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