Completed
Push — webpack ( 867754...f6e846 )
by Ingo
08:28
created

SecurityAdmin::Breadcrumbs()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 19
nc 5
nop 1
dl 0
loc 33
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Admin;
4
5
use SilverStripe\Control\HTTPRequest;
6
use SilverStripe\Control\HTTPResponse;
7
use SilverStripe\Core\Convert;
8
use SilverStripe\Forms\GridField\GridFieldDataColumns;
9
use SilverStripe\Forms\GridField\GridFieldDetailForm;
10
use SilverStripe\Forms\LiteralField;
11
use SilverStripe\Forms\Tab;
12
use SilverStripe\Forms\TabSet;
13
use SilverStripe\Forms\HiddenField;
14
use SilverStripe\Forms\FieldList;
15
use SilverStripe\Forms\HeaderField;
16
use SilverStripe\Forms\Form;
17
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
18
use SilverStripe\Forms\GridField\GridFieldButtonRow;
19
use SilverStripe\Forms\GridField\GridFieldExportButton;
20
use SilverStripe\Forms\GridField\GridField;
21
use SilverStripe\Security\Security;
22
use SilverStripe\Security\Member;
23
use SilverStripe\Security\Group;
24
use SilverStripe\Security\Permission;
25
use SilverStripe\Security\PermissionRole;
26
use SilverStripe\Security\PermissionProvider;
27
use SilverStripe\View\Requirements;
28
use SilverStripe\View\ArrayData;
29
30
/**
31
 * Security section of the CMS
32
 */
33
class SecurityAdmin extends LeftAndMain implements PermissionProvider {
34
35
	private static $url_segment = 'security';
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...
36
37
	private static $url_rule = '/$Action/$ID/$OtherID';
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
39
	private static $menu_title = 'Security';
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...
40
41
	private static $tree_class = 'SilverStripe\\Security\\Group';
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...
42
43
	private static $subitem_class = 'SilverStripe\\Security\\Member';
44
45
	private static $required_permission_codes = 'CMS_ACCESS_SecurityAdmin';
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...
46
47
	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...
48
		'EditForm',
49
		'MemberImportForm',
50
		'memberimport',
51
		'GroupImportForm',
52
		'groupimport',
53
		'groups',
54
		'users',
55
		'roles'
56
	);
57
58
	/**
59
	 * Shortcut action for setting the correct active tab.
60
	 *
61
	 * @param HTTPRequest $request
62
	 * @return HTTPResponse
63
	 */
64
	public function users($request) {
65
		return $this->index($request);
66
	}
67
68
	/**
69
	 * Shortcut action for setting the correct active tab.
70
	 *
71
	 * @param HTTPRequest $request
72
	 * @return HTTPResponse
73
	 */
74
	public function groups($request) {
75
		return $this->index($request);
76
	}
77
78
	/**
79
	 * Shortcut action for setting the correct active tab.
80
	 *
81
	 * @param HTTPRequest $request
82
	 * @return HTTPResponse
83
	 */
84
	public function roles($request) {
85
		return $this->index($request);
86
	}
87
88
	public function getEditForm($id = null, $fields = null) {
89
		// TODO Duplicate record fetching (see parent implementation)
90
		if(!$id) $id = $this->currentPageID();
91
		$form = parent::getEditForm($id);
0 ignored issues
show
Unused Code introduced by
$form is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
92
93
		// TODO Duplicate record fetching (see parent implementation)
94
		$record = $this->getRecord($id);
95
96
		if($record && !$record->canView()) {
97
			return Security::permissionFailure($this);
0 ignored issues
show
Bug Compatibility introduced by
The expression \SilverStripe\Security\S...rmissionFailure($this); of type SilverStripe\Control\HTTPResponse|null adds the type SilverStripe\Control\HTTPResponse to the return on line 97 which is incompatible with the return type of the parent method SilverStripe\Admin\LeftAndMain::getEditForm of type SilverStripe\Forms\Form|null.
Loading history...
98
		}
99
100
		$memberList = GridField::create(
101
			'Members',
102
			false,
103
			Member::get(),
104
			$memberListConfig = GridFieldConfig_RecordEditor::create()
105
				->addComponent(new GridFieldButtonRow('after'))
106
				->addComponent(new GridFieldExportButton('buttons-after-left'))
107
		)->addExtraClass("members_grid");
108
109
		if($record && method_exists($record, 'getValidator')) {
110
			$validator = $record->getValidator();
111
		} else {
112
			$validator = Member::singleton()->getValidator();
113
		}
114
115
		/** @var GridFieldDetailForm $detailForm */
116
		$detailForm = $memberListConfig->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDetailForm');
117
		$detailForm
118
			->setValidator($validator);
119
120
		$groupList = GridField::create(
121
			'Groups',
122
			false,
123
			Group::get(),
124
			GridFieldConfig_RecordEditor::create()
125
		);
126
		/** @var GridFieldDataColumns $columns */
127
		$columns = $groupList->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDataColumns');
128
		$columns->setDisplayFields(array(
129
			'Breadcrumbs' => Group::singleton()->fieldLabel('Title')
130
		));
131
		$columns->setFieldFormatting(array(
132
			'Breadcrumbs' => function($val, $item) {
133
				/** @var Group $item */
134
				return Convert::raw2xml($item->getBreadcrumbs(' > '));
135
			}
136
		));
137
138
		$fields = new FieldList(
139
			$root = new TabSet(
140
				'Root',
141
				$usersTab = new Tab('Users', _t('SecurityAdmin.Users', 'Users'),
142
143
					new LiteralField('MembersCautionText',
144
						sprintf('<div class="alert alert-warning" role="alert">%s</div>',
145
							_t(
146
								'SecurityAdmin.MemberListCaution',
147
								'Caution: Removing members from this list will remove them from all groups and the database'
148
							)
149
						)
150
					),
151
					$memberList
152
				),
153
				$groupsTab = new Tab('Groups', singleton('SilverStripe\\Security\\Group')->i18n_plural_name(),
154
					$groupList
155
				)
156
			),
157
			// necessary for tree node selection in LeftAndMain.EditForm.js
158
			new HiddenField('ID', false, 0)
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...
159
		);
160
161
		// Add import capabilities. Limit to admin since the import logic can affect assigned permissions
162
		if(Permission::check('ADMIN')) {
163
			$fields->addFieldsToTab('Root.Users', array(
164
				new HeaderField('ImportUsersHeader', _t('SecurityAdmin.IMPORTUSERS', 'Import users'), 3),
165
				new LiteralField(
166
					'MemberImportFormIframe',
167
					sprintf(
168
							'<iframe src="%s" id="MemberImportFormIframe" width="100%%" height="250px" frameBorder="0">'
169
						. '</iframe>',
170
						$this->Link('memberimport')
171
					)
172
				)
173
			));
174
			$fields->addFieldsToTab('Root.Groups', array(
175
				new HeaderField('ImportGroupsHeader', _t('SecurityAdmin.IMPORTGROUPS', 'Import groups'), 3),
176
				new LiteralField(
177
					'GroupImportFormIframe',
178
					sprintf(
179
							'<iframe src="%s" id="GroupImportFormIframe" width="100%%" height="250px" frameBorder="0">'
180
						. '</iframe>',
181
						$this->Link('groupimport')
182
					)
183
				)
184
			));
185
		}
186
187
		// Tab nav in CMS is rendered through separate template
188
		$root->setTemplate('SilverStripe\\Forms\\CMSTabSet');
189
190
		// Add roles editing interface
191
		$rolesTab = null;
192
		if(Permission::check('APPLY_ROLES')) {
193
			$rolesField = GridField::create('Roles',
194
				false,
195
				PermissionRole::get(),
196
				GridFieldConfig_RecordEditor::create()
197
			);
198
199
			$rolesTab = $fields->findOrMakeTab('Root.Roles', _t('SecurityAdmin.TABROLES', 'Roles'));
200
			$rolesTab->push($rolesField);
201
		}
202
203
		$actionParam = $this->getRequest()->param('Action');
204
		if($actionParam == 'groups') {
205
			$groupsTab->addExtraClass('ui-state-active');
206
		} elseif($actionParam == 'users') {
207
			$usersTab->addExtraClass('ui-state-active');
208
		} elseif($actionParam == 'roles' && $rolesTab) {
209
			$rolesTab->addExtraClass('ui-state-active');
210
		}
211
212
		$actions = new FieldList();
213
214
		$form = Form::create(
215
			$this,
216
			'EditForm',
217
			$fields,
218
			$actions
219
		)->setHTMLID('Form_EditForm');
220
		$form->addExtraClass('cms-edit-form');
221
		$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
222
		// Tab nav in CMS is rendered through separate template
223
		if($form->Fields()->hasTabSet()) {
224
			$form->Fields()->findOrMakeTab('Root')->setTemplate('SilverStripe\\Forms\\CMSTabSet');
225
		}
226
		$form->addExtraClass('center ss-tabset cms-tabset ' . $this->BaseCSSClasses());
227
		$form->setAttribute('data-pjax-fragment', 'CurrentForm');
228
229
		$this->extend('updateEditForm', $form);
230
231
		return $form;
232
	}
233
234 View Code Duplication
	public function memberimport() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
235
		Requirements::clear();
236
		Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/client/dist/js/bundle-lib.js');
237
		Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/client/dist/js/MemberImportForm.js');
238
		Requirements::css(FRAMEWORK_ADMIN_DIR . '/client/dist/styles/bundle.css');
239
240
		return $this->renderWith('BlankPage', array(
241
			'Form' => $this->MemberImportForm()->forTemplate(),
242
			'Content' => ' '
243
		));
244
	}
245
246
	/**
247
	 * @see SecurityAdmin_MemberImportForm
248
	 *
249
	 * @return Form
250
	 */
251
	public function MemberImportForm() {
252
		if(!Permission::check('ADMIN')) {
253
			return null;
254
		}
255
256
		/** @var Group $group */
257
		$group = $this->currentPage();
258
		/** @skipUpgrade */
259
		$form = new MemberImportForm(
260
			$this,
261
			'MemberImportForm'
262
		);
263
		$form->setGroup($group);
264
265
		return $form;
266
	}
267
268 View Code Duplication
	public function groupimport() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
269
		Requirements::clear();
270
		Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/client/dist/js/bundle-lib.js');
271
		Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/client/dist/js/MemberImportForm.js');
272
		Requirements::css(FRAMEWORK_ADMIN_DIR . '/client/dist/styles/bundle.css');
273
274
		return $this->renderWith('BlankPage', array(
275
			'Content' => ' ',
276
			'Form' => $this->GroupImportForm()->forTemplate()
277
		));
278
	}
279
280
	/**
281
	 * @see SecurityAdmin_MemberImportForm
282
	 *
283
	 * @skipUpgrade
284
	 * @return Form
285
	 */
286
	public function GroupImportForm() {
287
		if(!Permission::check('ADMIN')) {
288
			return null;
289
		}
290
291
		$form = new GroupImportForm($this, 'GroupImportForm');
292
		return $form;
293
	}
294
295
	/**
296
	 * Disable GridFieldDetailForm backlinks for this view, as its
297
	 */
298
	public function Backlink() {
299
		return false;
300
	}
301
302
	public function Breadcrumbs($unlinked = false) {
303
		$crumbs = parent::Breadcrumbs($unlinked);
304
305
		// Name root breadcrumb based on which record is edited,
306
		// which can only be determined by looking for the fieldname of the GridField.
307
		// Note: Titles should be same titles as tabs in RootForm().
308
		$params = $this->getRequest()->allParams();
309
		if(isset($params['FieldName'])) {
310
			// TODO FieldName param gets overwritten by nested GridFields,
311
			// so shows "Members" rather than "Groups" for the following URL:
312
			// admin/security/EditForm/field/Groups/item/2/ItemEditForm/field/Members/item/1/edit
313
			$firstCrumb = $crumbs->shift();
314
			if($params['FieldName'] == 'Groups') {
315
				$crumbs->unshift(new ArrayData(array(
316
					'Title' => Group::singleton()->i18n_plural_name(),
317
					'Link' => $this->Link('groups')
318
				)));
319
			} elseif($params['FieldName'] == 'Users') {
320
				$crumbs->unshift(new ArrayData(array(
321
					'Title' => _t('SecurityAdmin.Users', 'Users'),
322
					'Link' => $this->Link('users')
323
				)));
324
			} elseif($params['FieldName'] == 'Roles') {
325
				$crumbs->unshift(new ArrayData(array(
326
					'Title' => _t('SecurityAdmin.TABROLES', 'Roles'),
327
					'Link' => $this->Link('roles')
328
				)));
329
			}
330
			$crumbs->unshift($firstCrumb);
331
		}
332
333
		return $crumbs;
334
	}
335
336
	public function providePermissions() {
337
		$title = $this->menu_title();
338
		return array(
339
			"CMS_ACCESS_SecurityAdmin" => array(
340
				'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...
341
				'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access'),
342
				'help' => _t(
343
					'SecurityAdmin.ACCESS_HELP',
344
					'Allow viewing, adding and editing users, as well as assigning permissions and roles to them.'
345
				)
346
			),
347
			'EDIT_PERMISSIONS' => array(
348
				'name' => _t('SecurityAdmin.EDITPERMISSIONS', 'Manage permissions for groups'),
349
				'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
350
				'help' => _t('SecurityAdmin.EDITPERMISSIONS_HELP',
351
					'Ability to edit Permissions and IP Addresses for a group.'
352
					. ' Requires the "Access to \'Security\' section" permission.'),
353
				'sort' => 0
354
			),
355
			'APPLY_ROLES' => array(
356
				'name' => _t('SecurityAdmin.APPLY_ROLES', 'Apply roles to groups'),
357
				'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
358
				'help' => _t('SecurityAdmin.APPLY_ROLES_HELP', 'Ability to edit the roles assigned to a group.'
359
					. ' Requires the "Access to \'Users\' section" permission.'),
360
				'sort' => 0
361
			)
362
		);
363
	}
364
}
365