EditableFileField::getSubmittedFormField()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\UserForms\Model\EditableFormField;
4
5
use SilverStripe\Assets\File;
6
use SilverStripe\Assets\Folder;
7
use SilverStripe\Core\Config\Config;
8
use SilverStripe\Core\Convert;
9
use SilverStripe\Forms\FieldList;
10
use SilverStripe\Forms\FileField;
11
use SilverStripe\Forms\LiteralField;
12
use SilverStripe\Forms\NumericField;
13
use SilverStripe\Forms\TreeDropdownField;
14
use SilverStripe\ORM\ValidationResult;
15
use SilverStripe\Security\Member;
16
use SilverStripe\Security\InheritedPermissions;
17
use SilverStripe\UserForms\Control\UserDefinedFormAdmin;
18
use SilverStripe\UserForms\Control\UserDefinedFormController;
19
use SilverStripe\UserForms\Model\EditableFormField;
20
use SilverStripe\UserForms\Model\Submission\SubmittedFileField;
21
22
/**
23
 * Allows a user to add a field that can be used to upload a file.
24
 *
25
 * @method Folder Folder
26
 * @property int FolderID
27
 * @property boolean MaxFileSizeMB
28
 * @property boolean FolderConfirmed
29
 * @package userforms
30
 */
31
class EditableFileField extends EditableFormField
32
{
33
34
    private static $singular_name = 'File Upload Field';
0 ignored issues
show
introduced by
The private property $singular_name is not used, and could be removed.
Loading history...
35
36
    private static $plural_names = 'File Fields';
0 ignored issues
show
introduced by
The private property $plural_names is not used, and could be removed.
Loading history...
37
38
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
39
        'MaxFileSizeMB' => 'Float',
40
        'FolderConfirmed' => 'Boolean',
41
    ];
42
43
    private static $has_one = [
0 ignored issues
show
introduced by
The private property $has_one is not used, and could be removed.
Loading history...
44
        'Folder' => Folder::class // From CustomFields
45
    ];
46
47
    private static $table_name = 'EditableFileField';
0 ignored issues
show
introduced by
The private property $table_name is not used, and could be removed.
Loading history...
48
49
    /**
50
     * Further limit uploadable file extensions in addition to the restrictions
51
     * imposed by the File.allowed_extensions global configuration.
52
     * @config
53
     */
54
    private static $allowed_extensions_blacklist = [
0 ignored issues
show
introduced by
The private property $allowed_extensions_blacklist is not used, and could be removed.
Loading history...
55
        'htm', 'html', 'xhtml', 'swf', 'xml'
56
    ];
57
58
    /**
59
     * Returns a string describing the permissions of a folder
60
     * @param Folder|null $folder
61
     * @return string
62
     */
63
    public static function getFolderPermissionString(Folder $folder = null)
64
    {
65
        $folderPermissions = static::getFolderPermissionTuple($folder);
66
67
        $icon = 'font-icon-user-lock';
68
        if ($folderPermissions['CanViewType'] == InheritedPermissions::ANYONE) {
69
            $icon = 'font-icon-address-card-warning';
70
        }
71
72
        return sprintf(
73
            '<span class="icon %s form-description__icon" aria-hidden="true"></span>%s %s',
74
            $icon,
75
            $folderPermissions['CanViewTypeString'],
76
            htmlspecialchars(implode(', ', $folderPermissions['ViewerGroups']), ENT_QUOTES)
77
        );
78
    }
79
80
    /**
81
     * Returns an array with a view type string and the viewer groups
82
     * @param Folder|null $folder
83
     * @return array
84
     */
85
    private static function getFolderPermissionTuple(Folder $folder = null)
86
    {
87
        $viewersOptionsField = [
88
            InheritedPermissions::INHERIT => _t(__CLASS__.'.INHERIT', 'Visibility for this folder is inherited from the parent folder'),
89
            InheritedPermissions::ANYONE => _t(__CLASS__.'.ANYONE', 'Unrestricted access, uploads will be visible to anyone'),
90
            InheritedPermissions::LOGGED_IN_USERS => _t(__CLASS__.'.LOGGED_IN', 'Restricted access, uploads will be visible to logged-in users'),
91
            InheritedPermissions::ONLY_THESE_USERS => _t(__CLASS__.'.ONLY_GROUPS', 'Restricted access, uploads will be visible to the following groups:')
92
        ];
93
94
        if (!$folder) {
95
            return [
96
                'CanViewType' => InheritedPermissions::ANYONE,
97
                'CanViewTypeString' => $viewersOptionsField[InheritedPermissions::ANYONE],
98
                'ViewerGroups' => [],
99
            ];
100
        }
101
102
        $folder = static::getNonInheritedViewType($folder);
103
104
        // ViewerGroups may still exist when permissions have been loosened
105
        $viewerGroups = [];
106
        if ($folder->CanViewType === InheritedPermissions::ONLY_THESE_USERS) {
107
            $viewerGroups = $folder->ViewerGroups()->column('Title');
108
        }
109
110
        return [
111
            'CanViewType' => $folder->CanViewType,
112
            'CanViewTypeString' => $viewersOptionsField[$folder->CanViewType],
113
            'ViewerGroups' => $viewerGroups,
114
        ];
115
    }
116
117
    /**
118
     * Returns the nearest non-inherited view permission of the provided
119
     * @param File $file
120
     * @return File
121
     */
122
    private static function getNonInheritedViewType(File $file)
123
    {
124
        if ($file->CanViewType !== InheritedPermissions::INHERIT) {
125
            return $file;
126
        }
127
        $parent = $file->Parent();
128
        if ($parent->exists()) {
129
            return static::getNonInheritedViewType($parent);
130
        } else {
131
            // anyone can view top level files
132
            $file->CanViewType = InheritedPermissions::ANYONE;
133
        }
134
        return $file;
135
    }
136
137
    /**
138
     * @return FieldList
139
     */
140
    public function getCMSFields()
141
    {
142
        $fields = parent::getCMSFields();
143
144
        $treeView = TreeDropdownField::create(
145
            'FolderID',
146
            _t('EditableUploadField.SELECTUPLOADFOLDER', 'Select upload folder'),
147
            Folder::class
148
        );
149
        $treeView->setDescription(static::getFolderPermissionString($this->Folder()));
150
        $fields->addFieldToTab(
151
            'Root.Main',
152
            $treeView
153
        );
154
155
        // Warn the user if the folder targeted by this field is not restricted
156
        if ($this->FolderID && !$this->Folder()->hasRestrictedAccess()) {
157
            $fields->addFieldToTab("Root.Main", LiteralField::create(
158
                'FileUploadWarning',
159
                '<p class="alert alert-warning">' . _t(
160
                    'SilverStripe\\UserForms\\Model\\UserDefinedForm.UnrestrictedFileUploadWarning',
161
                    'Access to the current upload folder "{path}" is not restricted. Uploaded files will be publicly accessible if the exact URL is known.',
162
                    ['path' => Convert::raw2att($this->Folder()->Filename)]
163
                )
164
                . '</p>'
165
            ), 'Type');
166
        }
167
168
        $fields->addFieldToTab(
169
            'Root.Main',
170
            NumericField::create('MaxFileSizeMB')
171
                ->setTitle('Max File Size MB')
172
                ->setDescription("Note: Maximum php allowed size is {$this->getPHPMaxFileSizeMB()} MB")
173
        );
174
175
        $fields->removeByName('Default');
176
177
        return $fields;
178
    }
179
180
    /**
181
     * @return ValidationResult
182
     */
183
    public function validate()
184
    {
185
        $result = parent::validate();
186
187
        $max = static::get_php_max_file_size();
188
        if ($this->MaxFileSizeMB * 1024 > $max) {
189
            $result->addError("Your max file size limit can't be larger than the server's limit of {$this->getPHPMaxFileSizeMB()}.");
190
        }
191
192
        return $result;
193
    }
194
195
    public function getFormField()
196
    {
197
        $field = FileField::create($this->Name, $this->Title ?: false)
198
            ->setFieldHolderTemplate(EditableFormField::class . '_holder')
199
            ->setTemplate(__CLASS__);
200
201
        $field->setFieldHolderTemplate(EditableFormField::class . '_holder')
202
            ->setTemplate(__CLASS__);
203
204
        $field->getValidator()->setAllowedExtensions(
205
            array_diff(
206
                // filter out '' since this would be a regex problem on JS end
207
                array_filter(Config::inst()->get(File::class, 'allowed_extensions')),
208
                $this->config()->get('allowed_extensions_blacklist')
209
            )
210
        );
211
212
        if ($this->MaxFileSizeMB > 0) {
213
            $field->getValidator()->setAllowedMaxFileSize($this->MaxFileSizeMB * 1024 * 1024);
214
        } else {
215
            $field->getValidator()->setAllowedMaxFileSize(static::get_php_max_file_size());
216
        }
217
218
        $folder = $this->Folder();
219
        if ($folder && $folder->exists()) {
220
            $field->setFolderName(
221
                preg_replace("/^assets\//", "", $folder->Filename)
222
            );
223
        }
224
225
        $this->doUpdateFormField($field);
226
227
        return $field;
228
    }
229
230
231
    /**
232
     * Return the value for the database, link to the file is stored as a
233
     * relation so value for the field can be null.
234
     *
235
     * @return string
236
     */
237
    public function getValueFromData()
238
    {
239
        return null;
240
    }
241
242
    public function getSubmittedFormField()
243
    {
244
        return SubmittedFileField::create();
245
    }
246
247
    /**
248
     * @return float
249
     */
250
    public static function get_php_max_file_size()
251
    {
252
        $maxUpload = File::ini2bytes(ini_get('upload_max_filesize'));
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\Assets\File::ini2bytes() has been deprecated: 5.0 Use Convert::memstring2bytes() instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

252
        $maxUpload = /** @scrutinizer ignore-deprecated */ File::ini2bytes(ini_get('upload_max_filesize'));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
253
        $maxPost = File::ini2bytes(ini_get('post_max_size'));
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\Assets\File::ini2bytes() has been deprecated: 5.0 Use Convert::memstring2bytes() instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

253
        $maxPost = /** @scrutinizer ignore-deprecated */ File::ini2bytes(ini_get('post_max_size'));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
254
        return min($maxUpload, $maxPost);
255
    }
256
257
    public function getPHPMaxFileSizeMB()
258
    {
259
        return round(static::get_php_max_file_size() / 1024 / 1024, 1);
260
    }
261
262
    public function onBeforeWrite()
263
    {
264
        parent::onBeforeWrite();
265
266
        $folderChanged = $this->isChanged('FolderID');
267
268
        // Default to either an existing sibling's folder, or the default form submissions folder
269
        if ($this->FolderID === null) {
270
            $inheritableSibling = EditableFileField::get()->filter([
271
                'ParentID' => $this->ParentID,
0 ignored issues
show
Bug Best Practice introduced by
The property ParentID does not exist on SilverStripe\UserForms\M...Field\EditableFileField. Since you implemented __get, consider adding a @property annotation.
Loading history...
272
                'FolderConfirmed' => true,
273
            ])->first();
274
275
            if ($inheritableSibling) {
0 ignored issues
show
introduced by
$inheritableSibling is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
276
                $this->FolderID = $inheritableSibling->FolderID;
277
            } else {
278
                $folder = UserDefinedFormAdmin::getFormSubmissionFolder();
279
                $this->FolderID = $folder->ID;
280
            }
281
        }
282
283
        if ($folderChanged) {
284
            $this->FolderConfirmed = true;
285
        }
286
    }
287
}
288