Completed
Pull Request — master (#418)
by Damian
02:16
created

AssetAdmin   F

Complexity

Total Complexity 131

Size/Duplication

Total Lines 1158
Duplicated Lines 8.2 %

Coupling/Cohesion

Components 1
Dependencies 21

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 131
c 4
b 0
f 0
lcom 1
cbo 21
dl 95
loc 1158
rs 0.5217

39 Methods

Rating   Name   Duplication   Size   Complexity  
A legacyRedirectForEditView() 0 8 2
A getFileEditLink() 0 13 3
A init() 0 10 1
A getClientConfig() 0 49 1
C apiCreateFile() 37 75 11
D apiUploadFile() 15 81 15
D apiHistory() 0 88 16
A getNameGenerator() 0 5 1
A breadcrumbs() 0 4 1
A baseCSSClasses() 0 4 1
A providePermissions() 0 11 1
A getFormFactory() 0 13 3
A getFileEditForm() 0 4 1
A fileEditForm() 0 14 3
A getFileInsertForm() 0 4 1
A fileInsertForm() 0 7 2
B getAbstractFileForm() 9 39 3
A fileSelectForm() 0 7 2
A getFileSelectForm() 0 4 1
C getFileHistoryForm() 13 47 7
A schema() 0 23 2
B fileHistoryForm() 0 23 4
A save() 0 4 1
A publish() 0 4 1
C saveOrPublish() 4 47 10
B unpublish() 4 24 5
C getObjectFromData() 0 70 10
A addtocampaign() 0 16 2
A addToCampaignForm() 0 6 2
B getAddToCampaignForm() 13 34 3
A getUpload() 0 10 1
A getRecordUpdatedResponse() 0 7 1
A fileSearchForm() 0 5 1
A getFileSearchform() 0 4 1
A getRemoteCreateForm() 0 5 1
A remoteCreateForm() 0 4 1
A getRemoteEditForm() 0 8 1
A remoteEditForm() 0 4 1
C schemaWithValidate() 0 40 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AssetAdmin often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AssetAdmin, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SilverStripe\AssetAdmin\Controller;
4
5
use Embed\Exceptions\InvalidUrlException;
6
use InvalidArgumentException;
7
use SilverStripe\Admin\LeftAndMainFormRequestHandler;
8
use SilverStripe\CampaignAdmin\AddToCampaignHandler;
9
use SilverStripe\Admin\CMSBatchActionHandler;
10
use SilverStripe\Admin\LeftAndMain;
11
use SilverStripe\AssetAdmin\BatchAction\DeleteAssets;
12
use SilverStripe\AssetAdmin\Forms\AssetFormFactory;
13
use SilverStripe\AssetAdmin\Forms\FileSearchFormFactory;
14
use SilverStripe\AssetAdmin\Forms\RemoteFileFormFactory;
15
use SilverStripe\AssetAdmin\Forms\UploadField;
16
use SilverStripe\AssetAdmin\Forms\FileFormFactory;
17
use SilverStripe\AssetAdmin\Forms\FolderFormFactory;
18
use SilverStripe\AssetAdmin\Forms\FileHistoryFormFactory;
19
use SilverStripe\AssetAdmin\Forms\ImageFormFactory;
20
use SilverStripe\Assets\File;
21
use SilverStripe\Assets\Folder;
22
use SilverStripe\Assets\Image;
23
use SilverStripe\Assets\Storage\AssetNameGenerator;
24
use SilverStripe\Assets\Upload;
25
use SilverStripe\Control\Controller;
26
use SilverStripe\Control\HTTPRequest;
27
use SilverStripe\Control\HTTPResponse;
28
use SilverStripe\Core\Injector\Injector;
29
use SilverStripe\Forms\FieldList;
30
use SilverStripe\Forms\Form;
31
use SilverStripe\Forms\FormFactory;
32
use SilverStripe\ORM\ArrayList;
33
use SilverStripe\ORM\DataObject;
34
use SilverStripe\ORM\FieldType\DBHTMLText;
35
use SilverStripe\ORM\ValidationResult;
36
use SilverStripe\Security\Member;
37
use SilverStripe\Security\PermissionProvider;
38
use SilverStripe\Security\SecurityToken;
39
use SilverStripe\View\Requirements;
40
use SilverStripe\Versioned\Versioned;
41
use Exception;
42
43
/**
44
 * AssetAdmin is the 'file store' section of the CMS.
45
 * It provides an interface for manipulating the File and Folder objects in the system.
46
 */
47
class AssetAdmin extends LeftAndMain implements PermissionProvider
48
{
49
    private static $url_segment = 'assets';
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
50
51
    private static $url_rule = '/$Action/$ID';
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
52
53
    private static $menu_title = 'Files';
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
54
55
    private static $menu_icon_class = 'font-icon-image';
0 ignored issues
show
Unused Code introduced by
The property $menu_icon_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...
56
57
    private static $tree_class = Folder::class;
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...
58
59
    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...
60
        // Legacy redirect for SS3-style detail view
61
        'EditForm/field/File/item/$FileID/$Action' => 'legacyRedirectForEditView',
62
        // Pass all URLs to the index, for React to unpack
63
        'show/$FolderID/edit/$FileID' => 'index',
64
        // API access points with structured data
65
        'POST api/createFile' => 'apiCreateFile',
66
        'POST api/uploadFile' => 'apiUploadFile',
67
        'GET api/history' => 'apiHistory',
68
        // for validating before generating the schema
69
        'schemaWithValidate/$FormName' => 'schemaWithValidate',
70
        'fileEditForm/$ID' => 'fileEditForm',
71
        'fileHistoryForm/$ID/$VersionID' => 'fileHistoryForm',
72
    ];
73
74
    /**
75
     * Amount of results showing on a single page.
76
     *
77
     * @config
78
     * @var int
79
     */
80
    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...
81
82
    /**
83
     * @config
84
     * @see Upload->allowedMaxFileSize
85
     * @var int
86
     */
87
    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...
88
89
    /**
90
     * @config
91
     *
92
     * @var int
93
     */
94
    private static $max_history_entries = 100;
0 ignored issues
show
Unused Code introduced by
The property $max_history_entries 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...
95
96
    /**
97
     * @var array
98
     */
99
    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...
100
        'legacyRedirectForEditView',
101
        'apiCreateFile',
102
        'apiUploadFile',
103
        'apiHistory',
104
        'fileEditForm',
105
        'fileHistoryForm',
106
        'addToCampaignForm',
107
        'fileInsertForm',
108
        'remoteEditForm',
109
        'remoteCreateForm',
110
        'schema',
111
        'fileSelectForm',
112
        'fileSearchForm',
113
        'schemaWithValidate',
114
    );
115
116
    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...
117
118
    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...
119
120
    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...
121
122
    /**
123
     * Set up the controller
124
     */
125
    public function init()
126
    {
127
        parent::init();
128
129
        Requirements::add_i18n_javascript(ASSET_ADMIN_DIR . '/client/lang', false, true);
130
        Requirements::javascript(ASSET_ADMIN_DIR . "/client/dist/js/bundle.js");
131
        Requirements::css(ASSET_ADMIN_DIR . "/client/dist/styles/bundle.css");
132
133
        CMSBatchActionHandler::register('delete', DeleteAssets::class, Folder::class);
134
    }
135
136
    public function getClientConfig()
137
    {
138
        $baseLink = $this->Link();
139
        return array_merge(parent::getClientConfig(), [
140
            'reactRouter' => true,
141
            'createFileEndpoint' => [
142
                'url' => Controller::join_links($baseLink, 'api/createFile'),
143
                'method' => 'post',
144
                'payloadFormat' => 'urlencoded',
145
            ],
146
            'uploadFileEndpoint' => [
147
                'url' => Controller::join_links($baseLink, 'api/uploadFile'),
148
                'method' => 'post',
149
                'payloadFormat' => 'urlencoded',
150
            ],
151
            'historyEndpoint' => [
152
                'url' => Controller::join_links($baseLink, 'api/history'),
153
                'method' => 'get',
154
                'responseFormat' => 'json',
155
            ],
156
            'limit' => $this->config()->page_length,
157
            'form' => [
158
                'fileEditForm' => [
159
                    'schemaUrl' => $this->Link('schema/fileEditForm')
160
                ],
161
                'fileInsertForm' => [
162
                    'schemaUrl' => $this->Link('schema/fileInsertForm')
163
                ],
164
                'remoteEditForm' => [
165
                    'schemaUrl' => $this->Link('schemaWithValidate/remoteEditForm')
166
                ],
167
                'remoteCreateForm' => [
168
                    'schemaUrl' => $this->Link('schema/remoteCreateForm')
169
                ],
170
                'fileSelectForm' => [
171
                    'schemaUrl' => $this->Link('schema/fileSelectForm')
172
                ],
173
                'addToCampaignForm' => [
174
                    'schemaUrl' => $this->Link('schema/addToCampaignForm')
175
                ],
176
                'fileHistoryForm' => [
177
                    'schemaUrl' => $this->Link('schema/fileHistoryForm')
178
                ],
179
                'fileSearchForm' => [
180
                    'schemaUrl' => $this->Link('schema/fileSearchForm')
181
                ],
182
            ],
183
        ]);
184
    }
185
186
    /**
187
     * Creates a single file based on a form-urlencoded upload.
188
     *
189
     * @param HTTPRequest $request
190
     * @return HTTPRequest|HTTPResponse
191
     */
192
    public function apiCreateFile(HTTPRequest $request)
193
    {
194
        $data = $request->postVars();
195
196
        // When creating new files, rename on conflict
197
        $upload = $this->getUpload();
198
        $upload->setReplaceFile(false);
199
200
        // CSRF check
201
        $token = SecurityToken::inst();
202 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...
203
            return new HTTPResponse(null, 400);
204
        }
205
206
        // Check parent record
207
        /** @var Folder $parentRecord */
208
        $parentRecord = null;
209
        if (!empty($data['ParentID']) && is_numeric($data['ParentID'])) {
210
            $parentRecord = Folder::get()->byID($data['ParentID']);
211
        }
212
        $data['Parent'] = $parentRecord;
213
214
        $tmpFile = $request->postVar('Upload');
215 View Code Duplication
        if (!$upload->validate($tmpFile)) {
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...
216
            $result = ['message' => null];
217
            $errors = $upload->getErrors();
218
            if ($message = array_shift($errors)) {
219
                $result['message'] = [
220
                    'type' => 'error',
221
                    'value' => $message,
222
                ];
223
            }
224
            return (new HTTPResponse(json_encode($result), 400))
225
                ->addHeader('Content-Type', 'application/json');
226
        }
227
228
        // TODO Allow batch uploads
229
        $fileClass = File::get_class_for_file_extension(File::get_file_extension($tmpFile['name']));
230
        /** @var File $file */
231
        $file = Injector::inst()->create($fileClass);
232
233
        // check canCreate permissions
234 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...
235
            $result = ['message' => [
236
                'type' => 'error',
237
                'value' => _t(
238
                    'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.CreatePermissionDenied',
239
                    'You do not have permission to add files'
240
                )
241
            ]];
242
            return (new HTTPResponse(json_encode($result), 403))
243
                ->addHeader('Content-Type', 'application/json');
244
        }
245
246
        $uploadResult = $upload->loadIntoFile($tmpFile, $file, $parentRecord ? $parentRecord->getFilename() : '/');
247 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...
248
            $result = ['message' => [
249
                'type' => 'error',
250
                'value' => _t(
251
                    'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.LoadIntoFileFailed',
252
                    'Failed to load file'
253
                )
254
            ]];
255
            return (new HTTPResponse(json_encode($result), 400))
256
                ->addHeader('Content-Type', 'application/json');
257
        }
258
259
        $file->ParentID = $parentRecord ? $parentRecord->ID : 0;
260
        $file->write();
261
262
        $result = [$this->getObjectFromData($file)];
263
264
        return (new HTTPResponse(json_encode($result)))
265
            ->addHeader('Content-Type', 'application/json');
266
    }
267
268
    /**
269
     * Upload a new asset for a pre-existing record. Returns the asset tuple.
270
     *
271
     * Note that conflict resolution is as follows:
272
     *  - If uploading a file with the same extension, we simply keep the same filename,
273
     *    and overwrite any existing files (same name + sha = don't duplicate).
274
     *  - If uploading a new file with a different extension, then the filename will
275
     *    be replaced, and will be checked for uniqueness against other File dataobjects.
276
     *
277
     * @param HTTPRequest $request Request containing vars 'ID' of parent record ID,
278
     * and 'Name' as form filename value
279
     * @return HTTPRequest|HTTPResponse
280
     */
281
    public function apiUploadFile(HTTPRequest $request)
282
    {
283
        $data = $request->postVars();
284
285
        // When updating files, replace on conflict
286
        $upload = $this->getUpload();
287
        $upload->setReplaceFile(true);
288
289
        // CSRF check
290
        $token = SecurityToken::inst();
291 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...
292
            return new HTTPResponse(null, 400);
293
        }
294
        $tmpFile = $data['Upload'];
295
        if (empty($data['ID']) || empty($tmpFile['name']) || !array_key_exists('Name', $data)) {
296
            return new HTTPResponse('Invalid request', 400);
297
        }
298
299
        // Check parent record
300
        /** @var File $file */
301
        $file = File::get()->byID($data['ID']);
302
        if (!$file) {
303
            return new HTTPResponse('File not found', 404);
304
        }
305
        $folder = $file->ParentID ? $file->Parent()->getFilename() : '/';
306
307
        // If extension is the same, attempt to re-use existing name
308
        if (File::get_file_extension($tmpFile['name']) === File::get_file_extension($data['Name'])) {
309
            $tmpFile['name'] = $data['Name'];
310
        } else {
311
            // If we are allowing this upload to rename the underlying record,
312
            // do a uniqueness check.
313
            $renamer = $this->getNameGenerator($tmpFile['name']);
314
            foreach ($renamer as $name) {
315
                $filename = File::join_paths($folder, $name);
316
                if (!File::find($filename)) {
317
                    $tmpFile['name'] = $name;
318
                    break;
319
                }
320
            }
321
        }
322
323 View Code Duplication
        if (!$upload->validate($tmpFile)) {
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...
324
            $result = ['message' => null];
325
            $errors = $upload->getErrors();
326
            if ($message = array_shift($errors)) {
327
                $result['message'] = [
328
                    'type' => 'error',
329
                    'value' => $message,
330
                ];
331
            }
332
            return (new HTTPResponse(json_encode($result), 400))
333
                ->addHeader('Content-Type', 'application/json');
334
        }
335
336
        try {
337
            $tuple = $upload->load($tmpFile, $folder);
338
        } catch (Exception $e) {
339
            $result = [
340
                'message' => [
341
                    'type' => 'error',
342
                    'value' => $e->getMessage(),
343
                ]
344
            ];
345
            return (new HTTPResponse(json_encode($result), 400))
346
                ->addHeader('Content-Type', 'application/json');
347
        }
348
349
        if ($upload->isError()) {
350
            $result['message'] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
351
                'type' => 'error',
352
                'value' => implode(' ' . PHP_EOL, $upload->getErrors()),
353
            ];
354
            return (new HTTPResponse(json_encode($result), 400))
355
                ->addHeader('Content-Type', 'application/json');
356
        }
357
358
        $tuple['Name'] = basename($tuple['Filename']);
359
        return (new HTTPResponse(json_encode($tuple)))
360
            ->addHeader('Content-Type', 'application/json');
361
    }
362
363
    /**
364
     * Returns a JSON array for history of a given file ID. Returns a list of all the history.
365
     *
366
     * @param HTTPRequest $request
367
     * @return HTTPResponse
368
     */
369
    public function apiHistory(HTTPRequest $request)
370
    {
371
        // CSRF check not required as the GET request has no side effects.
372
        $fileId = $request->getVar('fileId');
373
374
        if (!$fileId || !is_numeric($fileId)) {
375
            return new HTTPResponse(null, 400);
376
        }
377
378
        $class = File::class;
379
        $file = DataObject::get($class)->byID($fileId);
380
381
        if (!$file) {
382
            return new HTTPResponse(null, 404);
383
        }
384
385
        if (!$file->canView()) {
386
            return new HTTPResponse(null, 403);
387
        }
388
389
        $versions = Versioned::get_all_versions($class, $fileId)
390
            ->limit($this->config()->max_history_entries)
391
            ->sort('Version', 'DESC');
392
393
        $output = array();
394
        $next = array();
395
        $prev = null;
396
397
        // swap the order so we can get the version number to compare against.
398
        // i.e version 3 needs to know version 2 is the previous version
399
        $copy = $versions->map('Version', 'Version')->toArray();
400
        foreach (array_reverse($copy) as $k => $v) {
401
            if ($prev) {
402
                $next[$v] = $prev;
403
            }
404
405
            $prev = $v;
406
        }
407
408
        $_cachedMembers = array();
409
410
        /** @var File $version */
411
        foreach ($versions as $version) {
412
            $author = null;
413
414
            if ($version->AuthorID) {
415
                if (!isset($_cachedMembers[$version->AuthorID])) {
416
                    $_cachedMembers[$version->AuthorID] = DataObject::get(Member::class)
417
                        ->byID($version->AuthorID);
418
                }
419
420
                $author = $_cachedMembers[$version->AuthorID];
421
            }
422
423
            if ($version->canView()) {
424
                if (isset($next[$version->Version])) {
425
                    $summary = $version->humanizedChanges(
426
                        $version->Version,
427
                        $next[$version->Version]
428
                    );
429
430
                    // if no summary returned by humanizedChanges, i.e we cannot work out what changed, just show a
431
                    // generic message
432
                    if (!$summary) {
433
                        $summary = _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.SAVEDFILE', "Saved file");
434
                    }
435
                } else {
436
                    $summary = _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.UPLOADEDFILE', "Uploaded file");
437
                }
438
439
                $output[] = array(
440
                    'versionid' => $version->Version,
441
                    'date_ago' => $version->dbObject('LastEdited')->Ago(),
442
                    'date_formatted' => $version->dbObject('LastEdited')->Nice(),
443
                    'status' => ($version->WasPublished) ? _t('File.PUBLISHED', 'Published') : '',
444
                    'author' => ($author)
445
                        ? $author->Name
446
                        : _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.UNKNOWN', "Unknown"),
447
                    'summary' => ($summary)
448
                        ? $summary
449
                        : _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.NOSUMMARY', "No summary available")
450
                );
451
            }
452
        }
453
454
        return
455
            (new HTTPResponse(json_encode($output)))->addHeader('Content-Type', 'application/json');
456
    }
457
458
    /**
459
     * Redirects 3.x style detail links to new 4.x style routing.
460
     *
461
     * @param HTTPRequest $request
462
     */
463
    public function legacyRedirectForEditView($request)
464
    {
465
        $fileID = $request->param('FileID');
466
        /** @var File $file */
467
        $file = File::get()->byID($fileID);
468
        $link = $this->getFileEditLink($file) ?: $this->Link();
469
        $this->redirect($link);
470
    }
471
472
    /**
473
     * Given a file return the CMS link to edit it
474
     *
475
     * @param File $file
476
     * @return string
477
     */
478
    public function getFileEditLink($file)
479
    {
480
        if (!$file || !$file->isInDB()) {
481
            return null;
482
        }
483
484
        return Controller::join_links(
485
            $this->Link('show'),
486
            $file->ParentID,
487
            'edit',
488
            $file->ID
489
        );
490
    }
491
492
    /**
493
     * Get an asset renamer for the given filename.
494
     *
495
     * @param  string             $filename Path name
496
     * @return AssetNameGenerator
497
     */
498
    protected function getNameGenerator($filename)
499
    {
500
        return Injector::inst()
501
            ->createWithArgs('AssetNameGenerator', array($filename));
502
    }
503
504
    /**
505
     * @todo Implement on client
506
     *
507
     * @param bool $unlinked
508
     * @return ArrayList
509
     */
510
    public function breadcrumbs($unlinked = false)
511
    {
512
        return null;
513
    }
514
515
516
    /**
517
     * Don't include class namespace in auto-generated CSS class
518
     */
519
    public function baseCSSClasses()
520
    {
521
        return 'AssetAdmin LeftAndMain';
522
    }
523
524
    public function providePermissions()
525
    {
526
        return array(
527
            "CMS_ACCESS_AssetAdmin" => array(
528
                'name' => _t('CMSMain.ACCESS', "Access to '{title}' section", array(
529
                    'title' => static::menu_title()
530
                )),
531
                'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
532
            )
533
        );
534
    }
535
536
    /**
537
     * Build a form scaffolder for this model
538
     *
539
     * NOTE: Volatile api. May be moved to {@see LeftAndMain}
540
     *
541
     * @param File $file
542
     * @return FormFactory
543
     */
544
    public function getFormFactory(File $file)
545
    {
546
        // Get service name based on file class
547
        $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...
548
        if ($file instanceof Folder) {
0 ignored issues
show
Bug introduced by
The class SilverStripe\Assets\Folder does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
549
            $name = FolderFormFactory::class;
550
        } elseif ($file instanceof Image) {
0 ignored issues
show
Bug introduced by
The class SilverStripe\Assets\Image does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
551
            $name = ImageFormFactory::class;
552
        } else {
553
            $name = FileFormFactory::class;
554
        }
555
        return Injector::inst()->get($name);
556
    }
557
558
    /**
559
     * The form is used to generate a form schema,
560
     * as well as an intermediary object to process data through API endpoints.
561
     * Since it's used directly on API endpoints, it does not have any form actions.
562
     * It handles both {@link File} and {@link Folder} records.
563
     *
564
     * @param int $id
565
     * @return Form
566
     */
567
    public function getFileEditForm($id)
568
    {
569
        return $this->getAbstractFileForm($id, 'fileEditForm');
570
    }
571
572
    /**
573
     * Get file edit form
574
     *
575
     * @param HTTPRequest $request
576
     * @return Form
577
     */
578
    public function fileEditForm($request = null)
579
    {
580
        // Get ID either from posted back value, or url parameter
581
        if (!$request) {
582
            $this->httpError(400);
583
            return null;
584
        }
585
        $id = $request->param('ID');
586
        if (!$id) {
587
            $this->httpError(400);
588
            return null;
589
        }
590
        return $this->getFileEditForm($id);
591
    }
592
593
    /**
594
     * The form is used to generate a form schema,
595
     * as well as an intermediary object to process data through API endpoints.
596
     * Since it's used directly on API endpoints, it does not have any form actions.
597
     * It handles both {@link File} and {@link Folder} records.
598
     *
599
     * @param int $id
600
     * @return Form
601
     */
602
    public function getFileInsertForm($id)
603
    {
604
        return $this->getAbstractFileForm($id, 'fileInsertForm', [ 'Type' => AssetFormFactory::TYPE_INSERT ]);
605
    }
606
607
    /**
608
     * Get file insert form
609
     *
610
     * @return Form
611
     */
612
    public function fileInsertForm()
613
    {
614
        // Get ID either from posted back value, or url parameter
615
        $request = $this->getRequest();
616
        $id = $request->param('ID') ?: $request->postVar('ID');
617
        return $this->getFileInsertForm($id);
618
    }
619
620
    /**
621
     * Abstract method for generating a form for a file
622
     *
623
     * @param int $id Record ID
624
     * @param string $name Form name
625
     * @param array $context Form context
626
     * @return Form
627
     */
628
    protected function getAbstractFileForm($id, $name, $context = [])
629
    {
630
        /** @var File $file */
631
        $file = File::get()->byID($id);
632
633
        if (!$file) {
634
            $this->httpError(404);
635
            return null;
636
        }
637
638 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...
639
            $this->httpError(403, _t(
640
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorItemPermissionDenied',
641
                'You don\'t have the necessary permissions to modify {ObjectTitle}',
642
                '',
643
                ['ObjectTitle' => $file->i18n_singular_name()]
644
            ));
645
            return null;
646
        }
647
648
        // Pass to form factory
649
        $augmentedContext = array_merge($context, ['Record' => $file]);
650
        $scaffolder = $this->getFormFactory($file);
651
        $form = $scaffolder->getForm($this, $name, $augmentedContext);
652
653
        // Set form action handler with ID included
654
        $form->setRequestHandler(
655
            LeftAndMainFormRequestHandler::create($form, [ $id ])
656
        );
657
658
        // Configure form to respond to validation errors with form schema
659
        // if requested via react.
660
        $form->setValidationResponseCallback(function (ValidationResult $error) use ($form, $id, $name) {
661
            $schemaId = Controller::join_links($this->Link('schema'), $name, $id);
662
            return $this->getSchemaResponse($schemaId, $form, $error);
663
        });
664
665
        return $form;
666
    }
667
668
    /**
669
     * Get form for selecting a file
670
     *
671
     * @return Form
672
     */
673
    public function fileSelectForm()
674
    {
675
        // Get ID either from posted back value, or url parameter
676
        $request = $this->getRequest();
677
        $id = $request->param('ID') ?: $request->postVar('ID');
678
        return $this->getFileSelectForm($id);
679
    }
680
681
    /**
682
     * Get form for selecting a file
683
     *
684
     * @param int $id ID of the record being selected
685
     * @return Form
686
     */
687
    public function getFileSelectForm($id)
688
    {
689
        return $this->getAbstractFileForm($id, 'fileSelectForm', [ 'Type' => AssetFormFactory::TYPE_SELECT ]);
690
    }
691
692
    /**
693
     * @param array $context
694
     * @return Form
695
     * @throws InvalidArgumentException
696
     */
697
    public function getFileHistoryForm($context)
698
    {
699
        // Check context
700
        if (!isset($context['RecordID']) || !isset($context['RecordVersion'])) {
701
            throw new InvalidArgumentException("Missing RecordID / RecordVersion for this form");
702
        }
703
        $id = $context['RecordID'];
704
        $versionId = $context['RecordVersion'];
705
        if (!$id || !$versionId) {
706
            return $this->httpError(404);
707
        }
708
709
        /** @var File $file */
710
        $file = Versioned::get_version(File::class, $id, $versionId);
711
        if (!$file) {
712
            return $this->httpError(404);
713
        }
714
715 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...
716
            $this->httpError(403, _t(
717
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorItemPermissionDenied',
718
                'You don\'t have the necessary permissions to modify {ObjectTitle}',
719
                '',
720
                ['ObjectTitle' => $file->i18n_singular_name()]
721
            ));
722
            return null;
723
        }
724
725
        $effectiveContext = array_merge($context, ['Record' => $file]);
726
        /** @var FormFactory $scaffolder */
727
        $scaffolder = Injector::inst()->get(FileHistoryFormFactory::class);
728
        $form = $scaffolder->getForm($this, 'fileHistoryForm', $effectiveContext);
729
730
        // Set form handler with ID / VersionID
731
        $form->setRequestHandler(
732
            LeftAndMainFormRequestHandler::create($form, [ $id, $versionId ])
733
        );
734
735
        // Configure form to respond to validation errors with form schema
736
        // if requested via react.
737 View Code Duplication
        $form->setValidationResponseCallback(function (ValidationResult $errors) use ($form, $id, $versionId) {
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...
738
            $schemaId = Controller::join_links($this->Link('schema/fileHistoryForm'), $id, $versionId);
739
            return $this->getSchemaResponse($schemaId, $form, $errors);
740
        });
741
742
        return $form;
743
    }
744
745
    /**
746
     * Gets a JSON schema representing the current edit form.
747
     *
748
     * WARNING: Experimental API.
749
     *
750
     * @param HTTPRequest $request
751
     * @return HTTPResponse
752
     */
753
    public function schema($request)
754
    {
755
        $formName = $request->param('FormName');
756
        if ($formName !== 'fileHistoryForm') {
757
            return parent::schema($request);
758
        }
759
760
        // Get schema for history form
761
        // @todo Eventually all form scaffolding will be based on context rather than record ID
762
        // See https://github.com/silverstripe/silverstripe-framework/issues/6362
763
        $itemID = $request->param('ItemID');
764
        $version = $request->param('OtherItemID');
765
        $form = $this->getFileHistoryForm([
766
            'RecordID' => $itemID,
767
            'RecordVersion' => $version,
768
        ]);
769
770
        // Respond with this schema
771
        $response = $this->getResponse();
772
        $response->addHeader('Content-Type', 'application/json');
773
        $schemaID = $this->getRequest()->getURL();
774
        return $this->getSchemaResponse($schemaID, $form);
775
    }
776
777
    /**
778
     * Get file history form
779
     *
780
     * @param HTTPRequest $request
781
     * @return Form
782
     */
783
    public function fileHistoryForm($request = null)
784
    {
785
        // Get ID either from posted back value, or url parameter
786
        if (!$request) {
787
            $this->httpError(400);
788
            return null;
789
        }
790
        $id = $request->param('ID');
791
        if (!$id) {
792
            $this->httpError(400);
793
            return null;
794
        }
795
        $versionID = $request->param('VersionID');
796
        if (!$versionID) {
797
            $this->httpError(400);
798
            return null;
799
        }
800
        $form = $this->getFileHistoryForm([
801
            'RecordID' => $id,
802
            'RecordVersion' => $versionID,
803
        ]);
804
        return $form;
805
    }
806
807
    /**
808
     * @param array $data
809
     * @param Form $form
810
     * @return HTTPResponse
811
     */
812
    public function save($data, $form)
813
    {
814
        return $this->saveOrPublish($data, $form, false);
815
    }
816
817
    /**
818
     * @param array $data
819
     * @param Form $form
820
     * @return HTTPResponse
821
     */
822
    public function publish($data, $form)
823
    {
824
        return $this->saveOrPublish($data, $form, true);
825
    }
826
827
    /**
828
     * Update thisrecord
829
     *
830
     * @param array $data
831
     * @param Form $form
832
     * @param bool $doPublish
833
     * @return HTTPResponse
834
     */
835
    protected function saveOrPublish($data, $form, $doPublish = false)
836
    {
837 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...
838
            return (new HTTPResponse(json_encode(['status' => 'error']), 400))
839
                ->addHeader('Content-Type', 'application/json');
840
        }
841
842
        $id = (int) $data['ID'];
843
        /** @var File $record */
844
        $record = DataObject::get_by_id(File::class, $id);
845
846
        if (!$record) {
847
            return (new HTTPResponse(json_encode(['status' => 'error']), 404))
848
                ->addHeader('Content-Type', 'application/json');
849
        }
850
851
        if (!$record->canEdit() || ($doPublish && !$record->canPublish())) {
852
            return (new HTTPResponse(json_encode(['status' => 'error']), 401))
853
                ->addHeader('Content-Type', 'application/json');
854
        }
855
856
        // check File extension
857
        if (!empty($data['FileFilename'])) {
858
            $extension = File::get_file_extension($data['FileFilename']);
859
            $newClass = File::get_class_for_file_extension($extension);
860
861
            // if the class has changed, cast it to the proper class
862
            if ($record->getClassName() !== $newClass) {
863
                $record = $record->newClassInstance($newClass);
864
865
                // update the allowed category for the new file extension
866
                $category = File::get_app_category($extension);
867
                $record->File->setAllowedCategories($category);
868
            }
869
        }
870
871
        $form->saveInto($record);
872
        $record->write();
873
874
        // Publish this record and owned objects
875
        if ($doPublish) {
876
            $record->publishRecursive();
877
        }
878
879
        // Note: Force return of schema / state in success result
880
        return $this->getRecordUpdatedResponse($record, $form);
881
    }
882
883
    public function unpublish($data, $form)
884
    {
885 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...
886
            return (new HTTPResponse(json_encode(['status' => 'error']), 400))
887
                ->addHeader('Content-Type', 'application/json');
888
        }
889
890
        $id = (int) $data['ID'];
891
        /** @var File $record */
892
        $record = DataObject::get_by_id(File::class, $id);
893
894
        if (!$record) {
895
            return (new HTTPResponse(json_encode(['status' => 'error']), 404))
896
                ->addHeader('Content-Type', 'application/json');
897
        }
898
899
        if (!$record->canUnpublish()) {
900
            return (new HTTPResponse(json_encode(['status' => 'error']), 401))
901
                ->addHeader('Content-Type', 'application/json');
902
        }
903
904
        $record->doUnpublish();
905
        return $this->getRecordUpdatedResponse($record, $form);
906
    }
907
908
    /**
909
     * @param File $file
910
     *
911
     * @return array
912
     */
913
    public function getObjectFromData(File $file)
914
    {
915
        $object = array(
916
            'id' => $file->ID,
917
            'created' => $file->Created,
918
            'lastUpdated' => $file->LastEdited,
919
            'owner' => null,
920
            'parent' => null,
921
            'title' => $file->Title,
922
            'exists' => $file->exists(), // Broken file check
923
            'type' => $file instanceof Folder ? 'folder' : $file->FileType,
0 ignored issues
show
Bug introduced by
The class SilverStripe\Assets\Folder does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
924
            'category' => $file instanceof Folder ? 'folder' : $file->appCategory(),
0 ignored issues
show
Bug introduced by
The class SilverStripe\Assets\Folder does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
925
            'name' => $file->Name,
926
            'filename' => $file->Filename,
927
            'extension' => $file->Extension,
928
            'size' => $file->AbsoluteSize,
929
            'url' => $file->AbsoluteURL,
930
            'published' => $file->isPublished(),
931
            'modified' => $file->isModifiedOnDraft(),
932
            'draft' => $file->isOnDraftOnly(),
933
            'canEdit' => $file->canEdit(),
934
            'canDelete' => $file->canArchive(),
935
        );
936
937
        /** @var Member $owner */
938
        $owner = $file->Owner();
939
940
        if ($owner) {
941
            $object['owner'] = array(
942
                'id' => $owner->ID,
943
                'title' => trim($owner->FirstName . ' ' . $owner->Surname),
944
            );
945
        }
946
947
        /** @var Folder $parent */
948
        $parent = $file->Parent();
949
950
        if ($parent) {
951
            $object['parent'] = array(
952
                'id' => $parent->ID,
953
                'title' => $parent->Title,
954
                'filename' => $parent->Filename,
955
            );
956
        }
957
958
        /** @var File $file */
959
        if ($file->getIsImage()) {
960
            // Small thumbnail
961
            $smallWidth = UploadField::config()->uninherited('thumbnail_width');
962
            $smallHeight = UploadField::config()->uninherited('thumbnail_height');
963
            $smallThumbnail = $file->FitMax($smallWidth, $smallHeight);
964
            if ($smallThumbnail && $smallThumbnail->exists()) {
965
                $object['smallThumbnail'] = $smallThumbnail->getAbsoluteURL();
966
            }
967
968
            // Large thumbnail
969
            $width = $this->config()->get('thumbnail_width');
970
            $height = $this->config()->get('thumbnail_height');
971
            $thumbnail = $file->FitMax($width, $height);
972
            if ($thumbnail && $thumbnail->exists()) {
973
                $object['thumbnail'] = $thumbnail->getAbsoluteURL();
974
            }
975
            $object['width'] = $file->Width;
976
            $object['height'] = $file->Height;
977
        } else {
978
            $object['thumbnail'] = $file->PreviewLink();
979
        }
980
981
        return $object;
982
    }
983
984
    /**
985
     * Action handler for adding pages to a campaign
986
     *
987
     * @param array $data
988
     * @param Form $form
989
     * @return DBHTMLText|HTTPResponse
990
     */
991
    public function addtocampaign($data, $form)
992
    {
993
        $id = $data['ID'];
994
        $record = File::get()->byID($id);
995
996
        $handler = AddToCampaignHandler::create($this, $record, 'addToCampaignForm');
997
        $results = $handler->addToCampaign($record, $data['Campaign']);
998
        if (!isset($results)) {
999
            return null;
1000
        }
1001
1002
        // Send extra "message" data with schema response
1003
        $extraData = ['message' => $results];
1004
        $schemaId = Controller::join_links($this->Link('schema/addToCampaignForm'), $id);
1005
        return $this->getSchemaResponse($schemaId, $form, null, $extraData);
1006
    }
1007
1008
    /**
1009
     * Url handler for add to campaign form
1010
     *
1011
     * @param HTTPRequest $request
1012
     * @return Form
1013
     */
1014
    public function addToCampaignForm($request)
1015
    {
1016
        // Get ID either from posted back value, or url parameter
1017
        $id = $request->param('ID') ?: $request->postVar('ID');
1018
        return $this->getAddToCampaignForm($id);
1019
    }
1020
1021
    /**
1022
     * @param int $id
1023
     * @return Form
1024
     */
1025
    public function getAddToCampaignForm($id)
1026
    {
1027
        // Get record-specific fields
1028
        $record = File::get()->byID($id);
1029
1030
        if (!$record) {
1031
            $this->httpError(404, _t(
1032
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorNotFound',
1033
                'That {Type} couldn\'t be found',
1034
                '',
1035
                ['Type' => File::singleton()->i18n_singular_name()]
1036
            ));
1037
            return null;
1038
        }
1039 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...
1040
            $this->httpError(403, _t(
1041
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorItemPermissionDenied',
1042
                'You don\'t have the necessary permissions to modify {ObjectTitle}',
1043
                '',
1044
                ['ObjectTitle' => $record->i18n_singular_name()]
1045
            ));
1046
            return null;
1047
        }
1048
1049
        $handler = AddToCampaignHandler::create($this, $record, 'addToCampaignForm');
1050
        $form = $handler->Form($record);
1051
1052 View Code Duplication
        $form->setValidationResponseCallback(function (ValidationResult $errors) 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...
1053
            $schemaId = Controller::join_links($this->Link('schema/addToCampaignForm'), $id);
1054
            return $this->getSchemaResponse($schemaId, $form, $errors);
1055
        });
1056
1057
        return $form;
1058
    }
1059
1060
    /**
1061
     * @return Upload
1062
     */
1063
    protected function getUpload()
1064
    {
1065
        $upload = Upload::create();
1066
        $upload->getValidator()->setAllowedExtensions(
1067
            // filter out '' since this would be a regex problem on JS end
1068
            array_filter(File::config()->uninherited('allowed_extensions'))
1069
        );
1070
1071
        return $upload;
1072
    }
1073
1074
    /**
1075
     * Get response for successfully updated record
1076
     *
1077
     * @param File $record
1078
     * @param Form $form
1079
     * @return HTTPResponse
1080
     */
1081
    protected function getRecordUpdatedResponse($record, $form)
1082
    {
1083
        // Return the record data in the same response as the schema to save a postback
1084
        $schemaData = ['record' => $this->getObjectFromData($record)];
1085
        $schemaId = Controller::join_links($this->Link('schema/fileEditForm'), $record->ID);
1086
        return $this->getSchemaResponse($schemaId, $form, null, $schemaData);
1087
    }
1088
1089
    /**
1090
     * Scaffold a search form.
1091
     * Note: This form does not submit to itself, but rather uses the apiReadFolder endpoint
1092
     * (to be replaced with graphql)
1093
     *
1094
     * @return Form
1095
     */
1096
    public function fileSearchForm()
1097
    {
1098
        $scaffolder = FileSearchFormFactory::singleton();
1099
        return $scaffolder->getForm($this, 'fileSearchForm', []);
1100
    }
1101
1102
    /**
1103
     * Allow search form to be accessible to schema
1104
     *
1105
     * @return Form
1106
     */
1107
    public function getFileSearchform()
1108
    {
1109
        return $this->fileSearchForm();
1110
    }
1111
1112
    /**
1113
     * Form for creating a new OEmbed object in the WYSIWYG, used by the InsertEmbedModal component
1114
     *
1115
     * @param null $id
1116
     * @return mixed
1117
     */
1118
    public function getRemoteCreateForm($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id 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...
1119
    {
1120
        return Injector::inst()->get(RemoteFileFormFactory::class)
1121
            ->getForm($this, 'remoteCreateForm', ['type' => 'create']);
1122
    }
1123
1124
    /**
1125
     * Allow form to be accessible for schema
1126
     *
1127
     * @return mixed
1128
     */
1129
    public function remoteCreateForm()
1130
    {
1131
        return $this->getRemoteCreateForm();
1132
    }
1133
1134
    /**
1135
     * Form for editing a OEmbed object in the WYSIWYG, used by the InsertEmbedModal component
1136
     *
1137
     * @return mixed
1138
     */
1139
    public function getRemoteEditForm()
1140
    {
1141
        $url = $this->request->requestVar('embedurl');
1142
        $form = null;
0 ignored issues
show
Unused Code introduced by
$form 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...
1143
        $form = Injector::inst()->get(RemoteFileFormFactory::class)
1144
            ->getForm($this, 'remoteEditForm', ['type' => 'edit', 'url' => $url]);
1145
        return $form;
1146
    }
1147
1148
    /**
1149
     * Allow form to be accessible for schema
1150
     *
1151
     * @return mixed
1152
     */
1153
    public function remoteEditForm()
1154
    {
1155
        return $this->getRemoteEditForm();
1156
    }
1157
1158
    /**
1159
     * Capture the schema handling process, as there is validation done to the URL provided before form is generated
1160
     *
1161
     * @param $request
1162
     * @return HTTPResponse
1163
     */
1164
    public function schemaWithValidate(HTTPRequest $request)
1165
    {
1166
        $formName = $request->param('FormName');
1167
        $itemID = $request->param('ItemID');
1168
1169
        if (!$formName) {
1170
            return (new HTTPResponse('Missing request params', 400));
1171
        }
1172
1173
        $formMethod = "get{$formName}";
1174
        if (!$this->hasMethod($formMethod)) {
1175
            return (new HTTPResponse('Form not found', 404));
1176
        }
1177
1178
        if (!$this->hasAction($formName)) {
1179
            return (new HTTPResponse('Form not accessible', 401));
1180
        }
1181
1182
        $schemaID = $request->getURL();
1183
        try {
1184
            if ($itemID) {
1185
                $form = $this->{$formMethod}($itemID);
1186
            } else {
1187
                $form = $this->{$formMethod}();
1188
            }
1189
            return $this->getSchemaResponse($schemaID, $form);
1190
        } catch (InvalidUrlException $exception) {
1191
            $errors = ValidationResult::create()
1192
                ->addError($exception->getMessage());
1193
            $form = Form::create(null, 'Form', FieldList::create(), FieldList::create());
1194
            $code = $exception->getCode();
1195
1196
            if ($code < 300) {
1197
                $code = 500;
1198
            }
1199
1200
            return $this->getSchemaResponse($schemaID, $form, $errors)
1201
                ->setStatusCode($code);
1202
        }
1203
    }
1204
}
1205