Completed
Push — fix-2494 ( 3153ee )
by Sam
07:19
created

Group::canEdit()   C

Complexity

Conditions 8
Paths 10

Size

Total Lines 29
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 14
nc 10
nop 1
dl 0
loc 29
rs 5.3846
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Security;
4
5
use SilverStripe\Admin\SecurityAdmin;
6
use SilverStripe\Core\Convert;
7
use SilverStripe\Forms\Form;
8
use SilverStripe\Forms\GridField\GridFieldAddExistingAutocompleter;
9
use SilverStripe\Forms\GridField\GridFieldDetailForm;
10
use SilverStripe\Forms\TextField;
11
use SilverStripe\Forms\DropdownField;
12
use SilverStripe\Forms\TextareaField;
13
use SilverStripe\Forms\Tab;
14
use SilverStripe\Forms\TabSet;
15
use SilverStripe\Forms\FieldList;
16
use SilverStripe\Forms\LiteralField;
17
use SilverStripe\Forms\ListboxField;
18
use SilverStripe\Forms\HiddenField;
19
use SilverStripe\Forms\HTMLEditor\HTMLEditorConfig;
20
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
21
use SilverStripe\Forms\GridField\GridFieldButtonRow;
22
use SilverStripe\Forms\GridField\GridFieldExportButton;
23
use SilverStripe\Forms\GridField\GridFieldPrintButton;
24
use SilverStripe\Forms\GridField\GridField;
25
use SilverStripe\ORM\ArrayList;
26
use SilverStripe\ORM\DataObject;
27
use SilverStripe\ORM\DataQuery;
28
use SilverStripe\ORM\HasManyList;
29
use SilverStripe\ORM\Hierarchy\Hierarchy;
30
use SilverStripe\ORM\ManyManyList;
31
use SilverStripe\ORM\UnsavedRelationList;
32
use SilverStripe\View\Requirements;
33
34
/**
35
 * A security group.
36
 *
37
 * @property string Title Name of the group
38
 * @property string Description Description of the group
39
 * @property string Code Group code
40
 * @property string Locked Boolean indicating whether group is locked in security panel
41
 * @property int Sort
42
 * @property string HtmlEditorConfig
43
 *
44
 * @property int ParentID ID of parent group
45
 *
46
 * @method Group Parent() Return parent group
47
 * @method HasManyList Permissions() List of group permissions
48
 * @method HasManyList Groups() List of child groups
49
 * @method ManyManyList Roles() List of PermissionRoles
50
 * @mixin Hierarchy
51
 */
52
class Group extends DataObject
53
{
54
55
    private static $db = 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...
56
        "Title" => "Varchar(255)",
57
        "Description" => "Text",
58
        "Code" => "Varchar(255)",
59
        "Locked" => "Boolean",
60
        "Sort" => "Int",
61
        "HtmlEditorConfig" => "Text"
62
    );
63
64
    private static $has_one = 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...
65
        "Parent" => Group::class,
66
    );
67
68
    private static $has_many = 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...
69
        "Permissions" => Permission::class,
70
        "Groups" => Group::class,
71
    );
72
73
    private static $many_many = 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...
74
        "Members" => Member::class,
75
        "Roles" => PermissionRole::class,
76
    );
77
78
    private static $extensions = 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...
79
        Hierarchy::class,
80
    );
81
82
    private static $table_name = "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...
83
84
    public function populateDefaults()
85
    {
86
        parent::populateDefaults();
87
88
        if (!$this->Title) {
89
            $this->Title = _t(__CLASS__.'.NEWGROUP', "New Group");
90
        }
91
    }
92
93
    public function getAllChildren()
94
    {
95
        $doSet = new ArrayList();
96
97
        $children = Group::get()->filter("ParentID", $this->ID);
98
        foreach ($children as $child) {
99
            $doSet->push($child);
100
            $doSet->merge($child->getAllChildren());
101
        }
102
103
        return $doSet;
104
    }
105
106
    /**
107
     * Caution: Only call on instances, not through a singleton.
108
     * The "root group" fields will be created through {@link SecurityAdmin->EditForm()}.
109
     *
110
     * @return FieldList
111
     */
112
    public function getCMSFields()
113
    {
114
        $fields = new FieldList(
115
            new TabSet(
116
                "Root",
117
                new Tab(
118
                    'Members',
119
                    _t(__CLASS__.'.MEMBERS', 'Members'),
120
                    new TextField("Title", $this->fieldLabel('Title')),
121
                    $parentidfield = DropdownField::create(
122
                        'ParentID',
123
                        $this->fieldLabel('Parent'),
124
                        Group::get()->exclude('ID', $this->ID)->map('ID', 'Breadcrumbs')
125
                    )->setEmptyString(' '),
126
                    new TextareaField('Description', $this->fieldLabel('Description'))
127
                ),
128
                $permissionsTab = new Tab(
129
                    'Permissions',
130
                    _t(__CLASS__.'.PERMISSIONS', 'Permissions'),
131
                    $permissionsField = new PermissionCheckboxSetField(
132
                        'Permissions',
133
                        false,
0 ignored issues
show
Documentation introduced by
false is of type boolean, 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...
134
                        Permission::class,
135
                        'GroupID',
136
                        $this
137
                    )
138
                )
139
            )
140
        );
141
142
        $parentidfield->setDescription(
143
            _t('SilverStripe\\Security\\Group.GroupReminder', 'If you choose a parent group, this group will take all it\'s roles')
144
        );
145
146
        if ($this->ID) {
147
            $group = $this;
148
            $config = GridFieldConfig_RelationEditor::create();
149
            $config->addComponent(new GridFieldButtonRow('after'));
150
            $config->addComponents(new GridFieldExportButton('buttons-after-left'));
151
            $config->addComponents(new GridFieldPrintButton('buttons-after-left'));
152
            /** @var GridFieldAddExistingAutocompleter $autocompleter */
153
            $autocompleter = $config->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldAddExistingAutocompleter');
154
            /** @skipUpgrade */
155
            $autocompleter
156
                ->setResultsFormat('$Title ($Email)')
157
                ->setSearchFields(array('FirstName', 'Surname', 'Email'));
158
            /** @var GridFieldDetailForm $detailForm */
159
            $detailForm = $config->getComponentByType(GridFieldDetailForm::class);
160
            $detailForm
161
                ->setValidator(Member_Validator::create())
162
                ->setItemEditFormCallback(function ($form, $component) use ($group) {
0 ignored issues
show
Unused Code introduced by
The parameter $component 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...
163
                    /** @var Form $form */
164
                    $record = $form->getRecord();
165
                    $groupsField = $form->Fields()->dataFieldByName('DirectGroups');
166
                    if ($groupsField) {
167
                        // If new records are created in a group context,
168
                        // set this group by default.
169
                        if ($record && !$record->ID) {
170
                            $groupsField->setValue($group->ID);
171
                        } elseif ($record && $record->ID) {
172
                            // TODO Mark disabled once chosen.js supports it
173
                            // $groupsField->setDisabledItems(array($group->ID));
174
                            $form->Fields()->replaceField(
175
                                'DirectGroups',
176
                                $groupsField->performReadonlyTransformation()
177
                            );
178
                        }
179
                    }
180
                });
181
            $memberList = GridField::create('Members', false, $this->DirectMembers(), $config)
182
                ->addExtraClass('members_grid');
183
            // @todo Implement permission checking on GridField
184
            //$memberList->setPermissions(array('edit', 'delete', 'export', 'add', 'inlineadd'));
185
            $fields->addFieldToTab('Root.Members', $memberList);
186
        }
187
188
        // Only add a dropdown for HTML editor configurations if more than one is available.
189
        // Otherwise Member->getHtmlEditorConfigForCMS() will default to the 'cms' configuration.
190
        $editorConfigMap = HTMLEditorConfig::get_available_configs_map();
191
        if (count($editorConfigMap) > 1) {
192
            $fields->addFieldToTab(
193
                'Root.Permissions',
194
                new DropdownField(
195
                    'HtmlEditorConfig',
196
                    'HTML Editor Configuration',
197
                    $editorConfigMap
198
                ),
199
                'Permissions'
200
            );
201
        }
202
203
        if (!Permission::check('EDIT_PERMISSIONS')) {
204
            $fields->removeFieldFromTab('Root', 'Permissions');
205
        }
206
207
        // Only show the "Roles" tab if permissions are granted to edit them,
208
        // and at least one role exists
209
        if (Permission::check('APPLY_ROLES') &&
210
            PermissionRole::get()->count() &&
211
            class_exists(SecurityAdmin::class)
212
        ) {
213
            $fields->findOrMakeTab('Root.Roles', _t(__CLASS__.'.ROLES', 'Roles'));
214
            $fields->addFieldToTab(
215
                'Root.Roles',
216
                new LiteralField(
217
                    "",
218
                    "<p>" .
219
                    _t(
220
                        __CLASS__.'.ROLESDESCRIPTION',
221
                        "Roles are predefined sets of permissions, and can be assigned to groups.<br />"
222
                        . "They are inherited from parent groups if required."
223
                    ) . '<br />' .
224
                    sprintf(
225
                        '<a href="%s" class="add-role">%s</a>',
226
                        SecurityAdmin::singleton()->Link('show/root#Root_Roles'),
227
                        // TODO This should include #Root_Roles to switch directly to the tab,
228
                        // but tabstrip.js doesn't display tabs when directly adressed through a URL pragma
229
                        _t('SilverStripe\\Security\\Group.RolesAddEditLink', 'Manage roles')
230
                    ) .
231
                    "</p>"
232
                )
233
            );
234
235
            // Add roles (and disable all checkboxes for inherited roles)
236
            $allRoles = PermissionRole::get();
237
            if (!Permission::check('ADMIN')) {
238
                $allRoles = $allRoles->filter("OnlyAdminCanApply", 0);
239
            }
240
            if ($this->ID) {
241
                $groupRoles = $this->Roles();
242
                $inheritedRoles = new ArrayList();
243
                $ancestors = $this->getAncestors();
244
                foreach ($ancestors as $ancestor) {
245
                    $ancestorRoles = $ancestor->Roles();
246
                    if ($ancestorRoles) {
247
                        $inheritedRoles->merge($ancestorRoles);
248
                    }
249
                }
250
                $groupRoleIDs = $groupRoles->column('ID') + $inheritedRoles->column('ID');
251
                $inheritedRoleIDs = $inheritedRoles->column('ID');
252
            } else {
253
                $groupRoleIDs = array();
254
                $inheritedRoleIDs = array();
255
            }
256
257
            $rolesField = ListboxField::create('Roles', false, $allRoles->map()->toArray())
258
                    ->setDefaultItems($groupRoleIDs)
259
                    ->setAttribute('data-placeholder', _t('SilverStripe\\Security\\Group.AddRole', 'Add a role for this group'))
260
                    ->setDisabledItems($inheritedRoleIDs);
261
            if (!$allRoles->count()) {
262
                $rolesField->setAttribute('data-placeholder', _t('SilverStripe\\Security\\Group.NoRoles', 'No roles found'));
263
            }
264
            $fields->addFieldToTab('Root.Roles', $rolesField);
265
        }
266
267
        $fields->push($idField = new HiddenField("ID"));
268
269
        $this->extend('updateCMSFields', $fields);
270
271
        return $fields;
272
    }
273
274
    /**
275
     * @param bool $includerelations Indicate if the labels returned include relation fields
276
     * @return array
277
     */
278
    public function fieldLabels($includerelations = true)
279
    {
280
        $labels = parent::fieldLabels($includerelations);
281
        $labels['Title'] = _t(__CLASS__.'.GROUPNAME', 'Group name');
282
        $labels['Description'] = _t('SilverStripe\\Security\\Group.Description', 'Description');
283
        $labels['Code'] = _t('SilverStripe\\Security\\Group.Code', 'Group Code', 'Programmatical code identifying a group');
284
        $labels['Locked'] = _t('SilverStripe\\Security\\Group.Locked', 'Locked?', 'Group is locked in the security administration area');
285
        $labels['Sort'] = _t('SilverStripe\\Security\\Group.Sort', 'Sort Order');
286
        if ($includerelations) {
287
            $labels['Parent'] = _t('SilverStripe\\Security\\Group.Parent', 'Parent Group', 'One group has one parent group');
288
            $labels['Permissions'] = _t('SilverStripe\\Security\\Group.has_many_Permissions', 'Permissions', 'One group has many permissions');
289
            $labels['Members'] = _t('SilverStripe\\Security\\Group.many_many_Members', 'Members', 'One group has many members');
290
        }
291
292
        return $labels;
293
    }
294
295
    /**
296
     * Get many-many relation to {@link Member},
297
     * including all members which are "inherited" from children groups of this record.
298
     * See {@link DirectMembers()} for retrieving members without any inheritance.
299
     *
300
     * @param String $filter
301
     * @return ManyManyList
302
     */
303
    public function Members($filter = '')
304
    {
305
        // First get direct members as a base result
306
        $result = $this->DirectMembers();
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->DirectMembers(); of type SilverStripe\ORM\Relatio...ORM\UnsavedRelationList adds the type SilverStripe\ORM\UnsavedRelationList to the return on line 310 which is incompatible with the return type documented by SilverStripe\Security\Group::Members of type SilverStripe\ORM\ManyManyList.
Loading history...
307
308
        // Unsaved group cannot have child groups because its ID is still 0.
309
        if (!$this->exists()) {
310
            return $result;
311
        }
312
313
        // Remove the default foreign key filter in prep for re-applying a filter containing all children groups.
314
        // Filters are conjunctive in DataQuery by default, so this filter would otherwise overrule any less specific
315
        // ones.
316
        if (!($result instanceof UnsavedRelationList)) {
317
            $result = $result->alterDataQuery(function ($query) {
318
                /** @var DataQuery $query */
319
                $query->removeFilterOn('Group_Members');
320
            });
321
        }
322
        // Now set all children groups as a new foreign key
323
        $groups = Group::get()->byIDs($this->collateFamilyIDs());
324
        $result = $result->forForeignID($groups->column('ID'))->where($filter);
325
326
        return $result;
327
    }
328
329
    /**
330
     * Return only the members directly added to this group
331
     */
332
    public function DirectMembers()
333
    {
334
        return $this->getManyManyComponents('Members');
335
    }
336
337
    /**
338
     * Return a set of this record's "family" of IDs - the IDs of
339
     * this record and all its descendants.
340
     *
341
     * @return array
342
     */
343
    public function collateFamilyIDs()
344
    {
345
        if (!$this->exists()) {
346
            throw new \InvalidArgumentException("Cannot call collateFamilyIDs on unsaved Group.");
347
        }
348
349
        $familyIDs = array();
350
        $chunkToAdd = array($this->ID);
351
352
        while ($chunkToAdd) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $chunkToAdd of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
353
            $familyIDs = array_merge($familyIDs, $chunkToAdd);
354
355
            // Get the children of *all* the groups identified in the previous chunk.
356
            // This minimises the number of SQL queries necessary
357
            $chunkToAdd = Group::get()->filter("ParentID", $chunkToAdd)->column('ID');
358
        }
359
360
        return $familyIDs;
361
    }
362
363
    /**
364
     * Returns an array of the IDs of this group and all its parents
365
     *
366
     * @return array
367
     */
368
    public function collateAncestorIDs()
369
    {
370
        $parent = $this;
371
        $items = [];
372
        while (isset($parent) && $parent instanceof Group) {
373
            $items[] = $parent->ID;
374
            $parent = $parent->Parent;
0 ignored issues
show
Bug introduced by
The property Parent does not seem to exist. Did you mean ParentID?

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...
375
        }
376
        return $items;
377
    }
378
379
    /**
380
     * This isn't a decendant of SiteTree, but needs this in case
381
     * the group is "reorganised";
382
     */
383
    public function cmsCleanup_parentChanged()
384
    {
385
    }
386
387
    /**
388
     * Override this so groups are ordered in the CMS
389
     */
390
    public function stageChildren()
391
    {
392
        return Group::get()
393
            ->filter("ParentID", $this->ID)
394
            ->exclude("ID", $this->ID)
395
            ->sort('"Sort"');
396
    }
397
398
    public function getTreeTitle()
399
    {
400
        if ($this->hasMethod('alternateTreeTitle')) {
401
            return $this->alternateTreeTitle();
402
        }
403
        return htmlspecialchars($this->Title, ENT_QUOTES);
404
    }
405
406
    /**
407
     * Overloaded to ensure the code is always descent.
408
     *
409
     * @param string
410
     */
411
    public function setCode($val)
412
    {
413
        $this->setField("Code", Convert::raw2url($val));
414
    }
415
416
    public function validate()
417
    {
418
        $result = parent::validate();
419
420
        // Check if the new group hierarchy would add certain "privileged permissions",
421
        // and require an admin to perform this change in case it does.
422
        // This prevents "sub-admin" users with group editing permissions to increase their privileges.
423
        if ($this->Parent()->exists() && !Permission::check('ADMIN')) {
424
            $inheritedCodes = Permission::get()
425
                ->filter('GroupID', $this->Parent()->collateAncestorIDs())
426
                ->column('Code');
427
            $privilegedCodes = Permission::config()->get('privileged_permissions');
428
            if (array_intersect($inheritedCodes, $privilegedCodes)) {
429
                $result->addError(sprintf(
430
                    _t(
431
                        'SilverStripe\\Security\\Group.HierarchyPermsError',
432
                        'Can\'t assign parent group "%s" with privileged permissions (requires ADMIN access)'
433
                    ),
434
                    $this->Parent()->Title
435
                ));
436
            }
437
        }
438
439
        return $result;
440
    }
441
442
    public function onBeforeWrite()
443
    {
444
        parent::onBeforeWrite();
445
446
        // Only set code property when the group has a custom title, and no code exists.
447
        // The "Code" attribute is usually treated as a more permanent identifier than database IDs
448
        // in custom application logic, so can't be changed after its first set.
449
        if (!$this->Code && $this->Title != _t(__CLASS__.'.NEWGROUP', "New Group")) {
450
            $this->setCode($this->Title);
451
        }
452
    }
453
454
    public function onBeforeDelete()
455
    {
456
        parent::onBeforeDelete();
457
458
        // if deleting this group, delete it's children as well
459
        foreach ($this->Groups() as $group) {
460
            $group->delete();
461
        }
462
463
        // Delete associated permissions
464
        foreach ($this->Permissions() as $permission) {
465
            $permission->delete();
466
        }
467
    }
468
469
    /**
470
     * Checks for permission-code CMS_ACCESS_SecurityAdmin.
471
     * If the group has ADMIN permissions, it requires the user to have ADMIN permissions as well.
472
     *
473
     * @param Member $member Member
474
     * @return boolean
475
     */
476
    public function canEdit($member = null)
477
    {
478
        if (!$member) {
479
            $member = Member::currentUser();
480
        }
481
482
        // extended access checks
483
        $results = $this->extend('canEdit', $member);
484
        if ($results && is_array($results)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $results of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
485
            if (!min($results)) {
486
                return false;
487
            }
488
        }
489
490
        if (// either we have an ADMIN
491
            (bool)Permission::checkMember($member, "ADMIN")
492
            || (
493
                // or a privileged CMS user and a group without ADMIN permissions.
494
                // without this check, a user would be able to add himself to an administrators group
495
                // with just access to the "Security" admin interface
496
                Permission::checkMember($member, "CMS_ACCESS_SecurityAdmin") &&
497
                !Permission::get()->filter(array('GroupID' => $this->ID, 'Code' => 'ADMIN'))->exists()
498
            )
499
        ) {
500
            return true;
501
        }
502
503
        return false;
504
    }
505
506
    /**
507
     * Checks for permission-code CMS_ACCESS_SecurityAdmin.
508
     *
509
     * @param Member $member
510
     * @return boolean
511
     */
512
    public function canView($member = null)
513
    {
514
        if (!$member) {
515
            $member = Member::currentUser();
516
        }
517
518
        // extended access checks
519
        $results = $this->extend('canView', $member);
520
        if ($results && is_array($results)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $results of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
521
            if (!min($results)) {
522
                return false;
523
            }
524
        }
525
526
        // user needs access to CMS_ACCESS_SecurityAdmin
527
        if (Permission::checkMember($member, "CMS_ACCESS_SecurityAdmin")) {
528
            return true;
529
        }
530
531
        return false;
532
    }
533
534
    public function canDelete($member = null)
535
    {
536
        if (!$member) {
537
            $member = Member::currentUser();
538
        }
539
540
        // extended access checks
541
        $results = $this->extend('canDelete', $member);
542
        if ($results && is_array($results)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $results of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
543
            if (!min($results)) {
544
                return false;
545
            }
546
        }
547
548
        return $this->canEdit($member);
549
    }
550
551
    /**
552
     * Returns all of the children for the CMS Tree.
553
     * Filters to only those groups that the current user can edit
554
     */
555
    public function AllChildrenIncludingDeleted()
556
    {
557
        /** @var Hierarchy $extInstance */
558
        $extInstance = $this->getExtensionInstance(Hierarchy::class);
559
        $extInstance->setOwner($this);
560
        $children = $extInstance->AllChildrenIncludingDeleted();
561
        $extInstance->clearOwner();
562
563
        $filteredChildren = new ArrayList();
564
565
        if ($children) {
566
            foreach ($children as $child) {
567
                if ($child->canView()) {
568
                    $filteredChildren->push($child);
569
                }
570
            }
571
        }
572
573
        return $filteredChildren;
574
    }
575
576
    /**
577
     * Add default records to database.
578
     *
579
     * This function is called whenever the database is built, after the
580
     * database tables have all been created.
581
     */
582
    public function requireDefaultRecords()
583
    {
584
        parent::requireDefaultRecords();
585
586
        // Add default author group if no other group exists
587
        $allGroups = Group::get();
588
        if (!$allGroups->count()) {
589
            $authorGroup = new Group();
590
            $authorGroup->Code = 'content-authors';
591
            $authorGroup->Title = _t('SilverStripe\\Security\\Group.DefaultGroupTitleContentAuthors', 'Content Authors');
592
            $authorGroup->Sort = 1;
593
            $authorGroup->write();
594
            Permission::grant($authorGroup->ID, 'CMS_ACCESS_CMSMain');
595
            Permission::grant($authorGroup->ID, 'CMS_ACCESS_AssetAdmin');
596
            Permission::grant($authorGroup->ID, 'CMS_ACCESS_ReportAdmin');
597
            Permission::grant($authorGroup->ID, 'SITETREE_REORGANISE');
598
        }
599
600
        // Add default admin group if none with permission code ADMIN exists
601
        $adminGroups = Permission::get_groups_by_permission('ADMIN');
602
        if (!$adminGroups->count()) {
603
            $adminGroup = new Group();
604
            $adminGroup->Code = 'administrators';
605
            $adminGroup->Title = _t('SilverStripe\\Security\\Group.DefaultGroupTitleAdministrators', 'Administrators');
606
            $adminGroup->Sort = 0;
607
            $adminGroup->write();
608
            Permission::grant($adminGroup->ID, 'ADMIN');
609
        }
610
611
        // Members are populated through Member->requireDefaultRecords()
612
    }
613
}
614