Completed
Pull Request — master (#406)
by
unknown
02:32
created

AssetAdmin::addToCampaignForm()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
1
<?php
2
3
namespace SilverStripe\AssetAdmin\Controller;
4
5
use Embed\Exceptions\InvalidUrlException;
6
use InvalidArgumentException;
7
use SilverStripe\Admin\AddToCampaignHandler;
8
use SilverStripe\Admin\CMSBatchActionHandler;
9
use SilverStripe\Admin\LeftAndMain;
10
use SilverStripe\AssetAdmin\BatchAction\DeleteAssets;
11
use SilverStripe\AssetAdmin\Forms\AssetFormFactory;
12
use SilverStripe\AssetAdmin\Forms\FileSearchFormFactory;
13
use SilverStripe\AssetAdmin\Forms\RemoteFileFormFactory;
14
use SilverStripe\AssetAdmin\Forms\UploadField;
15
use SilverStripe\AssetAdmin\Forms\FileFormFactory;
16
use SilverStripe\AssetAdmin\Forms\FolderFormFactory;
17
use SilverStripe\AssetAdmin\Forms\FileHistoryFormFactory;
18
use SilverStripe\AssetAdmin\Forms\ImageFormFactory;
19
use SilverStripe\Assets\File;
20
use SilverStripe\Assets\Folder;
21
use SilverStripe\Assets\Image;
22
use SilverStripe\Assets\Storage\AssetNameGenerator;
23
use SilverStripe\Assets\Upload;
24
use SilverStripe\Control\Controller;
25
use SilverStripe\Control\HTTPRequest;
26
use SilverStripe\Control\HTTPResponse;
27
use SilverStripe\Core\Injector\Injector;
28
use SilverStripe\Forms\FieldList;
29
use SilverStripe\Forms\Form;
30
use SilverStripe\Forms\FormFactory;
31
use SilverStripe\ORM\ArrayList;
32
use SilverStripe\ORM\DataObject;
33
use SilverStripe\ORM\FieldType\DBHTMLText;
34
use SilverStripe\ORM\ValidationResult;
35
use SilverStripe\Security\Member;
36
use SilverStripe\Security\PermissionProvider;
37
use SilverStripe\Security\SecurityToken;
38
use SilverStripe\View\Requirements;
39
use SilverStripe\ORM\Versioning\Versioned;
40
use Exception;
41
42
/**
43
 * AssetAdmin is the 'file store' section of the CMS.
44
 * It provides an interface for manipulating the File and Folder objects in the system.
45
 */
46
class AssetAdmin extends LeftAndMain implements PermissionProvider
47
{
48
    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...
49
50
    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...
51
52
    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...
53
54
    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...
55
56
    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...
57
58
    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...
59
        // Legacy redirect for SS3-style detail view
60
        'EditForm/field/File/item/$FileID/$Action' => 'legacyRedirectForEditView',
61
        // Pass all URLs to the index, for React to unpack
62
        'show/$FolderID/edit/$FileID' => 'index',
63
        // API access points with structured data
64
        'POST api/createFile' => 'apiCreateFile',
65
        'POST api/uploadFile' => 'apiUploadFile',
66
        'GET api/history' => 'apiHistory',
67
        // for validating before generating the schema
68
        'schemaWithValidate/$FormName' => 'schemaWithValidate'
69
    ];
70
71
    /**
72
     * Amount of results showing on a single page.
73
     *
74
     * @config
75
     * @var int
76
     */
77
    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...
78
79
    /**
80
     * @config
81
     * @see Upload->allowedMaxFileSize
82
     * @var int
83
     */
84
    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...
85
86
    /**
87
     * @config
88
     *
89
     * @var int
90
     */
91
    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...
92
93
    /**
94
     * @var array
95
     */
96
    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...
97
        'legacyRedirectForEditView',
98
        'apiCreateFile',
99
        'apiUploadFile',
100
        'apiHistory',
101
        'fileEditForm',
102
        'fileHistoryForm',
103
        'addToCampaignForm',
104
        'fileInsertForm',
105
        'remoteEditForm',
106
        'remoteCreateForm',
107
        'schema',
108
        'fileSelectForm',
109
        'fileSearchForm',
110
        'schemaWithValidate',
111
    );
112
113
    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...
114
115
    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...
116
117
    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...
118
119
    /**
120
     * Set up the controller
121
     */
122
    public function init()
123
    {
124
        parent::init();
125
126
        Requirements::add_i18n_javascript(ASSET_ADMIN_DIR . '/client/lang', false, true);
127
        Requirements::javascript(ASSET_ADMIN_DIR . "/client/dist/js/bundle.js");
128
        Requirements::css(ASSET_ADMIN_DIR . "/client/dist/styles/bundle.css");
129
130
        CMSBatchActionHandler::register('delete', DeleteAssets::class, Folder::class);
131
    }
132
133
    public function getClientConfig()
134
    {
135
        $baseLink = $this->Link();
136
        return array_merge(parent::getClientConfig(), [
137
            'reactRouter' => true,
138
            'createFileEndpoint' => [
139
                'url' => Controller::join_links($baseLink, 'api/createFile'),
140
                'method' => 'post',
141
                'payloadFormat' => 'urlencoded',
142
            ],
143
            'uploadFileEndpoint' => [
144
                'url' => Controller::join_links($baseLink, 'api/uploadFile'),
145
                'method' => 'post',
146
                'payloadFormat' => 'urlencoded',
147
            ],
148
            'historyEndpoint' => [
149
                'url' => Controller::join_links($baseLink, 'api/history'),
150
                'method' => 'get',
151
                'responseFormat' => 'json',
152
            ],
153
            'limit' => $this->config()->page_length,
154
            'form' => [
155
                'fileEditForm' => [
156
                    'schemaUrl' => $this->Link('schema/fileEditForm')
157
                ],
158
                'fileInsertForm' => [
159
                    'schemaUrl' => $this->Link('schema/fileInsertForm')
160
                ],
161
                'remoteEditForm' => [
162
                    'schemaUrl' => $this->Link('schemaWithValidate/remoteEditForm')
163
                ],
164
                'remoteCreateForm' => [
165
                    'schemaUrl' => $this->Link('schema/remoteCreateForm')
166
                ],
167
                'fileSelectForm' => [
168
                    'schemaUrl' => $this->Link('schema/fileSelectForm')
169
                ],
170
                'addToCampaignForm' => [
171
                    'schemaUrl' => $this->Link('schema/addToCampaignForm')
172
                ],
173
                'fileHistoryForm' => [
174
                    'schemaUrl' => $this->Link('schema/fileHistoryForm')
175
                ],
176
                'fileSearchForm' => [
177
                    'schemaUrl' => $this->Link('schema/fileSearchForm')
178
                ],
179
            ],
180
        ]);
181
    }
182
183
    /**
184
     * Creates a single file based on a form-urlencoded upload.
185
     *
186
     * @param HTTPRequest $request
187
     * @return HTTPRequest|HTTPResponse
188
     */
189
    public function apiCreateFile(HTTPRequest $request)
190
    {
191
        $data = $request->postVars();
192
193
        // When creating new files, rename on conflict
194
        $upload = $this->getUpload();
195
        $upload->setReplaceFile(false);
196
197
        // CSRF check
198
        $token = SecurityToken::inst();
199 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...
200
            return new HTTPResponse(null, 400);
201
        }
202
203
        // Check parent record
204
        /** @var Folder $parentRecord */
205
        $parentRecord = null;
206
        if (!empty($data['ParentID']) && is_numeric($data['ParentID'])) {
207
            $parentRecord = Folder::get()->byID($data['ParentID']);
208
        }
209
        $data['Parent'] = $parentRecord;
210
211
        $tmpFile = $request->postVar('Upload');
212 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...
213
            $result = ['message' => null];
214
            $errors = $upload->getErrors();
215
            if ($message = array_shift($errors)) {
216
                $result['message'] = [
217
                    'type' => 'error',
218
                    'value' => $message,
219
                ];
220
            }
221
            return (new HTTPResponse(json_encode($result), 400))
222
                ->addHeader('Content-Type', 'application/json');
223
        }
224
225
        // TODO Allow batch uploads
226
        $fileClass = File::get_class_for_file_extension(File::get_file_extension($tmpFile['name']));
227
        /** @var File $file */
228
        $file = Injector::inst()->create($fileClass);
229
230
        // check canCreate permissions
231 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...
232
            $result = ['message' => [
233
                'type' => 'error',
234
                'value' => _t(
235
                    'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.CreatePermissionDenied',
236
                    'You do not have permission to add files'
237
                )
238
            ]];
239
            return (new HTTPResponse(json_encode($result), 403))
240
                ->addHeader('Content-Type', 'application/json');
241
        }
242
243
        $uploadResult = $upload->loadIntoFile($tmpFile, $file, $parentRecord ? $parentRecord->getFilename() : '/');
244 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...
245
            $result = ['message' => [
246
                'type' => 'error',
247
                'value' => _t(
248
                    'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.LoadIntoFileFailed',
249
                    'Failed to load file'
250
                )
251
            ]];
252
            return (new HTTPResponse(json_encode($result), 400))
253
                ->addHeader('Content-Type', 'application/json');
254
        }
255
256
        $file->ParentID = $parentRecord ? $parentRecord->ID : 0;
257
        $file->write();
258
259
        $result = [$this->getObjectFromData($file)];
260
261
        return (new HTTPResponse(json_encode($result)))
262
            ->addHeader('Content-Type', 'application/json');
263
    }
264
265
    /**
266
     * Upload a new asset for a pre-existing record. Returns the asset tuple.
267
     *
268
     * Note that conflict resolution is as follows:
269
     *  - If uploading a file with the same extension, we simply keep the same filename,
270
     *    and overwrite any existing files (same name + sha = don't duplicate).
271
     *  - If uploading a new file with a different extension, then the filename will
272
     *    be replaced, and will be checked for uniqueness against other File dataobjects.
273
     *
274
     * @param HTTPRequest $request Request containing vars 'ID' of parent record ID,
275
     * and 'Name' as form filename value
276
     * @return HTTPRequest|HTTPResponse
277
     */
278
    public function apiUploadFile(HTTPRequest $request)
279
    {
280
        $data = $request->postVars();
281
282
        // When updating files, replace on conflict
283
        $upload = $this->getUpload();
284
        $upload->setReplaceFile(true);
285
286
        // CSRF check
287
        $token = SecurityToken::inst();
288 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...
289
            return new HTTPResponse(null, 400);
290
        }
291
        $tmpFile = $data['Upload'];
292
        if (empty($data['ID']) || empty($tmpFile['name']) || !array_key_exists('Name', $data)) {
293
            return new HTTPResponse('Invalid request', 400);
294
        }
295
296
        // Check parent record
297
        /** @var File $file */
298
        $file = File::get()->byID($data['ID']);
299
        if (!$file) {
300
            return new HTTPResponse('File not found', 404);
301
        }
302
        $folder = $file->ParentID ? $file->Parent()->getFilename() : '/';
303
304
        // If extension is the same, attempt to re-use existing name
305
        if (File::get_file_extension($tmpFile['name']) === File::get_file_extension($data['Name'])) {
306
            $tmpFile['name'] = $data['Name'];
307
        } else {
308
            // If we are allowing this upload to rename the underlying record,
309
            // do a uniqueness check.
310
            $renamer = $this->getNameGenerator($tmpFile['name']);
311
            foreach ($renamer as $name) {
312
                $filename = File::join_paths($folder, $name);
313
                if (!File::find($filename)) {
314
                    $tmpFile['name'] = $name;
315
                    break;
316
                }
317
            }
318
        }
319
320 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...
321
            $result = ['message' => null];
322
            $errors = $upload->getErrors();
323
            if ($message = array_shift($errors)) {
324
                $result['message'] = [
325
                    'type' => 'error',
326
                    'value' => $message,
327
                ];
328
            }
329
            return (new HTTPResponse(json_encode($result), 400))
330
                ->addHeader('Content-Type', 'application/json');
331
        }
332
333
        try {
334
            $tuple = $upload->load($tmpFile, $folder);
335
        } catch (Exception $e) {
336
            $result = [
337
                'message' => [
338
                    'type' => 'error',
339
                    'value' => $e->getMessage(),
340
                ]
341
            ];
342
            return (new HTTPResponse(json_encode($result), 400))
343
                ->addHeader('Content-Type', 'application/json');
344
        }
345
346
        if ($upload->isError()) {
347
            $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...
348
                'type' => 'error',
349
                'value' => implode(' ' . PHP_EOL, $upload->getErrors()),
350
            ];
351
            return (new HTTPResponse(json_encode($result), 400))
352
                ->addHeader('Content-Type', 'application/json');
353
        }
354
355
        $tuple['Name'] = basename($tuple['Filename']);
356
        return (new HTTPResponse(json_encode($tuple)))
357
            ->addHeader('Content-Type', 'application/json');
358
    }
359
360
    /**
361
     * Returns a JSON array for history of a given file ID. Returns a list of all the history.
362
     *
363
     * @param HTTPRequest $request
364
     * @return HTTPResponse
365
     */
366
    public function apiHistory(HTTPRequest $request)
367
    {
368
        // CSRF check not required as the GET request has no side effects.
369
        $fileId = $request->getVar('fileId');
370
371
        if (!$fileId || !is_numeric($fileId)) {
372
            return new HTTPResponse(null, 400);
373
        }
374
375
        $class = File::class;
376
        $file = DataObject::get($class)->byID($fileId);
377
378
        if (!$file) {
379
            return new HTTPResponse(null, 404);
380
        }
381
382
        if (!$file->canView()) {
383
            return new HTTPResponse(null, 403);
384
        }
385
386
        $versions = Versioned::get_all_versions($class, $fileId)
387
            ->limit($this->config()->max_history_entries)
388
            ->sort('Version', 'DESC');
389
390
        $output = array();
391
        $next = array();
392
        $prev = null;
393
394
        // swap the order so we can get the version number to compare against.
395
        // i.e version 3 needs to know version 2 is the previous version
396
        $copy = $versions->map('Version', 'Version')->toArray();
397
        foreach (array_reverse($copy) as $k => $v) {
398
            if ($prev) {
399
                $next[$v] = $prev;
400
            }
401
402
            $prev = $v;
403
        }
404
405
        $_cachedMembers = array();
406
407
        /** @var File $version */
408
        foreach ($versions as $version) {
409
            $author = null;
410
411
            if ($version->AuthorID) {
412
                if (!isset($_cachedMembers[$version->AuthorID])) {
413
                    $_cachedMembers[$version->AuthorID] = DataObject::get(Member::class)
414
                        ->byID($version->AuthorID);
415
                }
416
417
                $author = $_cachedMembers[$version->AuthorID];
418
            }
419
420
            if ($version->canView()) {
421
                if (isset($next[$version->Version])) {
422
                    $summary = $version->humanizedChanges(
423
                        $version->Version,
424
                        $next[$version->Version]
425
                    );
426
427
                    // if no summary returned by humanizedChanges, i.e we cannot work out what changed, just show a
428
                    // generic message
429
                    if (!$summary) {
430
                        $summary = _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.SAVEDFILE', "Saved file");
431
                    }
432
                } else {
433
                    $summary = _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.UPLOADEDFILE', "Uploaded file");
434
                }
435
436
                $output[] = array(
437
                    'versionid' => $version->Version,
438
                    'date_ago' => $version->dbObject('LastEdited')->Ago(),
439
                    'date_formatted' => $version->dbObject('LastEdited')->Nice(),
440
                    'status' => ($version->WasPublished) ? _t('File.PUBLISHED', 'Published') : '',
441
                    'author' => ($author)
442
                        ? $author->Name
443
                        : _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.UNKNOWN', "Unknown"),
444
                    'summary' => ($summary)
445
                        ? $summary
446
                        : _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.NOSUMMARY', "No summary available")
447
                );
448
            }
449
        }
450
451
        return
452
            (new HTTPResponse(json_encode($output)))->addHeader('Content-Type', 'application/json');
453
    }
454
455
    /**
456
     * Redirects 3.x style detail links to new 4.x style routing.
457
     *
458
     * @param HTTPRequest $request
459
     */
460
    public function legacyRedirectForEditView($request)
461
    {
462
        $fileID = $request->param('FileID');
463
        /** @var File $file */
464
        $file = File::get()->byID($fileID);
465
        $link = $this->getFileEditLink($file) ?: $this->Link();
466
        $this->redirect($link);
467
    }
468
469
    /**
470
     * Given a file return the CMS link to edit it
471
     *
472
     * @param File $file
473
     * @return string
474
     */
475
    public function getFileEditLink($file)
476
    {
477
        if (!$file || !$file->isInDB()) {
478
            return null;
479
        }
480
481
        return Controller::join_links(
482
            $this->Link('show'),
483
            $file->ParentID,
484
            'edit',
485
            $file->ID
486
        );
487
    }
488
489
    /**
490
     * Get an asset renamer for the given filename.
491
     *
492
     * @param  string             $filename Path name
493
     * @return AssetNameGenerator
494
     */
495
    protected function getNameGenerator($filename)
496
    {
497
        return Injector::inst()
498
            ->createWithArgs('AssetNameGenerator', array($filename));
499
    }
500
501
    /**
502
     * @todo Implement on client
503
     *
504
     * @param bool $unlinked
505
     * @return ArrayList
506
     */
507
    public function breadcrumbs($unlinked = false)
508
    {
509
        return null;
510
    }
511
512
513
    /**
514
     * Don't include class namespace in auto-generated CSS class
515
     */
516
    public function baseCSSClasses()
517
    {
518
        return 'AssetAdmin LeftAndMain';
519
    }
520
521
    public function providePermissions()
522
    {
523
        return array(
524
            "CMS_ACCESS_AssetAdmin" => array(
525
                'name' => _t('CMSMain.ACCESS', "Access to '{title}' section", array(
526
                    'title' => static::menu_title()
527
                )),
528
                'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
529
            )
530
        );
531
    }
532
533
    /**
534
     * Build a form scaffolder for this model
535
     *
536
     * NOTE: Volatile api. May be moved to {@see LeftAndMain}
537
     *
538
     * @param File $file
539
     * @return FormFactory
540
     */
541
    public function getFormFactory(File $file)
542
    {
543
        // Get service name based on file class
544
        $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...
545
        if ($file instanceof Folder) {
546
            $name = FolderFormFactory::class;
547
        } elseif ($file instanceof Image) {
548
            $name = ImageFormFactory::class;
549
        } else {
550
            $name = FileFormFactory::class;
551
        }
552
        return Injector::inst()->get($name);
553
    }
554
555
    /**
556
     * The form is used to generate a form schema,
557
     * as well as an intermediary object to process data through API endpoints.
558
     * Since it's used directly on API endpoints, it does not have any form actions.
559
     * It handles both {@link File} and {@link Folder} records.
560
     *
561
     * @param int $id
562
     * @return Form
563
     */
564
    public function getFileEditForm($id)
565
    {
566
        return $this->getAbstractFileForm($id, 'fileEditForm');
567
    }
568
569
    /**
570
     * Get file edit form
571
     *
572
     * @return Form
573
     */
574 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...
575
    {
576
        // Get ID either from posted back value, or url parameter
577
        $request = $this->getRequest();
578
        $id = $request->param('ID') ?: $request->postVar('ID');
579
        return $this->getFileEditForm($id);
580
    }
581
582
    /**
583
     * The form is used to generate a form schema,
584
     * as well as an intermediary object to process data through API endpoints.
585
     * Since it's used directly on API endpoints, it does not have any form actions.
586
     * It handles both {@link File} and {@link Folder} records.
587
     *
588
     * @param int $id
589
     * @return Form
590
     */
591
    public function getFileInsertForm($id)
592
    {
593
        return $this->getAbstractFileForm($id, 'fileInsertForm', [ 'Type' => AssetFormFactory::TYPE_INSERT ]);
594
    }
595
596
    /**
597
     * Get file insert form
598
     *
599
     * @return Form
600
     */
601 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...
602
    {
603
        // Get ID either from posted back value, or url parameter
604
        $request = $this->getRequest();
605
        $id = $request->param('ID') ?: $request->postVar('ID');
606
        return $this->getFileInsertForm($id);
607
    }
608
609
    /**
610
     * Abstract method for generating a form for a file
611
     *
612
     * @param int $id Record ID
613
     * @param string $name Form name
614
     * @param array $context Form context
615
     * @return Form
616
     */
617
    protected function getAbstractFileForm($id, $name, $context = [])
618
    {
619
        /** @var File $file */
620
        $file = File::get()->byID($id);
621
622 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...
623
            $this->httpError(403, _t(
624
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorItemPermissionDenied',
625
                'You don\'t have the necessary permissions to modify {ObjectTitle}',
626
                '',
627
                ['ObjectTitle' => $file->i18n_singular_name()]
628
            ));
629
            return null;
630
        }
631
632
        // Pass to form factory
633
        $augmentedContext = array_merge($context, ['Record' => $file]);
634
        $scaffolder = $this->getFormFactory($file);
635
        $form = $scaffolder->getForm($this, $name, $augmentedContext);
636
637
        // Configure form to respond to validation errors with form schema
638
        // if requested via react.
639
        $form->setValidationResponseCallback(function (ValidationResult $error) use ($form, $id, $name) {
640
            $schemaId = Controller::join_links($this->Link('schema'), $name, $id);
641
            return $this->getSchemaResponse($schemaId, $form, $error);
642
        });
643
644
        return $form;
645
    }
646
647
    /**
648
     * Get form for selecting a file
649
     *
650
     * @return Form
651
     */
652
    public function fileSelectForm()
653
    {
654
        // Get ID either from posted back value, or url parameter
655
        $request = $this->getRequest();
656
        $id = $request->param('ID') ?: $request->postVar('ID');
657
        return $this->getFileSelectForm($id);
658
    }
659
660
    /**
661
     * Get form for selecting a file
662
     *
663
     * @param int $id ID of the record being selected
664
     * @return Form
665
     */
666
    public function getFileSelectForm($id)
667
    {
668
        return $this->getAbstractFileForm($id, 'fileSelectForm', [ 'Type' => AssetFormFactory::TYPE_SELECT ]);
669
    }
670
671
    /**
672
     * @param array $context
673
     * @return Form
674
     * @throws InvalidArgumentException
675
     */
676
    public function getFileHistoryForm($context)
677
    {
678
        // Check context
679
        if (!isset($context['RecordID']) || !isset($context['RecordVersion'])) {
680
            throw new InvalidArgumentException("Missing RecordID / RecordVersion for this form");
681
        }
682
        $id = $context['RecordID'];
683
        $versionId = $context['RecordVersion'];
684
        if (!$id || !$versionId) {
685
            return $this->httpError(404);
686
        }
687
688
        /** @var File $file */
689
        $file = Versioned::get_version(File::class, $id, $versionId);
690
        if (!$file) {
691
            return $this->httpError(404);
692
        }
693
694 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...
695
            $this->httpError(403, _t(
696
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorItemPermissionDenied',
697
                'You don\'t have the necessary permissions to modify {ObjectTitle}',
698
                '',
699
                ['ObjectTitle' => $file->i18n_singular_name()]
700
            ));
701
            return null;
702
        }
703
704
        $effectiveContext = array_merge($context, ['Record' => $file]);
705
        /** @var FormFactory $scaffolder */
706
        $scaffolder = Injector::inst()->get(FileHistoryFormFactory::class);
707
        $form = $scaffolder->getForm($this, 'fileHistoryForm', $effectiveContext);
708
709
        // Configure form to respond to validation errors with form schema
710
        // if requested via react.
711 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...
712
            $schemaId = Controller::join_links($this->Link('schema/fileHistoryForm'), $id, $versionId);
713
            return $this->getSchemaResponse($schemaId, $form, $errors);
714
        });
715
716
        return $form;
717
    }
718
719
    /**
720
     * Gets a JSON schema representing the current edit form.
721
     *
722
     * WARNING: Experimental API.
723
     *
724
     * @param HTTPRequest $request
725
     * @return HTTPResponse
726
     */
727
    public function schema($request)
728
    {
729
        $formName = $request->param('FormName');
730
        if ($formName !== 'fileHistoryForm') {
731
            return parent::schema($request);
732
        }
733
734
        // Get schema for history form
735
        // @todo Eventually all form scaffolding will be based on context rather than record ID
736
        // See https://github.com/silverstripe/silverstripe-framework/issues/6362
737
        $itemID = $request->param('ItemID');
738
        $version = $request->param('OtherItemID');
739
        $form = $this->getFileHistoryForm([
740
            'RecordID' => $itemID,
741
            'RecordVersion' => $version,
742
        ]);
743
744
        // Respond with this schema
745
        $response = $this->getResponse();
746
        $response->addHeader('Content-Type', 'application/json');
747
        $schemaID = $this->getRequest()->getURL();
748
        return $this->getSchemaResponse($schemaID, $form);
749
    }
750
751
    /**
752
     * Get file history form
753
     *
754
     * @return Form
755
     */
756
    public function fileHistoryForm()
757
    {
758
        $request = $this->getRequest();
759
        $id = $request->param('ID') ?: $request->postVar('ID');
760
        $version = $request->param('OtherID') ?: $request->postVar('Version');
761
        $form = $this->getFileHistoryForm([
762
            'RecordID' => $id,
763
            'RecordVersion' => $version,
764
        ]);
765
        return $form;
766
    }
767
768
    /**
769
     * @param array $data
770
     * @param Form $form
771
     * @return HTTPResponse
772
     */
773
    public function save($data, $form)
774
    {
775
        return $this->saveOrPublish($data, $form, false);
776
    }
777
778
    /**
779
     * @param array $data
780
     * @param Form $form
781
     * @return HTTPResponse
782
     */
783
    public function publish($data, $form)
784
    {
785
        return $this->saveOrPublish($data, $form, true);
786
    }
787
788
    /**
789
     * Update thisrecord
790
     *
791
     * @param array $data
792
     * @param Form $form
793
     * @param bool $doPublish
794
     * @return HTTPResponse
795
     */
796
    protected function saveOrPublish($data, $form, $doPublish = false)
797
    {
798 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...
799
            return (new HTTPResponse(json_encode(['status' => 'error']), 400))
800
                ->addHeader('Content-Type', 'application/json');
801
        }
802
803
        $id = (int) $data['ID'];
804
        /** @var File $record */
805
        $record = DataObject::get_by_id(File::class, $id);
806
807
        if (!$record) {
808
            return (new HTTPResponse(json_encode(['status' => 'error']), 404))
809
                ->addHeader('Content-Type', 'application/json');
810
        }
811
812
        if (!$record->canEdit() || ($doPublish && !$record->canPublish())) {
813
            return (new HTTPResponse(json_encode(['status' => 'error']), 401))
814
                ->addHeader('Content-Type', 'application/json');
815
        }
816
817
        // check File extension
818
        if (!empty($data['FileFilename'])) {
819
            $extension = File::get_file_extension($data['FileFilename']);
820
            $newClass = File::get_class_for_file_extension($extension);
821
822
            // if the class has changed, cast it to the proper class
823
            if ($record->getClassName() !== $newClass) {
824
                $record = $record->newClassInstance($newClass);
825
826
                // update the allowed category for the new file extension
827
                $category = File::get_app_category($extension);
828
                $record->File->setAllowedCategories($category);
829
            }
830
        }
831
832
        $form->saveInto($record);
833
        $record->write();
834
835
        // Publish this record and owned objects
836
        if ($doPublish) {
837
            $record->publishRecursive();
838
        }
839
840
        // Note: Force return of schema / state in success result
841
        return $this->getRecordUpdatedResponse($record, $form);
0 ignored issues
show
Compatibility introduced by
$record of type object<SilverStripe\ORM\DataObject> is not a sub-type of object<SilverStripe\Assets\File>. It seems like you assume a child class of the class SilverStripe\ORM\DataObject to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
842
    }
843
844
    public function unpublish($data, $form)
845
    {
846 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...
847
            return (new HTTPResponse(json_encode(['status' => 'error']), 400))
848
                ->addHeader('Content-Type', 'application/json');
849
        }
850
851
        $id = (int) $data['ID'];
852
        /** @var File $record */
853
        $record = DataObject::get_by_id(File::class, $id);
854
855
        if (!$record) {
856
            return (new HTTPResponse(json_encode(['status' => 'error']), 404))
857
                ->addHeader('Content-Type', 'application/json');
858
        }
859
860
        if (!$record->canUnpublish()) {
861
            return (new HTTPResponse(json_encode(['status' => 'error']), 401))
862
                ->addHeader('Content-Type', 'application/json');
863
        }
864
865
        $record->doUnpublish();
866
        return $this->getRecordUpdatedResponse($record, $form);
867
    }
868
869
    /**
870
     * @param File $file
871
     *
872
     * @return array
873
     */
874
    public function getObjectFromData(File $file)
875
    {
876
        $object = array(
877
            'id' => $file->ID,
878
            'created' => $file->Created,
879
            'lastUpdated' => $file->LastEdited,
880
            'owner' => null,
881
            'parent' => null,
882
            'title' => $file->Title,
883
            'exists' => $file->exists(), // Broken file check
884
            'type' => $file instanceof Folder ? 'folder' : $file->FileType,
885
            'category' => $file instanceof Folder ? 'folder' : $file->appCategory(),
886
            'name' => $file->Name,
887
            'filename' => $file->Filename,
888
            '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...
889
            'size' => $file->AbsoluteSize,
890
            'url' => $file->AbsoluteURL,
891
            'published' => $file->isPublished(),
892
            'modified' => $file->isModifiedOnDraft(),
893
            'draft' => $file->isOnDraftOnly(),
894
            'canEdit' => $file->canEdit(),
895
            'canDelete' => $file->canArchive(),
896
        );
897
898
        /** @var Member $owner */
899
        $owner = $file->Owner();
900
901
        if ($owner) {
902
            $object['owner'] = array(
903
                'id' => $owner->ID,
904
                'title' => trim($owner->FirstName . ' ' . $owner->Surname),
905
            );
906
        }
907
908
        /** @var Folder $parent */
909
        $parent = $file->Parent();
910
911
        if ($parent) {
912
            $object['parent'] = array(
913
                'id' => $parent->ID,
914
                'title' => $parent->Title,
915
                'filename' => $parent->Filename,
916
            );
917
        }
918
919
        /** @var File $file */
920
        if ($file->getIsImage()) {
921
            // Small thumbnail
922
            $smallWidth = UploadField::config()->uninherited('thumbnail_width');
923
            $smallHeight = UploadField::config()->uninherited('thumbnail_height');
924
            $smallThumbnail = $file->FitMax($smallWidth, $smallHeight);
925
            if ($smallThumbnail && $smallThumbnail->exists()) {
926
                $object['smallThumbnail'] = $smallThumbnail->getAbsoluteURL();
927
            }
928
929
            // Large thumbnail
930
            $width = $this->config()->get('thumbnail_width');
931
            $height = $this->config()->get('thumbnail_height');
932
            $thumbnail = $file->FitMax($width, $height);
933
            if ($thumbnail && $thumbnail->exists()) {
934
                $object['thumbnail'] = $thumbnail->getAbsoluteURL();
935
            }
936
            $object['width'] = $file->Width;
937
            $object['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...
938
        } else {
939
            $object['thumbnail'] = $file->PreviewLink();
940
        }
941
942
        return $object;
943
    }
944
945
    /**
946
     * Action handler for adding pages to a campaign
947
     *
948
     * @param array $data
949
     * @param Form $form
950
     * @return DBHTMLText|HTTPResponse
951
     */
952
    public function addtocampaign($data, $form)
953
    {
954
        $id = $data['ID'];
955
        $record = File::get()->byID($id);
956
957
        $handler = AddToCampaignHandler::create($this, $record, 'addToCampaignForm');
958
        $results = $handler->addToCampaign($record, $data['Campaign']);
0 ignored issues
show
Bug introduced by
It seems like $record defined by \SilverStripe\Assets\File::get()->byID($id) on line 955 can be null; however, SilverStripe\Admin\AddTo...andler::addToCampaign() 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...
959
        if (!isset($results)) {
960
            return null;
961
        }
962
963
        // Send extra "message" data with schema response
964
        $extraData = ['message' => $results];
965
        $schemaId = Controller::join_links($this->Link('schema/addToCampaignForm'), $id);
966
        return $this->getSchemaResponse($schemaId, $form, null, $extraData);
967
    }
968
969
    /**
970
     * Url handler for add to campaign form
971
     *
972
     * @param HTTPRequest $request
973
     * @return Form
974
     */
975
    public function addToCampaignForm($request)
976
    {
977
        // Get ID either from posted back value, or url parameter
978
        $id = $request->param('ID') ?: $request->postVar('ID');
979
        return $this->getAddToCampaignForm($id);
980
    }
981
982
    /**
983
     * @param int $id
984
     * @return Form
985
     */
986
    public function getAddToCampaignForm($id)
987
    {
988
        // Get record-specific fields
989
        $record = File::get()->byID($id);
990
991
        if (!$record) {
992
            $this->httpError(404, _t(
993
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorNotFound',
994
                'That {Type} couldn\'t be found',
995
                '',
996
                ['Type' => File::singleton()->i18n_singular_name()]
997
            ));
998
            return null;
999
        }
1000 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...
1001
            $this->httpError(403, _t(
1002
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorItemPermissionDenied',
1003
                'You don\'t have the necessary permissions to modify {ObjectTitle}',
1004
                '',
1005
                ['ObjectTitle' => $record->i18n_singular_name()]
1006
            ));
1007
            return null;
1008
        }
1009
1010
        $handler = AddToCampaignHandler::create($this, $record, 'addToCampaignForm');
1011
        $form = $handler->Form($record);
1012
1013 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...
1014
            $schemaId = Controller::join_links($this->Link('schema/addToCampaignForm'), $id);
1015
            return $this->getSchemaResponse($schemaId, $form, $errors);
1016
        });
1017
1018
        return $form;
1019
    }
1020
1021
    /**
1022
     * @return Upload
1023
     */
1024
    protected function getUpload()
1025
    {
1026
        $upload = Upload::create();
1027
        $upload->getValidator()->setAllowedExtensions(
1028
            // filter out '' since this would be a regex problem on JS end
1029
            array_filter(File::config()->uninherited('allowed_extensions'))
1030
        );
1031
1032
        return $upload;
1033
    }
1034
1035
    /**
1036
     * Get response for successfully updated record
1037
     *
1038
     * @param File $record
1039
     * @param Form $form
1040
     * @return HTTPResponse
1041
     */
1042
    protected function getRecordUpdatedResponse($record, $form)
1043
    {
1044
        // Return the record data in the same response as the schema to save a postback
1045
        $schemaData = ['record' => $this->getObjectFromData($record)];
1046
        $schemaId = Controller::join_links($this->Link('schema/fileEditForm'), $record->ID);
1047
        return $this->getSchemaResponse($schemaId, $form, null, $schemaData);
1048
    }
1049
1050
    /**
1051
     * Scaffold a search form.
1052
     * Note: This form does not submit to itself, but rather uses the apiReadFolder endpoint
1053
     * (to be replaced with graphql)
1054
     *
1055
     * @return Form
1056
     */
1057
    public function fileSearchForm()
1058
    {
1059
        $scaffolder = FileSearchFormFactory::singleton();
1060
        return $scaffolder->getForm($this, 'fileSearchForm', []);
1061
    }
1062
1063
    /**
1064
     * Allow search form to be accessible to schema
1065
     *
1066
     * @return Form
1067
     */
1068
    public function getFileSearchform()
1069
    {
1070
        return $this->fileSearchForm();
1071
    }
1072
    
1073
    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...
1074
    {
1075
        return Injector::inst()->get(RemoteFileFormFactory::class)
1076
            ->getForm($this, 'remoteCreateForm', ['type' => 'create']);
1077
    }
1078
    
1079
    public function remoteCreateForm()
1080
    {
1081
        return $this->getRemoteCreateForm();
1082
    }
1083
    
1084
    public function getRemoteEditForm()
1085
    {
1086
        $url = $this->request->requestVar('embedurl');
1087
        $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...
1088
        $form = Injector::inst()->get(RemoteFileFormFactory::class)
1089
            ->getForm($this, 'remoteEditForm', ['type' => 'edit', 'url' => $url]);
1090
        return $form;
1091
    }
1092
    public function remoteEditForm()
1093
    {
1094
        return $this->getRemoteEditForm();
1095
    }
1096
    
1097
    public function schemaWithValidate($request)
1098
    {
1099
        $formName = $request->param('FormName');
1100
        $itemID = $request->param('ItemID');
1101
    
1102
        if (!$formName) {
1103
            return (new HTTPResponse('Missing request params', 400));
1104
        }
1105
    
1106
        $formMethod = "get{$formName}";
1107
        if (!$this->hasMethod($formMethod)) {
1108
            var_dump($formMethod);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($formMethod); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
1109
            return (new HTTPResponse('Form not found', 404));
1110
        }
1111
    
1112
        if (!$this->hasAction($formName)) {
1113
            return (new HTTPResponse('Form not accessible', 401));
1114
        }
1115
    
1116
        $schemaID = $request->getURL();
1117
        try {
1118
            if ($itemID) {
1119
                $form = $this->{$formMethod}($itemID);
1120
            } else {
1121
                $form = $this->{$formMethod}();
1122
            }
1123
            return $this->getSchemaResponse($schemaID, $form);
1124
        } catch (InvalidUrlException $exception) {
1125
            $errors = ValidationResult::create()
1126
                ->addError($exception->getMessage());
1127
            $form = Form::create(null, 'Form', FieldList::create(), FieldList::create());
1128
            $code = $exception->getCode();
1129
            
1130
            if ($code < 300) {
1131
                $code = 500;
1132
            }
1133
            
1134
            return $this->getSchemaResponse($schemaID, $form, $errors)
1135
                ->setStatusCode($code);
1136
        }
1137
    }
1138
}
1139