Completed
Pull Request — master (#1458)
by Sam
04:45 queued 02:13
created

AssetAdmin   D

Complexity

Total Complexity 84

Size/Duplication

Total Lines 601
Duplicated Lines 3.99 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 84
lcom 1
cbo 0
dl 24
loc 601
rs 4.8678
c 2
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A currentPageID() 0 11 4
C getList() 8 56 11
A getSearchContext() 0 48 3
A addfolder() 0 14 2
B delete() 3 18 6
A SearchForm() 0 21 1
B AddForm() 13 34 3
C doAdd() 0 57 16
A getNameGenerator() 0 4 1
B currentPage() 0 11 6
A getSiteTreeFor() 0 5 3
A getCMSTreeTitle() 0 3 1
A SiteTreeAsUL() 0 3 1
B Breadcrumbs() 0 26 3
A providePermissions() 0 9 1
F getEditForm() 0 177 21
A init() 0 10 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

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

Common duplication problems, and corresponding solutions are:

Complex Class

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

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

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

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

1
<?php
2
3
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';
15
16
	private static $url_rule = '/$Action/$ID';
17
18
	private static $menu_title = 'Files';
19
20
	private static $tree_class = 'Folder';
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(
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 (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/src/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));
147
148
		// File listing
149
		$gridFieldConfig = GridFieldConfig::create()->addComponents(
150
			new GridFieldToolbarHeader(),
151
			new GridFieldSortableHeader(),
152
			new GridFieldFilterHeader(),
153
			new GridFieldDataColumns(),
154
			new GridFieldPaginator(self::config()->page_length),
155
			new GridFieldEditButton(),
156
			new GridFieldDeleteAction(),
157
			new GridFieldDetailForm(),
158
			GridFieldLevelup::create($folder->ID)->setLinkSpec('admin/assets/show/%d')
159
		);
160
161
		$gridField = GridField::create('File', $title, $this->getList(), $gridFieldConfig);
162
		$columns = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
163
		$columns->setDisplayFields(array(
164
			'StripThumbnail' => '',
165
			'Title' => _t('File.Title', 'Title'),
166
			'Created' => _t('AssetAdmin.CREATED', 'Date'),
167
			'Size' => _t('AssetAdmin.SIZE', 'Size'),
168
		));
169
		$columns->setFieldCasting(array(
170
			'Created' => 'SS_Datetime->Nice'
171
		));
172
		$gridField->setAttribute(
173
			'data-url-folder-template',
174
			Controller::join_links($this->Link('show'), '%s')
175
		);
176
177
		if(!$folder->hasMethod('canAddChildren') || ($folder->hasMethod('canAddChildren') && $folder->canAddChildren())) {
178
			// TODO Will most likely be replaced by GridField logic
179
			$addFolderBtn = new LiteralField(
180
				'AddFolderButton',
181
				sprintf(
182
					'<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>',
183
					_t('Folder.AddFolderButton', 'Add folder'),
184
					Controller::join_links($this->Link('AddForm'), '?' . http_build_query(array(
185
						'action_doAdd' => 1,
186
						'ParentID' => $folder->ID,
187
						'SecurityID' => $form->getSecurityToken()->getValue()
188
					))),
189
					Controller::join_links($this->Link('addfolder'), '?ParentID=' . $folder->ID)
190
				)
191
			);
192
		} else {
193
			$addFolderBtn = '';
194
		}
195
196
		// Move existing fields to a "details" tab, unless they've already been tabbed out through extensions.
197
		// Required to keep Folder->getCMSFields() simple and reuseable,
198
		// without any dependencies into AssetAdmin (e.g. useful for "add folder" views).
199
		if(!$fields->hasTabset()) {
200
			$tabs = new TabSet('Root',
201
				$tabList = new Tab('ListView', _t('AssetAdmin.ListView', 'List View')),
202
				$tabTree = new Tab('TreeView', _t('AssetAdmin.TreeView', 'Tree View'))
203
			);
204
			$tabList->addExtraClass("content-listview cms-tabset-icon list");
205
			$tabTree->addExtraClass("content-treeview cms-tabset-icon tree");
206
			if($fields->Count() && $folder && $folder->isInDB()) {
207
				$tabs->push($tabDetails = new Tab('DetailsView', _t('AssetAdmin.DetailsView', 'Details')));
208
				$tabDetails->addExtraClass("content-galleryview cms-tabset-icon edit");
209
				foreach($fields as $field) {
210
					$fields->removeByName($field->getName());
211
					$tabDetails->push($field);
212
				}
213
			}
214
			$fields->push($tabs);
215
		}
216
217
		// we only add buttons if they're available. User might not have permission and therefore
218
		// the button shouldn't be available. Adding empty values into a ComposteField breaks template rendering.
219
		$actionButtonsComposite = CompositeField::create()->addExtraClass('cms-actions-row');
220
		if($addFolderBtn) $actionButtonsComposite->push($addFolderBtn);
221
222
		// Add the upload field for new media
223
		if($currentPageID = $this->currentPageID()){
224
			Session::set("{$this->class}.currentPage", $currentPageID);
225
		}
226
227
		$folder = $this->currentPage();
228
229
		$uploadField = UploadField::create('AssetUploadField', '');
230
		$uploadField->setConfig('previewMaxWidth', 40);
231
		$uploadField->setConfig('previewMaxHeight', 30);
232
		$uploadField->setConfig('changeDetection', false);
233
		$uploadField->addExtraClass('ss-assetuploadfield');
234
		$uploadField->removeExtraClass('ss-uploadfield');
235
		$uploadField->setTemplate('AssetUploadField');
236
237
		if($folder->exists()) {
238
			$path = $folder->getFilename();
239
			$uploadField->setFolderName($path);
240
		} else {
241
			$uploadField->setFolderName('/'); // root of the assets
242
		}
243
244
		$exts = $uploadField->getValidator()->getAllowedExtensions();
245
		asort($exts);
246
		$uploadField->Extensions = implode(', ', $exts);
247
248
		// List view
249
		$fields->addFieldsToTab('Root.ListView', array(
250
			$actionsComposite = CompositeField::create(
251
				$actionButtonsComposite
252
			)->addExtraClass('cms-content-toolbar field'),
253
			$uploadField,
254
			new HiddenField('ID'),
255
			$gridField
256
		));
257
258
		// Tree view
259
		$fields->addFieldsToTab('Root.TreeView', array(
260
			clone $actionsComposite,
261
			// TODO Replace with lazy loading on client to avoid performance hit of rendering potentially unused views
262
			new LiteralField(
263
				'Tree',
264
				FormField::create_tag(
265
					'div',
266
					array(
267
						'class' => 'cms-tree',
268
						'data-url-tree' => $this->Link('getsubtree'),
269
						'data-url-savetreenode' => $this->Link('savetreenode')
270
					),
271
					$this->SiteTreeAsUL()
272
				)
273
			)
274
		));
275
276
		// Move actions to "details" tab (they don't make sense on list/tree view)
277
		$actions = $form->Actions();
278
		$saveBtn = $actions->fieldByName('action_save');
279
		$deleteBtn = $actions->fieldByName('action_delete');
280
		$actions->removeByName('action_save');
281
		$actions->removeByName('action_delete');
282
		if(($saveBtn || $deleteBtn) && $fields->fieldByName('Root.DetailsView')) {
283
			$fields->addFieldToTab(
284
				'Root.DetailsView',
285
				CompositeField::create($saveBtn,$deleteBtn)->addExtraClass('Actions')
286
			);
287
		}
288
289
290
		$fields->setForm($form);
291
		$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
292
		// TODO Can't merge $FormAttributes in template at the moment
293
		$form->addExtraClass('cms-edit-form ' . $this->BaseCSSClasses());
294
		$form->setAttribute('data-pjax-fragment', 'CurrentForm');
295
		$form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
296
297
		// Optionally handle form submissions with 'X-Formschema-Request'
298
		// which rely on having validation errors returned as structured data
299
		$form->setValidationResponseCallback(function() use ($form) {
300
			$request = $this->getRequest();
301
			if($request->getHeader('X-Formschema-Request')) {
302
				$data = $this->getSchemaForForm($form);
303
				$response = new SS_HTTPResponse(Convert::raw2json($data));
304
				$response->addHeader('Content-Type', 'application/json');
305
				return $response;
306
307
			}
308
		});
309
310
311
		$this->extend('updateEditForm', $form);
312
313
		return $form;
314
	}
315
316
	public function addfolder($request) {
317
		$obj = $this->customise(array(
318
			'EditForm' => $this->AddForm()
319
		));
320
321
		if($request->isAjax()) {
322
			// Rendering is handled by template, which will call EditForm() eventually
323
			$content = $obj->renderWith($this->getTemplatesWithSuffix('_Content'));
324
		} else {
325
			$content = $obj->renderWith($this->getViewer('show'));
326
		}
327
328
		return $content;
329
	}
330
331
	public function delete($data, $form) {
332
		$className = $this->stat('tree_class');
333
334
		$record = DataObject::get_by_id($className, $data['ID']);
335
		if($record && !$record->canDelete()) {
336
			return Security::permissionFailure();
337
		}
338 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...
339
			throw new SS_HTTPResponse_Exception("Bad record ID #" . (int)$data['ID'], 404);
340
		}
341
		$parentID = $record->ParentID;
342
		$record->delete();
343
		$this->setCurrentPageID(null);
344
345
		$this->getResponse()->addHeader('X-Status', rawurlencode(_t('LeftAndMain.DELETED', 'Deleted.')));
346
		$this->getResponse()->addHeader('X-Pjax', 'Content');
347
		return $this->redirect(Controller::join_links($this->Link('show'), $parentID ? $parentID : 0));
348
	}
349
350
	/**
351
	 * Get the search context
352
	 *
353
	 * @return SearchContext
354
	 */
355
	public function getSearchContext() {
356
		$context = singleton('File')->getDefaultSearchContext();
357
358
		// Namespace fields, for easier detection if a search is present
359
		foreach($context->getFields() as $field) {
360
			$field->setName(sprintf('q[%s]', $field->getName()));
361
		}
362
		foreach($context->getFilters() as $filter) {
363
			$filter->setFullName(sprintf('q[%s]', $filter->getFullName()));
364
		}
365
366
		// Customize fields
367
		$dateHeader = HeaderField::create('q[Date]', _t('CMSSearch.FILTERDATEHEADING', 'Date'), 4);
368
		$dateFrom = DateField::create('q[CreatedFrom]', _t('CMSSearch.FILTERDATEFROM', 'From'))
369
		->setConfig('showcalendar', true);
370
		$dateTo = DateField::create('q[CreatedTo]',_t('CMSSearch.FILTERDATETO', 'To'))
371
		->setConfig('showcalendar', true);
372
		$dateGroup = FieldGroup::create(
373
			$dateHeader,
374
			$dateFrom,
375
			$dateTo
376
		);
377
		$context->addField($dateGroup);
378
		$appCategories = array(
379
			'archive' => _t('AssetAdmin.AppCategoryArchive', 'Archive', 'A collection of files'),
380
			'audio' => _t('AssetAdmin.AppCategoryAudio', 'Audio'),
381
			'document' => _t('AssetAdmin.AppCategoryDocument', 'Document'),
382
			'flash' => _t('AssetAdmin.AppCategoryFlash', 'Flash', 'The fileformat'),
383
			'image' => _t('AssetAdmin.AppCategoryImage', 'Image'),
384
			'video' => _t('AssetAdmin.AppCategoryVideo', 'Video'),
385
		);
386
		$context->addField(
387
			$typeDropdown = new DropdownField(
388
				'q[AppCategory]',
389
				_t('AssetAdmin.Filetype', 'File type'),
390
				$appCategories
391
			)
392
		);
393
394
		$typeDropdown->setEmptyString(' ');
395
396
		$context->addField(
397
			new CheckboxField('q[CurrentFolderOnly]', _t('AssetAdmin.CurrentFolderOnly', 'Limit to current folder?'))
398
		);
399
		$context->getFields()->removeByName('q[Title]');
400
401
		return $context;
402
	}
403
404
	/**
405
	 * Returns a form for filtering of files and assets gridfield.
406
	 * Result filtering takes place in {@link getList()}.
407
	 *
408
	 * @return Form
409
	 * @see AssetAdmin.js
410
	 */
411
	public function SearchForm() {
412
		$folder = $this->currentPage();
413
		$context = $this->getSearchContext();
414
415
		$fields = $context->getSearchFields();
416
		$actions = new FieldList(
417
			FormAction::create('doSearch',  _t('CMSMain_left_ss.APPLY_FILTER', 'Apply Filter'))
418
				->addExtraClass('ss-ui-action-constructive'),
419
			Object::create('ResetFormAction', 'clear', _t('CMSMain_left_ss.RESET', 'Reset'))
420
		);
421
422
		$form = new Form($this, 'filter', $fields, $actions);
423
		$form->setFormMethod('GET');
424
		$form->setFormAction(Controller::join_links($this->Link('show'), $folder->ID));
425
		$form->addExtraClass('cms-search-form');
426
		$form->loadDataFrom($this->getRequest()->getVars());
427
		$form->disableSecurityToken();
428
		// This have to match data-name attribute on the gridfield so that the javascript selectors work
429
		$form->setAttribute('data-gridfield', 'File');
430
		return $form;
431
	}
432
433
	public function AddForm() {
434
		$negotiator = $this->getResponseNegotiator();
435
		$form = Form::create(
436
			$this,
437
			'AddForm',
438
			new FieldList(
439
				new TextField("Name", _t('File.Name')),
440
				new HiddenField('ParentID', false, $this->getRequest()->getVar('ParentID'))
441
			),
442
			new FieldList(
443
				FormAction::create('doAdd', _t('AssetAdmin_left_ss.GO','Go'))
444
					->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept')
445
					->setTitle(_t('AssetAdmin.ActionAdd', 'Add folder'))
446
			)
447
		)->setHTMLID('Form_AddForm');
448 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...
449
			$request = $this->getRequest();
450
			if($request->isAjax() && $negotiator) {
451
				$form->setupFormErrors();
452
				$result = $form->forTemplate();
453
454
				return $negotiator->respond($request, array(
455
					'CurrentForm' => function() use($result) {
456
						return $result;
457
					}
458
				));
459
			}
460
		});
461
		$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
462
		// TODO Can't merge $FormAttributes in template at the moment
463
		$form->addExtraClass('add-form cms-add-form cms-edit-form cms-panel-padded center ' . $this->BaseCSSClasses());
464
465
		return $form;
466
	}
467
468
	/**
469
	 * Add a new group and return its details suitable for ajax.
470
	 *
471
	 * @todo Move logic into Folder class, and use LeftAndMain->doAdd() default implementation.
472
	 */
473
	public function doAdd($data, $form) {
474
		$class = $this->stat('tree_class');
475
476
		// check create permissions
477
		if(!singleton($class)->canCreate()) {
478
			return Security::permissionFailure($this);
479
		}
480
481
		// check addchildren permissions
482
		if(
483
			singleton($class)->hasExtension('Hierarchy')
484
			&& isset($data['ParentID'])
485
			&& is_numeric($data['ParentID'])
486
			&& $data['ParentID']
487
		) {
488
			$parentRecord = DataObject::get_by_id($class, $data['ParentID']);
489
			if($parentRecord->hasMethod('canAddChildren') && !$parentRecord->canAddChildren()) {
490
				return Security::permissionFailure($this);
491
			}
492
		} else {
493
			$parentRecord = null;
494
		}
495
496
		// Check parent
497
		$parentID = $parentRecord && $parentRecord->ID
498
			? (int)$parentRecord->ID
499
			: 0;
500
		// Build filename
501
		$filename = isset($data['Name'])
502
			? basename($data['Name'])
503
			: _t('AssetAdmin.NEWFOLDER',"NewFolder");
504
		if($parentRecord && $parentRecord->ID) {
505
			$filename = $parentRecord->getFilename() . '/' . $filename;
506
		}
507
508
		// Get the folder to be created
509
510
		// Ensure name is unique
511
		foreach($this->getNameGenerator($filename) as $filename) {
512
			if(! File::find($filename) ) {
513
				break;
514
			}
515
		}
516
517
		// Create record
518
		$record = Folder::create();
519
		$record->ParentID = $parentID;
520
		$record->Name = $record->Title = basename($filename);
521
		$record->write();
522
523
524
		if($parentRecord) {
525
			return $this->redirect(Controller::join_links($this->Link('show'), $parentRecord->ID));
526
		} else {
527
			return $this->redirect($this->Link());
528
		}
529
	}
530
531
	/**
532
	 * Get an asset renamer for the given filename.
533
	 *
534
	 * @param string $filename Path name
535
	 * @return AssetNameGenerator
536
	 */
537
	protected function getNameGenerator($filename){
538
		return Injector::inst()
539
			->createWithArgs('AssetNameGenerator', array($filename));
540
	}
541
542
	/**
543
	 * Custom currentPage() method to handle opening the 'root' folder
544
	 */
545
	public function currentPage() {
546
		$id = $this->currentPageID();
547
		if($id && is_numeric($id) && $id > 0) {
548
			$folder = DataObject::get_by_id('Folder', $id);
549
			if($folder && $folder->isInDB()) {
550
				return $folder;
551
			}
552
		}
553
		$this->setCurrentPageID(null);
554
		return new Folder();
555
	}
556
557
	public function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $numChildrenMethod = null, $filterFunction = null, $minNodeCount = 30) {
558
		if (!$childrenMethod) $childrenMethod = 'ChildFolders';
559
		if (!$numChildrenMethod) $numChildrenMethod = 'numChildFolders';
560
		return parent::getSiteTreeFor($className, $rootID, $childrenMethod, $numChildrenMethod, $filterFunction, $minNodeCount);
561
	}
562
563
	public function getCMSTreeTitle() {
564
		return Director::absoluteBaseURL() . "assets";
565
	}
566
567
	public function SiteTreeAsUL() {
568
		return $this->getSiteTreeFor($this->stat('tree_class'), null, 'ChildFolders', 'numChildFolders');
569
	}
570
571
	/**
572
	 * @param bool $unlinked
573
	 * @return ArrayList
574
	 */
575
	public function Breadcrumbs($unlinked = false) {
576
		$items = parent::Breadcrumbs($unlinked);
577
578
		// The root element should explicitly point to the root node.
579
		// Uses session state for current record otherwise.
580
		$items[0]->Link = Controller::join_links(singleton('AssetAdmin')->Link('show'), 0);
581
582
		// If a search is in progress, don't show the path
583
		if($this->getRequest()->requestVar('q')) {
584
			$items = $items->limit(1);
585
			$items->push(new ArrayData(array(
586
				'Title' => _t('LeftAndMain.SearchResults', 'Search Results'),
587
				'Link' => Controller::join_links($this->Link(), '?' . http_build_query(array('q' => $this->getRequest()->requestVar('q'))))
588
			)));
589
		}
590
591
		// If we're adding a folder, note that in breadcrumbs as well
592
		if($this->getRequest()->param('Action') == 'addfolder') {
593
			$items->push(new ArrayData(array(
594
				'Title' => _t('Folder.AddFolderButton', 'Add folder'),
595
				'Link' => false
596
			)));
597
		}
598
599
		return $items;
600
	}
601
602
	public function providePermissions() {
603
		$title = _t("AssetAdmin.MENUTITLE", LeftAndMain::menu_title_for_class($this->class));
604
		return array(
605
			"CMS_ACCESS_AssetAdmin" => array(
606
				'name' => _t('CMSMain.ACCESS', "Access to '{title}' section", array('title' => $title)),
607
				'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
608
			)
609
		);
610
	}
611
612
}
613
/**
614
 * Delete multiple {@link Folder} records (and the associated filesystem nodes).
615
 * Usually used through the {@link AssetAdmin} interface.
616
 *
617
 * @package cms
618
 * @subpackage batchactions
619
 */
620
class AssetAdmin_DeleteBatchAction extends CMSBatchAction {
621
	public function getActionTitle() {
622
		// _t('AssetAdmin_left_ss.SELECTTODEL','Select the folders that you want to delete and then click the button below')
623
		return _t('AssetAdmin_DeleteBatchAction.TITLE', 'Delete folders');
624
	}
625
626
	public function run(SS_List $records) {
627
		$status = array(
628
			'modified'=>array(),
629
			'deleted'=>array()
630
		);
631
632
		foreach($records as $record) {
633
			$id = $record->ID;
634
635
			// Perform the action
636
			if($record->canDelete()) $record->delete();
637
638
			$status['deleted'][$id] = array();
639
640
			$record->destroy();
641
			unset($record);
642
		}
643
644
		return Convert::raw2json($status);
645
	}
646
}
647