Completed
Push — master ( 5cf2d8...46bcff )
by Damian
13s
created

code/extensions/GroupSubsites.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SilverStripe\Subsites\Extensions;
4
5
use SilverStripe\Control\Cookie;
6
use SilverStripe\Core\Convert;
7
use SilverStripe\Forms\CheckboxSetField;
8
use SilverStripe\Forms\FieldList;
9
use SilverStripe\Forms\OptionsetField;
10
use SilverStripe\Forms\ReadonlyField;
11
use SilverStripe\ORM\DataExtension;
12
use SilverStripe\ORM\DataQuery;
13
use SilverStripe\ORM\DB;
14
use SilverStripe\ORM\Queries\SQLSelect;
15
use SilverStripe\Security\Group;
16
use SilverStripe\Security\PermissionProvider;
17
use SilverStripe\Subsites\Model\Subsite;
18
19
/**
20
 * Extension for the Group object to add subsites support
21
 *
22
 * @package subsites
23
 */
24
class GroupSubsites extends DataExtension implements PermissionProvider
25
{
26
    private static $db = [
27
        'AccessAllSubsites' => 'Boolean'
28
    ];
29
30
    private static $many_many = [
31
        'Subsites' => Subsite::class
32
    ];
33
34
    private static $defaults = [
35
        'AccessAllSubsites' => true
36
    ];
37
38
    /**
39
     * Migrations for GroupSubsites data.
40
     */
41
    public function requireDefaultRecords()
42
    {
43
        if (!$this->owner) {
44
            return;
45
        }
46
        // Migration for Group.SubsiteID data from when Groups only had a single subsite
47
        $ownerClass = get_class($this->owner);
48
        $schema = $ownerClass::getSchema();
49
        $groupFields = DB::field_list($schema->tableName(Group::class));
50
51
        // Detection of SubsiteID field is the trigger for old-style-subsiteID migration
52
        if (isset($groupFields['SubsiteID'])) {
53
            // Migrate subsite-specific data
54
            DB::query('INSERT INTO "Group_Subsites" ("GroupID", "SubsiteID")
55
				SELECT "ID", "SubsiteID" FROM "Group" WHERE "SubsiteID" > 0');
56
57
            // Migrate global-access data
58
            DB::query('UPDATE "Group" SET "AccessAllSubsites" = 1 WHERE "SubsiteID" = 0');
59
60
            // Move the field out of the way so that this migration doesn't get executed again
61
            DB::get_schema()->renameField(Group::class, 'SubsiteID', '_obsolete_SubsiteID');
62
63
            // No subsite access on anything means that we've just installed the subsites module.
64
            // Make all previous groups global-access groups
65
        } else {
66
            if (!DB::query('SELECT "Group"."ID" FROM "Group"
67
			LEFT JOIN "Group_Subsites" ON "Group_Subsites"."GroupID" = "Group"."ID" AND "Group_Subsites"."SubsiteID" > 0
68
			WHERE "AccessAllSubsites" = 1
69
			OR "Group_Subsites"."GroupID" IS NOT NULL ')->value()
70
            ) {
71
                DB::query('UPDATE "Group" SET "AccessAllSubsites" = 1');
72
            }
73
        }
74
    }
75
76
    public function updateCMSFields(FieldList $fields)
77
    {
78
        if ($this->owner->canEdit()) {
79
            // i18n tab
80
            $fields->findOrMakeTab('Root.Subsites', _t('GroupSubsites.SECURITYTABTITLE', 'Subsites'));
81
82
            $subsites = Subsite::accessible_sites(['ADMIN', 'SECURITY_SUBSITE_GROUP'], true);
83
            $subsiteMap = $subsites->map();
84
85
            // Prevent XSS injection
86
            $subsiteMap = Convert::raw2xml($subsiteMap->toArray());
87
88
            // Interface is different if you have the rights to modify subsite group values on
89
            // all subsites
90
            if (isset($subsiteMap[0])) {
91
                $fields->addFieldToTab('Root.Subsites', new OptionsetField(
92
                    'AccessAllSubsites',
93
                    _t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
94
                    [
95
                        1 => _t('GroupSubsites.ACCESSALL', 'All subsites'),
96
                        0 => _t('GroupSubsites.ACCESSONLY', 'Only these subsites'),
97
                    ]
98
                ));
99
100
                unset($subsiteMap[0]);
101
                $fields->addFieldToTab('Root.Subsites', new CheckboxSetField(
102
                    'Subsites',
103
                    '',
104
                    $subsiteMap
105
                ));
106
            } else {
107
                if (sizeof($subsiteMap) <= 1) {
108
                    $fields->addFieldToTab('Root.Subsites', new ReadonlyField(
109
                        'SubsitesHuman',
110
                        _t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
111
                        reset($subsiteMap)
112
                    ));
113
                } else {
114
                    $fields->addFieldToTab('Root.Subsites', new CheckboxSetField(
115
                        'Subsites',
116
                        _t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
117
                        $subsiteMap
118
                    ));
119
                }
120
            }
121
        }
122
    }
123
124
    /**
125
     * If this group belongs to a subsite,
126
     * append the subsites title to the group title
127
     * to make it easy to distinguish in the tree-view
128
     * of the security admin interface.
129
     */
130
    public function alternateTreeTitle()
131
    {
132
        if ($this->owner->AccessAllSubsites) {
133
            $title = _t('GroupSubsites.GlobalGroup', 'global group');
134
            return htmlspecialchars($this->owner->Title, ENT_QUOTES) . ' <i>(' . $title . ')</i>';
135
        }
136
137
        $subsites = Convert::raw2xml(implode(', ', $this->owner->Subsites()->column('Title')));
138
        return htmlspecialchars($this->owner->Title) . " <i>($subsites)</i>";
139
    }
140
141
    /**
142
     * Update any requests to limit the results to the current site
143
     * @param SQLSelect $query
144
     * @param DataQuery|null $dataQuery
145
     */
146
    public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
147
    {
148
        if (Subsite::$disable_subsite_filter) {
149
            return;
150
        }
151
        if (Cookie::get('noSubsiteFilter') == 'true') {
152
            return;
153
        }
154
155
        // If you're querying by ID, ignore the sub-site - this is a bit ugly...
156
        if (!$query->filtersOnID()) {
157
            /*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID;
0 ignored issues
show
Unused Code Comprehensibility introduced by
52% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
158
159
            else */
160
            $subsiteID = (int)Subsite::currentSubsiteID();
161
162
            // Don't filter by Group_Subsites if we've already done that
163
            $hasGroupSubsites = false;
164
            foreach ($query->getFrom() as $item) {
165
                if ((is_array($item) && strpos(
166
                    $item['table'],
167
                    'Group_Subsites'
168
                ) !== false) || (!is_array($item) && strpos(
169
                    $item,
170
                    'Group_Subsites'
171
                ) !== false)
172
                ) {
173
                    $hasGroupSubsites = true;
174
                    break;
175
                }
176
            }
177
178
            if (!$hasGroupSubsites) {
179
                if ($subsiteID) {
180
                    $query->addLeftJoin('Group_Subsites', "\"Group_Subsites\".\"GroupID\"
181
                        = \"Group\".\"ID\" AND \"Group_Subsites\".\"SubsiteID\" = $subsiteID");
182
                    $query->addWhere('("Group_Subsites"."SubsiteID" IS NOT NULL OR
183
                        "Group"."AccessAllSubsites" = 1)');
184
                } else {
185
                    $query->addWhere('"Group"."AccessAllSubsites" = 1');
186
                }
187
            }
188
189
            // WORKAROUND for databases that complain about an ORDER BY when the column wasn't selected (e.g. SQL Server)
190
            $select = $query->getSelect();
191
            if (isset($select[0]) && !$select[0] == 'COUNT(*)') {
192
                $query->addOrderBy('AccessAllSubsites', 'DESC');
193
            }
194
        }
195
    }
196
197
    public function onBeforeWrite()
198
    {
199
        // New record test approximated by checking whether the ID has changed.
200
        // Note also that the after write test is only used when we're *not* on a subsite
201
        if ($this->owner->isChanged('ID') && !Subsite::currentSubsiteID()) {
202
            $this->owner->AccessAllSubsites = 1;
203
        }
204
    }
205
206
    public function onAfterWrite()
207
    {
208
        // New record test approximated by checking whether the ID has changed.
209
        // Note also that the after write test is only used when we're on a subsite
210
        if ($this->owner->isChanged('ID') && $currentSubsiteID = Subsite::currentSubsiteID()) {
211
            $subsites = $this->owner->Subsites();
212
            $subsites->add($currentSubsiteID);
213
        }
214
    }
215
216
    public function alternateCanEdit()
217
    {
218
        // Find the sites that this group belongs to and the sites where we have appropriate perm.
219
        $accessibleSites = Subsite::accessible_sites('CMS_ACCESS_SecurityAdmin')->column('ID');
220
        $linkedSites = $this->owner->Subsites()->column('ID');
221
222
        // We are allowed to access this site if at we have CMS_ACCESS_SecurityAdmin permission on
223
        // at least one of the sites
224
        return (bool)array_intersect($accessibleSites, $linkedSites);
225
    }
226
227
    public function providePermissions()
228
    {
229
        return [
230
            'SECURITY_SUBSITE_GROUP' => [
231
                'name' => _t('GroupSubsites.MANAGE_SUBSITES', 'Manage subsites for groups'),
232
                'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
233
                'help' => _t(
234
                    'GroupSubsites.MANAGE_SUBSITES_HELP',
235
                    'Ability to limit the permissions for a group to one or more subsites.'
236
                ),
237
                'sort' => 200
238
            ]
239
        ];
240
    }
241
}
242