Completed
Push — master ( 30833b...8e8ccf )
by Hamish
11s
created

AssetAdmin::getFileEditLink()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 13
rs 9.4285
cc 3
eloc 10
nc 2
nop 1
1
<?php
2
3
namespace SilverStripe\AssetAdmin\Controller;
4
5
use SilverStripe\Filesystem\Storage\AssetNameGenerator;
6
use SilverStripe\AssetAdmin\FormField\AssetGalleryField;
7
use SilverStripe\AssetAdmin\FormField\DropzoneUploadField;
8
use LeftAndMain;
9
use PermissionProvider;
10
use DateField;
11
use TabSet;
12
use Tab;
13
use DropdownField;
14
use SS_HTTPResponse_Exception;
15
use Controller;
16
use TextField;
17
use FieldList;
18
use Form;
19
use FormAction;
20
use CheckboxField;
21
use Convert;
22
use ArrayData;
23
use File;
24
use Session;
25
use Requirements;
26
use CMSBatchActionHandler;
27
use HiddenField;
28
use DataObject;
29
use Injector;
30
use Folder;
31
use Security;
32
use CMSForm;
33
use CMSBatchAction;
34
use SS_List;
35
use CompositeField;
36
use SSViewer;
37
use HeaderField;
38
use FieldGroup;
39
use Object;
40
41
/**
42
 * AssetAdmin is the 'file store' section of the CMS.
43
 * It provides an interface for manipulating the File and Folder objects in the system.
44
 *
45
 * @package cms
46
 * @subpackage assets
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 $tree_class = '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
59
    private static $url_handlers = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $url_handlers is not used and could be removed.

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

Loading history...
60
        // Legacy redirect for SS3-style detail view
61
        'EditForm/field/File/item/$FileID/$Action' => 'legacyRedirectForEditView',
62
        // Pass all URLs to the index, for React to unpack
63
        'show/$FolderID/edit/$FileID' => 'index',
64
    ];
65
66
    /**
67
     * Amount of results showing on a single page.
68
     *
69
     * @config
70
     * @var int
71
     */
72
    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...
73
74
    /**
75
     * @config
76
     * @see Upload->allowedMaxFileSize
77
     * @var int
78
     */
79
    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...
80
81
    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...
82
        'addfolder',
83
        'delete',
84
        'AddForm',
85
        'SearchForm',
86
        'legacyRedirectForEditView',
87
    );
88
89
90
91
	public function getClientConfig() {
92
		return array_merge( parent::getClientConfig(), [
93
            'assetsRoute' => $this->Link() . ':folderAction?/:folderId?/:fileAction?/:fileId?',
94
            'assetsRouteHome' => $this->Link() . 'show/0',
95
        ]);
96
	}
97
98
    public function legacyRedirectForEditView($request)
99
    {
100
        $fileID = $request->param('FileID');
101
        $file = File::get()->byID($fileID);
102
        $link = $this->getFileEditLink($file) ?: $this->Link();
0 ignored issues
show
Documentation introduced by
$file is of type object<DataObject>|null, but the function expects a object<File>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
103
        $this->redirect($link);
104
    }
105
106
    /**
107
     * Given a file return the CMS link to edit it
108
     *
109
     * @param File $file
110
     * @return string
111
     */
112
    public function getFileEditLink($file) {
113
        if(!$file || !$file->isInDB()) {
114
            return null;
115
        }
116
        $fileID = $file->ID;
117
        $folderID = $file->ParentID;
118
        return Controller::join_links(
119
            $this->Link('show'),
120
            $folderID,
121
            'edit',
122
            $fileID
123
        );
124
    }
125
126
    /**
127
     * Return fake-ID "root" if no ID is found (needed to upload files into the root-folder)
128
     */
129
    public function currentPageID()
130
    {
131
        if (is_numeric($this->getRequest()->requestVar('ID'))) {
132
            return $this->getRequest()->requestVar('ID');
133
        } elseif (is_numeric($this->urlParams['ID'])) {
134
            return $this->urlParams['ID'];
135
        } elseif (Session::get("{$this->class}.currentPage")) {
136
            return Session::get("{$this->class}.currentPage");
137
        } else {
138
            return 0;
139
        }
140
    }
141
142
    /**
143
     * Gets the ID of the folder being requested.
144
     *
145
     * @return int
146
     */
147
    public function getCurrentFolderID()
148
    {
149
        $currentFolderID = 0;
150
151
        if ($this->urlParams['Action'] == 'show' && is_numeric($this->urlParams['ID'])) {
152
            $currentFolderID = $this->urlParams['ID'];
153
        }
154
155
        return $currentFolderID;
156
    }
157
158
    /**
159
     * Set up the controller
160
     */
161
    public function init()
162
    {
163
        parent::init();
164
165
        Requirements::javascript(FRAMEWORK_DIR . "/admin/client/dist/js/bundle-lib.js");
166
        Requirements::add_i18n_javascript(ASSET_ADMIN_DIR . '/client/lang', false, true);
167
        Requirements::css(ASSET_ADMIN_DIR . "/client/dist/styles/bundle.css");
168
169
        CMSBatchActionHandler::register('delete', 'SilverStripe\AssetAdmin\BatchAction\DeleteAssets', 'Folder');
170
    }
171
172
    /**
173
     * Returns the files and subfolders contained in the currently selected folder,
174
     * defaulting to the root node. Doubles as search results, if any search parameters
175
     * are set through {@link SearchForm()}.
176
     *
177
     * @return SS_List
178
     */
179
    public function getList()
180
    {
181
        $folder = $this->currentPage();
182
        $context = $this->getSearchContext();
183
        // Overwrite name filter to search both Name and Title attributes
184
        $context->removeFilterByName('Name');
185
        $params = $this->getRequest()->requestVar('q');
186
        $list = $context->getResults($params);
187
188
        // Don't filter list when a detail view is requested,
189
        // to avoid edge cases where the filtered list wouldn't contain the requested
190
        // record due to faulty session state (current folder not always encoded in URL, see #7408).
191
        if (!$folder->ID
192
            && $this->getRequest()->requestVar('ID') === null
193
            && ($this->getRequest()->param('ID') == 'field')
194
        ) {
195
            return $list;
196
        }
197
198
        // Re-add previously removed "Name" filter as combined filter
199
        // TODO Replace with composite SearchFilter once that API exists
200 View Code Duplication
        if (!empty($params['Name'])) {
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...
201
            $list = $list->filterAny(array(
202
                'Name:PartialMatch' => $params['Name'],
203
                'Title:PartialMatch' => $params['Name']
204
            ));
205
        }
206
207
        // Always show folders at the top
208
        $list = $list->sort('(CASE WHEN "File"."ClassName" = \'Folder\' THEN 0 ELSE 1 END), "Name"');
209
210
        // If a search is conducted, check for the "current folder" limitation.
211
        // Otherwise limit by the current folder as denoted by the URL.
212
        if (empty($params) || !empty($params['CurrentFolderOnly'])) {
213
            $list = $list->filter('ParentID', $folder->ID);
214
        }
215
216
        // Category filter
217 View Code Duplication
        if (!empty($params['AppCategory'])
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...
218
            && !empty(File::config()->app_categories[$params['AppCategory']])
219
        ) {
220
            $exts = File::config()->app_categories[$params['AppCategory']];
221
            $list = $list->filter('Name:PartialMatch', $exts);
222
        }
223
224
        // Date filter
225
        if (!empty($params['CreatedFrom'])) {
226
            $fromDate = new DateField(null, null, $params['CreatedFrom']);
227
            $list = $list->filter("Created:GreaterThanOrEqual", $fromDate->dataValue().' 00:00:00');
228
        }
229 View Code Duplication
        if (!empty($params['CreatedTo'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
230
            $toDate = new DateField(null, null, $params['CreatedTo']);
231
            $list = $list->filter("Created:LessThanOrEqual", $toDate->dataValue().' 23:59:59');
232
        }
233
234
        return $list;
235
    }
236
237
    public function getEditForm($id = null, $fields = null)
238
    {
239
        $form = parent::getEditForm($id, $fields);
240
        $folder = ($id && is_numeric($id)) ? DataObject::get_by_id('Folder', $id, false) : $this->currentPage();
241
        $title = ($folder && $folder->exists()) ? $folder->Title : _t('AssetAdmin.FILES', 'Files');
0 ignored issues
show
Unused Code introduced by
$title 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...
242
243
        // Override fields, ignore any details from Folder->getCMSFields() since they'll
244
        // be handled by a detail form instead.
245
        $fields = FieldList::create(
246
            HiddenField::create('ID', false, $folder ? $folder->ID : null)
247
        );
248
        $form->setFields($fields);
249
250
        // TODO Re-enable once GridField works with React
251
        // // File listing
252
        // $gridFieldConfig = GridFieldConfig::create()->addComponents(
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
253
        //     new GridFieldToolbarHeader(),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
254
        //     new GridFieldSortableHeader(),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
255
        //     new GridFieldFilterHeader(),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
256
        //     new GridFieldDataColumns(),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
257
        //     new GridFieldPaginator(self::config()->page_length),
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
258
        //     new GridFieldEditButton(),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
259
        //     new GridFieldDeleteAction(),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
260
        //     new GridFieldDetailForm(),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
261
        //     GridFieldLevelup::create($folder->ID)->setLinkSpec('admin/assets/show/%d')
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
262
        // );
263
        // $gridField = GridField::create('File', $title, $this->getList(), $gridFieldConfig);
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
264
        // $columns = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
265
        // $columns->setDisplayFields(array(
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
266
        //     'StripThumbnail' => '',
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
267
        //     'Title' => _t('File.Title', 'Title'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
268
        //     'Created' => _t('AssetAdmin.CREATED', 'Date'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
269
        //     'Size' => _t('AssetAdmin.SIZE', 'Size'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
270
        // ));
271
        // $columns->setFieldCasting(array(
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
272
        //     'Created' => 'SS_Datetime->Nice'
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
273
        // ));
274
        // $gridField->setAttribute(
275
        //     'data-url-folder-template',
276
        //     Controller::join_links($this->Link('show'), '%s')
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
277
        // );
278
279
        // Move existing fields to a "details" tab, unless they've already been tabbed out through extensions.
280
        // Required to keep Folder->getCMSFields() simple and reuseable,
281
        // without any dependencies into AssetAdmin (e.g. useful for "add folder" views).
282
        if (!$fields->hasTabset()) {
283
            $tabs = new TabSet(
284
                'Root',
285
                $tabList = new Tab('ListView', _t('AssetAdmin.ListView', 'List View'))
286
            );
287
            $tabList->addExtraClass("content-listview cms-tabset-icon list");
288
            if ($fields->Count() && $folder->exists()) {
289
                $tabs->push($tabDetails = new Tab('DetailsView', _t('AssetAdmin.DetailsView', 'Details')));
290
                $tabDetails->addExtraClass("content-galleryview cms-tabset-icon edit");
291
                foreach ($fields as $field) {
292
                    $fields->removeByName($field->getName());
293
                    $tabDetails->push($field);
294
                }
295
            }
296
            $fields->push($tabs);
297
        }
298
299
        // Add the upload field for new media
300
        if ($currentPageID = $this->currentPageID()) {
301
            Session::set("{$this->class}.currentPage", $currentPageID);
302
        }
303
304
        $folder = $this->currentPage();
305
306
        if ($folder->exists()) {
307
            $path = $folder->getFilename();
0 ignored issues
show
Unused Code introduced by
$path 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...
308
        }
309
310
        $galleryField = AssetGalleryField::create('Files')
311
            ->setCurrentFolder($this->getCurrentFolderID())
312
            ->setLimit(15);
313
314
        // List view
315
        $fields->addFieldsToTab('Root.ListView', array(
316
            $galleryField,
317
            new DropzoneUploadField('Upload')
318
        ));
319
320
        // Move actions to "details" tab (they don't make sense on list view)
321
        $actions = $form->Actions();
322
        $saveBtn = $actions->fieldByName('action_save');
323
        $deleteBtn = $actions->fieldByName('action_delete');
324
        $actions->removeByName('action_save');
325
        $actions->removeByName('action_delete');
326
        if (($saveBtn || $deleteBtn) && $fields->fieldByName('Root.DetailsView')) {
327
            $fields->addFieldToTab(
328
                'Root.DetailsView',
329
                CompositeField::create($saveBtn, $deleteBtn)->addExtraClass('Actions')
330
            );
331
        }
332
333
        $fields->setForm($form);
0 ignored issues
show
Bug introduced by
It seems like $form defined by parent::getEditForm($id, $fields) on line 239 can be null; however, FieldList::setForm() 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...
334
        $form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
0 ignored issues
show
Documentation introduced by
$this->getTemplatesWithSuffix('_EditForm') is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
335
        // TODO Can't merge $FormAttributes in template at the moment
336
        $form->addExtraClass('cms-edit-form ' . $this->BaseCSSClasses());
337
        $form->setAttribute('data-pjax-fragment', 'CurrentForm');
338
        $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
339
340
        $this->extend('updateEditForm', $form);
341
342
        return $form;
343
    }
344
345
    public function addfolder($request)
346
    {
347
        $obj = $this->customise(array(
348
            'EditForm' => $this->AddForm()
349
        ));
350
351
        if ($request->isAjax()) {
352
            // Rendering is handled by template, which will call EditForm() eventually
353
            $content = $obj->renderWith($this->getTemplatesWithSuffix('_Content'));
354
        } else {
355
            $content = $obj->renderWith($this->getViewer('show'));
356
        }
357
358
        return $content;
359
    }
360
361
    public function delete($data, $form)
362
    {
363
        $className = $this->stat('tree_class');
364
365
        $record = DataObject::get_by_id($className, $data['ID']);
366
        if ($record && !$record->canDelete()) {
367
            return Security::permissionFailure();
368
        }
369
        if (!$record || !$record->ID) {
370
            throw new SS_HTTPResponse_Exception("Bad record ID #" . (int) $data['ID'], 404);
371
        }
372
        $parentID = $record->ParentID;
373
        $record->delete();
374
        $this->setCurrentPageID(null);
375
376
        $this->getResponse()->addHeader('X-Status', rawurlencode(_t('LeftAndMain.DELETED', 'Deleted.')));
377
        $this->getResponse()->addHeader('X-Pjax', 'Content');
378
379
        return $this->redirect(Controller::join_links($this->Link('show'), $parentID ? $parentID : 0));
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->redirect(\Control...ntID ? $parentID : 0)); of type string|null adds the type string to the return on line 379 which is incompatible with the return type of the parent method LeftAndMain::delete of type SS_HTTPResponse|null.
Loading history...
380
    }
381
382
    /**
383
     * Get the search context
384
     *
385
     * @return SearchContext
386
     */
387
    public function getSearchContext()
388
    {
389
        $context = singleton('File')->getDefaultSearchContext();
390
391
        // Namespace fields, for easier detection if a search is present
392
        foreach ($context->getFields() as $field) {
393
            $field->setName(sprintf('q[%s]', $field->getName()));
394
        }
395
        foreach ($context->getFilters() as $filter) {
396
            $filter->setFullName(sprintf('q[%s]', $filter->getFullName()));
397
        }
398
399
        // Customize fields
400
        $dateHeader = HeaderField::create('q[Date]', _t('CMSSearch.FILTERDATEHEADING', 'Date'), 4);
401
        $dateFrom = DateField::create('q[CreatedFrom]', _t('CMSSearch.FILTERDATEFROM', 'From'))
402
        ->setConfig('showcalendar', true);
403
        $dateTo = DateField::create('q[CreatedTo]', _t('CMSSearch.FILTERDATETO', 'To'))
404
        ->setConfig('showcalendar', true);
405
        $dateGroup = FieldGroup::create(
406
            $dateHeader,
407
            $dateFrom,
408
            $dateTo
409
        );
410
        $context->addField($dateGroup);
411
        $appCategories = array(
412
            'archive' => _t('AssetAdmin.AppCategoryArchive', 'Archive', 'A collection of files'),
413
            'audio' => _t('AssetAdmin.AppCategoryAudio', 'Audio'),
414
            'document' => _t('AssetAdmin.AppCategoryDocument', 'Document'),
415
            'flash' => _t('AssetAdmin.AppCategoryFlash', 'Flash', 'The fileformat'),
416
            'image' => _t('AssetAdmin.AppCategoryImage', 'Image'),
417
            'video' => _t('AssetAdmin.AppCategoryVideo', 'Video'),
418
        );
419
        $context->addField(
420
            $typeDropdown = new DropdownField(
421
                'q[AppCategory]',
422
                _t('AssetAdmin.Filetype', 'File type'),
423
                $appCategories
424
            )
425
        );
426
427
        $typeDropdown->setEmptyString(' ');
428
429
        $context->addField(
430
            new CheckboxField('q[CurrentFolderOnly]', _t('AssetAdmin.CurrentFolderOnly', 'Limit to current folder?'))
431
        );
432
        $context->getFields()->removeByName('q[Title]');
433
434
        return $context;
435
    }
436
437
    /**
438
     * Returns a form for filtering of files and assets gridfield.
439
     * Result filtering takes place in {@link getList()}.
440
     *
441
     * @return Form
442
     *              @see AssetAdmin.js
443
     */
444
    public function searchForm()
445
    {
446
        $folder = $this->currentPage();
447
        $context = $this->getSearchContext();
448
449
        $fields = $context->getSearchFields();
450
        $actions = new FieldList(
451
            FormAction::create('doSearch', _t('CMSMain_left_ss.APPLY_FILTER', 'Apply Filter'))
452
                ->addExtraClass('ss-ui-action-constructive'),
453
            Object::create('ResetFormAction', 'clear', _t('CMSMain_left_ss.RESET', 'Reset'))
454
        );
455
456
        $form = new Form($this, 'filter', $fields, $actions);
457
        $form->setFormMethod('GET');
458
        $form->setFormAction(Controller::join_links($this->Link('show'), $folder->ID));
459
        $form->addExtraClass('cms-search-form');
460
        $form->loadDataFrom($this->getRequest()->getVars());
461
        $form->disableSecurityToken();
462
        // This have to match data-name attribute on the gridfield so that the javascript selectors work
463
        $form->setAttribute('data-gridfield', 'File');
464
465
        return $form;
466
    }
467
468
    public function addForm()
469
    {
470
        $form = CMSForm::create(
471
            $this,
472
            'AddForm',
473
            new FieldList(
474
                new TextField("Name", _t('File.Name')),
475
                new HiddenField('ParentID', false, $this->getRequest()->getVar('ParentID'))
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a null|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
476
            ),
477
            new FieldList(
478
                FormAction::create('doAdd', _t('AssetAdmin_left_ss.GO', 'Go'))
479
                    ->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept')
480
                    ->setTitle(_t('AssetAdmin.ActionAdd', 'Add folder'))
481
            )
482
        )->setHTMLID('Form_AddForm');
483
        $form->setResponseNegotiator($this->getResponseNegotiator());
484
        $form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
485
        // TODO Can't merge $FormAttributes in template at the moment
486
        $form->addExtraClass('add-form cms-add-form cms-edit-form cms-panel-padded center ' . $this->BaseCSSClasses());
487
488
        return $form;
489
    }
490
491
    /**
492
     * Add a new group and return its details suitable for ajax.
493
     *
494
     * @todo Move logic into Folder class, and use LeftAndMain->doAdd() default implementation.
495
     */
496
    public function doAdd($data, $form)
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

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

Loading history...
497
    {
498
        $class = $this->stat('tree_class');
499
500
        // check create permissions
501
        if (!singleton($class)->canCreate()) {
502
            return Security::permissionFailure($this);
503
        }
504
505
        // check addchildren permissions
506
        if (singleton($class)->hasExtension('Hierarchy')
507
            && isset($data['ParentID'])
508
            && is_numeric($data['ParentID'])
509
            && $data['ParentID']
510
        ) {
511
            $parentRecord = DataObject::get_by_id($class, $data['ParentID']);
512
            if ($parentRecord->hasMethod('canAddChildren') && !$parentRecord->canAddChildren()) {
513
                return Security::permissionFailure($this);
514
            }
515
        } else {
516
            $parentRecord = null;
517
        }
518
519
        // Check parent
520
        $parentID = $parentRecord && $parentRecord->ID
521
            ? (int) $parentRecord->ID
522
            : 0;
523
        // Build filename
524
        $filename = isset($data['Name'])
525
            ? basename($data['Name'])
526
            : _t('AssetAdmin.NEWFOLDER', "NewFolder");
527
        if ($parentRecord && $parentRecord->ID) {
528
            $filename = $parentRecord->getFilename() . '/' . $filename;
529
        }
530
531
        // Get the folder to be created
532
533
        // Ensure name is unique
534
        foreach ($this->getNameGenerator($filename) as $filename) {
535
            if (! File::find($filename)) {
536
                break;
537
            }
538
        }
539
540
        // Create record
541
        $record = Folder::create();
542
        $record->ParentID = $parentID;
543
        $record->Name = $record->Title = basename($filename);
544
        $record->write();
545
546
        if ($parentRecord) {
547
            return $this->redirect(Controller::join_links($this->Link('show'), $parentRecord->ID));
548
        } else {
549
            return $this->redirect($this->Link());
550
        }
551
    }
552
553
    /**
554
     * Get an asset renamer for the given filename.
555
     *
556
     * @param  string             $filename Path name
557
     * @return AssetNameGenerator
558
     */
559
    protected function getNameGenerator($filename)
560
    {
561
        return Injector::inst()
562
            ->createWithArgs('AssetNameGenerator', array($filename));
563
    }
564
565
    /**
566
     * Custom currentPage() method to handle opening the 'root' folder
567
     */
568
    public function currentPage()
569
    {
570
        $id = $this->currentPageID();
571
        if ($id && is_numeric($id) && $id > 0) {
572
            $folder = DataObject::get_by_id('Folder', $id);
573
            if ($folder && $folder->exists()) {
574
                return $folder;
575
            }
576
        }
577
        $this->setCurrentPageID(null);
578
579
        return new Folder();
580
    }
581
582
    /**
583
     * @param  bool      $unlinked
584
     * @return ArrayList
585
     */
586
    public function breadcrumbs($unlinked = false)
587
    {
588
        $items = parent::Breadcrumbs($unlinked);
589
590
        // The root element should explicitly point to the root node.
591
        // Uses session state for current record otherwise.
592
        $items[0]->Link = Controller::join_links(singleton(__class__)->Link('show'), 0);
593
594
        // If a search is in progress, don't show the path
595
        if ($this->getRequest()->requestVar('q')) {
596
            $items = $items->limit(1);
597
            $items->push(new ArrayData(array(
598
                'Title' => _t('LeftAndMain.SearchResults', 'Search Results'),
599
                'Link' => Controller::join_links(
600
                    $this->Link(),
601
                    '?' . http_build_query(array('q' => $this->getRequest()->requestVar('q')))
602
                )
603
            )));
604
        }
605
606
        // If we're adding a folder, note that in breadcrumbs as well
607
        if ($this->getRequest()->param('Action') == 'addfolder') {
608
            $items->push(new ArrayData(array(
609
                'Title' => _t('Folder.AddFolderButton', 'Add folder'),
610
                'Link' => false
611
            )));
612
        }
613
614
        return $items;
615
    }
616
617
618
    /**
619
     * Don't include class namespace in auto-generated CSS class
620
     */
621
    public function baseCSSClasses()
622
    {
623
        return 'AssetAdmin LeftAndMain';
624
    }
625
626
    /**
627
     * Don't include class namespace in template names
628
     * @todo Make code in framework more namespace-savvy so that we don't need this duplication
629
     */
630
    public function getTemplatesWithSuffix($suffix)
631
    {
632
        $className = get_class($this);
633
        $baseClass = 'LeftandMain';
634
635
        $templates = array();
636
        $classes = array_reverse(\ClassInfo::ancestry($className));
637
        foreach ($classes as $class) {
638
            $template = (new \ReflectionClass($class))->getShortName() . $suffix;
639
            if (\SSViewer::hasTemplate($template)) {
0 ignored issues
show
Documentation introduced by
$template is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
640
                $templates[] = $template;
641
            }
642
643
            // If the class is "Page_Controller", look for Page.ss
644
            if (stripos($class, '_controller') !== false) {
645
                $template = str_ireplace('_controller', '', $class) . $suffix;
646
                if (\SSViewer::hasTemplate($template)) {
0 ignored issues
show
Documentation introduced by
$template is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
647
                    $templates[] = $template;
648
                }
649
            }
650
651
            if ($baseClass && $class == $baseClass) {
652
                break;
653
            }
654
        }
655
656
        return $templates;
657
    }
658
659
    public function providePermissions()
660
    {
661
        $title = _t("AssetAdmin.MENUTITLE", LeftAndMain::menu_title_for_class($this->class));
662
663
        return array(
664
            "CMS_ACCESS_AssetAdmin" => array(
665
                'name' => _t('CMSMain.ACCESS', "Access to '{title}' section", array('title' => $title)),
0 ignored issues
show
Documentation introduced by
array('title' => $title) is of type array<string,string,{"title":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
666
                'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
667
            )
668
        );
669
    }
670
}
671