DMSUploadField::upload()   F
last analyzed

Complexity

Conditions 24
Paths 1220

Size

Total Lines 107

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 107
rs 0
c 0
b 0
f 0
cc 24
nc 1220
nop 1

How to fix   Long Method    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
/**
4
 * Field for uploading files into a DMSDocument. Replacing the existing file.
5
 * Not ideally suited for the purpose, as the base implementation
6
 * assumes to operate on a {@link File} record. We only use this as
7
 * a temporary container, which gets deleted as soon as the actual
8
 * {@link DMSDocument} is created.
9
 *
10
 * <b>NOTE: this Field will call write() on the supplied record</b>
11
 *
12
 * @author Julian Seidenberg
13
 * @package dms
14
 */
15
class DMSUploadField extends UploadField
16
{
17
    private static $allowed_actions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
18
        "upload",
19
    );
20
21
    /**
22
     * The temporary folder name to store files in during upload
23
     * @var string
24
     */
25
    protected $folderName = 'DMSTemporaryUploads';
26
27
    /**
28
     * Override the default behaviour of the UploadField and take the uploaded file (uploaded to assets) and
29
     * add it into the DMS storage, deleting the old/uploaded file.
30
     * @param File
31
     */
32
    protected function attachFile($file)
33
    {
34
        $dms = DMS::inst();
35
        $record = $this->getRecord();
36
37
        if ($record instanceof DMSDocument) {
38
            // If the edited record is a document,
39
            // assume we're replacing an existing file
40
            $doc = $record;
41
            $doc->ingestFile($file);
42
        } else {
43
            // Otherwise create it
44
            $doc = $dms->storeDocument($file);
45
            $file->delete();
46
        }
47
48
        // Relate to the underlying document set being edited.
49
        // Not applicable when editing the document itself and replacing it, or uploading from the ModelAdmin
50
        if ($record instanceof DMSDocumentSet) {
51
            $record->Documents()->add($doc, array('ManuallyAdded' => 1));
0 ignored issues
show
Bug introduced by
The method Documents() does not exist on DMSDocumentSet. Did you maybe mean getDocuments()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
52
        }
53
54
        return $doc;
55
    }
56
57
    public function validate($validator)
58
    {
59
        return true;
60
    }
61
62
    /**
63
     * Action to handle upload of a single file
64
     *
65
     * @param SS_HTTPRequest $request
66
     * @return string json
0 ignored issues
show
Documentation introduced by
Should the return type not be SS_HTTPResponse?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
67
     */
68
    public function upload(SS_HTTPRequest $request)
69
    {
70
        if ($recordId = $request->postVar('ID')) {
71
            $this->setRecord(DMSDocumentSet::get()->byId($recordId));
0 ignored issues
show
Bug introduced by
It seems like \DMSDocumentSet::get()->byId($recordId) can be null; however, setRecord() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
72
        }
73
74
        if ($this->isDisabled() || $this->isReadonly()) {
75
            return $this->httpError(403);
76
        }
77
78
        // Protect against CSRF on destructive action
79
        $token = $this->getForm()->getSecurityToken();
80
        if (!$token->checkRequest($request)) {
81
            return $this->httpError(400);
82
        }
83
84
        $name = $this->getName();
85
        $tmpfile = $request->postVar($name);
86
        $record = $this->getRecord();
87
88
        // Check if the file has been uploaded into the temporary storage.
89
        if (!$tmpfile) {
90
            $return = array('error' => _t('UploadField.FIELDNOTSET', 'File information not found'));
91
        } else {
92
            $return = array(
93
                'name' => $tmpfile['name'],
94
                'size' => $tmpfile['size'],
95
                'type' => $tmpfile['type'],
96
                'error' => $tmpfile['error']
97
            );
98
        }
99
100
        // Check for constraints on the record to which the file will be attached.
101
        if (!$return['error'] && $this->relationAutoSetting && $record && $record->exists()) {
102
            $tooManyFiles = false;
103
            // Some relationships allow many files to be attached.
104
            if ($this->getConfig('allowedMaxFileNumber') && ($record->hasMany($name) || $record->manyMany($name))) {
105
                if (!$record->isInDB()) {
106
                    $record->write();
107
                }
108
                $tooManyFiles = $record->{$name}()->count() >= $this->getConfig('allowedMaxFileNumber');
109
            // has_one only allows one file at any given time.
110
            } elseif ($record->hasOne($name)) {
111
                $tooManyFiles = $record->{$name}() && $record->{$name}()->exists();
112
            }
113
114
            // Report the constraint violation.
115
            if ($tooManyFiles) {
116
                if (!$this->getConfig('allowedMaxFileNumber')) {
117
                    $this->setConfig('allowedMaxFileNumber', 1);
118
                }
119
                $return['error'] = _t(
120
                    'UploadField.MAXNUMBEROFFILES',
121
                    'Max number of {count} file(s) exceeded.',
122
                    array('count' => $this->getConfig('allowedMaxFileNumber'))
0 ignored issues
show
Documentation introduced by
array('count' => $this->...allowedMaxFileNumber')) is of type array<string,*,{"count":"*"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
123
                );
124
            }
125
        }
126
127
        // Process the uploaded file
128
        if (!$return['error']) {
129
            $fileObject = null;
130
131
            if ($this->relationAutoSetting) {
132
                // Search for relations that can hold the uploaded files.
133
                if ($relationClass = $this->getRelationAutosetClass()) {
134
                    // Create new object explicitly. Otherwise rely on Upload::load to choose the class.
135
                    $fileObject = SS_Object::create($relationClass);
136
                }
137
            }
138
139
            // Get the uploaded file into a new file object.
140
            try {
141
                $this->upload->loadIntoFile($tmpfile, $fileObject, $this->getFolderName());
0 ignored issues
show
Documentation introduced by
$fileObject is of type this<DMSUploadField>|null, but the function expects a object<File>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->getFolderName() is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
142
            } catch (Exception $e) {
143
                // we shouldn't get an error here, but just in case
144
                $return['error'] = $e->getMessage();
145
            }
146
147
            if (!$return['error']) {
148
                if ($this->upload->isError()) {
149
                    $return['error'] = implode(' ' . PHP_EOL, $this->upload->getErrors());
150
                } else {
151
                    $file = $this->upload->getFile();
152
153
                    // CUSTOM Attach the file to the related record.
154
                    $document = $this->attachFile($file);
155
156
                    // Collect all output data.
157
                    $return = array_merge($return, array(
158
                        'id' => $document->ID,
159
                        'name' => $document->getTitle(),
160
                        'thumbnail_url' => $document->Icon($document->getExtension()),
161
                        'edit_url' => $this->getItemHandler($document->ID)->EditLink(),
162
                        'size' => $document->getFileSizeFormatted(),
163
                        'buttons' => (string) $document->renderWith($this->getTemplateFileButtons()),
164
                        'showeditform' => true
165
                    ));
166
167
                    // CUSTOM END
168
                }
169
            }
170
        }
171
        $response = new SS_HTTPResponse(Convert::raw2json(array($return)));
172
        $response->addHeader('Content-Type', 'text/plain');
173
        return $response;
174
    }
175
176
177
    /**
178
     * Never directly display items uploaded
179
     * @return SS_List
180
     */
181
    public function getItems()
182
    {
183
        return new ArrayList();
184
    }
185
186
    public function Field($properties = array())
187
    {
188
        $fields = parent::Field($properties);
189
190
        // Replace the download template with a new one only when access the upload field through a GridField.
191
        // Needs to be enabled through setConfig('downloadTemplateName', 'ss-dmsuploadfield-downloadtemplate');
192
        Requirements::javascript(DMS_DIR . '/javascript/DMSUploadField_downloadtemplate.js');
193
194
        // In the add dialog, add the addtemplate into the set of file that load.
195
        Requirements::javascript(DMS_DIR . '/javascript/DMSUploadField_addtemplate.js');
196
197
        return $fields;
198
    }
199
200
    /**
201
     * @param int $itemID
202
     * @return UploadField_ItemHandler
203
     */
204
    public function getItemHandler($itemID)
205
    {
206
        return DMSUploadField_ItemHandler::create($this, $itemID);
207
    }
208
209
210
    /**
211
     * FieldList $fields for the EditForm
212
     * @example 'getCMSFields'
213
     *
214
     * @param File $file File context to generate fields for
215
     * @return FieldList List of form fields
216
     */
217
    public function getDMSFileEditFields($file)
218
    {
219
220
        // Empty actions, generate default
221
        if (empty($this->fileEditFields)) {
222
            $fields = $file->getCMSFields();
223
            // Only display main tab, to avoid overly complex interface
224
            if ($fields->hasTabSet() && ($mainTab = $fields->findOrMakeTab('Root.Main'))) {
225
                $fields = $mainTab->Fields();
226
            }
227
            return $fields;
228
        }
229
230
        // Fields instance
231
        if ($this->fileEditFields instanceof FieldList) {
232
            return $this->fileEditFields;
233
        }
234
235
        // Method to call on the given file
236
        if ($file->hasMethod($this->fileEditFields)) {
237
            return $file->{$this->fileEditFields}();
238
        }
239
240
        user_error("Invalid value for UploadField::fileEditFields", E_USER_ERROR);
241
    }
242
243
    /**
244
     * FieldList $actions or string $name (of a method on File to provide a actions) for the EditForm
245
     * @example 'getCMSActions'
246
     *
247
     * @param File $file File context to generate form actions for
248
     * @return FieldList Field list containing FormAction
249
     */
250
    public function getDMSFileEditActions($file)
251
    {
252
253
        // Empty actions, generate default
254
        if (empty($this->fileEditActions)) {
255
            $actions = new FieldList($saveAction = new FormAction('doEdit', _t('UploadField.DOEDIT', 'Save')));
256
            $saveAction->addExtraClass('ss-ui-action-constructive icon-accept');
257
            return $actions;
258
        }
259
260
        // Actions instance
261
        if ($this->fileEditActions instanceof FieldList) {
262
            return $this->fileEditActions;
263
        }
264
265
        // Method to call on the given file
266
        if ($file->hasMethod($this->fileEditActions)) {
267
            return $file->{$this->fileEditActions}();
268
        }
269
270
        user_error("Invalid value for UploadField::fileEditActions", E_USER_ERROR);
271
    }
272
273
    /**
274
     * Determines the validator to use for the edit form
275
     * @example 'getCMSValidator'
276
     *
277
     * @param File $file File context to generate validator from
278
     * @return Validator Validator object
279
     */
280
    public function getDMSFileEditValidator($file)
281
    {
282
        // Empty validator
283
        if (empty($this->fileEditValidator)) {
284
            return null;
285
        }
286
287
        // Validator instance
288
        if ($this->fileEditValidator instanceof Validator) {
289
            return $this->fileEditValidator;
290
        }
291
292
        // Method to call on the given file
293
        if ($file->hasMethod($this->fileEditValidator)) {
294
            return $file->{$this->fileEditValidator}();
295
        }
296
297
        user_error("Invalid value for UploadField::fileEditValidator", E_USER_ERROR);
298
    }
299
300
    /**
301
     * Set the folder name to store DMS files in
302
     *
303
     * @param  string $folderName
304
     * @return $this
305
     */
306
    public function setFolderName($folderName)
307
    {
308
        $this->folderName = (string) $folderName;
309
        return $this;
310
    }
311
312
    /**
313
     * Get the folder name for storing the document
314
     *
315
     * @return string
316
     */
317
    public function getFolderName()
318
    {
319
        return $this->folderName;
320
    }
321
}
322