GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 0e9349...d2bdfd )
by Nicolas
08:54 queued 04:10
created

FieldUpload::validateFilename()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 29
Code Lines 14

Duplication

Lines 14
Ratio 48.28 %
Metric Value
dl 14
loc 29
rs 8.439
cc 5
eloc 14
nc 4
nop 2
1
<?php
2
3
/**
4
 * @package toolkit
5
 */
6
7
/**
8
 * A simple Upload field that essentially maps to HTML's `<input type='file '/>`.
9
 */
10
class FieldUpload extends Field implements ExportableField, ImportableField
11
{
12
    protected static $imageMimeTypes = array(
13
        'image/gif',
14
        'image/jpg',
15
        'image/jpeg',
16
        'image/pjpeg',
17
        'image/png',
18
        'image/x-png'
19
    );
20
21 View Code Duplication
    public function __construct()
22
    {
23
        parent::__construct();
24
25
        $this->_name = __('File Upload');
0 ignored issues
show
Bug introduced by
The property _name does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
26
        $this->_required = true;
27
28
        $this->set('location', 'sidebar');
29
        $this->set('required', 'no');
30
    }
31
32
    /*-------------------------------------------------------------------------
33
        Definition:
34
    -------------------------------------------------------------------------*/
35
36
    public function canFilter()
37
    {
38
        return true;
39
    }
40
41
    public function canPrePopulate()
42
    {
43
        return true;
44
    }
45
46
    public function isSortable()
47
    {
48
        return true;
49
    }
50
51
    public function fetchFilterableOperators()
52
    {
53
        return array(
54
            array(
55
                'title' => 'is',
56
                'filter' => ' ',
57
                'help' => __('Find files that are an exact match for the given string.')
58
            ),
59
            array(
60
                'title' => 'contains',
61
                'filter' => 'regexp: ',
62
                'help' => __('Find files that match the given <a href="%s">MySQL regular expressions</a>.', array(
63
                    'http://dev.mysql.com/doc/mysql/en/Regexp.html'
64
                ))
65
            ),
66
            array(
67
                'title' => 'does not contain',
68
                'filter' => 'not-regexp: ',
69
                'help' => __('Find files that do not match the given <a href="%s">MySQL regular expressions</a>.', array(
70
                    'http://dev.mysql.com/doc/mysql/en/Regexp.html'
71
                ))
72
            ),
73
            array(
74
                'title' => 'file type is',
75
                'filter' => 'mimetype: ',
76
                'help' => __('Find files that match the given mimetype.')
77
            ),
78
            array(
79
                'title' => 'size is',
80
                'filter' => 'size: ',
81
                'help' => __('Find files that match the given size.')
82
            )
83
        );
84
    }
85
86
    /*-------------------------------------------------------------------------
87
        Setup:
88
    -------------------------------------------------------------------------*/
89
90
    public function createTable()
91
    {
92
        return Symphony::Database()->query(
93
            "CREATE TABLE IF NOT EXISTS `tbl_entries_data_" . $this->get('id') . "` (
94
              `id` int(11) unsigned NOT null auto_increment,
95
              `entry_id` int(11) unsigned NOT null,
96
              `file` varchar(255) default null,
97
              `size` int(11) unsigned null,
98
              `mimetype` varchar(100) default null,
99
              `meta` varchar(255) default null,
100
              PRIMARY KEY  (`id`),
101
              UNIQUE KEY `entry_id` (`entry_id`),
102
              KEY `file` (`file`),
103
              KEY `mimetype` (`mimetype`)
104
            ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;"
105
        );
106
    }
107
108
    /*-------------------------------------------------------------------------
109
        Utilities:
110
    -------------------------------------------------------------------------*/
111
112
    public function entryDataCleanup($entry_id, $data = null)
113
    {
114
        $file_location = $this->getFilePath($data['file']);
115
116
        if (is_file($file_location)) {
117
            General::deleteFile($file_location);
118
        }
119
120
        parent::entryDataCleanup($entry_id);
121
122
        return true;
123
    }
124
125
    public static function getMetaInfo($file, $type)
126
    {
127
        $meta = array();
128
129
        if (!file_exists($file) || !is_readable($file)) {
130
            return $meta;
131
        }
132
133
        $meta['creation'] = DateTimeObj::get('c', filemtime($file));
134
135
        if (General::in_iarray($type, fieldUpload::$imageMimeTypes) && $array = @getimagesize($file)) {
136
            $meta['width'] = $array[0];
137
            $meta['height'] = $array[1];
138
        }
139
140
        return $meta;
141
    }
142
143
    public function getFilePath($filename)
144
    {
145
        /**
146
         * Ensure the file exists in the `WORKSPACE` directory
147
         * @link http://getsymphony.com/discuss/issues/view/610/
148
         */
149
        $file = WORKSPACE . preg_replace(array('%/+%', '%(^|/)\.\./%', '%\/workspace\/%'), '/', $this->get('destination') . '/' . $filename);
150
151
        return $file;
152
    }
153
154
    /*-------------------------------------------------------------------------
155
        Settings:
156
    -------------------------------------------------------------------------*/
157
158
    public function displaySettingsPanel(XMLElement &$wrapper, $errors = null)
159
    {
160
        parent::displaySettingsPanel($wrapper, $errors);
161
162
        // Destination Folder
163
        $ignore = array(
164
            '/workspace/events',
165
            '/workspace/data-sources',
166
            '/workspace/text-formatters',
167
            '/workspace/pages',
168
            '/workspace/utilities'
169
        );
170
        $directories = General::listDirStructure(WORKSPACE, null, true, DOCROOT, $ignore);
171
172
        $label = Widget::Label(__('Destination Directory'));
173
174
        $options = array();
175
        $options[] = array('/workspace', false, '/workspace');
176
177
        if (!empty($directories) && is_array($directories)) {
178
            foreach ($directories as $d) {
179
                $d = '/' . trim($d, '/');
180
181
                if (!in_array($d, $ignore)) {
182
                    $options[] = array($d, ($this->get('destination') == $d), $d);
183
                }
184
            }
185
        }
186
187
        $label->appendChild(Widget::Select('fields['.$this->get('sortorder').'][destination]', $options));
188
189 View Code Duplication
        if (isset($errors['destination'])) {
190
            $wrapper->appendChild(Widget::Error($label, $errors['destination']));
191
        } else {
192
            $wrapper->appendChild($label);
193
        }
194
195
        // Validation rule
196
        $this->buildValidationSelect($wrapper, $this->get('validator'), 'fields['.$this->get('sortorder').'][validator]', 'upload', $errors);
197
198
        // Requirements and table display
199
        $this->appendStatusFooter($wrapper);
200
    }
201
202
    public function checkFields(array &$errors, $checkForDuplicates = true)
203
    {
204
        if (is_dir(DOCROOT . $this->get('destination') . '/') === false) {
205
            $errors['destination'] = __('The destination directory, %s, does not exist.', array(
206
                '<code>' . $this->get('destination') . '</code>'
207
            ));
208 View Code Duplication
        } elseif (is_writable(DOCROOT . $this->get('destination') . '/') === false) {
209
            $errors['destination'] = __('The destination directory is not writable.')
210
                . ' '
211
                . __('Please check permissions on %s.', array(
212
                    '<code>' . $this->get('destination') . '</code>'
213
                ));
214
        }
215
216
        parent::checkFields($errors, $checkForDuplicates);
217
    }
218
219 View Code Duplication
    public function commit()
220
    {
221
        if (!parent::commit()) {
222
            return false;
223
        }
224
225
        $id = $this->get('id');
226
227
        if ($id === false) {
228
            return false;
229
        }
230
231
        $fields = array();
232
233
        $fields['destination'] = $this->get('destination');
234
        $fields['validator'] = ($fields['validator'] == 'custom' ? null : $this->get('validator'));
235
236
        return FieldManager::saveSettings($id, $fields);
237
    }
238
239
    /*-------------------------------------------------------------------------
240
        Publish:
241
    -------------------------------------------------------------------------*/
242
243
    public function displayPublishPanel(XMLElement &$wrapper, $data = null, $flagWithError = null, $fieldnamePrefix = null, $fieldnamePostfix = null, $entry_id = null)
244
    {
245
        if (is_dir(DOCROOT . $this->get('destination') . '/') === false) {
246
            $flagWithError = __('The destination directory, %s, does not exist.', array(
247
                '<code>' . $this->get('destination') . '</code>'
248
            ));
249 View Code Duplication
        } elseif ($flagWithError && is_writable(DOCROOT . $this->get('destination') . '/') === false) {
250
            $flagWithError = __('Destination folder is not writable.')
251
                . ' '
252
                . __('Please check permissions on %s.', array(
253
                    '<code>' . $this->get('destination') . '</code>'
254
                ));
255
        }
256
257
        $label = Widget::Label($this->get('label'));
258
        $label->setAttribute('class', 'file');
259
260
        if ($this->get('required') !== 'yes') {
261
            $label->appendChild(new XMLElement('i', __('Optional')));
262
        }
263
264
        $span = new XMLElement('span', null, array('class' => 'frame'));
265
266
        if (isset($data['file'])) {
267
            $filename = $this->get('destination') . '/' . basename($data['file']);
268
            $file = $this->getFilePath($data['file']);
269
            if (file_exists($file) === false || !is_readable($file)) {
270
                $flagWithError = __('The file uploaded is no longer available. Please check that it exists, and is readable.');
271
            }
272
273
            $span->appendChild(new XMLElement('span', Widget::Anchor(preg_replace("![^a-z0-9]+!i", "$0&#8203;", $filename), URL . $filename)));
274
        } else {
275
            $filename = null;
276
        }
277
278
        $span->appendChild(Widget::Input('fields'.$fieldnamePrefix.'['.$this->get('element_name').']'.$fieldnamePostfix, $filename, ($filename ? 'hidden' : 'file')));
279
280
        $label->appendChild($span);
281
282
        if ($flagWithError != null) {
283
            $wrapper->appendChild(Widget::Error($label, $flagWithError));
284
        } else {
285
            $wrapper->appendChild($label);
286
        }
287
    }
288
289
    public function validateFilename($file, &$message)
290
    {
291
        if ($this->get('validator') != null) {
292
            $rule = $this->get('validator');
293
294 View Code Duplication
            if (General::validateString($file, $rule) === false) {
295
                $message = __('File chosen in ‘%s’ does not match allowable file types for that field.', array(
296
                    $this->get('label')
297
                ));
298
299
                return self::__INVALID_FIELDS__;
300
            }
301
        }
302
        // If the developer did not specified any validator, check for the
303
        // blacklisted file types instead
304
        else {
305
            $blacklist = Symphony::Configuration()->get('upload_blacklist', 'admin');
306
307 View Code Duplication
            if (!empty($blacklist) && General::validateString($file, $blacklist)) {
308
                $message = __('File chosen in ‘%s’ is blacklisted for that field.', array(
309
                    $this->get('label')
310
                ));
311
312
                return self::__INVALID_FIELDS__;
313
            }
314
        }
315
316
        return self::__OK__;
317
    }
318
319
    public function checkPostFieldData($data, &$message, $entry_id = null)
0 ignored issues
show
Coding Style introduced by
checkPostFieldData uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
320
    {
321
        /**
322
         * For information about PHPs upload error constants see:
323
         * @link http://php.net/manual/en/features.file-upload.errors.php
324
         */
325
        $message = null;
326
327
        if (
328
            empty($data)
329
            || (
330
                is_array($data)
331
                && isset($data['error'])
332
                && $data['error'] == UPLOAD_ERR_NO_FILE
333
            )
334
        ) {
335 View Code Duplication
            if ($this->get('required') === 'yes') {
336
                $message = __('‘%s’ is a required field.', array($this->get('label')));
337
338
                return self::__MISSING_FIELDS__;
339
            }
340
341
            return self::__OK__;
342
        }
343
344
        // Its not an array, so just retain the current data and return
345
        if (is_array($data) === false) {
346
            $file = $this->getFilePath(basename($data));
347
            if (file_exists($file) === false || !is_readable($file)) {
348
                $message = __('The file uploaded is no longer available. Please check that it exists, and is readable.');
349
350
                return self::__INVALID_FIELDS__;
351
            }
352
353
            // Ensure that the file still matches the validator and hasn't
354
            // changed since it was uploaded.
355
            return $this->validateFilename($file, $message);
356
        }
357
358
        if (is_dir(DOCROOT . $this->get('destination') . '/') === false) {
359
            $message = __('The destination directory, %s, does not exist.', array(
360
                '<code>' . $this->get('destination') . '</code>'
361
            ));
362
363
            return self::__ERROR__;
364 View Code Duplication
        } elseif (is_writable(DOCROOT . $this->get('destination') . '/') === false) {
365
            $message = __('Destination folder is not writable.')
366
                . ' '
367
                . __('Please check permissions on %s.', array(
368
                    '<code>' . $this->get('destination') . '</code>'
369
                ));
370
371
            return self::__ERROR__;
372
        }
373
374
        if ($data['error'] != UPLOAD_ERR_NO_FILE && $data['error'] != UPLOAD_ERR_OK) {
375
            switch ($data['error']) {
376
                case UPLOAD_ERR_INI_SIZE:
377
                    $message = __('File chosen in ‘%1$s’ exceeds the maximum allowed upload size of %2$s specified by your host.', array($this->get('label'), (is_numeric(ini_get('upload_max_filesize')) ? General::formatFilesize(ini_get('upload_max_filesize')) : ini_get('upload_max_filesize'))));
378
                    break;
379
                case UPLOAD_ERR_FORM_SIZE:
380
                    $message = __('File chosen in ‘%1$s’ exceeds the maximum allowed upload size of %2$s, specified by Symphony.', array($this->get('label'), General::formatFilesize($_POST['MAX_FILE_SIZE'])));
381
                    break;
382
                case UPLOAD_ERR_PARTIAL:
383
                case UPLOAD_ERR_NO_TMP_DIR:
384
                    $message = __('File chosen in ‘%s’ was only partially uploaded due to an error.', array($this->get('label')));
385
                    break;
386
                case UPLOAD_ERR_CANT_WRITE:
387
                    $message = __('Uploading ‘%s’ failed. Could not write temporary file to disk.', array($this->get('label')));
388
                    break;
389
                case UPLOAD_ERR_EXTENSION:
390
                    $message = __('Uploading ‘%s’ failed. File upload stopped by extension.', array($this->get('label')));
391
                    break;
392
            }
393
394
            return self::__ERROR_CUSTOM__;
395
        }
396
397
        // Sanitize the filename
398
        $data['name'] = Lang::createFilename($data['name']);
399
400
        // Validate the filename
401
        return $this->validateFilename($data['name'], $message);
402
    }
403
404
    public function processRawFieldData($data, &$status, &$message = null, $simulate = false, $entry_id = null)
405
    {
406
        $status = self::__OK__;
407
408
        // No file given, save empty data:
409
        if ($data === null) {
410
            return array(
411
                'file' =>       null,
412
                'mimetype' =>   null,
413
                'size' =>       null,
414
                'meta' =>       null
415
            );
416
        }
417
418
        // Its not an array, so just retain the current data and return:
419
        if (is_array($data) === false) {
420
            $file = $this->getFilePath(basename($data));
421
422
            $result = array(
423
                'file' =>       $data,
424
                'mimetype' =>   null,
425
                'size' =>       null,
426
                'meta' =>       null
427
            );
428
429
            // Grab the existing entry data to preserve the MIME type and size information
430
            if (isset($entry_id)) {
431
                $row = Symphony::Database()->fetchRow(0, sprintf(
432
                    "SELECT `file`, `mimetype`, `size`, `meta` FROM `tbl_entries_data_%d` WHERE `entry_id` = %d",
433
                    $this->get('id'),
434
                    $entry_id
435
                ));
436
437
                if (empty($row) === false) {
438
                    $result = $row;
439
                }
440
            }
441
442
            // Found the file, add any missing meta information:
443
            if (file_exists($file) && is_readable($file)) {
444
                if (empty($result['mimetype'])) {
445
                    $result['mimetype'] = General::getMimeType($file);
446
                }
447
448
                if (empty($result['size'])) {
449
                    $result['size'] = filesize($file);
450
                }
451
452
                if (empty($result['meta'])) {
453
                    $result['meta'] = serialize(static::getMetaInfo($file, $result['mimetype']));
454
                }
455
456
                // The file was not found, or is unreadable:
457
            } else {
458
                $message = __('The file uploaded is no longer available. Please check that it exists, and is readable.');
459
                $status = self::__INVALID_FIELDS__;
460
            }
461
462
            return $result;
463
        }
464
465
        if ($simulate && is_null($entry_id)) {
466
            return $data;
467
        }
468
469
        // Check to see if the entry already has a file associated with it:
470
        if (is_null($entry_id) === false) {
471
            $row = Symphony::Database()->fetchRow(0, sprintf(
472
                "SELECT *
473
                FROM `tbl_entries_data_%s`
474
                WHERE `entry_id` = %d
475
                LIMIT 1",
476
                $this->get('id'),
477
                $entry_id
478
            ));
479
480
            $existing_file = isset($row['file']) ? $this->getFilePath($row['file']) : null;
481
482
            // File was removed:
483
            if (
484
                $data['error'] == UPLOAD_ERR_NO_FILE
485
                && !is_null($existing_file)
486
                && is_file($existing_file)
487
            ) {
488
                General::deleteFile($existing_file);
489
            }
490
        }
491
492
        // Do not continue on upload error:
493
        if ($data['error'] == UPLOAD_ERR_NO_FILE || $data['error'] != UPLOAD_ERR_OK) {
494
            return false;
495
        }
496
497
        // Where to upload the new file?
498
        $abs_path = DOCROOT . '/' . trim($this->get('destination'), '/');
499
        $rel_path = str_replace('/workspace', '', $this->get('destination'));
500
501
        // Sanitize the filename
502
        $data['name'] = Lang::createFilename($data['name']);
503
504
        // If a file already exists, then rename the file being uploaded by
505
        // adding `_1` to the filename. If `_1` already exists, the logic
506
        // will keep adding 1 until a filename is available (#672)
507
        if (file_exists($abs_path . '/' . $data['name'])) {
508
            $extension = General::getExtension($data['name']);
509
            $new_file = substr($abs_path . '/' . $data['name'], 0, -1 - strlen($extension));
510
            $renamed_file = $new_file;
0 ignored issues
show
Unused Code introduced by
$renamed_file is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
511
            $count = 1;
512
513
            do {
514
                $renamed_file = $new_file . '_' . $count . '.' . $extension;
515
                $count++;
516
            } while (file_exists($renamed_file));
517
518
            // Extract the name filename from `$renamed_file`.
519
            $data['name'] = str_replace($abs_path . '/', '', $renamed_file);
520
        }
521
522
        $file = $this->getFilePath($data['name']);
523
524
        // Attempt to upload the file:
525
        $uploaded = General::uploadFile(
526
            $abs_path,
527
            $data['name'],
528
            $data['tmp_name'],
529
            Symphony::Configuration()->get('write_mode', 'file')
0 ignored issues
show
Documentation introduced by
\Symphony::Configuration...t('write_mode', 'file') is of type array|string, but the function expects a integer.

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...
530
        );
531
532
        if ($uploaded === false) {
533
            $message = __(
534
                __('There was an error while trying to upload the file %1$s to the target directory %2$s.'),
535
                array(
536
                    '<code>' . $data['name'] . '</code>',
537
                    '<code>workspace/' . ltrim($rel_path, '/') . '</code>'
538
                )
539
            );
540
            $status = self::__ERROR_CUSTOM__;
541
542
            return false;
543
        }
544
545
        // File has been replaced:
546
        if (
547
            isset($existing_file)
548
            && $existing_file !== $file
549
            && is_file($existing_file)
550
        ) {
551
            General::deleteFile($existing_file);
552
        }
553
554
        // Get the mimetype, don't trust the browser. RE: #1609
555
        $data['type'] = General::getMimeType($file);
556
557
        return array(
558
            'file' =>       basename($file),
559
            'size' =>       $data['size'],
560
            'mimetype' =>   $data['type'],
561
            'meta' =>       serialize(static::getMetaInfo($file, $data['type']))
562
        );
563
    }
564
565
    /*-------------------------------------------------------------------------
566
        Output:
567
    -------------------------------------------------------------------------*/
568
569
    public function appendFormattedElement(XMLElement &$wrapper, $data, $encode = false, $mode = null, $entry_id = null)
570
    {
571
        // It is possible an array of null data will be passed in. Check for this.
572
        if (!is_array($data) || !isset($data['file']) || is_null($data['file'])) {
573
            return;
574
        }
575
576
        $file = $this->getFilePath($data['file']);
577
        $filesize = (file_exists($file) && is_readable($file)) ? filesize($file) : null;
578
        $item = new XMLElement($this->get('element_name'));
579
        $item->setAttributeArray(array(
580
            'size' =>   !is_null($filesize) ? General::formatFilesize($filesize) : 'unknown',
581
            'bytes' =>  !is_null($filesize) ? $filesize : 'unknown',
582
            'path' =>   General::sanitize(
583
                str_replace(WORKSPACE, null, dirname($file))
584
            ),
585
            'type' =>   $data['mimetype']
586
        ));
587
588
        $item->appendChild(new XMLElement('filename', General::sanitize(basename($file))));
589
590
        $m = unserialize($data['meta']);
591
592
        if (is_array($m) && !empty($m)) {
593
            $item->appendChild(new XMLElement('meta', null, $m));
594
        }
595
596
        $wrapper->appendChild($item);
597
    }
598
599
    public function prepareTableValue($data, XMLElement $link = null, $entry_id = null)
600
    {
601
        if (isset($data['file']) === false || !$file = $data['file']) {
602
            return parent::prepareTableValue(null, $link, $entry_id);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

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...
603
        }
604
605
        if ($link) {
606
            $link->setValue(basename($file));
607
            $link->setAttribute('data-path', $this->get('destination'));
608
609
            return $link->generate();
610
        } else {
611
            $link = Widget::Anchor(basename($file), URL . $this->get('destination') . '/' . $file);
612
            $link->setAttribute('data-path', $this->get('destination'));
613
614
            return $link->generate();
615
        }
616
    }
617
    
618
    public function prepareTextValue($data, $entry_id = null)
619
    {
620
        if (isset($data['file'])) {
621
            return $data['file'];
622
        }
623
        return null;
624
    }
625
626
    public function prepareAssociationsDrawerXMLElement(Entry $e, array $parent_association, $prepopulate = '')
627
    {
628
        $li = parent::prepareAssociationsDrawerXMLElement($e, $parent_association);
629
        $a = $li->getChild(0);
630
        $a->setAttribute('data-path', $this->get('destination'));
631
632
        return $li;
633
    }
634
635
    /*-------------------------------------------------------------------------
636
        Import:
637
    -------------------------------------------------------------------------*/
638
639
    public function getImportModes()
640
    {
641
        return array(
642
            'getValue' =>       ImportableField::STRING_VALUE,
643
            'getPostdata' =>    ImportableField::ARRAY_VALUE
644
        );
645
    }
646
647 View Code Duplication
    public function prepareImportValue($data, $mode, $entry_id = null)
648
    {
649
        $message = $status = null;
650
        $modes = (object)$this->getImportModes();
651
652
        if ($mode === $modes->getValue) {
653
            return $data;
654
        } elseif ($mode === $modes->getPostdata) {
655
            return $this->processRawFieldData($data, $status, $message, true, $entry_id);
656
        }
657
658
        return null;
659
    }
660
661
    /*-------------------------------------------------------------------------
662
        Export:
663
    -------------------------------------------------------------------------*/
664
665
    /**
666
     * Return a list of supported export modes for use with `prepareExportValue`.
667
     *
668
     * @return array
669
     */
670
    public function getExportModes()
671
    {
672
        return array(
673
            'getFilename' =>    ExportableField::VALUE,
674
            'getObject' =>      ExportableField::OBJECT,
675
            'getPostdata' =>    ExportableField::POSTDATA
676
        );
677
    }
678
679
    /**
680
     * Give the field some data and ask it to return a value using one of many
681
     * possible modes.
682
     *
683
     * @param mixed $data
684
     * @param integer $mode
685
     * @param integer $entry_id
686
     * @return array|string|null
687
     */
688
    public function prepareExportValue($data, $mode, $entry_id = null)
689
    {
690
        $modes = (object)$this->getExportModes();
691
692
        $filepath = $this->getFilePath($data['file']);
693
694
        // No file, or the file that the entry is meant to have no
695
        // longer exists.
696
        if (!isset($data['file']) || !is_file($filepath)) {
697
            return null;
698
        }
699
700
        if ($mode === $modes->getFilename) {
701
            return $data['file'];
702
        }
703
704
        if ($mode === $modes->getObject) {
705
            $object = (object)$data;
706
707
            if (isset($object->meta)) {
708
                $object->meta = unserialize($object->meta);
709
            }
710
711
            return $object;
712
        }
713
714
        if ($mode === $modes->getPostdata) {
715
            return $data['file'];
716
        }
717
    }
718
719
    /*-------------------------------------------------------------------------
720
        Filtering:
721
    -------------------------------------------------------------------------*/
722
723
    public function buildDSRetrievalSQL($data, &$joins, &$where, $andOperation = false)
724
    {
725
        $field_id = $this->get('id');
726
727
        if (preg_match('/^mimetype:/', $data[0])) {
728
            $data[0] = str_replace('mimetype:', '', $data[0]);
729
            $column = 'mimetype';
730
        } elseif (preg_match('/^size:/', $data[0])) {
731
            $data[0] = str_replace('size:', '', $data[0]);
732
            $column = 'size';
733
        } else {
734
            $column = 'file';
735
        }
736
737
        if (self::isFilterRegex($data[0])) {
738
            $this->buildRegexSQL($data[0], array($column), $joins, $where);
739
        } elseif ($andOperation) {
740
            foreach ($data as $value) {
741
                $this->_key++;
742
                $value = $this->cleanValue($value);
743
                $joins .= "
744
                    LEFT JOIN
745
                        `tbl_entries_data_{$field_id}` AS t{$field_id}_{$this->_key}
746
                        ON (e.id = t{$field_id}_{$this->_key}.entry_id)
747
                ";
748
                $where .= "
749
                    AND t{$field_id}_{$this->_key}.{$column} = '{$value}'
750
                ";
751
            }
752
        } else {
753
            if (!is_array($data)) {
754
                $data = array($data);
755
            }
756
757
            foreach ($data as &$value) {
758
                $value = $this->cleanValue($value);
759
            }
760
761
            $this->_key++;
762
            $data = implode("', '", $data);
763
            $joins .= "
764
                LEFT JOIN
765
                    `tbl_entries_data_{$field_id}` AS t{$field_id}_{$this->_key}
766
                    ON (e.id = t{$field_id}_{$this->_key}.entry_id)
767
            ";
768
            $where .= "
769
                AND t{$field_id}_{$this->_key}.{$column} IN ('{$data}')
770
            ";
771
        }
772
773
        return true;
774
    }
775
776
    /*-------------------------------------------------------------------------
777
        Sorting:
778
    -------------------------------------------------------------------------*/
779
780
    public function buildSortingSQL(&$joins, &$where, &$sort, $order = 'ASC')
781
    {
782
        if (in_array(strtolower($order), array('random', 'rand'))) {
783
            $sort = 'ORDER BY RAND()';
784
        } else {
785
            $sort = sprintf(
786
                'ORDER BY (
787
                    SELECT %s
788
                    FROM tbl_entries_data_%d AS `ed`
789
                    WHERE entry_id = e.id
790
                ) %s',
791
                '`ed`.file',
792
                $this->get('id'),
793
                $order
794
            );
795
        }
796
    }
797
798
    /*-------------------------------------------------------------------------
799
        Events:
800
    -------------------------------------------------------------------------*/
801
802 View Code Duplication
    public function getExampleFormMarkup()
803
    {
804
        $label = Widget::Label($this->get('label'));
805
        $label->appendChild(Widget::Input('fields['.$this->get('element_name').']', null, 'file'));
806
807
        return $label;
808
    }
809
}
810