Completed
Pull Request — master (#312)
by Damian
02:00
created

AssetAdmin::getObjectFromData()   C

Complexity

Conditions 10
Paths 80

Size

Total Lines 68
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 68
rs 6.0995
cc 10
eloc 46
nc 80
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
namespace SilverStripe\AssetAdmin\Controller;
4
5
use SilverStripe\Admin\AddToCampaignHandler;
6
use SilverStripe\Admin\CMSBatchActionHandler;
7
use SilverStripe\Admin\LeftAndMain;
8
use SilverStripe\AssetAdmin\Forms\FileField;
9
use SilverStripe\AssetAdmin\Forms\FileFormFactory;
10
use SilverStripe\AssetAdmin\Forms\FolderFormFactory;
11
use SilverStripe\AssetAdmin\Forms\ImageFormFactory;
12
use SilverStripe\Assets\File;
13
use SilverStripe\Assets\Folder;
14
use SilverStripe\Assets\Image;
15
use SilverStripe\Assets\Storage\AssetNameGenerator;
16
use SilverStripe\Assets\Upload;
17
use SilverStripe\Control\Controller;
18
use SilverStripe\Control\HTTPRequest;
19
use SilverStripe\Control\HTTPResponse;
20
use SilverStripe\Core\Convert;
21
use SilverStripe\Core\Injector\Injector;
22
use SilverStripe\Forms\CheckboxField;
23
use SilverStripe\Forms\DateField;
24
use SilverStripe\Forms\DropdownField;
25
use SilverStripe\Forms\FieldGroup;
26
use SilverStripe\Forms\Form;
27
use SilverStripe\Forms\FormFactory;
28
use SilverStripe\Forms\HeaderField;
29
use SilverStripe\ORM\ArrayList;
30
use SilverStripe\ORM\DataList;
31
use SilverStripe\ORM\DataObject;
32
use SilverStripe\ORM\FieldType\DBHTMLText;
33
use SilverStripe\ORM\Search\SearchContext;
34
use SilverStripe\Security\Member;
35
use SilverStripe\Security\PermissionProvider;
36
use SilverStripe\Security\SecurityToken;
37
use SilverStripe\View\Requirements;
38
39
/**
40
 * AssetAdmin is the 'file store' section of the CMS.
41
 * It provides an interface for manipulating the File and Folder objects in the system.
42
 */
43
class AssetAdmin extends LeftAndMain implements PermissionProvider
44
{
45
    private static $url_segment = 'assets';
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...
Unused Code introduced by
The property $url_segment is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
46
47
    private static $url_rule = '/$Action/$ID';
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...
Unused Code introduced by
The property $url_rule is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
48
49
    private static $menu_title = 'Files';
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...
Unused Code introduced by
The property $menu_title is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
50
51
    private static $tree_class = 'SilverStripe\\Assets\\Folder';
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...
Unused Code introduced by
The property $tree_class is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
52
53
    private static $url_handlers = [
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...
Unused Code introduced by
The property $url_handlers is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
54
        // Legacy redirect for SS3-style detail view
55
        'EditForm/field/File/item/$FileID/$Action' => 'legacyRedirectForEditView',
56
        // Pass all URLs to the index, for React to unpack
57
        'show/$FolderID/edit/$FileID' => 'index',
58
        // API access points with structured data
59
        'POST api/createFolder' => 'apiCreateFolder',
60
        'POST api/createFile' => 'apiCreateFile',
61
        'GET api/readFolder' => 'apiReadFolder',
62
        'PUT api/updateFolder' => 'apiUpdateFolder',
63
        'DELETE api/delete' => 'apiDelete',
64
        'GET api/search' => 'apiSearch',
65
    ];
66
67
    /**
68
     * Amount of results showing on a single page.
69
     *
70
     * @config
71
     * @var int
72
     */
73
    private static $page_length = 15;
0 ignored issues
show
Unused Code introduced by
The property $page_length is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
74
75
    /**
76
     * @config
77
     * @see Upload->allowedMaxFileSize
78
     * @var int
79
     */
80
    private static $allowed_max_file_size;
0 ignored issues
show
Unused Code introduced by
The property $allowed_max_file_size is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
81
82
    /**
83
     * @var array
84
     */
85
    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...
Unused Code introduced by
The property $allowed_actions is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
86
        'legacyRedirectForEditView',
87
        'apiCreateFolder',
88
        'apiCreateFile',
89
        'apiReadFolder',
90
        'apiUpdateFolder',
91
        'apiDelete',
92
        'apiSearch',
93
        'FileEditForm',
94
        'AddToCampaignForm',
95
        'FileInsertForm',
96
    );
97
98
    private static $required_permission_codes = 'CMS_ACCESS_AssetAdmin';
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...
Unused Code introduced by
The property $required_permission_codes is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
99
100
    private static $thumbnail_width = 400;
0 ignored issues
show
Unused Code introduced by
The property $thumbnail_width is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
101
102
    private static $thumbnail_height = 300;
0 ignored issues
show
Unused Code introduced by
The property $thumbnail_height is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
103
104
    /**
105
     * Set up the controller
106
     */
107
    public function init()
108
    {
109
        parent::init();
110
111
        Requirements::add_i18n_javascript(ASSET_ADMIN_DIR . '/client/lang', false, true);
112
        Requirements::javascript(ASSET_ADMIN_DIR . "/client/dist/js/bundle.js");
113
        Requirements::css(ASSET_ADMIN_DIR . "/client/dist/styles/bundle.css");
114
115
        CMSBatchActionHandler::register(
116
            'delete',
117
            'SilverStripe\AssetAdmin\BatchAction\DeleteAssets',
118
            'SilverStripe\\Assets\\Folder'
119
        );
120
    }
121
122
    public function getClientConfig()
123
    {
124
        $baseLink = $this->Link();
125
        return array_merge( parent::getClientConfig(), [
126
            'reactRouter' => true,
127
            'createFileEndpoint' => [
128
                'url' => Controller::join_links($baseLink, 'api/createFile'),
129
                'method' => 'post',
130
                'payloadFormat' => 'urlencoded',
131
            ],
132
            'createFolderEndpoint' => [
133
                'url' => Controller::join_links($baseLink, 'api/createFolder'),
134
                'method' => 'post',
135
                'payloadFormat' => 'urlencoded',
136
            ],
137
            'readFolderEndpoint' => [
138
                'url' => Controller::join_links($baseLink, 'api/readFolder'),
139
                'method' => 'get',
140
                'responseFormat' => 'json',
141
            ],
142
            'searchEndpoint' => [
143
                'url' => Controller::join_links($baseLink, 'api/search'),
144
                'method' => 'get',
145
                'responseFormat' => 'json',
146
            ],
147
            'updateFolderEndpoint' => [
148
                'url' => Controller::join_links($baseLink, 'api/updateFolder'),
149
                'method' => 'put',
150
                'payloadFormat' => 'urlencoded',
151
            ],
152
            'deleteEndpoint' => [
153
                'url' => Controller::join_links($baseLink, 'api/delete'),
154
                'method' => 'delete',
155
                'payloadFormat' => 'urlencoded',
156
            ],
157
            'limit' => $this->config()->page_length,
158
            'form' => [
159
                'FileEditForm' => [
160
                    'schemaUrl' => $this->Link('schema/FileEditForm')
161
                ],
162
                'FileInsertForm' => [
163
                    'schemaUrl' => $this->Link('schema/FileInsertForm')
164
                ],
165
                'AddToCampaignForm' => [
166
                    'schemaUrl' => $this->Link('schema/AddToCampaignForm')
167
                ],
168
            ],
169
        ]);
170
    }
171
172
    /**
173
     * Fetches a collection of files by ParentID.
174
     *
175
     * @param HTTPRequest $request
176
     * @return HTTPResponse
177
     */
178
    public function apiReadFolder(HTTPRequest $request)
179
    {
180
        $params = $request->requestVars();
181
        $items = array();
182
        $parentId = null;
0 ignored issues
show
Unused Code introduced by
$parentId 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...
183
        $folderID = null;
0 ignored issues
show
Unused Code introduced by
$folderID 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...
184
185
        if (!isset($params['id']) && !strlen($params['id'])) {
186
            $this->httpError(400);
187
        }
188
189
        $folderID = (int)$params['id'];
190
        /** @var Folder $folder */
191
        $folder = $folderID ? Folder::get()->byID($folderID) : Folder::singleton();
192
193
        if (!$folder) {
194
            $this->httpError(400);
195
        }
196
197
        // TODO Limit results to avoid running out of memory (implement client-side pagination)
198
        $files = $this->getList()->filter('ParentID', $folderID);
199
200
        if ($files) {
201
            /** @var File $file */
202
            foreach ($files as $file) {
203
                if (!$file->canView()) {
204
                    continue;
205
                }
206
207
                $items[] = $this->getObjectFromData($file);
208
            }
209
        }
210
211
        // Build parents (for breadcrumbs)
212
        $parents = [];
213
        $next = $folder->Parent();
214
        while($next && $next->exists()) {
215
            array_unshift($parents, [
216
                'id' => $next->ID,
217
                'title' => $next->getTitle(),
218
                'filename' => $next->getFilename(),
219
            ]);
220
            if($next->ParentID) {
221
                $next = $next->Parent();
222
            } else {
223
                break;
224
            }
225
        }
226
227
        // Build response
228
        $response = new HTTPResponse();
229
        $response->addHeader('Content-Type', 'application/json');
230
        $response->setBody(json_encode([
231
            'files' => $items,
232
            'title' => $folder->getTitle(),
233
            'count' => count($items),
234
            'parents' => $parents,
235
            'parent' => $parents ? $parents[count($parents) - 1] : null,
236
            'parentID' => $folder->exists() ? $folder->ParentID : null, // grandparent
237
            'folderID' => $folderID,
238
            'canEdit' => $folder->canEdit(),
239
            'canDelete' => $folder->canArchive(),
240
        ]));
241
242
        return $response;
243
    }
244
245
    /**
246
     * @param HTTPRequest $request
247
     *
248
     * @return HTTPResponse
249
     */
250
    public function apiSearch(HTTPRequest $request)
251
    {
252
        $params = $request->getVars();
253
        $list = $this->getList($params);
254
255
        $response = new HTTPResponse();
256
        $response->addHeader('Content-Type', 'application/json');
257
        $response->setBody(json_encode([
258
            // Serialisation
259
            "files" => array_map(function($file) {
260
                return $this->getObjectFromData($file);
261
            }, $list->toArray()),
262
            "count" => $list->count(),
263
        ]));
264
265
        return $response;
266
    }
267
268
    /**
269
     * @param HTTPRequest $request
270
     *
271
     * @return HTTPResponse
272
     */
273
    public function apiDelete(HTTPRequest $request)
274
    {
275
        parse_str($request->getBody(), $vars);
276
277
        // CSRF check
278
        $token = SecurityToken::inst();
279 View Code Duplication
        if (empty($vars[$token->getName()]) || !$token->check($vars[$token->getName()])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
280
            return new HTTPResponse(null, 400);
281
        }
282
283 View Code Duplication
        if (!isset($vars['ids']) || !$vars['ids']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
284
            return (new HTTPResponse(json_encode(['status' => 'error']), 400))
285
                ->addHeader('Content-Type', 'application/json');
286
        }
287
288
        $fileIds = $vars['ids'];
289
        $files = $this->getList()->filter("ID", $fileIds)->toArray();
290
291 View Code Duplication
        if (!count($files)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
292
            return (new HTTPResponse(json_encode(['status' => 'error']), 404))
293
                ->addHeader('Content-Type', 'application/json');
294
        }
295
296
        if (!min(array_map(function (File $file) {
297
            return $file->canArchive();
298
        }, $files))) {
299
            return (new HTTPResponse(json_encode(['status' => 'error']), 401))
300
                ->addHeader('Content-Type', 'application/json');
301
        }
302
303
        /** @var File $file */
304
        foreach ($files as $file) {
305
            $file->doArchive();
306
        }
307
308
        return (new HTTPResponse(json_encode(['status' => 'file was deleted'])))
309
            ->addHeader('Content-Type', 'application/json');
310
    }
311
312
    /**
313
     * Creates a single file based on a form-urlencoded upload.
314
     *
315
     * @param HTTPRequest $request
316
     * @return HTTPRequest|HTTPResponse
317
     */
318
    public function apiCreateFile(HTTPRequest $request)
319
    {
320
        $data = $request->postVars();
321
        $upload = $this->getUpload();
322
323
        // CSRF check
324
        $token = SecurityToken::inst();
325 View Code Duplication
        if (empty($data[$token->getName()]) || !$token->check($data[$token->getName()])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
326
            return new HTTPResponse(null, 400);
327
        }
328
329
        // Check parent record
330
        /** @var Folder $parentRecord */
331
        $parentRecord = null;
332
        if (!empty($data['ParentID']) && is_numeric($data['ParentID'])) {
333
            $parentRecord = Folder::get()->byID($data['ParentID']);
334
        }
335
        $data['Parent'] = $parentRecord;
336
337
        $tmpFile = $request->postVar('Upload');
338
        if(!$upload->validate($tmpFile)) {
339
            $result = ['message' => null];
340
            $errors = $upload->getErrors();
341
            if ($message = array_shift($errors)) {
342
                $result['message'] = [
343
                    'type' => 'error',
344
                    'value' => $message,
345
                ];
346
            }
347
            return (new HTTPResponse(json_encode($result), 400))
348
                ->addHeader('Content-Type', 'application/json');
349
        }
350
351
        // TODO Allow batch uploads
352
        $fileClass = File::get_class_for_file_extension(File::get_file_extension($tmpFile['name']));
353
        /** @var File $file */
354
        $file = Injector::inst()->create($fileClass);
355
356
        // check canCreate permissions
357 View Code Duplication
        if (!$file->canCreate(null, $data)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
358
            $result = ['message' => [
359
                'type' => 'error',
360
                'value' => _t(
361
                    'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.CreatePermissionDenied',
362
                    'You do not have permission to add files'
363
                )
364
            ]];
365
            return (new HTTPResponse(json_encode($result), 403))
366
                ->addHeader('Content-Type', 'application/json');
367
        }
368
369
        $uploadResult = $upload->loadIntoFile($tmpFile, $file, $parentRecord ? $parentRecord->getFilename() : '/');
370 View Code Duplication
        if(!$uploadResult) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
371
            $result = ['message' => [
372
                'type' => 'error',
373
                'value' => _t(
374
                    'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.LoadIntoFileFailed',
375
                    'Failed to load file'
376
                )
377
            ]];
378
            return (new HTTPResponse(json_encode($result), 400))
379
                ->addHeader('Content-Type', 'application/json');
380
        }
381
382
        $file->ParentID = $parentRecord ? $parentRecord->ID : 0;
383
        $file->write();
384
385
        $result = [$this->getObjectFromData($file)];
386
387
        return (new HTTPResponse(json_encode($result)))
388
            ->addHeader('Content-Type', 'application/json');
389
    }
390
391
    /**
392
     * Creates a single folder, within an optional parent folder.
393
     *
394
     * @param HTTPRequest $request
395
     * @return HTTPRequest|HTTPResponse
396
     */
397
    public function apiCreateFolder(HTTPRequest $request)
398
    {
399
        $data = $request->postVars();
400
401
        $class = 'SilverStripe\\Assets\\Folder';
402
403
        // CSRF check
404
        $token = SecurityToken::inst();
405 View Code Duplication
        if (empty($data[$token->getName()]) || !$token->check($data[$token->getName()])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
406
            return new HTTPResponse(null, 400);
407
        }
408
409
        // check addchildren permissions
410
        /** @var Folder $parentRecord */
411
        $parentRecord = null;
412
        if (!empty($data['ParentID']) && is_numeric($data['ParentID'])) {
413
            $parentRecord = DataObject::get_by_id($class, $data['ParentID']);
414
        }
415
        $data['Parent'] = $parentRecord;
416
        $data['ParentID'] = $parentRecord ? (int)$parentRecord->ID : 0;
417
418
        // Build filename
419
        $baseFilename = isset($data['Name'])
420
            ? basename($data['Name'])
421
            : _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.NEWFOLDER', "NewFolder");
422
423
        if ($parentRecord && $parentRecord->ID) {
424
            $baseFilename = $parentRecord->getFilename() . '/' . $baseFilename;
425
        }
426
427
        // Ensure name is unique
428
        $nameGenerator = $this->getNameGenerator($baseFilename);
429
        $filename = null;
430
        foreach ($nameGenerator as $filename) {
431
            if (! File::find($filename)) {
432
                break;
433
            }
434
        }
435
        $data['Name'] = basename($filename);
436
437
        // Create record
438
        /** @var Folder $record */
439
        $record = Injector::inst()->create($class);
440
441
        // check create permissions
442
        if (!$record->canCreate(null, $data)) {
443
            return (new HTTPResponse(null, 403))
444
                ->addHeader('Content-Type', 'application/json');
445
        }
446
447
        $record->ParentID = $data['ParentID'];
448
        $record->Name = $record->Title = basename($data['Name']);
449
        $record->write();
450
451
        $result = $this->getObjectFromData($record);
452
453
        return (new HTTPResponse(json_encode($result)))->addHeader('Content-Type', 'application/json');
454
    }
455
456
    /**
457
     * Redirects 3.x style detail links to new 4.x style routing.
458
     *
459
     * @param HTTPRequest $request
460
     */
461
    public function legacyRedirectForEditView($request)
462
    {
463
        $fileID = $request->param('FileID');
464
        /** @var File $file */
465
        $file = File::get()->byID($fileID);
466
        $link = $this->getFileEditLink($file) ?: $this->Link();
467
        $this->redirect($link);
468
    }
469
470
    /**
471
     * Given a file return the CMS link to edit it
472
     *
473
     * @param File $file
474
     * @return string
475
     */
476
    public function getFileEditLink($file)
477
    {
478
        if(!$file || !$file->isInDB()) {
479
            return null;
480
        }
481
482
        return Controller::join_links(
483
            $this->Link('show'),
484
            $file->ParentID,
485
            'edit',
486
            $file->ID
487
        );
488
    }
489
490
    /**
491
     * Get the search context from {@link File}, used to create the search form
492
     * as well as power the /search API endpoint.
493
     *
494
     * @return SearchContext
495
     */
496
    public function getSearchContext()
497
    {
498
        $context = File::singleton()->getDefaultSearchContext();
499
500
        // Customize fields
501
        $dateHeader = HeaderField::create('Date', _t('CMSSearch.FILTERDATEHEADING', 'Date'), 4);
502
        $dateFrom = DateField::create('CreatedFrom', _t('CMSSearch.FILTERDATEFROM', 'From'))
503
        ->setConfig('showcalendar', true);
504
        $dateTo = DateField::create('CreatedTo', _t('CMSSearch.FILTERDATETO', 'To'))
505
        ->setConfig('showcalendar', true);
506
        $dateGroup = FieldGroup::create(
507
            $dateHeader,
508
            $dateFrom,
509
            $dateTo
510
        );
511
        $context->addField($dateGroup);
512
        /** @skipUpgrade */
513
        $appCategories = array(
514
            'archive' => _t(
515
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.AppCategoryArchive',
516
                'Archive'
517
            ),
518
            'audio' => _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.AppCategoryAudio', 'Audio'),
519
            'document' => _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.AppCategoryDocument', 'Document'),
520
            'flash' => _t(
521
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.AppCategoryFlash',
522
                'Flash',
523
                'The fileformat'
524
            ),
525
            'image' => _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.AppCategoryImage', 'Image'),
526
            'video' => _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.AppCategoryVideo', 'Video'),
527
        );
528
        $context->addField(
529
            $typeDropdown = new DropdownField(
530
                'AppCategory',
531
                _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.Filetype', 'File type'),
532
                $appCategories
533
            )
534
        );
535
536
        $typeDropdown->setEmptyString(' ');
537
538
        $currentfolderLabel = _t(
539
            'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.CurrentFolderOnly',
540
            'Limit to current folder?'
541
        );
542
        $context->addField(
543
            new CheckboxField('CurrentFolderOnly', $currentfolderLabel)
544
        );
545
        $context->getFields()->removeByName('Title');
546
547
        return $context;
548
    }
549
550
    /**
551
     * Get an asset renamer for the given filename.
552
     *
553
     * @param  string             $filename Path name
554
     * @return AssetNameGenerator
555
     */
556
    protected function getNameGenerator($filename)
557
    {
558
        return Injector::inst()
559
            ->createWithArgs('AssetNameGenerator', array($filename));
560
    }
561
562
    /**
563
     * @todo Implement on client
564
     *
565
     * @param bool $unlinked
566
     * @return ArrayList
567
     */
568
    public function breadcrumbs($unlinked = false)
569
    {
570
        return null;
571
    }
572
573
574
    /**
575
     * Don't include class namespace in auto-generated CSS class
576
     */
577
    public function baseCSSClasses()
578
    {
579
        return 'AssetAdmin LeftAndMain';
580
    }
581
582
    public function providePermissions()
583
    {
584
        return array(
585
            "CMS_ACCESS_AssetAdmin" => array(
586
                'name' => _t('CMSMain.ACCESS', "Access to '{title}' section", array(
0 ignored issues
show
Documentation introduced by
array('title' => static::menu_title()) is of type array<string,string,{"title":"string"}>, 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...
587
                    'title' => static::menu_title()
588
                )),
589
                'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
590
            )
591
        );
592
    }
593
594
    /**
595
     * Build a form scaffolder for this model
596
     *
597
     * NOTE: Volatile api. May be moved to {@see LeftAndMain}
598
     *
599
     * @param File $file
600
     * @return FormFactory
601
     */
602
    public function getFormFactory(File $file)
603
    {
604
        // Get service name based on file class
605
        $name = null;
0 ignored issues
show
Unused Code introduced by
$name 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...
606
        if ($file instanceof Folder) {
607
            $name = FolderFormFactory::class;
608
        } elseif ($file instanceof Image) {
609
            $name = ImageFormFactory::class;
610
        } else {
611
            $name = FileFormFactory::class;
612
        }
613
        return Injector::inst()->get($name);
614
    }
615
616
    /**
617
     * The form is used to generate a form schema,
618
     * as well as an intermediary object to process data through API endpoints.
619
     * Since it's used directly on API endpoints, it does not have any form actions.
620
     * It handles both {@link File} and {@link Folder} records.
621
     *
622
     * @param int $id
623
     * @return Form
624
     */
625
    public function getFileEditForm($id)
626
    {
627
        /** @var File $file */
628
        $file = $this->getList()->byID($id);
629
630 View Code Duplication
        if (!$file->canView()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
631
            $this->httpError(403, _t(
632
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorItemPermissionDenied',
633
                'You don\'t have the necessary permissions to modify {ObjectTitle}',
634
                '',
635
                ['ObjectTitle' => $file->i18n_singular_name()]
636
            ));
637
            return null;
638
        }
639
640
        $scaffolder = $this->getFormFactory($file);
641
        $form = $scaffolder->getForm($this, 'FileEditForm', [
642
            'Record' => $file
643
        ]);
644
645
        // Configure form to respond to validation errors with form schema
646
        // if requested via react.
647 View Code Duplication
        $form->setValidationResponseCallback(function() use ($form, $file) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
648
            $schemaId = Controller::join_links($this->Link('schema/FileEditForm'), $file->exists() ? $file->ID : '');
649
            return $this->getSchemaResponse($form, $schemaId);
650
        });
651
652
        return $form;
653
    }
654
655
    /**
656
     * Get file edit form
657
     *
658
     * @return Form
659
     */
660 View Code Duplication
    public function FileEditForm()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
661
    {
662
        // Get ID either from posted back value, or url parameter
663
        $request = $this->getRequest();
664
        $id = $request->param('ID') ?: $request->postVar('ID');
665
        return $this->getFileEditForm($id);
666
    }
667
    
668
    /**
669
     * The form is used to generate a form schema,
670
     * as well as an intermediary object to process data through API endpoints.
671
     * Since it's used directly on API endpoints, it does not have any form actions.
672
     * It handles both {@link File} and {@link Folder} records.
673
     *
674
     * @param int $id
675
     * @return Form
676
     */
677
    public function getFileInsertForm($id)
678
    {
679
        /** @var File $file */
680
        $file = $this->getList()->byID($id);
681
        
682 View Code Duplication
        if (!$file->canView()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
683
            $this->httpError(403, _t(
684
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorItemPermissionDenied',
685
                'You don\'t have the necessary permissions to modify {ObjectTitle}',
686
                '',
687
                ['ObjectTitle' => $file->i18n_singular_name()]
688
            ));
689
            return null;
690
        }
691
        
692
        $scaffolder = $this->getFormFactory($file);
693
        $form = $scaffolder->getForm($this, 'FileInsertForm', [
694
            'Record' => $file,
695
            'Type' => 'insert',
696
        ]);
697
        
698
        return $form;
699
    }
700
    
701
    /**
702
     * Get file insert form
703
     *
704
     * @return Form
705
     */
706 View Code Duplication
    public function FileInsertForm()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
707
    {
708
        // Get ID either from posted back value, or url parameter
709
        $request = $this->getRequest();
710
        $id = $request->param('ID') ?: $request->postVar('ID');
711
        return $this->getFileInsertForm($id);
712
    }
713
714
    /**
715
     * @param array $data
716
     * @param Form $form
717
     * @return HTTPResponse
718
     */
719
    public function save($data, $form)
720
    {
721
        return $this->saveOrPublish($data, $form, false);
722
    }
723
724
    /**
725
     * @param array $data
726
     * @param Form $form
727
     * @return HTTPResponse
728
     */
729
    public function publish($data, $form)
730
    {
731
        return $this->saveOrPublish($data, $form, true);
732
    }
733
734
    /**
735
     * Update thisrecord
736
     *
737
     * @param array $data
738
     * @param Form $form
739
     * @param bool $doPublish
740
     * @return HTTPResponse
741
     */
742
    protected function saveOrPublish($data, $form, $doPublish = false)
743
    {
744 View Code Duplication
        if (!isset($data['ID']) || !is_numeric($data['ID'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
745
            return (new HTTPResponse(json_encode(['status' => 'error']), 400))
746
                ->addHeader('Content-Type', 'application/json');
747
        }
748
749
        $id = (int) $data['ID'];
750
        /** @var File $record */
751
        $record = $this->getList()->filter('ID', $id)->first();
752
753 View Code Duplication
        if (!$record) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
754
            return (new HTTPResponse(json_encode(['status' => 'error']), 404))
755
                ->addHeader('Content-Type', 'application/json');
756
        }
757
758
        if (!$record->canEdit() || ($doPublish && !$record->canPublish())) {
759
            return (new HTTPResponse(json_encode(['status' => 'error']), 401))
760
                ->addHeader('Content-Type', 'application/json');
761
        }
762
763
        $form->saveInto($record);
764
        $record->write();
765
766
        // Publish this record and owned objects
767
        if ($doPublish) {
768
            $record->publishRecursive();
769
        }
770
771
        // Return the record data in the same response as the schema to save a postback
772
        $schemaId = Controller::join_links($this->Link('schema/FileEditForm'), $record->exists() ? $record->ID : '');
773
        $schemaData = $this->getSchemaForForm($this->getFileEditForm($id), $schemaId);
0 ignored issues
show
Bug introduced by
It seems like $this->getFileEditForm($id) can be null; however, getSchemaForForm() 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...
774
        $schemaData['record'] = $this->getObjectFromData($record);
775
        $response = new HTTPResponse(Convert::raw2json($schemaData));
776
        $response->addHeader('Content-Type', 'application/json');
777
        return $response;
778
    }
779
780
    public function unpublish($data, $form)
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
781
    {
782 View Code Duplication
        if (!isset($data['ID']) || !is_numeric($data['ID'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
783
            return (new HTTPResponse(json_encode(['status' => 'error']), 400))
784
                ->addHeader('Content-Type', 'application/json');
785
        }
786
787
        $id = (int) $data['ID'];
788
        /** @var File $record */
789
        $record = $this->getList()->filter('ID', $id)->first();
790
791
        if (!$record) {
792
            return (new HTTPResponse(json_encode(['status' => 'error']), 404))
793
                ->addHeader('Content-Type', 'application/json');
794
        }
795
796
        if (!$record->canUnpublish()) {
797
            return (new HTTPResponse(json_encode(['status' => 'error']), 401))
798
                ->addHeader('Content-Type', 'application/json');
799
        }
800
801
        $record->doUnpublish();
802
803
        // Return the record data in the same response as the schema to save a postback
804
        $schemaId = Controller::join_links($this->Link('schema/FileEditForm'), $record->exists() ? $record->ID : '');
805
        $schemaData = $this->getSchemaForForm($this->getFileEditForm($id), $schemaId);
0 ignored issues
show
Bug introduced by
It seems like $this->getFileEditForm($id) can be null; however, getSchemaForForm() 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...
806
        $schemaData['record'] = $this->getObjectFromData($record);
807
        $response = new HTTPResponse(Convert::raw2json($schemaData));
808
        $response->addHeader('Content-Type', 'application/json');
809
        return $response;
810
    }
811
812
    /**
813
     * @param File $file
814
     *
815
     * @return array
816
     */
817
    public function getObjectFromData(File $file)
818
    {
819
        $object = array(
820
            'id' => $file->ID,
821
            'created' => $file->Created,
822
            'lastUpdated' => $file->LastEdited,
823
            'owner' => null,
824
            'parent' => null,
825
            'title' => $file->Title,
826
            'exists' => $file->exists(), // Broken file check
827
            'type' => $file instanceof Folder ? 'folder' : $file->FileType,
828
            'category' => $file instanceof Folder ? 'folder' : $file->appCategory(),
829
            'name' => $file->Name,
830
            'filename' => $file->Filename,
831
            'extension' => $file->Extension,
0 ignored issues
show
Bug introduced by
The property Extension does not seem to exist. Did you mean allowed_extensions?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
832
            'size' => $file->Size,
833
            'url' => $file->AbsoluteURL,
834
            'published' => $file->isPublished(),
835
            'modified' => $file->isModifiedOnDraft(),
836
            'draft' => $file->isOnDraftOnly(),
837
            'canEdit' => $file->canEdit(),
838
            'canDelete' => $file->canArchive(),
839
        );
840
841
        /** @var Member $owner */
842
        $owner = $file->Owner();
843
844
        if ($owner) {
845
            $object['owner'] = array(
846
                'id' => $owner->ID,
847
                'title' => trim($owner->FirstName . ' ' . $owner->Surname),
848
            );
849
        }
850
851
        /** @var Folder $parent */
852
        $parent = $file->Parent();
853
854
        if ($parent) {
855
            $object['parent'] = array(
856
                'id' => $parent->ID,
857
                'title' => $parent->Title,
858
                'filename' => $parent->Filename,
859
            );
860
        }
861
862
        /** @var File $file */
863
        if ($file->getIsImage()) {
864
            // Small thumbnail
865
            $smallWidth = FileField::config()->get('thumbnail_width');
866
            $smallHeight = FileField::config()->get('thumbnail_height');
867
            $smallThumbnail = $file->FitMax($smallWidth, $smallHeight);
868
            if ($smallThumbnail && $smallThumbnail->exists()) {
869
                $object['smallThumbnail'] = $smallThumbnail->getAbsoluteURL();
870
            }
871
872
            // Large thumbnail
873
            $width = $this->config()->get('thumbnail_width');
874
            $height = $this->config()->get('thumbnail_height');
875
            $thumbnail = $file->FitMax($width, $height);
876
            if ($thumbnail && $thumbnail->exists()) {
877
                $object['thumbnail'] = $thumbnail->getAbsoluteURL();
878
            }
879
            $object['dimensions']['width'] = $file->Width;
880
            $object['dimensions']['height'] = $file->Height;
0 ignored issues
show
Bug introduced by
The property Height does not seem to exist. Did you mean asset_preview_height?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
881
        }
882
883
        return $object;
884
    }
885
886
    /**
887
     * Returns the files and subfolders contained in the currently selected folder,
888
     * defaulting to the root node. Doubles as search results, if any search parameters
889
     * are set through {@link SearchForm()}.
890
     *
891
     * @param array $params Unsanitised request parameters
892
     * @return DataList
893
     */
894
    protected function getList($params = array())
895
    {
896
        $context = $this->getSearchContext();
897
898
        // Overwrite name filter to search both Name and Title attributes
899
        $context->removeFilterByName('Name');
900
901
        // Lazy loaded list. Allows adding new filters through SearchContext.
902
        /** @var DataList $list */
903
        $list = $context->getResults($params);
904
905
        // Re-add previously removed "Name" filter as combined filter
906
        // TODO Replace with composite SearchFilter once that API exists
907
        if(!empty($params['Name'])) {
908
            $list = $list->filterAny(array(
909
                'Name:PartialMatch' => $params['Name'],
910
                'Title:PartialMatch' => $params['Name']
911
            ));
912
        }
913
914
        // Optionally limit search to a folder (non-recursive)
915
        if(!empty($params['ParentID']) && is_numeric($params['ParentID'])) {
916
            $list = $list->filter('ParentID', $params['ParentID']);
917
        }
918
919
        // Date filtering
920 View Code Duplication
        if (!empty($params['CreatedFrom'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
921
            $fromDate = new DateField(null, null, $params['CreatedFrom']);
922
            $list = $list->filter("Created:GreaterThanOrEqual", $fromDate->dataValue().' 00:00:00');
923
        }
924 View Code Duplication
        if (!empty($params['CreatedTo'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
925
            $toDate = new DateField(null, null, $params['CreatedTo']);
926
            $list = $list->filter("Created:LessThanOrEqual", $toDate->dataValue().' 23:59:59');
927
        }
928
929
        // Categories
930
        if (!empty($filters['AppCategory']) && !empty(File::config()->app_categories[$filters['AppCategory']])) {
0 ignored issues
show
Bug introduced by
The variable $filters seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
931
            $extensions = File::config()->app_categories[$filters['AppCategory']];
932
            $list = $list->filter('Name:PartialMatch', $extensions);
933
        }
934
935
        // Sort folders first
936
        $list = $list->sort(
937
            '(CASE WHEN "File"."ClassName" = \'Folder\' THEN 0 ELSE 1 END), "Name"'
938
        );
939
940
        // Pagination
941
        if (isset($filters['page']) && isset($filters['limit'])) {
942
            $page = $filters['page'];
943
            $limit = $filters['limit'];
944
            $offset = ($page - 1) * $limit;
945
            $list = $list->limit($limit, $offset);
946
        }
947
948
        // Access checks
949
        $list = $list->filterByCallback(function(File $file) {
950
            return $file->canView();
951
        });
952
953
        return $list;
954
    }
955
956
    /**
957
     * Action handler for adding pages to a campaign
958
     *
959
     * @param array $data
960
     * @param Form $form
961
     * @return DBHTMLText|HTTPResponse
962
     */
963
    public function addtocampaign($data, $form)
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
964
    {
965
        $id = $data['ID'];
966
        $record = $this->getList()->byID($id);
967
968
        $handler = AddToCampaignHandler::create($this, $record);
969
        $results = $handler->addToCampaign($record, $data['Campaign']);
970
        if (!isset($results)) {
971
            return null;
972
        }
973
        $request = $this->getRequest();
974
        if($request->getHeader('X-Formschema-Request')) {
975
            $data = $this->getSchemaForForm($handler->Form($record));
976
            $data['message'] = $results;
977
978
            $response = new HTTPResponse(Convert::raw2json($data));
979
            $response->addHeader('Content-Type', 'application/json');
980
            return $response;
981
        }
982
        return $results;
983
    }
984
985
    /**
986
     * Url handler for add to campaign form
987
     *
988
     * @param HTTPRequest $request
989
     * @return Form
990
     */
991
    public function AddToCampaignForm($request)
992
    {
993
        // Get ID either from posted back value, or url parameter
994
        $id = $request->param('ID') ?: $request->postVar('ID');
995
        return $this->getAddToCampaignForm($id);
996
    }
997
998
    /**
999
     * @param int $id
1000
     * @return Form
1001
     */
1002
    public function getAddToCampaignForm($id)
1003
    {
1004
        // Get record-specific fields
1005
        $record = $this->getList()->byID($id);
1006
1007
        if (!$record) {
1008
            $this->httpError(404, _t(
1009
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorNotFound',
1010
                'That {Type} couldn\'t be found',
1011
                '',
1012
                ['Type' => File::singleton()->i18n_singular_name()]
1013
            ));
1014
            return null;
1015
        }
1016 View Code Duplication
        if (!$record->canView()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1017
            $this->httpError(403, _t(
1018
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorItemPermissionDenied',
1019
                'You don\'t have the necessary permissions to modify {ObjectTitle}',
1020
                '',
1021
                ['ObjectTitle' => $record->i18n_singular_name()]
1022
            ));
1023
            return null;
1024
        }
1025
1026
        $handler = AddToCampaignHandler::create($this, $record);
1027
        $form = $handler->Form($record);
1028
1029 View Code Duplication
        $form->setValidationResponseCallback(function() use ($form, $id) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1030
            $schemaId = Controller::join_links($this->Link('schema/AddToCampaignForm'), $id);
1031
            return $this->getSchemaResponse($form, $schemaId);
1032
        });
1033
1034
        return $form;
1035
    }
1036
1037
    /**
1038
     * @return Upload
1039
     */
1040
    protected function getUpload()
1041
    {
1042
        $upload = Upload::create();
1043
        $upload->getValidator()->setAllowedExtensions(
1044
            // filter out '' since this would be a regex problem on JS end
1045
            array_filter(File::config()->get('allowed_extensions'))
1046
        );
1047
1048
        return $upload;
1049
    }
1050
}
1051