1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SilverStripe\Security; |
4
|
|
|
|
5
|
|
|
use SilverStripe\ORM\DataObject; |
6
|
|
|
use SilverStripe\ORM\DB; |
7
|
|
|
use SilverStripe\ORM\ManyManyList; |
8
|
|
|
use SilverStripe\ORM\Queries\SQLDelete; |
9
|
|
|
use SilverStripe\ORM\Queries\SQLSelect; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* Represents a set of Groups attached to a member. |
13
|
|
|
* Handles the hierarchy logic. |
14
|
|
|
*/ |
15
|
|
|
class Member_GroupSet extends ManyManyList |
16
|
|
|
{ |
17
|
|
|
|
18
|
|
|
protected function linkJoinTable() |
19
|
|
|
{ |
20
|
|
|
// Do not join the table directly |
21
|
|
|
if ($this->extraFields) { |
|
|
|
|
22
|
|
|
user_error('Member_GroupSet does not support many_many_extraFields', E_USER_ERROR); |
23
|
|
|
} |
24
|
|
|
} |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Link this group set to a specific member. |
28
|
|
|
* |
29
|
|
|
* Recursively selects all groups applied to this member, as well as any |
30
|
|
|
* parent groups of any applied groups |
31
|
|
|
* |
32
|
|
|
* @param array|integer $id (optional) An ID or an array of IDs - if not provided, will use the current |
33
|
|
|
* ids as per getForeignID |
34
|
|
|
* @return array Condition In array(SQL => parameters format) |
35
|
|
|
*/ |
36
|
|
|
public function foreignIDFilter($id = null) |
37
|
|
|
{ |
38
|
|
|
if ($id === null) { |
39
|
|
|
$id = $this->getForeignID(); |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
// Find directly applied groups |
43
|
|
|
$manyManyFilter = parent::foreignIDFilter($id); |
44
|
|
|
$query = new SQLSelect('"Group_Members"."GroupID"', '"Group_Members"', $manyManyFilter); |
45
|
|
|
$groupIDs = $query->execute()->column(); |
46
|
|
|
|
47
|
|
|
// Get all ancestors, iteratively merging these into the master set |
48
|
|
|
$allGroupIDs = array(); |
49
|
|
|
while ($groupIDs) { |
|
|
|
|
50
|
|
|
$allGroupIDs = array_merge($allGroupIDs, $groupIDs); |
51
|
|
|
$groupIDs = DataObject::get("SilverStripe\\Security\\Group")->byIDs($groupIDs)->column("ParentID"); |
52
|
|
|
$groupIDs = array_filter($groupIDs); |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
// Add a filter to this DataList |
56
|
|
|
if (!empty($allGroupIDs)) { |
57
|
|
|
$allGroupIDsPlaceholders = DB::placeholders($allGroupIDs); |
58
|
|
|
return array("\"Group\".\"ID\" IN ($allGroupIDsPlaceholders)" => $allGroupIDs); |
59
|
|
|
} else { |
60
|
|
|
return array('"Group"."ID"' => 0); |
61
|
|
|
} |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
public function foreignIDWriteFilter($id = null) |
65
|
|
|
{ |
66
|
|
|
// Use the ManyManyList::foreignIDFilter rather than the one |
67
|
|
|
// in this class, otherwise we end up selecting all inherited groups |
68
|
|
|
return parent::foreignIDFilter($id); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
public function add($item, $extraFields = null) |
72
|
|
|
{ |
73
|
|
|
// Get Group.ID |
74
|
|
|
$itemID = null; |
75
|
|
|
if (is_numeric($item)) { |
76
|
|
|
$itemID = $item; |
77
|
|
|
} else { |
78
|
|
|
if ($item instanceof Group) { |
79
|
|
|
$itemID = $item->ID; |
80
|
|
|
} |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
// Check if this group is allowed to be added |
84
|
|
|
if ($this->canAddGroups(array($itemID))) { |
85
|
|
|
parent::add($item, $extraFields); |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
public function removeAll() |
90
|
|
|
{ |
91
|
|
|
// Remove the join to the join table to avoid MySQL row locking issues. |
92
|
|
|
$query = $this->dataQuery(); |
93
|
|
|
$foreignFilter = $query->getQueryParam('Foreign.Filter'); |
94
|
|
|
$query->removeFilterOn($foreignFilter); |
95
|
|
|
|
96
|
|
|
// Select ID column |
97
|
|
|
$selectQuery = $query->query(); |
98
|
|
|
$dataClassIDColumn = DataObject::getSchema()->sqlColumnForField($this->dataClass(), 'ID'); |
99
|
|
|
$selectQuery->setSelect($dataClassIDColumn); |
100
|
|
|
|
101
|
|
|
$from = $selectQuery->getFrom(); |
102
|
|
|
unset($from[$this->joinTable]); |
103
|
|
|
$selectQuery->setFrom($from); |
104
|
|
|
$selectQuery->setOrderBy(); // ORDER BY in subselects breaks MS SQL Server and is not necessary here |
105
|
|
|
$selectQuery->setDistinct(false); |
106
|
|
|
|
107
|
|
|
// Use a sub-query as SQLite does not support setting delete targets in |
108
|
|
|
// joined queries. |
109
|
|
|
$delete = new SQLDelete(); |
110
|
|
|
$delete->setFrom("\"{$this->joinTable}\""); |
111
|
|
|
$delete->addWhere(parent::foreignIDFilter()); |
112
|
|
|
$subSelect = $selectQuery->sql($parameters); |
113
|
|
|
$delete->addWhere(array( |
114
|
|
|
"\"{$this->joinTable}\".\"{$this->localKey}\" IN ($subSelect)" => $parameters |
115
|
|
|
)); |
116
|
|
|
$delete->execute(); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Determine if the following groups IDs can be added |
121
|
|
|
* |
122
|
|
|
* @param array $itemIDs |
123
|
|
|
* @return boolean |
124
|
|
|
*/ |
125
|
|
|
protected function canAddGroups($itemIDs) |
126
|
|
|
{ |
127
|
|
|
if (empty($itemIDs)) { |
128
|
|
|
return true; |
129
|
|
|
} |
130
|
|
|
$member = $this->getMember(); |
131
|
|
|
return empty($member) || $member->onChangeGroups($itemIDs); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Get foreign member record for this relation |
136
|
|
|
* |
137
|
|
|
* @return Member |
138
|
|
|
*/ |
139
|
|
|
protected function getMember() |
140
|
|
|
{ |
141
|
|
|
$id = $this->getForeignID(); |
142
|
|
|
if ($id) { |
143
|
|
|
return DataObject::get_by_id(Member::class, $id); |
|
|
|
|
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
|
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.