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

AssetAdmin::fileHistoryForm()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 23
rs 8.7972
cc 4
eloc 16
nc 4
nop 1
1
<?php
2
3
namespace SilverStripe\AssetAdmin\Controller;
4
5
use Embed\Exceptions\InvalidUrlException;
6
use InvalidArgumentException;
7
use SilverStripe\AssetAdmin\Forms\FolderCreateFormFactory;
8
use SilverStripe\AssetAdmin\Forms\FolderFormFactory;
9
use SilverStripe\Admin\LeftAndMainFormRequestHandler;
10
use SilverStripe\CampaignAdmin\AddToCampaignHandler;
11
use SilverStripe\Admin\CMSBatchActionHandler;
12
use SilverStripe\Admin\LeftAndMain;
13
use SilverStripe\AssetAdmin\BatchAction\DeleteAssets;
14
use SilverStripe\AssetAdmin\Forms\AssetFormFactory;
15
use SilverStripe\AssetAdmin\Forms\FileSearchFormFactory;
16
use SilverStripe\AssetAdmin\Forms\RemoteFileFormFactory;
17
use SilverStripe\AssetAdmin\Forms\UploadField;
18
use SilverStripe\AssetAdmin\Forms\FileFormFactory;
19
use SilverStripe\AssetAdmin\Forms\FileHistoryFormFactory;
20
use SilverStripe\AssetAdmin\Forms\ImageFormFactory;
21
use SilverStripe\Assets\File;
22
use SilverStripe\Assets\Folder;
23
use SilverStripe\Assets\Image;
24
use SilverStripe\Assets\Storage\AssetNameGenerator;
25
use SilverStripe\Assets\Upload;
26
use SilverStripe\Control\Controller;
27
use SilverStripe\Control\HTTPRequest;
28
use SilverStripe\Control\HTTPResponse;
29
use SilverStripe\Core\Injector\Injector;
30
use SilverStripe\Forms\FieldList;
31
use SilverStripe\Forms\Form;
32
use SilverStripe\Forms\FormFactory;
33
use SilverStripe\ORM\ArrayList;
34
use SilverStripe\ORM\DataObject;
35
use SilverStripe\ORM\FieldType\DBHTMLText;
36
use SilverStripe\ORM\ValidationResult;
37
use SilverStripe\Security\Member;
38
use SilverStripe\Security\PermissionProvider;
39
use SilverStripe\Security\SecurityToken;
40
use SilverStripe\View\Requirements;
41
use SilverStripe\Versioned\Versioned;
42
use Exception;
43
44
/**
45
 * AssetAdmin is the 'file store' section of the CMS.
46
 * It provides an interface for manipulating the File and Folder objects in the system.
47
 */
48
class AssetAdmin extends LeftAndMain implements PermissionProvider
49
{
50
    private static $url_segment = 'assets';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $url_segment is not used and could be removed.

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

Loading history...
51
52
    private static $url_rule = '/$Action/$ID';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $url_rule is not used and could be removed.

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

Loading history...
53
54
    private static $menu_title = 'Files';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $menu_title is not used and could be removed.

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

Loading history...
55
56
    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...
57
58
    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...
59
60
    private static $url_handlers = [
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
61
        // Legacy redirect for SS3-style detail view
62
        'EditForm/field/File/item/$FileID/$Action' => 'legacyRedirectForEditView',
63
        // Pass all URLs to the index, for React to unpack
64
        'show/$FolderID/edit/$FileID' => 'index',
65
        // API access points with structured data
66
        'POST api/createFile' => 'apiCreateFile',
67
        'POST api/uploadFile' => 'apiUploadFile',
68
        'GET api/history' => 'apiHistory',
69
        // for validating before generating the schema
70
        'schemaWithValidate/$FormName' => 'schemaWithValidate',
71
        'fileEditForm/$ID' => 'fileEditForm',
72
        'fileHistoryForm/$ID/$VersionID' => 'fileHistoryForm',
73
        'folderCreateForm/$ParentID' => 'folderCreateForm',
74
    ];
75
76
    /**
77
     * Amount of results showing on a single page.
78
     *
79
     * @config
80
     * @var int
81
     */
82
    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...
83
84
    /**
85
     * @config
86
     * @see Upload->allowedMaxFileSize
87
     * @var int
88
     */
89
    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...
90
91
    /**
92
     * @config
93
     *
94
     * @var int
95
     */
96
    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...
97
98
    /**
99
     * @var array
100
     */
101
    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...
102
        'legacyRedirectForEditView',
103
        'apiCreateFile',
104
        'apiUploadFile',
105
        'apiHistory',
106
        'folderCreateForm',
107
        'fileEditForm',
108
        'fileHistoryForm',
109
        'addToCampaignForm',
110
        'fileInsertForm',
111
        'remoteEditForm',
112
        'remoteCreateForm',
113
        'schema',
114
        'fileSelectForm',
115
        'fileSearchForm',
116
        'schemaWithValidate',
117
    );
118
119
    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...
120
121
    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...
122
123
    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...
124
125
    /**
126
     * Set up the controller
127
     */
128
    public function init()
129
    {
130
        parent::init();
131
132
        Requirements::add_i18n_javascript(ASSET_ADMIN_DIR . '/client/lang', false, true);
133
        Requirements::javascript(ASSET_ADMIN_DIR . "/client/dist/js/bundle.js");
134
        Requirements::css(ASSET_ADMIN_DIR . "/client/dist/styles/bundle.css");
135
136
        CMSBatchActionHandler::register('delete', DeleteAssets::class, Folder::class);
137
    }
138
139
    public function getClientConfig()
140
    {
141
        $baseLink = $this->Link();
142
        return array_merge(parent::getClientConfig(), [
143
            'reactRouter' => true,
144
            'createFileEndpoint' => [
145
                'url' => Controller::join_links($baseLink, 'api/createFile'),
146
                'method' => 'post',
147
                'payloadFormat' => 'urlencoded',
148
            ],
149
            'uploadFileEndpoint' => [
150
                'url' => Controller::join_links($baseLink, 'api/uploadFile'),
151
                'method' => 'post',
152
                'payloadFormat' => 'urlencoded',
153
            ],
154
            'historyEndpoint' => [
155
                'url' => Controller::join_links($baseLink, 'api/history'),
156
                'method' => 'get',
157
                'responseFormat' => 'json',
158
            ],
159
            'limit' => $this->config()->page_length,
160
            'form' => [
161
                'fileEditForm' => [
162
                    'schemaUrl' => $this->Link('schema/fileEditForm')
163
                ],
164
                'fileInsertForm' => [
165
                    'schemaUrl' => $this->Link('schema/fileInsertForm')
166
                ],
167
                'remoteEditForm' => [
168
                    'schemaUrl' => $this->Link('schemaWithValidate/remoteEditForm')
169
                ],
170
                'remoteCreateForm' => [
171
                    'schemaUrl' => $this->Link('schema/remoteCreateForm')
172
                ],
173
                'fileSelectForm' => [
174
                    'schemaUrl' => $this->Link('schema/fileSelectForm')
175
                ],
176
                'addToCampaignForm' => [
177
                    'schemaUrl' => $this->Link('schema/addToCampaignForm')
178
                ],
179
                'fileHistoryForm' => [
180
                    'schemaUrl' => $this->Link('schema/fileHistoryForm')
181
                ],
182
                'fileSearchForm' => [
183
                    'schemaUrl' => $this->Link('schema/fileSearchForm')
184
                ],
185
                'folderCreateForm' => [
186
                    'schemaUrl' => $this->Link('schema/folderCreateForm')
187
                ],
188
            ],
189
        ]);
190
    }
191
192
    /**
193
     * Creates a single file based on a form-urlencoded upload.
194
     *
195
     * @param HTTPRequest $request
196
     * @return HTTPRequest|HTTPResponse
197
     */
198
    public function apiCreateFile(HTTPRequest $request)
199
    {
200
        $data = $request->postVars();
201
202
        // When creating new files, rename on conflict
203
        $upload = $this->getUpload();
204
        $upload->setReplaceFile(false);
205
206
        // CSRF check
207
        $token = SecurityToken::inst();
208 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...
209
            return new HTTPResponse(null, 400);
210
        }
211
212
        // Check parent record
213
        /** @var Folder $parentRecord */
214
        $parentRecord = null;
215
        if (!empty($data['ParentID']) && is_numeric($data['ParentID'])) {
216
            $parentRecord = Folder::get()->byID($data['ParentID']);
217
        }
218
        $data['Parent'] = $parentRecord;
219
220
        $tmpFile = $request->postVar('Upload');
221 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...
222
            $result = ['message' => null];
223
            $errors = $upload->getErrors();
224
            if ($message = array_shift($errors)) {
225
                $result['message'] = [
226
                    'type' => 'error',
227
                    'value' => $message,
228
                ];
229
            }
230
            return (new HTTPResponse(json_encode($result), 400))
231
                ->addHeader('Content-Type', 'application/json');
232
        }
233
234
        // TODO Allow batch uploads
235
        $fileClass = File::get_class_for_file_extension(File::get_file_extension($tmpFile['name']));
236
        /** @var File $file */
237
        $file = Injector::inst()->create($fileClass);
238
239
        // check canCreate permissions
240 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...
241
            $result = ['message' => [
242
                'type' => 'error',
243
                'value' => _t(
244
                    'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.CreatePermissionDenied',
245
                    'You do not have permission to add files'
246
                )
247
            ]];
248
            return (new HTTPResponse(json_encode($result), 403))
249
                ->addHeader('Content-Type', 'application/json');
250
        }
251
252
        $uploadResult = $upload->loadIntoFile($tmpFile, $file, $parentRecord ? $parentRecord->getFilename() : '/');
253 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...
254
            $result = ['message' => [
255
                'type' => 'error',
256
                'value' => _t(
257
                    'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.LoadIntoFileFailed',
258
                    'Failed to load file'
259
                )
260
            ]];
261
            return (new HTTPResponse(json_encode($result), 400))
262
                ->addHeader('Content-Type', 'application/json');
263
        }
264
265
        $file->ParentID = $parentRecord ? $parentRecord->ID : 0;
266
        $file->write();
267
268
        $result = [$this->getObjectFromData($file)];
269
270
        return (new HTTPResponse(json_encode($result)))
271
            ->addHeader('Content-Type', 'application/json');
272
    }
273
274
    /**
275
     * Upload a new asset for a pre-existing record. Returns the asset tuple.
276
     *
277
     * Note that conflict resolution is as follows:
278
     *  - If uploading a file with the same extension, we simply keep the same filename,
279
     *    and overwrite any existing files (same name + sha = don't duplicate).
280
     *  - If uploading a new file with a different extension, then the filename will
281
     *    be replaced, and will be checked for uniqueness against other File dataobjects.
282
     *
283
     * @param HTTPRequest $request Request containing vars 'ID' of parent record ID,
284
     * and 'Name' as form filename value
285
     * @return HTTPRequest|HTTPResponse
286
     */
287
    public function apiUploadFile(HTTPRequest $request)
288
    {
289
        $data = $request->postVars();
290
291
        // When updating files, replace on conflict
292
        $upload = $this->getUpload();
293
        $upload->setReplaceFile(true);
294
295
        // CSRF check
296
        $token = SecurityToken::inst();
297 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...
298
            return new HTTPResponse(null, 400);
299
        }
300
        $tmpFile = $data['Upload'];
301
        if (empty($data['ID']) || empty($tmpFile['name']) || !array_key_exists('Name', $data)) {
302
            return new HTTPResponse('Invalid request', 400);
303
        }
304
305
        // Check parent record
306
        /** @var File $file */
307
        $file = File::get()->byID($data['ID']);
308
        if (!$file) {
309
            return new HTTPResponse('File not found', 404);
310
        }
311
        $folder = $file->ParentID ? $file->Parent()->getFilename() : '/';
312
313
        // If extension is the same, attempt to re-use existing name
314
        if (File::get_file_extension($tmpFile['name']) === File::get_file_extension($data['Name'])) {
315
            $tmpFile['name'] = $data['Name'];
316
        } else {
317
            // If we are allowing this upload to rename the underlying record,
318
            // do a uniqueness check.
319
            $renamer = $this->getNameGenerator($tmpFile['name']);
320
            foreach ($renamer as $name) {
321
                $filename = File::join_paths($folder, $name);
322
                if (!File::find($filename)) {
323
                    $tmpFile['name'] = $name;
324
                    break;
325
                }
326
            }
327
        }
328
329 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...
330
            $result = ['message' => null];
331
            $errors = $upload->getErrors();
332
            if ($message = array_shift($errors)) {
333
                $result['message'] = [
334
                    'type' => 'error',
335
                    'value' => $message,
336
                ];
337
            }
338
            return (new HTTPResponse(json_encode($result), 400))
339
                ->addHeader('Content-Type', 'application/json');
340
        }
341
342
        try {
343
            $tuple = $upload->load($tmpFile, $folder);
344
        } catch (Exception $e) {
345
            $result = [
346
                'message' => [
347
                    'type' => 'error',
348
                    'value' => $e->getMessage(),
349
                ]
350
            ];
351
            return (new HTTPResponse(json_encode($result), 400))
352
                ->addHeader('Content-Type', 'application/json');
353
        }
354
355
        if ($upload->isError()) {
356
            $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...
357
                'type' => 'error',
358
                'value' => implode(' ' . PHP_EOL, $upload->getErrors()),
359
            ];
360
            return (new HTTPResponse(json_encode($result), 400))
361
                ->addHeader('Content-Type', 'application/json');
362
        }
363
364
        $tuple['Name'] = basename($tuple['Filename']);
365
        return (new HTTPResponse(json_encode($tuple)))
366
            ->addHeader('Content-Type', 'application/json');
367
    }
368
369
    /**
370
     * Returns a JSON array for history of a given file ID. Returns a list of all the history.
371
     *
372
     * @param HTTPRequest $request
373
     * @return HTTPResponse
374
     */
375
    public function apiHistory(HTTPRequest $request)
376
    {
377
        // CSRF check not required as the GET request has no side effects.
378
        $fileId = $request->getVar('fileId');
379
380
        if (!$fileId || !is_numeric($fileId)) {
381
            return new HTTPResponse(null, 400);
382
        }
383
384
        $class = File::class;
385
        $file = DataObject::get($class)->byID($fileId);
386
387
        if (!$file) {
388
            return new HTTPResponse(null, 404);
389
        }
390
391
        if (!$file->canView()) {
392
            return new HTTPResponse(null, 403);
393
        }
394
395
        $versions = Versioned::get_all_versions($class, $fileId)
396
            ->limit($this->config()->max_history_entries)
397
            ->sort('Version', 'DESC');
398
399
        $output = array();
400
        $next = array();
401
        $prev = null;
402
403
        // swap the order so we can get the version number to compare against.
404
        // i.e version 3 needs to know version 2 is the previous version
405
        $copy = $versions->map('Version', 'Version')->toArray();
406
        foreach (array_reverse($copy) as $k => $v) {
407
            if ($prev) {
408
                $next[$v] = $prev;
409
            }
410
411
            $prev = $v;
412
        }
413
414
        $_cachedMembers = array();
415
416
        /** @var File $version */
417
        foreach ($versions as $version) {
418
            $author = null;
419
420
            if ($version->AuthorID) {
421
                if (!isset($_cachedMembers[$version->AuthorID])) {
422
                    $_cachedMembers[$version->AuthorID] = DataObject::get(Member::class)
423
                        ->byID($version->AuthorID);
424
                }
425
426
                $author = $_cachedMembers[$version->AuthorID];
427
            }
428
429
            if ($version->canView()) {
430
                if (isset($next[$version->Version])) {
431
                    $summary = $version->humanizedChanges(
432
                        $version->Version,
433
                        $next[$version->Version]
434
                    );
435
436
                    // if no summary returned by humanizedChanges, i.e we cannot work out what changed, just show a
437
                    // generic message
438
                    if (!$summary) {
439
                        $summary = _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.SAVEDFILE', "Saved file");
440
                    }
441
                } else {
442
                    $summary = _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.UPLOADEDFILE', "Uploaded file");
443
                }
444
445
                $output[] = array(
446
                    'versionid' => $version->Version,
447
                    'date_ago' => $version->dbObject('LastEdited')->Ago(),
448
                    'date_formatted' => $version->dbObject('LastEdited')->Nice(),
449
                    'status' => ($version->WasPublished) ? _t('File.PUBLISHED', 'Published') : '',
450
                    'author' => ($author)
451
                        ? $author->Name
452
                        : _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.UNKNOWN', "Unknown"),
453
                    'summary' => ($summary)
454
                        ? $summary
455
                        : _t('SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.NOSUMMARY', "No summary available")
456
                );
457
            }
458
        }
459
460
        return
461
            (new HTTPResponse(json_encode($output)))->addHeader('Content-Type', 'application/json');
462
    }
463
464
    /**
465
     * Redirects 3.x style detail links to new 4.x style routing.
466
     *
467
     * @param HTTPRequest $request
468
     */
469
    public function legacyRedirectForEditView($request)
470
    {
471
        $fileID = $request->param('FileID');
472
        /** @var File $file */
473
        $file = File::get()->byID($fileID);
474
        $link = $this->getFileEditLink($file) ?: $this->Link();
475
        $this->redirect($link);
476
    }
477
478
    /**
479
     * Given a file return the CMS link to edit it
480
     *
481
     * @param File $file
482
     * @return string
483
     */
484
    public function getFileEditLink($file)
485
    {
486
        if (!$file || !$file->isInDB()) {
487
            return null;
488
        }
489
490
        return Controller::join_links(
491
            $this->Link('show'),
492
            $file->ParentID,
493
            'edit',
494
            $file->ID
495
        );
496
    }
497
498
    /**
499
     * Get an asset renamer for the given filename.
500
     *
501
     * @param  string             $filename Path name
502
     * @return AssetNameGenerator
503
     */
504
    protected function getNameGenerator($filename)
505
    {
506
        return Injector::inst()
507
            ->createWithArgs('AssetNameGenerator', array($filename));
508
    }
509
510
    /**
511
     * @todo Implement on client
512
     *
513
     * @param bool $unlinked
514
     * @return ArrayList
515
     */
516
    public function breadcrumbs($unlinked = false)
517
    {
518
        return null;
519
    }
520
521
522
    /**
523
     * Don't include class namespace in auto-generated CSS class
524
     */
525
    public function baseCSSClasses()
526
    {
527
        return 'AssetAdmin LeftAndMain';
528
    }
529
530
    public function providePermissions()
531
    {
532
        return array(
533
            "CMS_ACCESS_AssetAdmin" => array(
534
                'name' => _t('CMSMain.ACCESS', "Access to '{title}' section", array(
535
                    'title' => static::menu_title()
536
                )),
537
                'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
538
            )
539
        );
540
    }
541
542
    /**
543
     * Build a form scaffolder for this model
544
     *
545
     * NOTE: Volatile api. May be moved to {@see LeftAndMain}
546
     *
547
     * @param File $file
548
     * @return FormFactory
549
     */
550
    public function getFormFactory(File $file)
551
    {
552
        // Get service name based on file class
553
        $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...
554
        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...
555
            $name = FolderFormFactory::class;
556
        } 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...
557
            $name = ImageFormFactory::class;
558
        } else {
559
            $name = FileFormFactory::class;
560
        }
561
        return Injector::inst()->get($name);
562
    }
563
564
    /**
565
     * The form is used to generate a form schema,
566
     * as well as an intermediary object to process data through API endpoints.
567
     * Since it's used directly on API endpoints, it does not have any form actions.
568
     * It handles both {@link File} and {@link Folder} records.
569
     *
570
     * @param int $id
571
     * @return Form
572
     */
573
    public function getFileEditForm($id)
574
    {
575
        return $this->getAbstractFileForm($id, 'fileEditForm');
576
    }
577
578
    /**
579
     * Get file edit form
580
     *
581
     * @param HTTPRequest $request
582
     * @return Form
583
     */
584
    public function fileEditForm($request = null)
585
    {
586
        // Get ID either from posted back value, or url parameter
587
        if (!$request) {
588
            $this->httpError(400);
589
            return null;
590
        }
591
        $id = $request->param('ID');
592
        if (!$id) {
593
            $this->httpError(400);
594
            return null;
595
        }
596
        return $this->getFileEditForm($id);
597
    }
598
599
    /**
600
     * The form is used to generate a form schema,
601
     * as well as an intermediary object to process data through API endpoints.
602
     * Since it's used directly on API endpoints, it does not have any form actions.
603
     * It handles both {@link File} and {@link Folder} records.
604
     *
605
     * @param int $id
606
     * @return Form
607
     */
608
    public function getFileInsertForm($id)
609
    {
610
        return $this->getAbstractFileForm($id, 'fileInsertForm', [ 'Type' => AssetFormFactory::TYPE_INSERT ]);
611
    }
612
613
    /**
614
     * Get file insert form
615
     *
616
     * @return Form
617
     */
618
    public function fileInsertForm()
619
    {
620
        // Get ID either from posted back value, or url parameter
621
        $request = $this->getRequest();
622
        $id = $request->param('ID') ?: $request->postVar('ID');
623
        return $this->getFileInsertForm($id);
624
    }
625
626
    /**
627
     * Abstract method for generating a form for a file
628
     *
629
     * @param int $id Record ID
630
     * @param string $name Form name
631
     * @param array $context Form context
632
     * @return Form
633
     */
634
    protected function getAbstractFileForm($id, $name, $context = [])
635
    {
636
        /** @var File $file */
637
        $file = File::get()->byID($id);
638
639
        if (!$file) {
640
            $this->httpError(404);
641
            return null;
642
        }
643
644 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...
645
            $this->httpError(403, _t(
646
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorItemPermissionDenied',
647
                'You don\'t have the necessary permissions to modify {ObjectTitle}',
648
                '',
649
                ['ObjectTitle' => $file->i18n_singular_name()]
650
            ));
651
            return null;
652
        }
653
654
        // Pass to form factory
655
        $augmentedContext = array_merge($context, ['Record' => $file]);
656
        $scaffolder = $this->getFormFactory($file);
657
        $form = $scaffolder->getForm($this, $name, $augmentedContext);
658
659
        // Set form action handler with ID included
660
        $form->setRequestHandler(
661
            LeftAndMainFormRequestHandler::create($form, [ $id ])
662
        );
663
664
        // Configure form to respond to validation errors with form schema
665
        // if requested via react.
666
        $form->setValidationResponseCallback(function (ValidationResult $error) use ($form, $id, $name) {
667
            $schemaId = Controller::join_links($this->Link('schema'), $name, $id);
668
            return $this->getSchemaResponse($schemaId, $form, $error);
669
        });
670
671
        return $form;
672
    }
673
674
    /**
675
     * Get form for selecting a file
676
     *
677
     * @return Form
678
     */
679
    public function fileSelectForm()
680
    {
681
        // Get ID either from posted back value, or url parameter
682
        $request = $this->getRequest();
683
        $id = $request->param('ID') ?: $request->postVar('ID');
684
        return $this->getFileSelectForm($id);
685
    }
686
687
    /**
688
     * Get form for selecting a file
689
     *
690
     * @param int $id ID of the record being selected
691
     * @return Form
692
     */
693
    public function getFileSelectForm($id)
694
    {
695
        return $this->getAbstractFileForm($id, 'fileSelectForm', [ 'Type' => AssetFormFactory::TYPE_SELECT ]);
696
    }
697
698
    /**
699
     * @param array $context
700
     * @return Form
701
     * @throws InvalidArgumentException
702
     */
703
    public function getFileHistoryForm($context)
704
    {
705
        // Check context
706
        if (!isset($context['RecordID']) || !isset($context['RecordVersion'])) {
707
            throw new InvalidArgumentException("Missing RecordID / RecordVersion for this form");
708
        }
709
        $id = $context['RecordID'];
710
        $versionId = $context['RecordVersion'];
711
        if (!$id || !$versionId) {
712
            return $this->httpError(404);
713
        }
714
715
        /** @var File $file */
716
        $file = Versioned::get_version(File::class, $id, $versionId);
717
        if (!$file) {
718
            return $this->httpError(404);
719
        }
720
721 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...
722
            $this->httpError(403, _t(
723
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorItemPermissionDenied',
724
                'You don\'t have the necessary permissions to modify {ObjectTitle}',
725
                '',
726
                ['ObjectTitle' => $file->i18n_singular_name()]
727
            ));
728
            return null;
729
        }
730
731
        $effectiveContext = array_merge($context, ['Record' => $file]);
732
        /** @var FormFactory $scaffolder */
733
        $scaffolder = Injector::inst()->get(FileHistoryFormFactory::class);
734
        $form = $scaffolder->getForm($this, 'fileHistoryForm', $effectiveContext);
735
736
        // Set form handler with ID / VersionID
737
        $form->setRequestHandler(
738
            LeftAndMainFormRequestHandler::create($form, [ $id, $versionId ])
739
        );
740
741
        // Configure form to respond to validation errors with form schema
742
        // if requested via react.
743 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...
744
            $schemaId = Controller::join_links($this->Link('schema/fileHistoryForm'), $id, $versionId);
745
            return $this->getSchemaResponse($schemaId, $form, $errors);
746
        });
747
748
        return $form;
749
    }
750
751
    /**
752
     * Gets a JSON schema representing the current edit form.
753
     *
754
     * WARNING: Experimental API.
755
     *
756
     * @param HTTPRequest $request
757
     * @return HTTPResponse
758
     */
759
    public function schema($request)
760
    {
761
        $formName = $request->param('FormName');
762
        if ($formName !== 'fileHistoryForm') {
763
            return parent::schema($request);
764
        }
765
766
        // Get schema for history form
767
        // @todo Eventually all form scaffolding will be based on context rather than record ID
768
        // See https://github.com/silverstripe/silverstripe-framework/issues/6362
769
        $itemID = $request->param('ItemID');
770
        $version = $request->param('OtherItemID');
771
        $form = $this->getFileHistoryForm([
772
            'RecordID' => $itemID,
773
            'RecordVersion' => $version,
774
        ]);
775
776
        // Respond with this schema
777
        $response = $this->getResponse();
778
        $response->addHeader('Content-Type', 'application/json');
779
        $schemaID = $this->getRequest()->getURL();
780
        return $this->getSchemaResponse($schemaID, $form);
781
    }
782
783
    /**
784
     * Get file history form
785
     *
786
     * @param HTTPRequest $request
787
     * @return Form
788
     */
789
    public function fileHistoryForm($request = null)
790
    {
791
        // Get ID either from posted back value, or url parameter
792
        if (!$request) {
793
            $this->httpError(400);
794
            return null;
795
        }
796
        $id = $request->param('ID');
797
        if (!$id) {
798
            $this->httpError(400);
799
            return null;
800
        }
801
        $versionID = $request->param('VersionID');
802
        if (!$versionID) {
803
            $this->httpError(400);
804
            return null;
805
        }
806
        $form = $this->getFileHistoryForm([
807
            'RecordID' => $id,
808
            'RecordVersion' => $versionID,
809
        ]);
810
        return $form;
811
    }
812
813
    /**
814
     * @param array $data
815
     * @param Form $form
816
     * @return HTTPResponse
817
     */
818
    public function createfolder($data, $form)
819
    {
820
        $parentID = isset($data['ParentID']) ? intval($data['ParentID']) : 0;
821
        $data['Parent'] = null;
822
        if ($parentID) {
823
            $parent = Folder::get()->byID($parentID);
824
            if (!$parent) {
825
                throw new \InvalidArgumentException(sprintf(
826
                    '%s#%s not found',
827
                    Folder::class,
828
                    $parentID
829
                ));
830
            }
831
            $data['Parent'] = $parent;
832
        }
833
834
        // Check permission
835
        if (!Folder::singleton()->canCreate(Member::currentUser(), $data)) {
836
            throw new \InvalidArgumentException(sprintf(
837
                '%s create not allowed',
838
                Folder::class
839
            ));
840
        }
841
842
        $folder = Folder::create();
843
        $form->saveInto($folder);
844
        $folder->write();
845
846
        // Return the record data in the same response as the schema to save a postback
847
        $schemaData = ['record' => $this->getObjectFromData($folder)];
848
        $schemaId = Controller::join_links($this->Link('schema/folderCreateForm'), $folder->ID);
849
        return $this->getSchemaResponse($schemaId, $form, null, $schemaData);
850
    }
851
852
    /**
853
     * @param array $data
854
     * @param Form $form
855
     * @return HTTPResponse
856
     */
857
    public function save($data, $form)
858
    {
859
        return $this->saveOrPublish($data, $form, false);
860
    }
861
862
    /**
863
     * @param array $data
864
     * @param Form $form
865
     * @return HTTPResponse
866
     */
867
    public function publish($data, $form)
868
    {
869
        return $this->saveOrPublish($data, $form, true);
870
    }
871
872
    /**
873
     * Update thisrecord
874
     *
875
     * @param array $data
876
     * @param Form $form
877
     * @param bool $doPublish
878
     * @return HTTPResponse
879
     */
880
    protected function saveOrPublish($data, $form, $doPublish = false)
881
    {
882 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...
883
            return (new HTTPResponse(json_encode(['status' => 'error']), 400))
884
                ->addHeader('Content-Type', 'application/json');
885
        }
886
887
        $id = (int) $data['ID'];
888
        /** @var File $record */
889
        $record = DataObject::get_by_id(File::class, $id);
890
891
        if (!$record) {
892
            return (new HTTPResponse(json_encode(['status' => 'error']), 404))
893
                ->addHeader('Content-Type', 'application/json');
894
        }
895
896
        if (!$record->canEdit() || ($doPublish && !$record->canPublish())) {
897
            return (new HTTPResponse(json_encode(['status' => 'error']), 401))
898
                ->addHeader('Content-Type', 'application/json');
899
        }
900
901
        // check File extension
902
        if (!empty($data['FileFilename'])) {
903
            $extension = File::get_file_extension($data['FileFilename']);
904
            $newClass = File::get_class_for_file_extension($extension);
905
906
            // if the class has changed, cast it to the proper class
907
            if ($record->getClassName() !== $newClass) {
908
                $record = $record->newClassInstance($newClass);
909
910
                // update the allowed category for the new file extension
911
                $category = File::get_app_category($extension);
912
                $record->File->setAllowedCategories($category);
913
            }
914
        }
915
916
        $form->saveInto($record);
917
        $record->write();
918
919
        // Publish this record and owned objects
920
        if ($doPublish) {
921
            $record->publishRecursive();
922
        }
923
924
        // Note: Force return of schema / state in success result
925
        return $this->getRecordUpdatedResponse($record, $form);
926
    }
927
928
    public function unpublish($data, $form)
929
    {
930 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...
931
            return (new HTTPResponse(json_encode(['status' => 'error']), 400))
932
                ->addHeader('Content-Type', 'application/json');
933
        }
934
935
        $id = (int) $data['ID'];
936
        /** @var File $record */
937
        $record = DataObject::get_by_id(File::class, $id);
938
939
        if (!$record) {
940
            return (new HTTPResponse(json_encode(['status' => 'error']), 404))
941
                ->addHeader('Content-Type', 'application/json');
942
        }
943
944
        if (!$record->canUnpublish()) {
945
            return (new HTTPResponse(json_encode(['status' => 'error']), 401))
946
                ->addHeader('Content-Type', 'application/json');
947
        }
948
949
        $record->doUnpublish();
950
        return $this->getRecordUpdatedResponse($record, $form);
951
    }
952
953
    /**
954
     * @param File $file
955
     *
956
     * @return array
957
     */
958
    public function getObjectFromData(File $file)
959
    {
960
        $object = array(
961
            'id' => $file->ID,
962
            'created' => $file->Created,
963
            'lastUpdated' => $file->LastEdited,
964
            'owner' => null,
965
            'parent' => null,
966
            'title' => $file->Title,
967
            'exists' => $file->exists(), // Broken file check
968
            '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...
969
            '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...
970
            'name' => $file->Name,
971
            'filename' => $file->Filename,
972
            'extension' => $file->Extension,
973
            'size' => $file->AbsoluteSize,
974
            'url' => $file->AbsoluteURL,
975
            'published' => $file->isPublished(),
976
            'modified' => $file->isModifiedOnDraft(),
977
            'draft' => $file->isOnDraftOnly(),
978
            'canEdit' => $file->canEdit(),
979
            'canDelete' => $file->canArchive(),
980
        );
981
982
        /** @var Member $owner */
983
        $owner = $file->Owner();
984
985
        if ($owner) {
986
            $object['owner'] = array(
987
                'id' => $owner->ID,
988
                'title' => trim($owner->FirstName . ' ' . $owner->Surname),
989
            );
990
        }
991
992
        /** @var Folder $parent */
993
        $parent = $file->Parent();
994
995
        if ($parent) {
996
            $object['parent'] = array(
997
                'id' => $parent->ID,
998
                'title' => $parent->Title,
999
                'filename' => $parent->Filename,
1000
            );
1001
        }
1002
1003
        /** @var File $file */
1004
        if ($file->getIsImage()) {
1005
            // Small thumbnail
1006
            $smallWidth = UploadField::config()->uninherited('thumbnail_width');
1007
            $smallHeight = UploadField::config()->uninherited('thumbnail_height');
1008
            $smallThumbnail = $file->FitMax($smallWidth, $smallHeight);
1009
            if ($smallThumbnail && $smallThumbnail->exists()) {
1010
                $object['smallThumbnail'] = $smallThumbnail->getAbsoluteURL();
1011
            }
1012
1013
            // Large thumbnail
1014
            $width = $this->config()->get('thumbnail_width');
1015
            $height = $this->config()->get('thumbnail_height');
1016
            $thumbnail = $file->FitMax($width, $height);
1017
            if ($thumbnail && $thumbnail->exists()) {
1018
                $object['thumbnail'] = $thumbnail->getAbsoluteURL();
1019
            }
1020
            $object['width'] = $file->Width;
1021
            $object['height'] = $file->Height;
1022
        } else {
1023
            $object['thumbnail'] = $file->PreviewLink();
1024
        }
1025
1026
        return $object;
1027
    }
1028
1029
    /**
1030
     * Action handler for adding pages to a campaign
1031
     *
1032
     * @param array $data
1033
     * @param Form $form
1034
     * @return DBHTMLText|HTTPResponse
1035
     */
1036
    public function addtocampaign($data, $form)
1037
    {
1038
        $id = $data['ID'];
1039
        $record = File::get()->byID($id);
1040
1041
        $handler = AddToCampaignHandler::create($this, $record, 'addToCampaignForm');
1042
        $results = $handler->addToCampaign($record, $data['Campaign']);
1043
        if (!isset($results)) {
1044
            return null;
1045
        }
1046
1047
        // Send extra "message" data with schema response
1048
        $extraData = ['message' => $results];
1049
        $schemaId = Controller::join_links($this->Link('schema/addToCampaignForm'), $id);
1050
        return $this->getSchemaResponse($schemaId, $form, null, $extraData);
1051
    }
1052
1053
    /**
1054
     * Url handler for add to campaign form
1055
     *
1056
     * @param HTTPRequest $request
1057
     * @return Form
1058
     */
1059
    public function addToCampaignForm($request)
1060
    {
1061
        // Get ID either from posted back value, or url parameter
1062
        $id = $request->param('ID') ?: $request->postVar('ID');
1063
        return $this->getAddToCampaignForm($id);
1064
    }
1065
1066
    /**
1067
     * @param int $id
1068
     * @return Form
1069
     */
1070
    public function getAddToCampaignForm($id)
1071
    {
1072
        // Get record-specific fields
1073
        $record = File::get()->byID($id);
1074
1075
        if (!$record) {
1076
            $this->httpError(404, _t(
1077
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorNotFound',
1078
                'That {Type} couldn\'t be found',
1079
                '',
1080
                ['Type' => File::singleton()->i18n_singular_name()]
1081
            ));
1082
            return null;
1083
        }
1084 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...
1085
            $this->httpError(403, _t(
1086
                'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.ErrorItemPermissionDenied',
1087
                'You don\'t have the necessary permissions to modify {ObjectTitle}',
1088
                '',
1089
                ['ObjectTitle' => $record->i18n_singular_name()]
1090
            ));
1091
            return null;
1092
        }
1093
1094
        $handler = AddToCampaignHandler::create($this, $record, 'addToCampaignForm');
1095
        $form = $handler->Form($record);
1096
1097 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...
1098
            $schemaId = Controller::join_links($this->Link('schema/addToCampaignForm'), $id);
1099
            return $this->getSchemaResponse($schemaId, $form, $errors);
1100
        });
1101
1102
        return $form;
1103
    }
1104
1105
    /**
1106
     * @return Upload
1107
     */
1108
    protected function getUpload()
1109
    {
1110
        $upload = Upload::create();
1111
        $upload->getValidator()->setAllowedExtensions(
1112
            // filter out '' since this would be a regex problem on JS end
1113
            array_filter(File::config()->uninherited('allowed_extensions'))
1114
        );
1115
1116
        return $upload;
1117
    }
1118
1119
    /**
1120
     * Get response for successfully updated record
1121
     *
1122
     * @param File $record
1123
     * @param Form $form
1124
     * @return HTTPResponse
1125
     */
1126
    protected function getRecordUpdatedResponse($record, $form)
1127
    {
1128
        // Return the record data in the same response as the schema to save a postback
1129
        $schemaData = ['record' => $this->getObjectFromData($record)];
1130
        $schemaId = Controller::join_links($this->Link('schema/fileEditForm'), $record->ID);
1131
        return $this->getSchemaResponse($schemaId, $form, null, $schemaData);
1132
    }
1133
1134
    /**
1135
     * @param HTTPRequest $request
1136
     * @return Form
1137
     */
1138
    public function folderCreateForm($request = null)
1139
    {
1140
        // Get ID either from posted back value, or url parameter
1141
        if (!$request) {
1142
            $this->httpError(400);
1143
            return null;
1144
        }
1145
        $id = $request->param('ParentID');
1146
        // Fail on null ID (but not parent)
1147
        if (!isset($id)) {
1148
            $this->httpError(400);
1149
            return null;
1150
        }
1151
        return $this->getFolderCreateForm($id);
1152
    }
1153
1154
    /**
1155
     * Returns the form to be used for creating a new folder
1156
     * @param $parentId
1157
     * @return Form
1158
     */
1159
    public function getFolderCreateForm($parentId = 0)
1160
    {
1161
        /** @var FolderCreateFormFactory $factory */
1162
        $factory = Injector::inst()->get(FolderCreateFormFactory::class);
1163
        $form = $factory->getForm($this, 'folderCreateForm', [ 'ParentID' => $parentId ]);
1164
1165
        // Set form action handler with ParentID included
1166
        $form->setRequestHandler(
1167
            LeftAndMainFormRequestHandler::create($form, [ $parentId ])
1168
        );
1169
1170
        return $form;
1171
    }
1172
1173
    /**
1174
     * Scaffold a search form.
1175
     * Note: This form does not submit to itself, but rather uses the apiReadFolder endpoint
1176
     * (to be replaced with graphql)
1177
     *
1178
     * @return Form
1179
     */
1180
    public function fileSearchForm()
1181
    {
1182
        $scaffolder = FileSearchFormFactory::singleton();
1183
        return $scaffolder->getForm($this, 'fileSearchForm', []);
1184
    }
1185
1186
    /**
1187
     * Allow search form to be accessible to schema
1188
     *
1189
     * @return Form
1190
     */
1191
    public function getFileSearchform()
1192
    {
1193
        return $this->fileSearchForm();
1194
    }
1195
1196
    /**
1197
     * Form for creating a new OEmbed object in the WYSIWYG, used by the InsertEmbedModal component
1198
     *
1199
     * @param null $id
1200
     * @return mixed
1201
     */
1202
    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...
1203
    {
1204
        return Injector::inst()->get(RemoteFileFormFactory::class)
1205
            ->getForm($this, 'remoteCreateForm', ['type' => 'create']);
1206
    }
1207
1208
    /**
1209
     * Allow form to be accessible for schema
1210
     *
1211
     * @return mixed
1212
     */
1213
    public function remoteCreateForm()
1214
    {
1215
        return $this->getRemoteCreateForm();
1216
    }
1217
1218
    /**
1219
     * Form for editing a OEmbed object in the WYSIWYG, used by the InsertEmbedModal component
1220
     *
1221
     * @return mixed
1222
     */
1223
    public function getRemoteEditForm()
1224
    {
1225
        $url = $this->request->requestVar('embedurl');
1226
        $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...
1227
        $form = Injector::inst()->get(RemoteFileFormFactory::class)
1228
            ->getForm($this, 'remoteEditForm', ['type' => 'edit', 'url' => $url]);
1229
        return $form;
1230
    }
1231
1232
    /**
1233
     * Allow form to be accessible for schema
1234
     *
1235
     * @return mixed
1236
     */
1237
    public function remoteEditForm()
1238
    {
1239
        return $this->getRemoteEditForm();
1240
    }
1241
1242
    /**
1243
     * Capture the schema handling process, as there is validation done to the URL provided before form is generated
1244
     *
1245
     * @param $request
1246
     * @return HTTPResponse
1247
     */
1248
    public function schemaWithValidate(HTTPRequest $request)
1249
    {
1250
        $formName = $request->param('FormName');
1251
        $itemID = $request->param('ItemID');
1252
1253
        if (!$formName) {
1254
            return (new HTTPResponse('Missing request params', 400));
1255
        }
1256
1257
        $formMethod = "get{$formName}";
1258
        if (!$this->hasMethod($formMethod)) {
1259
            return (new HTTPResponse('Form not found', 404));
1260
        }
1261
1262
        if (!$this->hasAction($formName)) {
1263
            return (new HTTPResponse('Form not accessible', 401));
1264
        }
1265
1266
        $schemaID = $request->getURL();
1267
        try {
1268
            if ($itemID) {
1269
                $form = $this->{$formMethod}($itemID);
1270
            } else {
1271
                $form = $this->{$formMethod}();
1272
            }
1273
            return $this->getSchemaResponse($schemaID, $form);
1274
        } catch (InvalidUrlException $exception) {
1275
            $errors = ValidationResult::create()
1276
                ->addError($exception->getMessage());
1277
            $form = Form::create(null, 'Form', FieldList::create(), FieldList::create());
1278
            $code = $exception->getCode();
1279
1280
            if ($code < 300) {
1281
                $code = 500;
1282
            }
1283
1284
            return $this->getSchemaResponse($schemaID, $form, $errors)
1285
                ->setStatusCode($code);
1286
        }
1287
    }
1288
}
1289