Completed
Push — 3.7 ( 81b2d8...ef0909 )
by
unknown
09:42
created

SecurityAdmin::GroupImportForm()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Security section of the CMS
5
 *
6
 * @package framework
7
 * @subpackage admin
8
 */
9
class SecurityAdmin extends LeftAndMain implements PermissionProvider {
10
11
	private static $url_segment = 'security';
12
13
	private static $url_rule = '/$Action/$ID/$OtherID';
14
15
	private static $menu_title = 'Security';
16
17
	private static $tree_class = 'Group';
18
19
	private static $subitem_class = 'Member';
20
21
	private static $allowed_actions = array(
22
		'EditForm',
23
		'MemberImportForm',
24
		'memberimport',
25
		'GroupImportForm',
26
		'groupimport',
27
		'groups',
28
		'users',
29
		'roles'
30
	);
31
32
	public function init() {
33
		parent::init();
34
		Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/SecurityAdmin.js');
35
	}
36
37
	/**
38
	 * Shortcut action for setting the correct active tab.
39
	 */
40
	public function users($request) {
41
		return $this->index($request);
42
	}
43
44
	/**
45
	 * Shortcut action for setting the correct active tab.
46
	 */
47
	public function groups($request) {
48
		return $this->index($request);
49
	}
50
51
	/**
52
	 * Shortcut action for setting the correct active tab.
53
	 */
54
	public function roles($request) {
55
		return $this->index($request);
56
	}
57
58
	public function getEditForm($id = null, $fields = null) {
59
		// TODO Duplicate record fetching (see parent implementation)
60
		if(!$id) $id = $this->currentPageID();
61
		$form = parent::getEditForm($id);
62
63
		// TODO Duplicate record fetching (see parent implementation)
64
		$record = $this->getRecord($id);
65
66
		if($record && !$record->canView()) {
67
			return Security::permissionFailure($this);
68
		}
69
70
		$memberList = GridField::create(
71
			'Members',
72
			false,
73
			Member::get(),
74
			$memberListConfig = GridFieldConfig_RecordEditor::create()
75
				->addComponent(new GridFieldButtonRow('after'))
76
				->addComponent(new GridFieldExportButton('buttons-after-left'))
77
		)->addExtraClass("members_grid");
78
79
		if($record && method_exists($record, 'getValidator')) {
80
			$validator = $record->getValidator();
81
		} else {
82
			$validator = Injector::inst()->get('Member')->getValidator();
83
		}
84
85
		$memberListConfig
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface GridFieldComponent as the method setValidator() does only exist in the following implementations of said interface: GridFieldDetailForm.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
86
			->getComponentByType('GridFieldDetailForm')
87
			->setValidator($validator);
88
89
		$groupList = GridField::create(
90
			'Groups',
91
			false,
92
			Group::get(),
93
			GridFieldConfig_RecordEditor::create()
94
		);
95
		$columns = $groupList->getConfig()->getComponentByType('GridFieldDataColumns');
96
		$columns->setDisplayFields(array(
97
			'Title' => singleton('Group')->fieldLabel('Title')
98
		));
99
		$columns->setFieldFormatting(array(
100
			'Title' => function($val, $item) {
101
				return Convert::raw2xml($item->getBreadcrumbs(' > '));
102
			}
103
		));
104
105
		$fields = new FieldList(
106
			$root = new TabSet(
107
				'Root',
108
				$usersTab = new Tab('Users', _t('SecurityAdmin.Users', 'Users'),
109
					$memberList,
110
					new LiteralField('MembersCautionText',
111
						sprintf('<p class="caution-remove"><strong>%s</strong></p>',
112
							_t(
113
								'SecurityAdmin.MemberListCaution',
114
								'Caution: Removing members from this list will remove them from all groups and the'
115
									. ' database'
116
							)
117
						)
118
					)
119
				),
120
				$groupsTab = new Tab('Groups', singleton('Group')->i18n_plural_name(),
121
					$groupList
122
				)
123
			),
124
			// necessary for tree node selection in LeftAndMain.EditForm.js
125
			new HiddenField('ID', false, 0)
126
		);
127
128
		// Add import capabilities. Limit to admin since the import logic can affect assigned permissions
129
		if(Permission::check('ADMIN')) {
130
			$fields->addFieldsToTab('Root.Users', array(
131
				new HeaderField(_t('SecurityAdmin.IMPORTUSERS', 'Import users'), 3),
132
				new LiteralField(
133
					'MemberImportFormIframe',
134
					sprintf(
135
							'<iframe src="%s" id="MemberImportFormIframe" width="100%%" height="250px" frameBorder="0">'
136
						. '</iframe>',
137
						$this->Link('memberimport')
138
					)
139
				)
140
			));
141
			$fields->addFieldsToTab('Root.Groups', array(
142
				new HeaderField(_t('SecurityAdmin.IMPORTGROUPS', 'Import groups'), 3),
143
				new LiteralField(
144
					'GroupImportFormIframe',
145
					sprintf(
146
							'<iframe src="%s" id="GroupImportFormIframe" width="100%%" height="250px" frameBorder="0">'
147
						. '</iframe>',
148
						$this->Link('groupimport')
149
					)
150
				)
151
			));
152
		}
153
154
		// Tab nav in CMS is rendered through separate template
155
		$root->setTemplate('CMSTabSet');
156
157
		// Add roles editing interface
158
		if(Permission::check('APPLY_ROLES')) {
159
			$rolesField = GridField::create('Roles',
160
				false,
161
				PermissionRole::get(),
162
				GridFieldConfig_RecordEditor::create()
163
			);
164
165
			$rolesTab = $fields->findOrMakeTab('Root.Roles', _t('SecurityAdmin.TABROLES', 'Roles'));
166
			$rolesTab->push($rolesField);
167
		}
168
169
		$actionParam = $this->getRequest()->param('Action');
170
		if($actionParam == 'groups') {
171
			$groupsTab->addExtraClass('ui-state-active');
172
		} elseif($actionParam == 'users') {
173
			$usersTab->addExtraClass('ui-state-active');
174
		} elseif($actionParam == 'roles' && isset($rolesTab)) {
175
			$rolesTab->addExtraClass('ui-state-active');
176
		}
177
178
		$actions = new FieldList();
179
180
		$form = CMSForm::create(
181
			$this,
182
			'EditForm',
183
			$fields,
184
			$actions
185
		)->setHTMLID('Form_EditForm');
186
		$form->setResponseNegotiator($this->getResponseNegotiator());
187
		$form->addExtraClass('cms-edit-form');
188
		$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
189
		// Tab nav in CMS is rendered through separate template
190
		if($form->Fields()->hasTabset()) {
191
			$form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
192
		}
193
		$form->addExtraClass('center ss-tabset cms-tabset ' . $this->BaseCSSClasses());
194
		$form->setAttribute('data-pjax-fragment', 'CurrentForm');
195
196
		$this->extend('updateEditForm', $form);
197
198
		return $form;
199
	}
200
201
	public function memberimport() {
202
		Requirements::clear();
203
		Requirements::css(FRAMEWORK_ADMIN_DIR . '/css/screen.css');
204
		Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
205
		Requirements::css(FRAMEWORK_ADMIN_DIR . '/css/MemberImportForm.css');
206
		Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js');
207
		Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/MemberImportForm.js');
208
209
		return $this->renderWith('BlankPage', array(
210
			'Form' => $this->MemberImportForm()->forTemplate(),
211
			'Content' => ' '
212
		));
213
	}
214
215
	/**
216
	 * @see SecurityAdmin_MemberImportForm
217
	 *
218
	 * @return Form
219
	 */
220
	public function MemberImportForm() {
221
		if(!Permission::check('ADMIN')) return false;
222
223
		$group = $this->currentPage();
224
		$form = new MemberImportForm(
225
			$this,
226
			'MemberImportForm'
227
		);
228
		$form->setGroup($group);
229
230
		return $form;
231
	}
232
233
	public function groupimport() {
234
		Requirements::clear();
235
		Requirements::css(FRAMEWORK_ADMIN_DIR . '/css/screen.css');
236
		Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
237
		Requirements::css(FRAMEWORK_ADMIN_DIR . '/css/MemberImportForm.css');
238
		Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js');
239
		Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/MemberImportForm.js');
240
241
		return $this->renderWith('BlankPage', array(
242
			'Content' => ' ',
243
			'Form' => $this->GroupImportForm()->forTemplate()
244
		));
245
	}
246
247
	/**
248
	 * @see SecurityAdmin_MemberImportForm
249
	 *
250
	 * @return Form
251
	 */
252
	public function GroupImportForm() {
253
		if(!Permission::check('ADMIN')) return false;
254
255
		$form = new GroupImportForm(
256
			$this,
257
			'GroupImportForm'
258
		);
259
260
		return $form;
261
	}
262
263
	/**
264
	 * Disable GridFieldDetailForm backlinks for this view, as its
265
	 */
266
	public function Backlink() {
267
		return false;
268
	}
269
270
	public function Breadcrumbs($unlinked = false) {
271
		$crumbs = parent::Breadcrumbs($unlinked);
272
273
		// Name root breadcrumb based on which record is edited,
274
		// which can only be determined by looking for the fieldname of the GridField.
275
		// Note: Titles should be same titles as tabs in RootForm().
276
		$params = $this->getRequest()->allParams();
277
		if(isset($params['FieldName'])) {
278
			// TODO FieldName param gets overwritten by nested GridFields,
279
			// so shows "Members" rather than "Groups" for the following URL:
280
			// admin/security/EditForm/field/Groups/item/2/ItemEditForm/field/Members/item/1/edit
281
			$firstCrumb = $crumbs->shift();
282
			if($params['FieldName'] == 'Groups') {
283
				$crumbs->unshift(new ArrayData(array(
284
					'Title' => singleton('Group')->i18n_plural_name(),
285
					'Link' => $this->Link('groups')
286
				)));
287
			} elseif($params['FieldName'] == 'Users') {
288
				$crumbs->unshift(new ArrayData(array(
289
					'Title' => _t('SecurityAdmin.Users', 'Users'),
290
					'Link' => $this->Link('users')
291
				)));
292
			} elseif($params['FieldName'] == 'Roles') {
293
				$crumbs->unshift(new ArrayData(array(
294
					'Title' => _t('SecurityAdmin.TABROLES', 'Roles'),
295
					'Link' => $this->Link('roles')
296
				)));
297
			}
298
			$crumbs->unshift($firstCrumb);
299
		}
300
301
		return $crumbs;
302
	}
303
304
	public function providePermissions() {
305
		$title = _t("SecurityAdmin.MENUTITLE", LeftAndMain::menu_title_for_class($this->class));
306
		return array(
307
			"CMS_ACCESS_SecurityAdmin" => array(
308
				'name' => _t('CMSMain.ACCESS', "Access to '{title}' section", array('title' => $title)),
309
				'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access'),
310
				'help' => _t(
311
					'SecurityAdmin.ACCESS_HELP',
312
					'Allow viewing, adding and editing users, as well as assigning permissions and roles to them.'
313
				)
314
			),
315
			'EDIT_PERMISSIONS' => array(
316
				'name' => _t('SecurityAdmin.EDITPERMISSIONS', 'Manage permissions for groups'),
317
				'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
318
				'help' => _t('SecurityAdmin.EDITPERMISSIONS_HELP',
319
					'Ability to edit Permissions and IP Addresses for a group.'
320
					. ' Requires the "Access to \'Security\' section" permission.'),
321
				'sort' => 0
322
			),
323
			'APPLY_ROLES' => array(
324
				'name' => _t('SecurityAdmin.APPLY_ROLES', 'Apply roles to groups'),
325
				'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
326
				'help' => _t('SecurityAdmin.APPLY_ROLES_HELP', 'Ability to edit the roles assigned to a group.'
327
					. ' Requires the "Access to \'Users\' section" permission.'),
328
				'sort' => 0
329
			)
330
		);
331
	}
332
333
	/**
334
	 * The permissions represented in the $codes will not appearing in the form
335
	 * containing {@link PermissionCheckboxSetField} so as not to be checked / unchecked.
336
	 *
337
	 * @deprecated 4.0 Use "Permission.hidden_permissions" config setting instead
338
	 * @param $codes String|Array
339
	 */
340
	public static function add_hidden_permission($codes){
341
		if(is_string($codes)) $codes = array($codes);
342
		Deprecation::notice('4.0', 'Use "Permission.hidden_permissions" config setting instead');
343
		Config::inst()->update('Permission', 'hidden_permissions', $codes);
344
	}
345
346
	/**
347
	 * @deprecated 4.0 Use "Permission.hidden_permissions" config setting instead
348
	 * @param $codes String|Array
349
	 */
350
	public static function remove_hidden_permission($codes){
351
		if(is_string($codes)) $codes = array($codes);
352
		Deprecation::notice('4.0', 'Use "Permission.hidden_permissions" config setting instead');
353
		Config::inst()->remove('Permission', 'hidden_permissions', $codes);
354
	}
355
356
	/**
357
	 * @deprecated 4.0 Use "Permission.hidden_permissions" config setting instead
358
	 * @return Array
359
	 */
360
	public static function get_hidden_permissions(){
361
		Deprecation::notice('4.0', 'Use "Permission.hidden_permissions" config setting instead');
362
		Config::inst()->get('Permission', 'hidden_permissions', Config::FIRST_SET);
363
	}
364
365
	/**
366
	 * Clear all permissions previously hidden with {@link add_hidden_permission}
367
	 *
368
	 * @deprecated 4.0 Use "Permission.hidden_permissions" config setting instead
369
	 */
370
	public static function clear_hidden_permissions(){
371
		Deprecation::notice('4.0', 'Use "Permission.hidden_permissions" config setting instead');
372
		Config::inst()->remove('Permission', 'hidden_permissions', Config::anything());
373
	}
374
}
375