Completed
Push — master ( 933a29...3be0f3 )
by Damian
02:31
created

AssetAdmin::currentPageID()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 11
rs 8.8571
cc 5
eloc 9
nc 4
nop 0
1
<?php
2
3
use SilverStripe\Filesystem\Storage\AssetNameGenerator;
4
5
/**
6
 * AssetAdmin is the 'file store' section of the CMS.
7
 * It provides an interface for manipulating the File and Folder objects in the system.
8
 *
9
 * @package cms
10
 * @subpackage assets
11
 */
12
class AssetAdmin extends LeftAndMain implements PermissionProvider{
13
14
	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...
15
16
	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...
17
18
	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...
19
20
	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...
21
22
	/**
23
	 * Amount of results showing on a single page.
24
	 *
25
	 * @config
26
	 * @var int
27
	 */
28
	private static $page_length = 15;
29
30
	/**
31
	 * @config
32
	 * @see Upload->allowedMaxFileSize
33
	 * @var int
34
	 */
35
	private static $allowed_max_file_size;
36
37
	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...
38
		'addfolder',
39
		'delete',
40
		'AddForm',
41
		'SearchForm',
42
		'getsubtree'
43
	);
44
45
	/**
46
	 * Return fake-ID "root" if no ID is found (needed to upload files into the root-folder)
47
	 */
48
	public function currentPageID() {
49
		if(is_numeric($this->getRequest()->requestVar('ID')))	{
50
			return $this->getRequest()->requestVar('ID');
51
		} elseif (isset($this->urlParams['ID']) && is_numeric($this->urlParams['ID'])) {
52
			return $this->urlParams['ID'];
53
		} elseif(Session::get("{$this->class}.currentPage")) {
54
			return Session::get("{$this->class}.currentPage");
55
		} else {
56
			return 0;
57
		}
58
	}
59
60
	/**
61
	 * Set up the controller
62
	 */
63
	public function init() {
64
		parent::init();
65
66
		Versioned::set_stage(Versioned::DRAFT);
67
68
		Requirements::javascript(CMS_DIR . "/client/dist/js/AssetAdmin.js");
69
		Requirements::add_i18n_javascript(CMS_DIR . '/client/lang', false, true);
70
		Requirements::css(CMS_DIR . '/client/dist/styles/bundle.css');
71
		CMSBatchActionHandler::register('delete', 'AssetAdmin_DeleteBatchAction', 'Folder');
72
	}
73
74
	/**
75
	 * Returns the files and subfolders contained in the currently selected folder,
76
	 * defaulting to the root node. Doubles as search results, if any search parameters
77
	 * are set through {@link SearchForm()}.
78
	 *
79
	 * @return SS_List
80
	 */
81
	public function getList() {
82
		$folder = $this->currentPage();
83
		$context = $this->getSearchContext();
84
		// Overwrite name filter to search both Name and Title attributes
85
		$context->removeFilterByName('Name');
86
		$params = $this->getRequest()->requestVar('q');
87
		$list = $context->getResults($params);
88
89
		// Don't filter list when a detail view is requested,
90
		// to avoid edge cases where the filtered list wouldn't contain the requested
91
		// record due to faulty session state (current folder not always encoded in URL, see #7408).
92
		if(!$folder->ID
93
			&& $this->getRequest()->requestVar('ID') === null
94
			&& ($this->getRequest()->param('ID') == 'field')
95
		) {
96
			return $list;
97
		}
98
99
		// Re-add previously removed "Name" filter as combined filter
100
		// TODO Replace with composite SearchFilter once that API exists
101
		if(!empty($params['Name'])) {
102
			$list = $list->filterAny(array(
103
				'Name:PartialMatch' => $params['Name'],
104
				'Title:PartialMatch' => $params['Name']
105
			));
106
		}
107
108
		// Always show folders at the top
109
		$list = $list->sort('(CASE WHEN "File"."ClassName" = \'Folder\' THEN 0 ELSE 1 END), "Name"');
110
111
		// If a search is conducted, check for the "current folder" limitation.
112
		// Otherwise limit by the current folder as denoted by the URL.
113
		if(empty($params) || !empty($params['CurrentFolderOnly'])) {
114
			$list = $list->filter('ParentID', $folder->ID);
115
		}
116
117
		// Category filter
118
		if(!empty($params['AppCategory'])
119
			&& !empty(File::config()->app_categories[$params['AppCategory']])
120
		) {
121
			$exts = File::config()->app_categories[$params['AppCategory']];
122
			$list = $list->filter('Name:PartialMatch', $exts);
123
		}
124
125
		// Date filter
126 View Code Duplication
		if(!empty($params['CreatedFrom'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
127
			$fromDate = new DateField(null, null, $params['CreatedFrom']);
128
			$list = $list->filter("Created:GreaterThanOrEqual", $fromDate->dataValue().' 00:00:00');
129
		}
130 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...
131
			$toDate = new DateField(null, null, $params['CreatedTo']);
132
			$list = $list->filter("Created:LessThanOrEqual", $toDate->dataValue().' 23:59:59');
133
		}
134
135
		return $list;
136
	}
137
138
	public function getEditForm($id = null, $fields = null) {
139
		Requirements::javascript(FRAMEWORK_DIR . '/client/dist/js/AssetUploadField.js');
140
		Requirements::css(FRAMEWORK_DIR . '/client/dist/styles/AssetUploadField.css');
141
142
		$form = parent::getEditForm($id, $fields);
143
		$folder = ($id && is_numeric($id)) ? DataObject::get_by_id('Folder', $id, false) : $this->currentPage();
144
		$fields = $form->Fields();
145
		$title = ($folder && $folder->isInDB()) ? $folder->Title : _t('AssetAdmin.FILES', 'Files');
146
		$fields->push(new HiddenField('ID', false, $folder ? $folder->ID : null));
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...
147
148
		// Remove legacy previewable behaviour.
149
		$form->removeExtraClass('cms-previewable');
150
		$form->Fields()->removeByName('SilverStripeNavigator');
151
152
		// File listing
153
		$gridFieldConfig = GridFieldConfig::create()->addComponents(
154
			new GridFieldToolbarHeader(),
155
			new GridFieldSortableHeader(),
156
			new GridFieldFilterHeader(),
157
			new GridFieldDataColumns(),
158
			new GridFieldPaginator(self::config()->page_length),
159
			new GridFieldEditButton(),
160
			new GridFieldDeleteAction(),
161
			new GridFieldDetailForm(),
162
			GridFieldLevelup::create($folder->ID)->setLinkSpec('admin/assets/show/%d')
163
		);
164
165
		$gridField = GridField::create('File', $title, $this->getList(), $gridFieldConfig);
166
		$columns = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
167
		$columns->setDisplayFields(array(
168
			'StripThumbnail' => '',
169
			'Title' => _t('File.Title', 'Title'),
170
			'Created' => _t('AssetAdmin.CREATED', 'Date'),
171
			'Size' => _t('AssetAdmin.SIZE', 'Size'),
172
		));
173
		$columns->setFieldCasting(array(
174
			'Created' => 'SS_Datetime->Nice'
175
		));
176
		$gridField->setAttribute(
177
			'data-url-folder-template',
178
			Controller::join_links($this->Link('show'), '%s')
179
		);
180
181
		if(!$folder->hasMethod('canAddChildren') || ($folder->hasMethod('canAddChildren') && $folder->canAddChildren())) {
182
			// TODO Will most likely be replaced by GridField logic
183
			$addFolderBtn = new LiteralField(
184
				'AddFolderButton',
185
				sprintf(
186
					'<a class="ss-ui-button font-icon-folder-add no-text cms-add-folder-link" title="%s" data-icon="add" data-url="%s" href="%s"></a>',
187
					_t('Folder.AddFolderButton', 'Add folder'),
188
					Controller::join_links($this->Link('AddForm'), '?' . http_build_query(array(
189
						'action_doAdd' => 1,
190
						'ParentID' => $folder->ID,
191
						'SecurityID' => $form->getSecurityToken()->getValue()
192
					))),
193
					Controller::join_links($this->Link('addfolder'), '?ParentID=' . $folder->ID)
194
				)
195
			);
196
		} else {
197
			$addFolderBtn = '';
198
		}
199
200
		// Move existing fields to a "details" tab, unless they've already been tabbed out through extensions.
201
		// Required to keep Folder->getCMSFields() simple and reuseable,
202
		// without any dependencies into AssetAdmin (e.g. useful for "add folder" views).
203
		if(!$fields->hasTabset()) {
204
			$tabs = new TabSet('Root',
205
				$tabList = new Tab('ListView', _t('AssetAdmin.ListView', 'List View')),
206
				$tabTree = new Tab('TreeView', _t('AssetAdmin.TreeView', 'Tree View'))
207
			);
208
			$tabList->addExtraClass("content-listview cms-tabset-icon list");
209
			$tabTree->addExtraClass("content-treeview cms-tabset-icon tree");
210
			if($fields->Count() && $folder && $folder->isInDB()) {
211
				$tabs->push($tabDetails = new Tab('DetailsView', _t('AssetAdmin.DetailsView', 'Details')));
212
				$tabDetails->addExtraClass("content-galleryview cms-tabset-icon edit");
213
				foreach($fields as $field) {
214
					$fields->removeByName($field->getName());
215
					$tabDetails->push($field);
216
				}
217
			}
218
			$fields->push($tabs);
219
		}
220
221
		// we only add buttons if they're available. User might not have permission and therefore
222
		// the button shouldn't be available. Adding empty values into a ComposteField breaks template rendering.
223
		$actionButtonsComposite = CompositeField::create()->addExtraClass('cms-actions-row');
224
		if($addFolderBtn) $actionButtonsComposite->push($addFolderBtn);
0 ignored issues
show
Bug introduced by
It seems like $addFolderBtn defined by '' on line 197 can also be of type string; however, CompositeField::push() does only seem to accept object<FormField>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
225
226
		// Add the upload field for new media
227
		if($currentPageID = $this->currentPageID()){
228
			Session::set("{$this->class}.currentPage", $currentPageID);
229
		}
230
231
		$folder = $this->currentPage();
232
233
		$uploadField = UploadField::create('AssetUploadField', '');
234
		$uploadField->setConfig('previewMaxWidth', 40);
235
		$uploadField->setConfig('previewMaxHeight', 30);
236
		$uploadField->setConfig('changeDetection', false);
237
		$uploadField->addExtraClass('ss-assetuploadfield');
238
		$uploadField->removeExtraClass('ss-uploadfield');
239
		$uploadField->setTemplate('AssetUploadField');
240
241
		if($folder->exists()) {
242
			$path = $folder->getFilename();
243
			$uploadField->setFolderName($path);
244
		} else {
245
			$uploadField->setFolderName('/'); // root of the assets
246
		}
247
248
		$exts = $uploadField->getValidator()->getAllowedExtensions();
249
		asort($exts);
250
		$uploadField->Extensions = implode(', ', $exts);
0 ignored issues
show
Bug introduced by
The property Extensions does not seem to exist. Did you mean extensions?

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

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

Loading history...
251
252
		// List view
253
		$fields->addFieldsToTab('Root.ListView', array(
254
			$actionsComposite = CompositeField::create(
255
				$actionButtonsComposite
256
			)->addExtraClass('cms-content-toolbar field'),
257
			$uploadField,
258
			new HiddenField('ID'),
259
			$gridField
260
		));
261
262
		// Tree view
263
		$fields->addFieldsToTab('Root.TreeView', array(
264
			clone $actionsComposite,
265
			// TODO Replace with lazy loading on client to avoid performance hit of rendering potentially unused views
266
			new LiteralField(
267
				'Tree',
268
				FormField::create_tag(
269
					'div',
270
					array(
271
						'class' => 'cms-tree',
272
						'data-url-tree' => $this->Link('getsubtree'),
273
						'data-url-savetreenode' => $this->Link('savetreenode')
274
					),
275
					$this->SiteTreeAsUL()
276
				)
277
			)
278
		));
279
280
		// Move actions to "details" tab (they don't make sense on list/tree view)
281
		$actions = $form->Actions();
282
		$saveBtn = $actions->fieldByName('action_save');
283
		$deleteBtn = $actions->fieldByName('action_delete');
284
		$actions->removeByName('action_save');
285
		$actions->removeByName('action_delete');
286
		if(($saveBtn || $deleteBtn) && $fields->fieldByName('Root.DetailsView')) {
287
			$fields->addFieldToTab(
288
				'Root.DetailsView',
289
				CompositeField::create($saveBtn,$deleteBtn)->addExtraClass('Actions')
290
			);
291
		}
292
293
294
		$fields->setForm($form);
295
		$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...
296
		// TODO Can't merge $FormAttributes in template at the moment
297
		$form->addExtraClass('cms-edit-form ' . $this->BaseCSSClasses());
298
		$form->setAttribute('data-pjax-fragment', 'CurrentForm');
299
		$form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
300
301
		// Optionally handle form submissions with 'X-Formschema-Request'
302
		// which rely on having validation errors returned as structured data
303
		$form->setValidationResponseCallback(function() use ($form) {
304
			$request = $this->getRequest();
305
			if($request->getHeader('X-Formschema-Request')) {
306
				$data = $this->getSchemaForForm($form);
0 ignored issues
show
Bug introduced by
It seems like $form defined by parent::getEditForm($id, $fields) on line 142 can be null; however, LeftAndMain::getSchemaForForm() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
307
				$response = new SS_HTTPResponse(Convert::raw2json($data));
308
				$response->addHeader('Content-Type', 'application/json');
309
				return $response;
310
311
			}
312
		});
313
314
315
		$this->extend('updateEditForm', $form);
316
317
		return $form;
318
	}
319
320
	public function addfolder($request) {
321
		$obj = $this->customise(array(
322
			'EditForm' => $this->AddForm()
323
		));
324
325
		if($request->isAjax()) {
326
			// Rendering is handled by template, which will call EditForm() eventually
327
			$content = $obj->renderWith($this->getTemplatesWithSuffix('_Content'));
328
		} else {
329
			$content = $obj->renderWith($this->getViewer('show'));
330
		}
331
332
		return $content;
333
	}
334
335
	public function delete($data, $form) {
336
		$className = $this->stat('tree_class');
337
338
		$record = DataObject::get_by_id($className, $data['ID']);
339
		if($record && !$record->canDelete()) {
340
			return Security::permissionFailure();
341
		}
342 View Code Duplication
		if(!$record || !$record->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...
343
			throw new SS_HTTPResponse_Exception("Bad record ID #" . (int)$data['ID'], 404);
344
		}
345
		$parentID = $record->ParentID;
346
		$record->delete();
347
		$this->setCurrentPageID(null);
348
349
		$this->getResponse()->addHeader('X-Status', rawurlencode(_t('LeftAndMain.DELETED', 'Deleted.')));
350
		$this->getResponse()->addHeader('X-Pjax', 'Content');
351
		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 351 which is incompatible with the return type of the parent method LeftAndMain::delete of type SS_HTTPResponse|null.
Loading history...
352
	}
353
354
	/**
355
	 * Get the search context
356
	 *
357
	 * @return SearchContext
358
	 */
359
	public function getSearchContext() {
360
		$context = singleton('File')->getDefaultSearchContext();
361
362
		// Namespace fields, for easier detection if a search is present
363
		foreach($context->getFields() as $field) {
364
			$field->setName(sprintf('q[%s]', $field->getName()));
365
		}
366
		foreach($context->getFilters() as $filter) {
367
			$filter->setFullName(sprintf('q[%s]', $filter->getFullName()));
368
		}
369
370
		// Customize fields
371
		$dateHeader = HeaderField::create('q[Date]', _t('CMSSearch.FILTERDATEHEADING', 'Date'), 4);
372
		$dateFrom = DateField::create('q[CreatedFrom]', _t('CMSSearch.FILTERDATEFROM', 'From'))
373
		->setConfig('showcalendar', true);
374
		$dateTo = DateField::create('q[CreatedTo]',_t('CMSSearch.FILTERDATETO', 'To'))
375
		->setConfig('showcalendar', true);
376
		$dateGroup = FieldGroup::create(
377
			$dateHeader,
378
			$dateFrom,
379
			$dateTo
380
		);
381
		$context->addField($dateGroup);
382
		$appCategories = array(
383
			'archive' => _t('AssetAdmin.AppCategoryArchive', 'Archive', 'A collection of files'),
384
			'audio' => _t('AssetAdmin.AppCategoryAudio', 'Audio'),
385
			'document' => _t('AssetAdmin.AppCategoryDocument', 'Document'),
386
			'flash' => _t('AssetAdmin.AppCategoryFlash', 'Flash', 'The fileformat'),
387
			'image' => _t('AssetAdmin.AppCategoryImage', 'Image'),
388
			'video' => _t('AssetAdmin.AppCategoryVideo', 'Video'),
389
		);
390
		$context->addField(
391
			$typeDropdown = new DropdownField(
392
				'q[AppCategory]',
393
				_t('AssetAdmin.Filetype', 'File type'),
394
				$appCategories
395
			)
396
		);
397
398
		$typeDropdown->setEmptyString(' ');
399
400
		$context->addField(
401
			new CheckboxField('q[CurrentFolderOnly]', _t('AssetAdmin.CurrentFolderOnly', 'Limit to current folder?'))
402
		);
403
		$context->getFields()->removeByName('q[Title]');
404
405
		return $context;
406
	}
407
408
	/**
409
	 * Returns a form for filtering of files and assets gridfield.
410
	 * Result filtering takes place in {@link getList()}.
411
	 *
412
	 * @return Form
413
	 * @see AssetAdmin.js
414
	 */
415
	public function SearchForm() {
416
		$folder = $this->currentPage();
417
		$context = $this->getSearchContext();
418
419
		$fields = $context->getSearchFields();
420
		$actions = new FieldList(
421
			FormAction::create('doSearch',  _t('CMSMain_left_ss.APPLY_FILTER', 'Apply Filter'))
422
				->addExtraClass('ss-ui-action-constructive'),
423
			Object::create('ResetFormAction', 'clear', _t('CMSMain_left_ss.RESET', 'Reset'))
424
		);
425
426
		$form = new Form($this, 'filter', $fields, $actions);
427
		$form->setFormMethod('GET');
428
		$form->setFormAction(Controller::join_links($this->Link('show'), $folder->ID));
429
		$form->addExtraClass('cms-search-form');
430
		$form->loadDataFrom($this->getRequest()->getVars());
431
		$form->disableSecurityToken();
432
		// This have to match data-name attribute on the gridfield so that the javascript selectors work
433
		$form->setAttribute('data-gridfield', 'File');
434
		return $form;
435
	}
436
437
	public function AddForm() {
438
		$negotiator = $this->getResponseNegotiator();
439
		$form = Form::create(
440
			$this,
441
			'AddForm',
442
			new FieldList(
443
				new TextField("Name", _t('File.Name')),
444
				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...
445
			),
446
			new FieldList(
447
				FormAction::create('doAdd', _t('AssetAdmin_left_ss.GO','Go'))
448
					->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept')
449
					->setTitle(_t('AssetAdmin.ActionAdd', 'Add folder'))
450
			)
451
		)->setHTMLID('Form_AddForm');
452 View Code Duplication
		$form->setValidationResponseCallback(function() use ($negotiator, $form) {
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...
453
			$request = $this->getRequest();
454
			if($request->isAjax() && $negotiator) {
455
				$form->setupFormErrors();
456
				$result = $form->forTemplate();
457
458
				return $negotiator->respond($request, array(
459
					'CurrentForm' => function() use($result) {
460
						return $result;
461
					}
462
				));
463
			}
464
		});
465
		$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...
466
		// TODO Can't merge $FormAttributes in template at the moment
467
		$form->addExtraClass('add-form cms-add-form cms-edit-form cms-panel-padded center ' . $this->BaseCSSClasses());
468
469
		return $form;
470
	}
471
472
	/**
473
	 * Add a new group and return its details suitable for ajax.
474
	 *
475
	 * @todo Move logic into Folder class, and use LeftAndMain->doAdd() default implementation.
476
	 */
477
	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...
478
		$class = $this->stat('tree_class');
479
480
		// check create permissions
481
		if(!singleton($class)->canCreate()) {
482
			return Security::permissionFailure($this);
483
		}
484
485
		// check addchildren permissions
486
		if(
487
			singleton($class)->hasExtension('Hierarchy')
488
			&& isset($data['ParentID'])
489
			&& is_numeric($data['ParentID'])
490
			&& $data['ParentID']
491
		) {
492
			$parentRecord = DataObject::get_by_id($class, $data['ParentID']);
493
			if($parentRecord->hasMethod('canAddChildren') && !$parentRecord->canAddChildren()) {
494
				return Security::permissionFailure($this);
495
			}
496
		} else {
497
			$parentRecord = null;
498
		}
499
500
		// Check parent
501
		$parentID = $parentRecord && $parentRecord->ID
502
			? (int)$parentRecord->ID
503
			: 0;
504
		// Build filename
505
		$filename = isset($data['Name'])
506
			? basename($data['Name'])
507
			: _t('AssetAdmin.NEWFOLDER',"NewFolder");
508
		if($parentRecord && $parentRecord->ID) {
509
			$filename = $parentRecord->getFilename() . '/' . $filename;
510
		}
511
512
		// Get the folder to be created
513
514
		// Ensure name is unique
515
		foreach($this->getNameGenerator($filename) as $filename) {
516
			if(! File::find($filename) ) {
517
				break;
518
			}
519
		}
520
521
		// Create record
522
		$record = Folder::create();
523
		$record->ParentID = $parentID;
524
		$record->Name = $record->Title = basename($filename);
525
		$record->write();
526
527
528
		if($parentRecord) {
529
			return $this->redirect(Controller::join_links($this->Link('show'), $parentRecord->ID));
530
		} else {
531
			return $this->redirect($this->Link());
532
		}
533
	}
534
535
	/**
536
	 * Get an asset renamer for the given filename.
537
	 *
538
	 * @param string $filename Path name
539
	 * @return AssetNameGenerator
540
	 */
541
	protected function getNameGenerator($filename){
542
		return Injector::inst()
543
			->createWithArgs('AssetNameGenerator', array($filename));
544
	}
545
546
	/**
547
	 * Custom currentPage() method to handle opening the 'root' folder
548
	 */
549
	public function currentPage() {
550
		$id = $this->currentPageID();
551
		if($id && is_numeric($id) && $id > 0) {
552
			$folder = DataObject::get_by_id('Folder', $id);
553
			if($folder && $folder->isInDB()) {
554
				return $folder;
555
			}
556
		}
557
		$this->setCurrentPageID(null);
558
		return new Folder();
559
	}
560
561
	public function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $numChildrenMethod = null, $filterFunction = null, $minNodeCount = 30) {
562
		if (!$childrenMethod) $childrenMethod = 'ChildFolders';
563
		if (!$numChildrenMethod) $numChildrenMethod = 'numChildFolders';
564
		return parent::getSiteTreeFor($className, $rootID, $childrenMethod, $numChildrenMethod, $filterFunction, $minNodeCount);
565
	}
566
567
	public function getCMSTreeTitle() {
568
		return Director::absoluteBaseURL() . "assets";
569
	}
570
571
	public function SiteTreeAsUL() {
572
		return $this->getSiteTreeFor($this->stat('tree_class'), null, 'ChildFolders', 'numChildFolders');
573
	}
574
575
	/**
576
	 * @param bool $unlinked
577
	 * @return ArrayList
578
	 */
579
	public function Breadcrumbs($unlinked = false) {
580
		$items = parent::Breadcrumbs($unlinked);
581
582
		// The root element should explicitly point to the root node.
583
		// Uses session state for current record otherwise.
584
		$items[0]->Link = Controller::join_links(singleton('AssetAdmin')->Link('show'), 0);
585
586
		// If a search is in progress, don't show the path
587
		if($this->getRequest()->requestVar('q')) {
588
			$items = $items->limit(1);
589
			$items->push(new ArrayData(array(
590
				'Title' => _t('LeftAndMain.SearchResults', 'Search Results'),
591
				'Link' => Controller::join_links($this->Link(), '?' . http_build_query(array('q' => $this->getRequest()->requestVar('q'))))
592
			)));
593
		}
594
595
		// If we're adding a folder, note that in breadcrumbs as well
596
		if($this->getRequest()->param('Action') == 'addfolder') {
597
			$items->push(new ArrayData(array(
598
				'Title' => _t('Folder.AddFolderButton', 'Add folder'),
599
				'Link' => false
600
			)));
601
		}
602
603
		return $items;
604
	}
605
606
	public static function menu_title($class = null, $localised = true) {
607
		// Deprecate this menu title if installed alongside new asset admin
608
		if($localised && class_exists('SilverStripe\AssetAdmin\Controller\AssetAdmin')) {
609
			// Don't conflict with legacy translations
610
			return _t('AssetAdmin.CMSMENU_OLD', 'Files (old)');
611
		}
612
		return parent::menu_title(null, $localised);
613
	}
614
615
	public function providePermissions() {
616
		$title = static::menu_title();
617
		return array(
618
			"CMS_ACCESS_AssetAdmin" => array(
619
				'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...
620
				'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
621
			)
622
		);
623
	}
624
625
}
626
/**
627
 * Delete multiple {@link Folder} records (and the associated filesystem nodes).
628
 * Usually used through the {@link AssetAdmin} interface.
629
 *
630
 * @package cms
631
 * @subpackage batchactions
632
 */
633
class AssetAdmin_DeleteBatchAction extends CMSBatchAction {
634
	public function getActionTitle() {
635
		// _t('AssetAdmin_left_ss.SELECTTODEL','Select the folders that you want to delete and then click the button below')
636
		return _t('AssetAdmin_DeleteBatchAction.TITLE', 'Delete folders');
637
	}
638
639
	public function run(SS_List $records) {
640
		$status = array(
641
			'modified'=>array(),
642
			'deleted'=>array()
643
		);
644
645
		foreach($records as $record) {
646
			$id = $record->ID;
647
648
			// Perform the action
649
			if($record->canDelete()) $record->delete();
650
651
			$status['deleted'][$id] = array();
652
653
			$record->destroy();
654
			unset($record);
655
		}
656
657
		return Convert::raw2json($status);
658
	}
659
}
660