@@ -131,7 +131,7 @@ discard block |
||
131 | 131 | * Get primary attributes missing that are missing |
132 | 132 | * |
133 | 133 | * @param \stdClass $row Database row |
134 | - * @return array |
|
134 | + * @return boolean |
|
135 | 135 | */ |
136 | 136 | protected function isMissingPrimaries($row) { |
137 | 137 | return array_diff(self::$primary_attr_names, array_keys($row)) !== array(); |
@@ -141,7 +141,7 @@ discard block |
||
141 | 141 | * Get secondary attributes that are missing |
142 | 142 | * |
143 | 143 | * @param \stdClass $row Database row |
144 | - * @return array |
|
144 | + * @return boolean |
|
145 | 145 | */ |
146 | 146 | protected function isMissingSecondaries($row) { |
147 | 147 | return array_diff($this->secondary_attr_names, array_keys($row)) !== array(); |
@@ -11,275 +11,275 @@ |
||
11 | 11 | */ |
12 | 12 | class AttributeLoader { |
13 | 13 | |
14 | - /** |
|
15 | - * @var array names of attributes in all entities |
|
16 | - * |
|
17 | - * @todo require this to be injected and get it from \ElggEntity |
|
18 | - */ |
|
19 | - protected static $primary_attr_names = array( |
|
20 | - 'guid', |
|
21 | - 'type', |
|
22 | - 'subtype', |
|
23 | - 'owner_guid', |
|
24 | - 'container_guid', |
|
25 | - 'site_guid', |
|
26 | - 'access_id', |
|
27 | - 'time_created', |
|
28 | - 'time_updated', |
|
29 | - 'last_action', |
|
30 | - 'enabled', |
|
31 | - ); |
|
32 | - |
|
33 | - /** |
|
34 | - * @var array names of attributes in all entities that should be stored as integer values |
|
35 | - */ |
|
36 | - protected static $integer_attr_names = array( |
|
37 | - 'guid', |
|
38 | - 'owner_guid', |
|
39 | - 'container_guid', |
|
40 | - 'site_guid', |
|
41 | - 'access_id', |
|
42 | - 'time_created', |
|
43 | - 'time_updated', |
|
44 | - 'last_action', |
|
45 | - // \ElggUser |
|
46 | - 'prev_last_action', |
|
47 | - 'last_login', |
|
48 | - 'prev_last_login' |
|
49 | - ); |
|
50 | - |
|
51 | - /** |
|
52 | - * @var array names of attributes in all entities that should be stored as null if empty |
|
53 | - */ |
|
54 | - protected static $null_attr_names = array( |
|
55 | - 'name', |
|
56 | - 'title', |
|
57 | - 'description', |
|
58 | - 'url', |
|
59 | - ); |
|
60 | - |
|
61 | - /** |
|
62 | - * @var array names of secondary attributes required for the entity |
|
63 | - */ |
|
64 | - protected $secondary_attr_names = array(); |
|
65 | - |
|
66 | - /** |
|
67 | - * @var string entity type (not class) required for fetched primaries |
|
68 | - */ |
|
69 | - protected $required_type; |
|
70 | - |
|
71 | - /** |
|
72 | - * @var array |
|
73 | - */ |
|
74 | - protected $initialized_attributes; |
|
75 | - |
|
76 | - /** |
|
77 | - * @var string class of object being loaded |
|
78 | - */ |
|
79 | - protected $class; |
|
80 | - |
|
81 | - /** |
|
82 | - * @var bool should access control be considered when fetching entity? |
|
83 | - */ |
|
84 | - public $requires_access_control = true; |
|
85 | - |
|
86 | - /** |
|
87 | - * @var callable function used to load attributes from {prefix}entities table |
|
88 | - */ |
|
89 | - public $primary_loader = 'get_entity_as_row'; |
|
90 | - |
|
91 | - /** |
|
92 | - * @var callable function used to load attributes from secondary table |
|
93 | - */ |
|
94 | - public $secondary_loader = ''; |
|
95 | - |
|
96 | - /** |
|
97 | - * @var callable function used to load all necessary attributes |
|
98 | - */ |
|
99 | - public $full_loader = ''; |
|
100 | - |
|
101 | - /** |
|
102 | - * @var array retrieved values that are not attributes |
|
103 | - */ |
|
104 | - protected $additional_select_values = array(); |
|
105 | - |
|
106 | - /** |
|
107 | - * Constructor |
|
108 | - * |
|
109 | - * @param string $class class of object being loaded |
|
110 | - * @param string $required_type entity type this is being used to populate |
|
111 | - * @param array $initialized_attrs attributes after initializeAttributes() has been run |
|
112 | - * @throws \InvalidArgumentException |
|
113 | - */ |
|
114 | - public function __construct($class, $required_type, array $initialized_attrs) { |
|
115 | - if (!is_string($class)) { |
|
116 | - throw new \InvalidArgumentException('$class must be a class name.'); |
|
117 | - } |
|
118 | - $this->class = $class; |
|
119 | - |
|
120 | - if (!is_string($required_type)) { |
|
121 | - throw new \InvalidArgumentException('$requiredType must be a system entity type.'); |
|
122 | - } |
|
123 | - $this->required_type = $required_type; |
|
124 | - |
|
125 | - $this->initialized_attributes = $initialized_attrs; |
|
126 | - $all_attr_names = array_keys($initialized_attrs); |
|
127 | - $this->secondary_attr_names = array_diff($all_attr_names, self::$primary_attr_names); |
|
128 | - } |
|
129 | - |
|
130 | - /** |
|
131 | - * Get primary attributes missing that are missing |
|
132 | - * |
|
133 | - * @param \stdClass $row Database row |
|
134 | - * @return array |
|
135 | - */ |
|
136 | - protected function isMissingPrimaries($row) { |
|
137 | - return array_diff(self::$primary_attr_names, array_keys($row)) !== array(); |
|
138 | - } |
|
139 | - |
|
140 | - /** |
|
141 | - * Get secondary attributes that are missing |
|
142 | - * |
|
143 | - * @param \stdClass $row Database row |
|
144 | - * @return array |
|
145 | - */ |
|
146 | - protected function isMissingSecondaries($row) { |
|
147 | - return array_diff($this->secondary_attr_names, array_keys($row)) !== array(); |
|
148 | - } |
|
149 | - |
|
150 | - /** |
|
151 | - * Check that the type is correct |
|
152 | - * |
|
153 | - * @param \stdClass $row Database row |
|
154 | - * @return void |
|
155 | - * @throws \InvalidClassException |
|
156 | - */ |
|
157 | - protected function checkType($row) { |
|
158 | - if ($row['type'] !== $this->required_type) { |
|
159 | - $msg = "GUID:" . $row['guid'] . " is not a valid " . $this->class; |
|
160 | - throw new \InvalidClassException($msg); |
|
161 | - } |
|
162 | - } |
|
163 | - |
|
164 | - /** |
|
165 | - * Get values selected from the database that are not attributes |
|
166 | - * |
|
167 | - * @return array |
|
168 | - */ |
|
169 | - public function getAdditionalSelectValues() { |
|
170 | - return $this->additional_select_values; |
|
171 | - } |
|
14 | + /** |
|
15 | + * @var array names of attributes in all entities |
|
16 | + * |
|
17 | + * @todo require this to be injected and get it from \ElggEntity |
|
18 | + */ |
|
19 | + protected static $primary_attr_names = array( |
|
20 | + 'guid', |
|
21 | + 'type', |
|
22 | + 'subtype', |
|
23 | + 'owner_guid', |
|
24 | + 'container_guid', |
|
25 | + 'site_guid', |
|
26 | + 'access_id', |
|
27 | + 'time_created', |
|
28 | + 'time_updated', |
|
29 | + 'last_action', |
|
30 | + 'enabled', |
|
31 | + ); |
|
32 | + |
|
33 | + /** |
|
34 | + * @var array names of attributes in all entities that should be stored as integer values |
|
35 | + */ |
|
36 | + protected static $integer_attr_names = array( |
|
37 | + 'guid', |
|
38 | + 'owner_guid', |
|
39 | + 'container_guid', |
|
40 | + 'site_guid', |
|
41 | + 'access_id', |
|
42 | + 'time_created', |
|
43 | + 'time_updated', |
|
44 | + 'last_action', |
|
45 | + // \ElggUser |
|
46 | + 'prev_last_action', |
|
47 | + 'last_login', |
|
48 | + 'prev_last_login' |
|
49 | + ); |
|
50 | + |
|
51 | + /** |
|
52 | + * @var array names of attributes in all entities that should be stored as null if empty |
|
53 | + */ |
|
54 | + protected static $null_attr_names = array( |
|
55 | + 'name', |
|
56 | + 'title', |
|
57 | + 'description', |
|
58 | + 'url', |
|
59 | + ); |
|
60 | + |
|
61 | + /** |
|
62 | + * @var array names of secondary attributes required for the entity |
|
63 | + */ |
|
64 | + protected $secondary_attr_names = array(); |
|
65 | + |
|
66 | + /** |
|
67 | + * @var string entity type (not class) required for fetched primaries |
|
68 | + */ |
|
69 | + protected $required_type; |
|
70 | + |
|
71 | + /** |
|
72 | + * @var array |
|
73 | + */ |
|
74 | + protected $initialized_attributes; |
|
75 | + |
|
76 | + /** |
|
77 | + * @var string class of object being loaded |
|
78 | + */ |
|
79 | + protected $class; |
|
80 | + |
|
81 | + /** |
|
82 | + * @var bool should access control be considered when fetching entity? |
|
83 | + */ |
|
84 | + public $requires_access_control = true; |
|
85 | + |
|
86 | + /** |
|
87 | + * @var callable function used to load attributes from {prefix}entities table |
|
88 | + */ |
|
89 | + public $primary_loader = 'get_entity_as_row'; |
|
90 | + |
|
91 | + /** |
|
92 | + * @var callable function used to load attributes from secondary table |
|
93 | + */ |
|
94 | + public $secondary_loader = ''; |
|
95 | + |
|
96 | + /** |
|
97 | + * @var callable function used to load all necessary attributes |
|
98 | + */ |
|
99 | + public $full_loader = ''; |
|
100 | + |
|
101 | + /** |
|
102 | + * @var array retrieved values that are not attributes |
|
103 | + */ |
|
104 | + protected $additional_select_values = array(); |
|
105 | + |
|
106 | + /** |
|
107 | + * Constructor |
|
108 | + * |
|
109 | + * @param string $class class of object being loaded |
|
110 | + * @param string $required_type entity type this is being used to populate |
|
111 | + * @param array $initialized_attrs attributes after initializeAttributes() has been run |
|
112 | + * @throws \InvalidArgumentException |
|
113 | + */ |
|
114 | + public function __construct($class, $required_type, array $initialized_attrs) { |
|
115 | + if (!is_string($class)) { |
|
116 | + throw new \InvalidArgumentException('$class must be a class name.'); |
|
117 | + } |
|
118 | + $this->class = $class; |
|
119 | + |
|
120 | + if (!is_string($required_type)) { |
|
121 | + throw new \InvalidArgumentException('$requiredType must be a system entity type.'); |
|
122 | + } |
|
123 | + $this->required_type = $required_type; |
|
124 | + |
|
125 | + $this->initialized_attributes = $initialized_attrs; |
|
126 | + $all_attr_names = array_keys($initialized_attrs); |
|
127 | + $this->secondary_attr_names = array_diff($all_attr_names, self::$primary_attr_names); |
|
128 | + } |
|
129 | + |
|
130 | + /** |
|
131 | + * Get primary attributes missing that are missing |
|
132 | + * |
|
133 | + * @param \stdClass $row Database row |
|
134 | + * @return array |
|
135 | + */ |
|
136 | + protected function isMissingPrimaries($row) { |
|
137 | + return array_diff(self::$primary_attr_names, array_keys($row)) !== array(); |
|
138 | + } |
|
139 | + |
|
140 | + /** |
|
141 | + * Get secondary attributes that are missing |
|
142 | + * |
|
143 | + * @param \stdClass $row Database row |
|
144 | + * @return array |
|
145 | + */ |
|
146 | + protected function isMissingSecondaries($row) { |
|
147 | + return array_diff($this->secondary_attr_names, array_keys($row)) !== array(); |
|
148 | + } |
|
149 | + |
|
150 | + /** |
|
151 | + * Check that the type is correct |
|
152 | + * |
|
153 | + * @param \stdClass $row Database row |
|
154 | + * @return void |
|
155 | + * @throws \InvalidClassException |
|
156 | + */ |
|
157 | + protected function checkType($row) { |
|
158 | + if ($row['type'] !== $this->required_type) { |
|
159 | + $msg = "GUID:" . $row['guid'] . " is not a valid " . $this->class; |
|
160 | + throw new \InvalidClassException($msg); |
|
161 | + } |
|
162 | + } |
|
163 | + |
|
164 | + /** |
|
165 | + * Get values selected from the database that are not attributes |
|
166 | + * |
|
167 | + * @return array |
|
168 | + */ |
|
169 | + public function getAdditionalSelectValues() { |
|
170 | + return $this->additional_select_values; |
|
171 | + } |
|
172 | 172 | |
173 | - /** |
|
174 | - * Get all required attributes for the entity, validating any that are passed in. Returns empty array |
|
175 | - * if can't be loaded (Check $failure_reason). |
|
176 | - * |
|
177 | - * This function splits loading between "primary" attributes (those in {prefix}entities table) and |
|
178 | - * "secondary" attributes (e.g. those in {prefix}objects_entity), but can load all at once if a |
|
179 | - * combined loader is available. |
|
180 | - * |
|
181 | - * @param mixed $row a row loaded from DB (array or \stdClass) or a GUID |
|
182 | - * @return array will be empty if failed to load all attributes (access control or entity doesn't exist) |
|
183 | - * |
|
184 | - * @throws \InvalidArgumentException|\LogicException|\IncompleteEntityException |
|
185 | - */ |
|
186 | - public function getRequiredAttributes($row) { |
|
187 | - if (!is_array($row) && !($row instanceof \stdClass)) { |
|
188 | - // assume row is the GUID |
|
189 | - $row = array('guid' => $row); |
|
190 | - } |
|
191 | - $row = (array) $row; |
|
192 | - if (empty($row['guid'])) { |
|
193 | - throw new \InvalidArgumentException('$row must be or contain a GUID'); |
|
194 | - } |
|
195 | - |
|
196 | - $was_missing_primaries = $this->isMissingPrimaries($row); |
|
197 | - $was_missing_secondaries = $this->isMissingSecondaries($row); |
|
198 | - |
|
199 | - // some types have a function to load all attributes at once, it should be faster |
|
200 | - if (($was_missing_primaries || $was_missing_secondaries) && is_callable($this->full_loader)) { |
|
201 | - $fetched = (array) call_user_func($this->full_loader, $row['guid']); |
|
202 | - if (!$fetched) { |
|
203 | - return array(); |
|
204 | - } |
|
205 | - $row = array_merge($row, $fetched); |
|
206 | - $this->checkType($row); |
|
207 | - } else { |
|
208 | - if ($was_missing_primaries) { |
|
209 | - if (!is_callable($this->primary_loader)) { |
|
210 | - throw new \LogicException('Primary attribute loader must be callable'); |
|
211 | - } |
|
212 | - if ($this->requires_access_control) { |
|
213 | - $fetched = (array) call_user_func($this->primary_loader, $row['guid']); |
|
214 | - } else { |
|
215 | - $ignoring_access = elgg_set_ignore_access(); |
|
216 | - $fetched = (array) call_user_func($this->primary_loader, $row['guid']); |
|
217 | - elgg_set_ignore_access($ignoring_access); |
|
218 | - } |
|
219 | - if (!$fetched) { |
|
220 | - return array(); |
|
221 | - } |
|
222 | - $row = array_merge($row, $fetched); |
|
223 | - } |
|
224 | - |
|
225 | - // We must test type before trying to load the secondaries so that InvalidClassException |
|
226 | - // gets thrown. Otherwise the secondary loader will fail and return false. |
|
227 | - $this->checkType($row); |
|
228 | - |
|
229 | - if ($was_missing_secondaries) { |
|
230 | - if (!is_callable($this->secondary_loader)) { |
|
231 | - throw new \LogicException('Secondary attribute loader must be callable'); |
|
232 | - } |
|
233 | - $fetched = (array) call_user_func($this->secondary_loader, $row['guid']); |
|
234 | - if (!$fetched) { |
|
235 | - throw new \IncompleteEntityException("Secondary loader failed to return row for {$row['guid']}"); |
|
236 | - } |
|
237 | - $row = array_merge($row, $fetched); |
|
238 | - } |
|
239 | - } |
|
240 | - |
|
241 | - $row = $this->filterAddedColumns($row); |
|
242 | - |
|
243 | - $row['subtype'] = (int)$row['subtype']; |
|
244 | - |
|
245 | - // set to null when reading empty value, to match default empty value; See #5456 |
|
246 | - foreach (self::$null_attr_names as $key) { |
|
247 | - if (isset($row[$key]) && !$row[$key]) { |
|
248 | - $row[$key] = null; |
|
249 | - } |
|
250 | - } |
|
251 | - |
|
252 | - // Note: If there are still missing attributes, we're running on a 1.7 or earlier schema. We let |
|
253 | - // this pass so the upgrades can run. |
|
254 | - |
|
255 | - // guid needs to be an int https://github.com/elgg/elgg/issues/4111 |
|
256 | - foreach (self::$integer_attr_names as $key) { |
|
257 | - if (isset($row[$key])) { |
|
258 | - $row[$key] = (int) $row[$key]; |
|
259 | - } |
|
260 | - } |
|
261 | - return $row; |
|
262 | - } |
|
263 | - |
|
264 | - /** |
|
265 | - * Filter non-attribute keys into $this->additional_select_values |
|
266 | - * |
|
267 | - * @param array $row All columns from the query |
|
268 | - * @return array Columns acceptable for the entity's attributes |
|
269 | - */ |
|
270 | - protected function filterAddedColumns($row) { |
|
271 | - // make an array with keys as acceptable attribute names |
|
272 | - $acceptable_attrs = self::$primary_attr_names; |
|
273 | - array_splice($acceptable_attrs, count($acceptable_attrs), 0, $this->secondary_attr_names); |
|
274 | - $acceptable_attrs = array_combine($acceptable_attrs, $acceptable_attrs); |
|
275 | - |
|
276 | - foreach ($row as $key => $val) { |
|
277 | - if (!isset($acceptable_attrs[$key])) { |
|
278 | - $this->additional_select_values[$key] = $val; |
|
279 | - unset($row[$key]); |
|
280 | - } |
|
281 | - } |
|
282 | - return $row; |
|
283 | - } |
|
173 | + /** |
|
174 | + * Get all required attributes for the entity, validating any that are passed in. Returns empty array |
|
175 | + * if can't be loaded (Check $failure_reason). |
|
176 | + * |
|
177 | + * This function splits loading between "primary" attributes (those in {prefix}entities table) and |
|
178 | + * "secondary" attributes (e.g. those in {prefix}objects_entity), but can load all at once if a |
|
179 | + * combined loader is available. |
|
180 | + * |
|
181 | + * @param mixed $row a row loaded from DB (array or \stdClass) or a GUID |
|
182 | + * @return array will be empty if failed to load all attributes (access control or entity doesn't exist) |
|
183 | + * |
|
184 | + * @throws \InvalidArgumentException|\LogicException|\IncompleteEntityException |
|
185 | + */ |
|
186 | + public function getRequiredAttributes($row) { |
|
187 | + if (!is_array($row) && !($row instanceof \stdClass)) { |
|
188 | + // assume row is the GUID |
|
189 | + $row = array('guid' => $row); |
|
190 | + } |
|
191 | + $row = (array) $row; |
|
192 | + if (empty($row['guid'])) { |
|
193 | + throw new \InvalidArgumentException('$row must be or contain a GUID'); |
|
194 | + } |
|
195 | + |
|
196 | + $was_missing_primaries = $this->isMissingPrimaries($row); |
|
197 | + $was_missing_secondaries = $this->isMissingSecondaries($row); |
|
198 | + |
|
199 | + // some types have a function to load all attributes at once, it should be faster |
|
200 | + if (($was_missing_primaries || $was_missing_secondaries) && is_callable($this->full_loader)) { |
|
201 | + $fetched = (array) call_user_func($this->full_loader, $row['guid']); |
|
202 | + if (!$fetched) { |
|
203 | + return array(); |
|
204 | + } |
|
205 | + $row = array_merge($row, $fetched); |
|
206 | + $this->checkType($row); |
|
207 | + } else { |
|
208 | + if ($was_missing_primaries) { |
|
209 | + if (!is_callable($this->primary_loader)) { |
|
210 | + throw new \LogicException('Primary attribute loader must be callable'); |
|
211 | + } |
|
212 | + if ($this->requires_access_control) { |
|
213 | + $fetched = (array) call_user_func($this->primary_loader, $row['guid']); |
|
214 | + } else { |
|
215 | + $ignoring_access = elgg_set_ignore_access(); |
|
216 | + $fetched = (array) call_user_func($this->primary_loader, $row['guid']); |
|
217 | + elgg_set_ignore_access($ignoring_access); |
|
218 | + } |
|
219 | + if (!$fetched) { |
|
220 | + return array(); |
|
221 | + } |
|
222 | + $row = array_merge($row, $fetched); |
|
223 | + } |
|
224 | + |
|
225 | + // We must test type before trying to load the secondaries so that InvalidClassException |
|
226 | + // gets thrown. Otherwise the secondary loader will fail and return false. |
|
227 | + $this->checkType($row); |
|
228 | + |
|
229 | + if ($was_missing_secondaries) { |
|
230 | + if (!is_callable($this->secondary_loader)) { |
|
231 | + throw new \LogicException('Secondary attribute loader must be callable'); |
|
232 | + } |
|
233 | + $fetched = (array) call_user_func($this->secondary_loader, $row['guid']); |
|
234 | + if (!$fetched) { |
|
235 | + throw new \IncompleteEntityException("Secondary loader failed to return row for {$row['guid']}"); |
|
236 | + } |
|
237 | + $row = array_merge($row, $fetched); |
|
238 | + } |
|
239 | + } |
|
240 | + |
|
241 | + $row = $this->filterAddedColumns($row); |
|
242 | + |
|
243 | + $row['subtype'] = (int)$row['subtype']; |
|
244 | + |
|
245 | + // set to null when reading empty value, to match default empty value; See #5456 |
|
246 | + foreach (self::$null_attr_names as $key) { |
|
247 | + if (isset($row[$key]) && !$row[$key]) { |
|
248 | + $row[$key] = null; |
|
249 | + } |
|
250 | + } |
|
251 | + |
|
252 | + // Note: If there are still missing attributes, we're running on a 1.7 or earlier schema. We let |
|
253 | + // this pass so the upgrades can run. |
|
254 | + |
|
255 | + // guid needs to be an int https://github.com/elgg/elgg/issues/4111 |
|
256 | + foreach (self::$integer_attr_names as $key) { |
|
257 | + if (isset($row[$key])) { |
|
258 | + $row[$key] = (int) $row[$key]; |
|
259 | + } |
|
260 | + } |
|
261 | + return $row; |
|
262 | + } |
|
263 | + |
|
264 | + /** |
|
265 | + * Filter non-attribute keys into $this->additional_select_values |
|
266 | + * |
|
267 | + * @param array $row All columns from the query |
|
268 | + * @return array Columns acceptable for the entity's attributes |
|
269 | + */ |
|
270 | + protected function filterAddedColumns($row) { |
|
271 | + // make an array with keys as acceptable attribute names |
|
272 | + $acceptable_attrs = self::$primary_attr_names; |
|
273 | + array_splice($acceptable_attrs, count($acceptable_attrs), 0, $this->secondary_attr_names); |
|
274 | + $acceptable_attrs = array_combine($acceptable_attrs, $acceptable_attrs); |
|
275 | + |
|
276 | + foreach ($row as $key => $val) { |
|
277 | + if (!isset($acceptable_attrs[$key])) { |
|
278 | + $this->additional_select_values[$key] = $val; |
|
279 | + unset($row[$key]); |
|
280 | + } |
|
281 | + } |
|
282 | + return $row; |
|
283 | + } |
|
284 | 284 | } |
285 | 285 |
@@ -156,7 +156,7 @@ discard block |
||
156 | 156 | */ |
157 | 157 | protected function checkType($row) { |
158 | 158 | if ($row['type'] !== $this->required_type) { |
159 | - $msg = "GUID:" . $row['guid'] . " is not a valid " . $this->class; |
|
159 | + $msg = "GUID:".$row['guid']." is not a valid ".$this->class; |
|
160 | 160 | throw new \InvalidClassException($msg); |
161 | 161 | } |
162 | 162 | } |
@@ -240,7 +240,7 @@ discard block |
||
240 | 240 | |
241 | 241 | $row = $this->filterAddedColumns($row); |
242 | 242 | |
243 | - $row['subtype'] = (int)$row['subtype']; |
|
243 | + $row['subtype'] = (int) $row['subtype']; |
|
244 | 244 | |
245 | 245 | // set to null when reading empty value, to match default empty value; See #5456 |
246 | 246 | foreach (self::$null_attr_names as $key) { |
@@ -33,7 +33,7 @@ |
||
33 | 33 | * |
34 | 34 | * @param int $id The id of the annotation object being retrieved. |
35 | 35 | * |
36 | - * @return \ElggAnnotation|false |
|
36 | + * @return \ElggExtender |
|
37 | 37 | */ |
38 | 38 | function get($id) { |
39 | 39 | return _elgg_get_metastring_based_object_from_id($id, 'annotation'); |
@@ -12,481 +12,481 @@ |
||
12 | 12 | */ |
13 | 13 | class Annotations { |
14 | 14 | |
15 | - use \Elgg\TimeUsing; |
|
15 | + use \Elgg\TimeUsing; |
|
16 | 16 | |
17 | - /** |
|
18 | - * @var \Elgg\Database |
|
19 | - */ |
|
20 | - protected $db; |
|
17 | + /** |
|
18 | + * @var \Elgg\Database |
|
19 | + */ |
|
20 | + protected $db; |
|
21 | 21 | |
22 | - /** |
|
23 | - * @var \ElggSession |
|
24 | - */ |
|
25 | - protected $session; |
|
22 | + /** |
|
23 | + * @var \ElggSession |
|
24 | + */ |
|
25 | + protected $session; |
|
26 | 26 | |
27 | - /** |
|
28 | - * @var \Elgg\EventsService |
|
29 | - */ |
|
30 | - protected $events; |
|
27 | + /** |
|
28 | + * @var \Elgg\EventsService |
|
29 | + */ |
|
30 | + protected $events; |
|
31 | 31 | |
32 | - /** |
|
33 | - * Constructor |
|
34 | - * |
|
35 | - * @param \Elgg\Database $db Database |
|
36 | - * @param \ElggSession $session Session |
|
37 | - * @param \Elgg\EventsService $events Events |
|
38 | - */ |
|
39 | - public function __construct(\Elgg\Database $db, \ElggSession $session, \Elgg\EventsService $events) { |
|
40 | - $this->db = $db; |
|
41 | - $this->session = $session; |
|
42 | - $this->events = $events; |
|
43 | - } |
|
32 | + /** |
|
33 | + * Constructor |
|
34 | + * |
|
35 | + * @param \Elgg\Database $db Database |
|
36 | + * @param \ElggSession $session Session |
|
37 | + * @param \Elgg\EventsService $events Events |
|
38 | + */ |
|
39 | + public function __construct(\Elgg\Database $db, \ElggSession $session, \Elgg\EventsService $events) { |
|
40 | + $this->db = $db; |
|
41 | + $this->session = $session; |
|
42 | + $this->events = $events; |
|
43 | + } |
|
44 | 44 | |
45 | - /** |
|
46 | - * Get a specific annotation by its id. |
|
47 | - * If you want multiple annotation objects, use |
|
48 | - * {@link elgg_get_annotations()}. |
|
49 | - * |
|
50 | - * @param int $id The id of the annotation object being retrieved. |
|
51 | - * |
|
52 | - * @return \ElggAnnotation|false |
|
53 | - */ |
|
54 | - function get($id) { |
|
55 | - return _elgg_get_metastring_based_object_from_id($id, 'annotation'); |
|
56 | - } |
|
57 | - |
|
58 | - /** |
|
59 | - * Deletes an annotation using its ID. |
|
60 | - * |
|
61 | - * @param int $id The annotation ID to delete. |
|
62 | - * @return bool |
|
63 | - */ |
|
64 | - function delete($id) { |
|
65 | - $annotation = $this->get($id); |
|
66 | - if (!$annotation) { |
|
67 | - return false; |
|
68 | - } |
|
69 | - return $annotation->delete(); |
|
70 | - } |
|
71 | - |
|
72 | - /** |
|
73 | - * Create a new annotation. |
|
74 | - * |
|
75 | - * @param int $entity_guid GUID of entity to be annotated |
|
76 | - * @param string $name Name of annotation |
|
77 | - * @param string $value Value of annotation |
|
78 | - * @param string $value_type Type of value (default is auto detection) |
|
79 | - * @param int $owner_guid Owner of annotation (default is logged in user) |
|
80 | - * @param int $access_id Access level of annotation |
|
81 | - * |
|
82 | - * @return int|bool id on success or false on failure |
|
83 | - */ |
|
84 | - function create($entity_guid, $name, $value, $value_type = '', $owner_guid = 0, $access_id = ACCESS_PRIVATE) { |
|
45 | + /** |
|
46 | + * Get a specific annotation by its id. |
|
47 | + * If you want multiple annotation objects, use |
|
48 | + * {@link elgg_get_annotations()}. |
|
49 | + * |
|
50 | + * @param int $id The id of the annotation object being retrieved. |
|
51 | + * |
|
52 | + * @return \ElggAnnotation|false |
|
53 | + */ |
|
54 | + function get($id) { |
|
55 | + return _elgg_get_metastring_based_object_from_id($id, 'annotation'); |
|
56 | + } |
|
57 | + |
|
58 | + /** |
|
59 | + * Deletes an annotation using its ID. |
|
60 | + * |
|
61 | + * @param int $id The annotation ID to delete. |
|
62 | + * @return bool |
|
63 | + */ |
|
64 | + function delete($id) { |
|
65 | + $annotation = $this->get($id); |
|
66 | + if (!$annotation) { |
|
67 | + return false; |
|
68 | + } |
|
69 | + return $annotation->delete(); |
|
70 | + } |
|
71 | + |
|
72 | + /** |
|
73 | + * Create a new annotation. |
|
74 | + * |
|
75 | + * @param int $entity_guid GUID of entity to be annotated |
|
76 | + * @param string $name Name of annotation |
|
77 | + * @param string $value Value of annotation |
|
78 | + * @param string $value_type Type of value (default is auto detection) |
|
79 | + * @param int $owner_guid Owner of annotation (default is logged in user) |
|
80 | + * @param int $access_id Access level of annotation |
|
81 | + * |
|
82 | + * @return int|bool id on success or false on failure |
|
83 | + */ |
|
84 | + function create($entity_guid, $name, $value, $value_type = '', $owner_guid = 0, $access_id = ACCESS_PRIVATE) { |
|
85 | 85 | |
86 | 86 | |
87 | - $result = false; |
|
87 | + $result = false; |
|
88 | 88 | |
89 | - $entity_guid = (int)$entity_guid; |
|
90 | - $value_type = \ElggExtender::detectValueType($value, $value_type); |
|
89 | + $entity_guid = (int)$entity_guid; |
|
90 | + $value_type = \ElggExtender::detectValueType($value, $value_type); |
|
91 | 91 | |
92 | - $owner_guid = (int)$owner_guid; |
|
93 | - if ($owner_guid == 0) { |
|
94 | - $owner_guid = $this->session->getLoggedInUserGuid(); |
|
95 | - } |
|
92 | + $owner_guid = (int)$owner_guid; |
|
93 | + if ($owner_guid == 0) { |
|
94 | + $owner_guid = $this->session->getLoggedInUserGuid(); |
|
95 | + } |
|
96 | 96 | |
97 | - $access_id = (int)$access_id; |
|
98 | - $time = $this->getCurrentTime()->getTimestamp(); |
|
97 | + $access_id = (int)$access_id; |
|
98 | + $time = $this->getCurrentTime()->getTimestamp(); |
|
99 | 99 | |
100 | - $value_id = elgg_get_metastring_id($value); |
|
101 | - if (!$value_id) { |
|
102 | - return false; |
|
103 | - } |
|
100 | + $value_id = elgg_get_metastring_id($value); |
|
101 | + if (!$value_id) { |
|
102 | + return false; |
|
103 | + } |
|
104 | 104 | |
105 | - $name_id = elgg_get_metastring_id($name); |
|
106 | - if (!$name_id) { |
|
107 | - return false; |
|
108 | - } |
|
105 | + $name_id = elgg_get_metastring_id($name); |
|
106 | + if (!$name_id) { |
|
107 | + return false; |
|
108 | + } |
|
109 | 109 | |
110 | - // @todo we don't check that the entity is loaded which means the user may |
|
111 | - // not have access to the entity |
|
112 | - $entity = get_entity($entity_guid); |
|
110 | + // @todo we don't check that the entity is loaded which means the user may |
|
111 | + // not have access to the entity |
|
112 | + $entity = get_entity($entity_guid); |
|
113 | 113 | |
114 | - if ($this->events->trigger('annotate', $entity->type, $entity)) { |
|
115 | - $result = $this->db->insertData("INSERT INTO {$this->db->prefix}annotations |
|
114 | + if ($this->events->trigger('annotate', $entity->type, $entity)) { |
|
115 | + $result = $this->db->insertData("INSERT INTO {$this->db->prefix}annotations |
|
116 | 116 | (entity_guid, name_id, value_id, value_type, owner_guid, time_created, access_id) VALUES |
117 | 117 | ($entity_guid, $name_id, $value_id, '$value_type', $owner_guid, $time, $access_id)"); |
118 | 118 | |
119 | - if ($result !== false) { |
|
120 | - $obj = elgg_get_annotation_from_id($result); |
|
121 | - if ($this->events->trigger('create', 'annotation', $obj)) { |
|
122 | - return $result; |
|
123 | - } else { |
|
124 | - // plugin returned false to reject annotation |
|
125 | - elgg_delete_annotation_by_id($result); |
|
126 | - return false; |
|
127 | - } |
|
128 | - } |
|
129 | - } |
|
130 | - |
|
131 | - return $result; |
|
132 | - } |
|
133 | - |
|
134 | - /** |
|
135 | - * Update an annotation. |
|
136 | - * |
|
137 | - * @param int $annotation_id Annotation ID |
|
138 | - * @param string $name Name of annotation |
|
139 | - * @param string $value Value of annotation |
|
140 | - * @param string $value_type Type of value |
|
141 | - * @param int $owner_guid Owner of annotation |
|
142 | - * @param int $access_id Access level of annotation |
|
143 | - * |
|
144 | - * @return bool |
|
145 | - */ |
|
146 | - function update($annotation_id, $name, $value, $value_type, $owner_guid, $access_id) { |
|
119 | + if ($result !== false) { |
|
120 | + $obj = elgg_get_annotation_from_id($result); |
|
121 | + if ($this->events->trigger('create', 'annotation', $obj)) { |
|
122 | + return $result; |
|
123 | + } else { |
|
124 | + // plugin returned false to reject annotation |
|
125 | + elgg_delete_annotation_by_id($result); |
|
126 | + return false; |
|
127 | + } |
|
128 | + } |
|
129 | + } |
|
130 | + |
|
131 | + return $result; |
|
132 | + } |
|
133 | + |
|
134 | + /** |
|
135 | + * Update an annotation. |
|
136 | + * |
|
137 | + * @param int $annotation_id Annotation ID |
|
138 | + * @param string $name Name of annotation |
|
139 | + * @param string $value Value of annotation |
|
140 | + * @param string $value_type Type of value |
|
141 | + * @param int $owner_guid Owner of annotation |
|
142 | + * @param int $access_id Access level of annotation |
|
143 | + * |
|
144 | + * @return bool |
|
145 | + */ |
|
146 | + function update($annotation_id, $name, $value, $value_type, $owner_guid, $access_id) { |
|
147 | 147 | |
148 | 148 | |
149 | - $annotation_id = (int)$annotation_id; |
|
149 | + $annotation_id = (int)$annotation_id; |
|
150 | 150 | |
151 | - $annotation = $this->get($annotation_id); |
|
152 | - if (!$annotation) { |
|
153 | - return false; |
|
154 | - } |
|
155 | - if (!$annotation->canEdit()) { |
|
156 | - return false; |
|
157 | - } |
|
151 | + $annotation = $this->get($annotation_id); |
|
152 | + if (!$annotation) { |
|
153 | + return false; |
|
154 | + } |
|
155 | + if (!$annotation->canEdit()) { |
|
156 | + return false; |
|
157 | + } |
|
158 | 158 | |
159 | - $name = trim($name); |
|
160 | - $value_type = \ElggExtender::detectValueType($value, $value_type); |
|
159 | + $name = trim($name); |
|
160 | + $value_type = \ElggExtender::detectValueType($value, $value_type); |
|
161 | 161 | |
162 | - $owner_guid = (int)$owner_guid; |
|
163 | - if ($owner_guid == 0) { |
|
164 | - $owner_guid = $this->session->getLoggedInUserGuid(); |
|
165 | - } |
|
162 | + $owner_guid = (int)$owner_guid; |
|
163 | + if ($owner_guid == 0) { |
|
164 | + $owner_guid = $this->session->getLoggedInUserGuid(); |
|
165 | + } |
|
166 | 166 | |
167 | - $access_id = (int)$access_id; |
|
167 | + $access_id = (int)$access_id; |
|
168 | 168 | |
169 | - $value_id = elgg_get_metastring_id($value); |
|
170 | - if (!$value_id) { |
|
171 | - return false; |
|
172 | - } |
|
169 | + $value_id = elgg_get_metastring_id($value); |
|
170 | + if (!$value_id) { |
|
171 | + return false; |
|
172 | + } |
|
173 | 173 | |
174 | - $name_id = elgg_get_metastring_id($name); |
|
175 | - if (!$name_id) { |
|
176 | - return false; |
|
177 | - } |
|
174 | + $name_id = elgg_get_metastring_id($name); |
|
175 | + if (!$name_id) { |
|
176 | + return false; |
|
177 | + } |
|
178 | 178 | |
179 | - $result = $this->db->updateData("UPDATE {$this->db->prefix}annotations |
|
179 | + $result = $this->db->updateData("UPDATE {$this->db->prefix}annotations |
|
180 | 180 | SET name_id = $name_id, value_id = $value_id, value_type = '$value_type', |
181 | 181 | access_id = $access_id, owner_guid = $owner_guid |
182 | 182 | WHERE id = $annotation_id"); |
183 | 183 | |
184 | - if ($result !== false) { |
|
185 | - // @todo add plugin hook that sends old and new annotation information before db access |
|
186 | - $obj = $this->get($annotation_id); |
|
187 | - $this->events->trigger('update', 'annotation', $obj); |
|
188 | - } |
|
189 | - |
|
190 | - return $result; |
|
191 | - } |
|
192 | - |
|
193 | - /** |
|
194 | - * Returns annotations. Accepts all elgg_get_entities() options for entity |
|
195 | - * restraints. |
|
196 | - * |
|
197 | - * @see elgg_get_entities |
|
198 | - * |
|
199 | - * @param array $options Array in format: |
|
200 | - * |
|
201 | - * annotation_names => null|ARR Annotation names |
|
202 | - * annotation_values => null|ARR Annotation values |
|
203 | - * annotation_ids => null|ARR annotation ids |
|
204 | - * annotation_case_sensitive => BOOL Overall Case sensitive |
|
205 | - * annotation_owner_guids => null|ARR guids for annotation owners |
|
206 | - * annotation_created_time_lower => INT Lower limit for created time. |
|
207 | - * annotation_created_time_upper => INT Upper limit for created time. |
|
208 | - * annotation_calculation => STR Perform the MySQL function on the annotation values returned. |
|
209 | - * Do not confuse this "annotation_calculation" option with the |
|
210 | - * "calculation" option to elgg_get_entities_from_annotation_calculation(). |
|
211 | - * The "annotation_calculation" option causes this function to |
|
212 | - * return the result of performing a mathematical calculation on |
|
213 | - * all annotations that match the query instead of \ElggAnnotation |
|
214 | - * objects. |
|
215 | - * See the docs for elgg_get_entities_from_annotation_calculation() |
|
216 | - * for the proper use of the "calculation" option. |
|
217 | - * |
|
218 | - * |
|
219 | - * @return \ElggAnnotation[]|mixed |
|
220 | - */ |
|
221 | - function find(array $options = array()) { |
|
184 | + if ($result !== false) { |
|
185 | + // @todo add plugin hook that sends old and new annotation information before db access |
|
186 | + $obj = $this->get($annotation_id); |
|
187 | + $this->events->trigger('update', 'annotation', $obj); |
|
188 | + } |
|
189 | + |
|
190 | + return $result; |
|
191 | + } |
|
192 | + |
|
193 | + /** |
|
194 | + * Returns annotations. Accepts all elgg_get_entities() options for entity |
|
195 | + * restraints. |
|
196 | + * |
|
197 | + * @see elgg_get_entities |
|
198 | + * |
|
199 | + * @param array $options Array in format: |
|
200 | + * |
|
201 | + * annotation_names => null|ARR Annotation names |
|
202 | + * annotation_values => null|ARR Annotation values |
|
203 | + * annotation_ids => null|ARR annotation ids |
|
204 | + * annotation_case_sensitive => BOOL Overall Case sensitive |
|
205 | + * annotation_owner_guids => null|ARR guids for annotation owners |
|
206 | + * annotation_created_time_lower => INT Lower limit for created time. |
|
207 | + * annotation_created_time_upper => INT Upper limit for created time. |
|
208 | + * annotation_calculation => STR Perform the MySQL function on the annotation values returned. |
|
209 | + * Do not confuse this "annotation_calculation" option with the |
|
210 | + * "calculation" option to elgg_get_entities_from_annotation_calculation(). |
|
211 | + * The "annotation_calculation" option causes this function to |
|
212 | + * return the result of performing a mathematical calculation on |
|
213 | + * all annotations that match the query instead of \ElggAnnotation |
|
214 | + * objects. |
|
215 | + * See the docs for elgg_get_entities_from_annotation_calculation() |
|
216 | + * for the proper use of the "calculation" option. |
|
217 | + * |
|
218 | + * |
|
219 | + * @return \ElggAnnotation[]|mixed |
|
220 | + */ |
|
221 | + function find(array $options = array()) { |
|
222 | 222 | |
223 | - // support shortcut of 'count' => true for 'annotation_calculation' => 'count' |
|
224 | - if (isset($options['count']) && $options['count']) { |
|
225 | - $options['annotation_calculation'] = 'count'; |
|
226 | - unset($options['count']); |
|
227 | - } |
|
223 | + // support shortcut of 'count' => true for 'annotation_calculation' => 'count' |
|
224 | + if (isset($options['count']) && $options['count']) { |
|
225 | + $options['annotation_calculation'] = 'count'; |
|
226 | + unset($options['count']); |
|
227 | + } |
|
228 | 228 | |
229 | - $options['metastring_type'] = 'annotations'; |
|
230 | - return _elgg_get_metastring_based_objects($options); |
|
231 | - } |
|
232 | - |
|
233 | - /** |
|
234 | - * Deletes annotations based on $options. |
|
235 | - * |
|
236 | - * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
|
237 | - * This requires at least one constraint: annotation_owner_guid(s), |
|
238 | - * annotation_name(s), annotation_value(s), or guid(s) must be set. |
|
239 | - * |
|
240 | - * @param array $options An options array. {@link elgg_get_annotations()} |
|
241 | - * @return bool|null true on success, false on failure, null if no annotations to delete. |
|
242 | - */ |
|
243 | - function deleteAll(array $options) { |
|
244 | - if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|
245 | - return false; |
|
246 | - } |
|
247 | - |
|
248 | - $options['metastring_type'] = 'annotations'; |
|
249 | - return _elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false); |
|
250 | - } |
|
251 | - |
|
252 | - /** |
|
253 | - * Disables annotations based on $options. |
|
254 | - * |
|
255 | - * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
|
256 | - * |
|
257 | - * @param array $options An options array. {@link elgg_get_annotations()} |
|
258 | - * @return bool|null true on success, false on failure, null if no annotations disabled. |
|
259 | - */ |
|
260 | - function disableAll(array $options) { |
|
261 | - if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|
262 | - return false; |
|
263 | - } |
|
229 | + $options['metastring_type'] = 'annotations'; |
|
230 | + return _elgg_get_metastring_based_objects($options); |
|
231 | + } |
|
232 | + |
|
233 | + /** |
|
234 | + * Deletes annotations based on $options. |
|
235 | + * |
|
236 | + * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
|
237 | + * This requires at least one constraint: annotation_owner_guid(s), |
|
238 | + * annotation_name(s), annotation_value(s), or guid(s) must be set. |
|
239 | + * |
|
240 | + * @param array $options An options array. {@link elgg_get_annotations()} |
|
241 | + * @return bool|null true on success, false on failure, null if no annotations to delete. |
|
242 | + */ |
|
243 | + function deleteAll(array $options) { |
|
244 | + if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|
245 | + return false; |
|
246 | + } |
|
247 | + |
|
248 | + $options['metastring_type'] = 'annotations'; |
|
249 | + return _elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false); |
|
250 | + } |
|
251 | + |
|
252 | + /** |
|
253 | + * Disables annotations based on $options. |
|
254 | + * |
|
255 | + * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
|
256 | + * |
|
257 | + * @param array $options An options array. {@link elgg_get_annotations()} |
|
258 | + * @return bool|null true on success, false on failure, null if no annotations disabled. |
|
259 | + */ |
|
260 | + function disableAll(array $options) { |
|
261 | + if (!_elgg_is_valid_options_for_batch_operation($options, 'annotation')) { |
|
262 | + return false; |
|
263 | + } |
|
264 | 264 | |
265 | - // if we can see hidden (disabled) we need to use the offset |
|
266 | - // otherwise we risk an infinite loop if there are more than 50 |
|
267 | - $inc_offset = access_get_show_hidden_status(); |
|
268 | - |
|
269 | - $options['metastring_type'] = 'annotations'; |
|
270 | - return _elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset); |
|
271 | - } |
|
272 | - |
|
273 | - /** |
|
274 | - * Enables annotations based on $options. |
|
275 | - * |
|
276 | - * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
|
277 | - * |
|
278 | - * @warning In order to enable annotations, you must first use |
|
279 | - * {@link access_show_hidden_entities()}. |
|
280 | - * |
|
281 | - * @param array $options An options array. {@link elgg_get_annotations()} |
|
282 | - * @return bool|null true on success, false on failure, null if no metadata enabled. |
|
283 | - */ |
|
284 | - function enableAll(array $options) { |
|
285 | - if (!$options || !is_array($options)) { |
|
286 | - return false; |
|
287 | - } |
|
288 | - |
|
289 | - $options['metastring_type'] = 'annotations'; |
|
290 | - return _elgg_batch_metastring_based_objects($options, 'elgg_batch_enable_callback'); |
|
291 | - } |
|
292 | - |
|
293 | - /** |
|
294 | - * Returns entities based upon annotations. Also accepts all options available |
|
295 | - * to elgg_get_entities() and elgg_get_entities_from_metadata(). |
|
296 | - * |
|
297 | - * @see elgg_get_entities |
|
298 | - * @see elgg_get_entities_from_metadata |
|
299 | - * |
|
300 | - * @param array $options Array in format: |
|
301 | - * |
|
302 | - * annotation_names => null|ARR annotations names |
|
303 | - * |
|
304 | - * annotation_values => null|ARR annotations values |
|
305 | - * |
|
306 | - * annotation_name_value_pairs => null|ARR (name = 'name', value => 'value', |
|
307 | - * 'operator' => '=', 'case_sensitive' => true) entries. |
|
308 | - * Currently if multiple values are sent via an array (value => array('value1', 'value2') |
|
309 | - * the pair's operator will be forced to "IN". |
|
310 | - * |
|
311 | - * annotation_name_value_pairs_operator => null|STR The operator to use for combining |
|
312 | - * (name = value) OPERATOR (name = value); default AND |
|
313 | - * |
|
314 | - * annotation_case_sensitive => BOOL Overall Case sensitive |
|
315 | - * |
|
316 | - * order_by_annotation => null|ARR (array('name' => 'annotation_text1', 'direction' => ASC|DESC, |
|
317 | - * 'as' => text|integer), |
|
318 | - * |
|
319 | - * Also supports array('name' => 'annotation_text1') |
|
320 | - * |
|
321 | - * annotation_owner_guids => null|ARR guids for annotaiton owners |
|
322 | - * |
|
323 | - * @return mixed If count, int. If not count, array. false on errors. |
|
324 | - */ |
|
325 | - function getEntities(array $options = array()) { |
|
326 | - $defaults = array( |
|
327 | - 'annotation_names' => ELGG_ENTITIES_ANY_VALUE, |
|
328 | - 'annotation_values' => ELGG_ENTITIES_ANY_VALUE, |
|
329 | - 'annotation_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
330 | - |
|
331 | - 'annotation_name_value_pairs_operator' => 'AND', |
|
332 | - 'annotation_case_sensitive' => true, |
|
333 | - 'order_by_annotation' => array(), |
|
334 | - |
|
335 | - 'annotation_created_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
336 | - 'annotation_created_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
337 | - |
|
338 | - 'annotation_owner_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
339 | - ); |
|
340 | - |
|
341 | - $options = array_merge($defaults, $options); |
|
342 | - |
|
343 | - $singulars = array('annotation_name', 'annotation_value', |
|
344 | - 'annotation_name_value_pair', 'annotation_owner_guid'); |
|
345 | - |
|
346 | - $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
347 | - $options = _elgg_entities_get_metastrings_options('annotation', $options); |
|
348 | - |
|
349 | - if (!$options) { |
|
350 | - return false; |
|
351 | - } |
|
352 | - |
|
353 | - // because of performance issues support for ordering by maxtime has been dropped |
|
354 | - // @see https://github.com/Elgg/Elgg/issues/6638 |
|
355 | - if (isset($options['order_by']) && preg_match('~\bmaxtime\b~i', $options['order_by'])) { |
|
356 | - // check if the user provided maxtime |
|
357 | - $deprecated = true; |
|
358 | - if (isset($options['selects'])) { |
|
359 | - $selects = $options['selects']; |
|
360 | - if (!is_array($selects)) { |
|
361 | - $selects = array($selects); |
|
362 | - } |
|
265 | + // if we can see hidden (disabled) we need to use the offset |
|
266 | + // otherwise we risk an infinite loop if there are more than 50 |
|
267 | + $inc_offset = access_get_show_hidden_status(); |
|
268 | + |
|
269 | + $options['metastring_type'] = 'annotations'; |
|
270 | + return _elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset); |
|
271 | + } |
|
272 | + |
|
273 | + /** |
|
274 | + * Enables annotations based on $options. |
|
275 | + * |
|
276 | + * @warning Unlike elgg_get_annotations() this will not accept an empty options array! |
|
277 | + * |
|
278 | + * @warning In order to enable annotations, you must first use |
|
279 | + * {@link access_show_hidden_entities()}. |
|
280 | + * |
|
281 | + * @param array $options An options array. {@link elgg_get_annotations()} |
|
282 | + * @return bool|null true on success, false on failure, null if no metadata enabled. |
|
283 | + */ |
|
284 | + function enableAll(array $options) { |
|
285 | + if (!$options || !is_array($options)) { |
|
286 | + return false; |
|
287 | + } |
|
288 | + |
|
289 | + $options['metastring_type'] = 'annotations'; |
|
290 | + return _elgg_batch_metastring_based_objects($options, 'elgg_batch_enable_callback'); |
|
291 | + } |
|
292 | + |
|
293 | + /** |
|
294 | + * Returns entities based upon annotations. Also accepts all options available |
|
295 | + * to elgg_get_entities() and elgg_get_entities_from_metadata(). |
|
296 | + * |
|
297 | + * @see elgg_get_entities |
|
298 | + * @see elgg_get_entities_from_metadata |
|
299 | + * |
|
300 | + * @param array $options Array in format: |
|
301 | + * |
|
302 | + * annotation_names => null|ARR annotations names |
|
303 | + * |
|
304 | + * annotation_values => null|ARR annotations values |
|
305 | + * |
|
306 | + * annotation_name_value_pairs => null|ARR (name = 'name', value => 'value', |
|
307 | + * 'operator' => '=', 'case_sensitive' => true) entries. |
|
308 | + * Currently if multiple values are sent via an array (value => array('value1', 'value2') |
|
309 | + * the pair's operator will be forced to "IN". |
|
310 | + * |
|
311 | + * annotation_name_value_pairs_operator => null|STR The operator to use for combining |
|
312 | + * (name = value) OPERATOR (name = value); default AND |
|
313 | + * |
|
314 | + * annotation_case_sensitive => BOOL Overall Case sensitive |
|
315 | + * |
|
316 | + * order_by_annotation => null|ARR (array('name' => 'annotation_text1', 'direction' => ASC|DESC, |
|
317 | + * 'as' => text|integer), |
|
318 | + * |
|
319 | + * Also supports array('name' => 'annotation_text1') |
|
320 | + * |
|
321 | + * annotation_owner_guids => null|ARR guids for annotaiton owners |
|
322 | + * |
|
323 | + * @return mixed If count, int. If not count, array. false on errors. |
|
324 | + */ |
|
325 | + function getEntities(array $options = array()) { |
|
326 | + $defaults = array( |
|
327 | + 'annotation_names' => ELGG_ENTITIES_ANY_VALUE, |
|
328 | + 'annotation_values' => ELGG_ENTITIES_ANY_VALUE, |
|
329 | + 'annotation_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
330 | + |
|
331 | + 'annotation_name_value_pairs_operator' => 'AND', |
|
332 | + 'annotation_case_sensitive' => true, |
|
333 | + 'order_by_annotation' => array(), |
|
334 | + |
|
335 | + 'annotation_created_time_lower' => ELGG_ENTITIES_ANY_VALUE, |
|
336 | + 'annotation_created_time_upper' => ELGG_ENTITIES_ANY_VALUE, |
|
337 | + |
|
338 | + 'annotation_owner_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
339 | + ); |
|
340 | + |
|
341 | + $options = array_merge($defaults, $options); |
|
342 | + |
|
343 | + $singulars = array('annotation_name', 'annotation_value', |
|
344 | + 'annotation_name_value_pair', 'annotation_owner_guid'); |
|
345 | + |
|
346 | + $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
347 | + $options = _elgg_entities_get_metastrings_options('annotation', $options); |
|
348 | + |
|
349 | + if (!$options) { |
|
350 | + return false; |
|
351 | + } |
|
352 | + |
|
353 | + // because of performance issues support for ordering by maxtime has been dropped |
|
354 | + // @see https://github.com/Elgg/Elgg/issues/6638 |
|
355 | + if (isset($options['order_by']) && preg_match('~\bmaxtime\b~i', $options['order_by'])) { |
|
356 | + // check if the user provided maxtime |
|
357 | + $deprecated = true; |
|
358 | + if (isset($options['selects'])) { |
|
359 | + $selects = $options['selects']; |
|
360 | + if (!is_array($selects)) { |
|
361 | + $selects = array($selects); |
|
362 | + } |
|
363 | 363 | |
364 | - foreach ($selects as $select) { |
|
365 | - if (preg_match('~\bmaxtime\b~i', $options['order_by'])) { |
|
366 | - $deprecated = false; |
|
367 | - break; |
|
368 | - } |
|
369 | - } |
|
370 | - } |
|
364 | + foreach ($selects as $select) { |
|
365 | + if (preg_match('~\bmaxtime\b~i', $options['order_by'])) { |
|
366 | + $deprecated = false; |
|
367 | + break; |
|
368 | + } |
|
369 | + } |
|
370 | + } |
|
371 | 371 | |
372 | - // the user didn't provide maxtime |
|
373 | - if ($deprecated) { |
|
374 | - // special sorting for annotations |
|
375 | - elgg_deprecated_notice(__FUNCTION__ . ": no longer orders by annotations by default. If you order" |
|
376 | - . " by maxtime, you must provide that column via \$options['selects']. See" |
|
377 | - . " https://github.com/Elgg/Elgg/issues/6638#issuecomment-41562034", "1.10"); |
|
372 | + // the user didn't provide maxtime |
|
373 | + if ($deprecated) { |
|
374 | + // special sorting for annotations |
|
375 | + elgg_deprecated_notice(__FUNCTION__ . ": no longer orders by annotations by default. If you order" |
|
376 | + . " by maxtime, you must provide that column via \$options['selects']. See" |
|
377 | + . " https://github.com/Elgg/Elgg/issues/6638#issuecomment-41562034", "1.10"); |
|
378 | 378 | |
379 | - $options['selects'][] = "MAX(n_table.time_created) AS maxtime"; |
|
380 | - $options['group_by'] = 'n_table.entity_guid'; |
|
381 | - } |
|
382 | - } |
|
379 | + $options['selects'][] = "MAX(n_table.time_created) AS maxtime"; |
|
380 | + $options['group_by'] = 'n_table.entity_guid'; |
|
381 | + } |
|
382 | + } |
|
383 | 383 | |
384 | - $time_wheres = _elgg_get_entity_time_where_sql('n_table', $options['annotation_created_time_upper'], |
|
385 | - $options['annotation_created_time_lower']); |
|
384 | + $time_wheres = _elgg_get_entity_time_where_sql('n_table', $options['annotation_created_time_upper'], |
|
385 | + $options['annotation_created_time_lower']); |
|
386 | 386 | |
387 | - if ($time_wheres) { |
|
388 | - $options['wheres'][] = $time_wheres; |
|
389 | - } |
|
390 | - |
|
391 | - return elgg_get_entities_from_metadata($options); |
|
392 | - } |
|
393 | - |
|
394 | - /** |
|
395 | - * Get entities ordered by a mathematical calculation on annotation values |
|
396 | - * |
|
397 | - * @tip Note that this function uses { @link elgg_get_annotations() } to return a list of entities ordered by a mathematical |
|
398 | - * calculation on annotation values, and { @link elgg_get_entities_from_annotations() } to return a count of entities |
|
399 | - * if $options['count'] is set to a truthy value |
|
400 | - * |
|
401 | - * @param array $options An options array: |
|
402 | - * 'calculation' => The calculation to use. Must be a valid MySQL function. |
|
403 | - * Defaults to sum. Result selected as 'annotation_calculation'. |
|
404 | - * Don't confuse this "calculation" option with the |
|
405 | - * "annotation_calculation" option to elgg_get_annotations(). |
|
406 | - * This "calculation" option is applied to each entity's set of |
|
407 | - * annotations and is selected as annotation_calculation for that row. |
|
408 | - * See the docs for elgg_get_annotations() for proper use of the |
|
409 | - * "annotation_calculation" option. |
|
410 | - * 'order_by' => The order for the sorting. Defaults to 'annotation_calculation desc'. |
|
411 | - * 'annotation_names' => The names of annotations on the entity. |
|
412 | - * 'annotation_values' => The values of annotations on the entity. |
|
413 | - * |
|
414 | - * 'metadata_names' => The name of metadata on the entity. |
|
415 | - * 'metadata_values' => The value of metadata on the entitiy. |
|
416 | - * 'callback' => Callback function to pass each row through. |
|
417 | - * @tip This function is different from other ege* functions, |
|
418 | - * as it uses a metastring-based getter function { @link elgg_get_annotations() }, |
|
419 | - * therefore the callback function should be a derivative of { @link entity_row_to_elggstar() } |
|
420 | - * and not of { @link row_to_annotation() } |
|
421 | - * |
|
422 | - * @return \ElggEntity[]|int An array or a count of entities |
|
423 | - * @see elgg_get_annotations() |
|
424 | - * @see elgg_get_entities_from_annotations() |
|
425 | - */ |
|
426 | - function getEntitiesFromCalculation($options) { |
|
387 | + if ($time_wheres) { |
|
388 | + $options['wheres'][] = $time_wheres; |
|
389 | + } |
|
390 | + |
|
391 | + return elgg_get_entities_from_metadata($options); |
|
392 | + } |
|
393 | + |
|
394 | + /** |
|
395 | + * Get entities ordered by a mathematical calculation on annotation values |
|
396 | + * |
|
397 | + * @tip Note that this function uses { @link elgg_get_annotations() } to return a list of entities ordered by a mathematical |
|
398 | + * calculation on annotation values, and { @link elgg_get_entities_from_annotations() } to return a count of entities |
|
399 | + * if $options['count'] is set to a truthy value |
|
400 | + * |
|
401 | + * @param array $options An options array: |
|
402 | + * 'calculation' => The calculation to use. Must be a valid MySQL function. |
|
403 | + * Defaults to sum. Result selected as 'annotation_calculation'. |
|
404 | + * Don't confuse this "calculation" option with the |
|
405 | + * "annotation_calculation" option to elgg_get_annotations(). |
|
406 | + * This "calculation" option is applied to each entity's set of |
|
407 | + * annotations and is selected as annotation_calculation for that row. |
|
408 | + * See the docs for elgg_get_annotations() for proper use of the |
|
409 | + * "annotation_calculation" option. |
|
410 | + * 'order_by' => The order for the sorting. Defaults to 'annotation_calculation desc'. |
|
411 | + * 'annotation_names' => The names of annotations on the entity. |
|
412 | + * 'annotation_values' => The values of annotations on the entity. |
|
413 | + * |
|
414 | + * 'metadata_names' => The name of metadata on the entity. |
|
415 | + * 'metadata_values' => The value of metadata on the entitiy. |
|
416 | + * 'callback' => Callback function to pass each row through. |
|
417 | + * @tip This function is different from other ege* functions, |
|
418 | + * as it uses a metastring-based getter function { @link elgg_get_annotations() }, |
|
419 | + * therefore the callback function should be a derivative of { @link entity_row_to_elggstar() } |
|
420 | + * and not of { @link row_to_annotation() } |
|
421 | + * |
|
422 | + * @return \ElggEntity[]|int An array or a count of entities |
|
423 | + * @see elgg_get_annotations() |
|
424 | + * @see elgg_get_entities_from_annotations() |
|
425 | + */ |
|
426 | + function getEntitiesFromCalculation($options) { |
|
427 | 427 | |
428 | - if (isset($options['count']) && $options['count']) { |
|
429 | - return elgg_get_entities_from_annotations($options); |
|
430 | - } |
|
428 | + if (isset($options['count']) && $options['count']) { |
|
429 | + return elgg_get_entities_from_annotations($options); |
|
430 | + } |
|
431 | 431 | |
432 | - $db_prefix = $this->db->prefix; |
|
433 | - $defaults = array( |
|
434 | - 'calculation' => 'sum', |
|
435 | - 'order_by' => 'annotation_calculation desc' |
|
436 | - ); |
|
432 | + $db_prefix = $this->db->prefix; |
|
433 | + $defaults = array( |
|
434 | + 'calculation' => 'sum', |
|
435 | + 'order_by' => 'annotation_calculation desc' |
|
436 | + ); |
|
437 | 437 | |
438 | - $options = array_merge($defaults, $options); |
|
438 | + $options = array_merge($defaults, $options); |
|
439 | 439 | |
440 | - $function = sanitize_string(elgg_extract('calculation', $options, 'sum', false)); |
|
440 | + $function = sanitize_string(elgg_extract('calculation', $options, 'sum', false)); |
|
441 | 441 | |
442 | - // you must cast this as an int or it sorts wrong. |
|
443 | - $options['selects'][] = 'e.*'; |
|
444 | - $options['selects'][] = "$function(CAST(a_msv.string AS signed)) AS annotation_calculation"; |
|
442 | + // you must cast this as an int or it sorts wrong. |
|
443 | + $options['selects'][] = 'e.*'; |
|
444 | + $options['selects'][] = "$function(CAST(a_msv.string AS signed)) AS annotation_calculation"; |
|
445 | 445 | |
446 | - // need our own join to get the values because the lower level functions don't |
|
447 | - // add all the joins if it's a different callback. |
|
448 | - $options['joins'][] = "JOIN {$db_prefix}metastrings a_msv ON n_table.value_id = a_msv.id"; |
|
446 | + // need our own join to get the values because the lower level functions don't |
|
447 | + // add all the joins if it's a different callback. |
|
448 | + $options['joins'][] = "JOIN {$db_prefix}metastrings a_msv ON n_table.value_id = a_msv.id"; |
|
449 | 449 | |
450 | - // don't need access control because it's taken care of by elgg_get_annotations. |
|
451 | - $options['group_by'] = 'n_table.entity_guid'; |
|
450 | + // don't need access control because it's taken care of by elgg_get_annotations. |
|
451 | + $options['group_by'] = 'n_table.entity_guid'; |
|
452 | 452 | |
453 | - // do not default to a callback function used in elgg_get_annotation() |
|
454 | - if (!isset($options['callback'])) { |
|
455 | - $options['callback'] = 'entity_row_to_elggstar'; |
|
456 | - } |
|
453 | + // do not default to a callback function used in elgg_get_annotation() |
|
454 | + if (!isset($options['callback'])) { |
|
455 | + $options['callback'] = 'entity_row_to_elggstar'; |
|
456 | + } |
|
457 | 457 | |
458 | - return elgg_get_annotations($options); |
|
459 | - } |
|
460 | - |
|
461 | - /** |
|
462 | - * Check to see if a user has already created an annotation on an object |
|
463 | - * |
|
464 | - * @param int $entity_guid Entity guid |
|
465 | - * @param string $annotation_type Type of annotation |
|
466 | - * @param int $owner_guid Defaults to logged in user. |
|
467 | - * |
|
468 | - * @return bool |
|
469 | - */ |
|
470 | - function exists($entity_guid, $annotation_type, $owner_guid = null) { |
|
458 | + return elgg_get_annotations($options); |
|
459 | + } |
|
460 | + |
|
461 | + /** |
|
462 | + * Check to see if a user has already created an annotation on an object |
|
463 | + * |
|
464 | + * @param int $entity_guid Entity guid |
|
465 | + * @param string $annotation_type Type of annotation |
|
466 | + * @param int $owner_guid Defaults to logged in user. |
|
467 | + * |
|
468 | + * @return bool |
|
469 | + */ |
|
470 | + function exists($entity_guid, $annotation_type, $owner_guid = null) { |
|
471 | 471 | |
472 | 472 | |
473 | - if (!$owner_guid && !($owner_guid = $this->session->getLoggedInUserGuid())) { |
|
474 | - return false; |
|
475 | - } |
|
473 | + if (!$owner_guid && !($owner_guid = $this->session->getLoggedInUserGuid())) { |
|
474 | + return false; |
|
475 | + } |
|
476 | 476 | |
477 | - $entity_guid = sanitize_int($entity_guid); |
|
478 | - $owner_guid = sanitize_int($owner_guid); |
|
479 | - $annotation_type = sanitize_string($annotation_type); |
|
477 | + $entity_guid = sanitize_int($entity_guid); |
|
478 | + $owner_guid = sanitize_int($owner_guid); |
|
479 | + $annotation_type = sanitize_string($annotation_type); |
|
480 | 480 | |
481 | - $sql = "SELECT a.id FROM {$this->db->prefix}annotations a" . |
|
482 | - " JOIN {$this->db->prefix}metastrings m ON a.name_id = m.id" . |
|
483 | - " WHERE a.owner_guid = $owner_guid AND a.entity_guid = $entity_guid" . |
|
484 | - " AND m.string = '$annotation_type'"; |
|
481 | + $sql = "SELECT a.id FROM {$this->db->prefix}annotations a" . |
|
482 | + " JOIN {$this->db->prefix}metastrings m ON a.name_id = m.id" . |
|
483 | + " WHERE a.owner_guid = $owner_guid AND a.entity_guid = $entity_guid" . |
|
484 | + " AND m.string = '$annotation_type'"; |
|
485 | 485 | |
486 | - if ($this->db->getDataRow($sql)) { |
|
487 | - return true; |
|
488 | - } |
|
486 | + if ($this->db->getDataRow($sql)) { |
|
487 | + return true; |
|
488 | + } |
|
489 | 489 | |
490 | - return false; |
|
491 | - } |
|
490 | + return false; |
|
491 | + } |
|
492 | 492 | } |
493 | 493 | \ No newline at end of file |
@@ -86,15 +86,15 @@ discard block |
||
86 | 86 | |
87 | 87 | $result = false; |
88 | 88 | |
89 | - $entity_guid = (int)$entity_guid; |
|
89 | + $entity_guid = (int) $entity_guid; |
|
90 | 90 | $value_type = \ElggExtender::detectValueType($value, $value_type); |
91 | 91 | |
92 | - $owner_guid = (int)$owner_guid; |
|
92 | + $owner_guid = (int) $owner_guid; |
|
93 | 93 | if ($owner_guid == 0) { |
94 | 94 | $owner_guid = $this->session->getLoggedInUserGuid(); |
95 | 95 | } |
96 | 96 | |
97 | - $access_id = (int)$access_id; |
|
97 | + $access_id = (int) $access_id; |
|
98 | 98 | $time = $this->getCurrentTime()->getTimestamp(); |
99 | 99 | |
100 | 100 | $value_id = elgg_get_metastring_id($value); |
@@ -146,7 +146,7 @@ discard block |
||
146 | 146 | function update($annotation_id, $name, $value, $value_type, $owner_guid, $access_id) { |
147 | 147 | |
148 | 148 | |
149 | - $annotation_id = (int)$annotation_id; |
|
149 | + $annotation_id = (int) $annotation_id; |
|
150 | 150 | |
151 | 151 | $annotation = $this->get($annotation_id); |
152 | 152 | if (!$annotation) { |
@@ -159,12 +159,12 @@ discard block |
||
159 | 159 | $name = trim($name); |
160 | 160 | $value_type = \ElggExtender::detectValueType($value, $value_type); |
161 | 161 | |
162 | - $owner_guid = (int)$owner_guid; |
|
162 | + $owner_guid = (int) $owner_guid; |
|
163 | 163 | if ($owner_guid == 0) { |
164 | 164 | $owner_guid = $this->session->getLoggedInUserGuid(); |
165 | 165 | } |
166 | 166 | |
167 | - $access_id = (int)$access_id; |
|
167 | + $access_id = (int) $access_id; |
|
168 | 168 | |
169 | 169 | $value_id = elgg_get_metastring_id($value); |
170 | 170 | if (!$value_id) { |
@@ -372,7 +372,7 @@ discard block |
||
372 | 372 | // the user didn't provide maxtime |
373 | 373 | if ($deprecated) { |
374 | 374 | // special sorting for annotations |
375 | - elgg_deprecated_notice(__FUNCTION__ . ": no longer orders by annotations by default. If you order" |
|
375 | + elgg_deprecated_notice(__FUNCTION__.": no longer orders by annotations by default. If you order" |
|
376 | 376 | . " by maxtime, you must provide that column via \$options['selects']. See" |
377 | 377 | . " https://github.com/Elgg/Elgg/issues/6638#issuecomment-41562034", "1.10"); |
378 | 378 | |
@@ -478,9 +478,9 @@ discard block |
||
478 | 478 | $owner_guid = sanitize_int($owner_guid); |
479 | 479 | $annotation_type = sanitize_string($annotation_type); |
480 | 480 | |
481 | - $sql = "SELECT a.id FROM {$this->db->prefix}annotations a" . |
|
482 | - " JOIN {$this->db->prefix}metastrings m ON a.name_id = m.id" . |
|
483 | - " WHERE a.owner_guid = $owner_guid AND a.entity_guid = $entity_guid" . |
|
481 | + $sql = "SELECT a.id FROM {$this->db->prefix}annotations a". |
|
482 | + " JOIN {$this->db->prefix}metastrings m ON a.name_id = m.id". |
|
483 | + " WHERE a.owner_guid = $owner_guid AND a.entity_guid = $entity_guid". |
|
484 | 484 | " AND m.string = '$annotation_type'"; |
485 | 485 | |
486 | 486 | if ($this->db->getDataRow($sql)) { |
@@ -76,7 +76,7 @@ discard block |
||
76 | 76 | * |
77 | 77 | * @param int $id The id of the metadata object being retrieved. |
78 | 78 | * |
79 | - * @return \ElggMetadata|false false if not found |
|
79 | + * @return \ElggExtender false if not found |
|
80 | 80 | */ |
81 | 81 | function get($id) { |
82 | 82 | return _elgg_get_metastring_based_object_from_id($id, 'metadata'); |
@@ -767,7 +767,7 @@ discard block |
||
767 | 767 | * |
768 | 768 | * @param int $id Metadata ID |
769 | 769 | * |
770 | - * @return mixed |
|
770 | + * @return string|false |
|
771 | 771 | */ |
772 | 772 | function getUrl($id) { |
773 | 773 | $extender = $this->get($id); |
@@ -817,7 +817,7 @@ discard block |
||
817 | 817 | * @param string $object_type The type of object |
818 | 818 | * @param \ElggEntity $object The entity itself |
819 | 819 | * |
820 | - * @return true |
|
820 | + * @return boolean |
|
821 | 821 | * @access private Set as private in 1.9.0 |
822 | 822 | */ |
823 | 823 | function handleUpdate($event, $object_type, $object) { |
@@ -20,233 +20,233 @@ discard block |
||
20 | 20 | */ |
21 | 21 | class MetadataTable { |
22 | 22 | |
23 | - use \Elgg\TimeUsing; |
|
23 | + use \Elgg\TimeUsing; |
|
24 | 24 | |
25 | - /** @var array */ |
|
26 | - protected $independents = array(); |
|
25 | + /** @var array */ |
|
26 | + protected $independents = array(); |
|
27 | 27 | |
28 | - /** @var Cache */ |
|
29 | - protected $cache; |
|
28 | + /** @var Cache */ |
|
29 | + protected $cache; |
|
30 | 30 | |
31 | - /** @var Database */ |
|
32 | - protected $db; |
|
31 | + /** @var Database */ |
|
32 | + protected $db; |
|
33 | 33 | |
34 | - /** @var EntityTable */ |
|
35 | - protected $entityTable; |
|
34 | + /** @var EntityTable */ |
|
35 | + protected $entityTable; |
|
36 | 36 | |
37 | - /** @var MetastringsTable */ |
|
38 | - protected $metastringsTable; |
|
37 | + /** @var MetastringsTable */ |
|
38 | + protected $metastringsTable; |
|
39 | 39 | |
40 | - /** @var Events */ |
|
41 | - protected $events; |
|
40 | + /** @var Events */ |
|
41 | + protected $events; |
|
42 | 42 | |
43 | - /** @var Session */ |
|
44 | - protected $session; |
|
43 | + /** @var Session */ |
|
44 | + protected $session; |
|
45 | 45 | |
46 | - /** @var string */ |
|
47 | - protected $table; |
|
46 | + /** @var string */ |
|
47 | + protected $table; |
|
48 | 48 | |
49 | - /** |
|
50 | - * Constructor |
|
51 | - * |
|
52 | - * @param Cache $cache A cache for this table |
|
53 | - * @param Database $db The Elgg database |
|
54 | - * @param EntityTable $entityTable The entities table |
|
55 | - * @param Events $events The events registry |
|
56 | - * @param MetastringsTable $metastringsTable The metastrings table |
|
57 | - * @param Session $session The session |
|
58 | - */ |
|
59 | - public function __construct( |
|
60 | - Cache $cache, |
|
61 | - Database $db, |
|
62 | - EntityTable $entityTable, |
|
63 | - Events $events, |
|
64 | - MetastringsTable $metastringsTable, |
|
65 | - Session $session) { |
|
66 | - $this->cache = $cache; |
|
67 | - $this->db = $db; |
|
68 | - $this->entityTable = $entityTable; |
|
69 | - $this->events = $events; |
|
70 | - $this->metastringsTable = $metastringsTable; |
|
71 | - $this->session = $session; |
|
72 | - $this->table = $this->db->prefix . "metadata"; |
|
73 | - } |
|
49 | + /** |
|
50 | + * Constructor |
|
51 | + * |
|
52 | + * @param Cache $cache A cache for this table |
|
53 | + * @param Database $db The Elgg database |
|
54 | + * @param EntityTable $entityTable The entities table |
|
55 | + * @param Events $events The events registry |
|
56 | + * @param MetastringsTable $metastringsTable The metastrings table |
|
57 | + * @param Session $session The session |
|
58 | + */ |
|
59 | + public function __construct( |
|
60 | + Cache $cache, |
|
61 | + Database $db, |
|
62 | + EntityTable $entityTable, |
|
63 | + Events $events, |
|
64 | + MetastringsTable $metastringsTable, |
|
65 | + Session $session) { |
|
66 | + $this->cache = $cache; |
|
67 | + $this->db = $db; |
|
68 | + $this->entityTable = $entityTable; |
|
69 | + $this->events = $events; |
|
70 | + $this->metastringsTable = $metastringsTable; |
|
71 | + $this->session = $session; |
|
72 | + $this->table = $this->db->prefix . "metadata"; |
|
73 | + } |
|
74 | 74 | |
75 | - /** |
|
76 | - * Get a specific metadata object by its id. |
|
77 | - * If you want multiple metadata objects, use |
|
78 | - * {@link elgg_get_metadata()}. |
|
79 | - * |
|
80 | - * @param int $id The id of the metadata object being retrieved. |
|
81 | - * |
|
82 | - * @return \ElggMetadata|false false if not found |
|
83 | - */ |
|
84 | - function get($id) { |
|
85 | - return _elgg_get_metastring_based_object_from_id($id, 'metadata'); |
|
86 | - } |
|
87 | - |
|
88 | - /** |
|
89 | - * Deletes metadata using its ID. |
|
90 | - * |
|
91 | - * @param int $id The metadata ID to delete. |
|
92 | - * @return bool |
|
93 | - */ |
|
94 | - function delete($id) { |
|
95 | - $metadata = $this->get($id); |
|
75 | + /** |
|
76 | + * Get a specific metadata object by its id. |
|
77 | + * If you want multiple metadata objects, use |
|
78 | + * {@link elgg_get_metadata()}. |
|
79 | + * |
|
80 | + * @param int $id The id of the metadata object being retrieved. |
|
81 | + * |
|
82 | + * @return \ElggMetadata|false false if not found |
|
83 | + */ |
|
84 | + function get($id) { |
|
85 | + return _elgg_get_metastring_based_object_from_id($id, 'metadata'); |
|
86 | + } |
|
87 | + |
|
88 | + /** |
|
89 | + * Deletes metadata using its ID. |
|
90 | + * |
|
91 | + * @param int $id The metadata ID to delete. |
|
92 | + * @return bool |
|
93 | + */ |
|
94 | + function delete($id) { |
|
95 | + $metadata = $this->get($id); |
|
96 | 96 | |
97 | - return $metadata ? $metadata->delete() : false; |
|
98 | - } |
|
99 | - |
|
100 | - /** |
|
101 | - * Create a new metadata object, or update an existing one. |
|
102 | - * |
|
103 | - * Metadata can be an array by setting allow_multiple to true, but it is an |
|
104 | - * indexed array with no control over the indexing. |
|
105 | - * |
|
106 | - * @param int $entity_guid The entity to attach the metadata to |
|
107 | - * @param string $name Name of the metadata |
|
108 | - * @param string $value Value of the metadata |
|
109 | - * @param string $value_type 'text', 'integer', or '' for automatic detection |
|
110 | - * @param int $owner_guid GUID of entity that owns the metadata. Default is logged in user. |
|
111 | - * @param int $access_id Default is ACCESS_PRIVATE |
|
112 | - * @param bool $allow_multiple Allow multiple values for one key. Default is false |
|
113 | - * |
|
114 | - * @return int|false id of metadata or false if failure |
|
115 | - */ |
|
116 | - function create($entity_guid, $name, $value, $value_type = '', $owner_guid = 0, |
|
117 | - $access_id = ACCESS_PRIVATE, $allow_multiple = false) { |
|
97 | + return $metadata ? $metadata->delete() : false; |
|
98 | + } |
|
99 | + |
|
100 | + /** |
|
101 | + * Create a new metadata object, or update an existing one. |
|
102 | + * |
|
103 | + * Metadata can be an array by setting allow_multiple to true, but it is an |
|
104 | + * indexed array with no control over the indexing. |
|
105 | + * |
|
106 | + * @param int $entity_guid The entity to attach the metadata to |
|
107 | + * @param string $name Name of the metadata |
|
108 | + * @param string $value Value of the metadata |
|
109 | + * @param string $value_type 'text', 'integer', or '' for automatic detection |
|
110 | + * @param int $owner_guid GUID of entity that owns the metadata. Default is logged in user. |
|
111 | + * @param int $access_id Default is ACCESS_PRIVATE |
|
112 | + * @param bool $allow_multiple Allow multiple values for one key. Default is false |
|
113 | + * |
|
114 | + * @return int|false id of metadata or false if failure |
|
115 | + */ |
|
116 | + function create($entity_guid, $name, $value, $value_type = '', $owner_guid = 0, |
|
117 | + $access_id = ACCESS_PRIVATE, $allow_multiple = false) { |
|
118 | 118 | |
119 | - $entity_guid = (int)$entity_guid; |
|
120 | - // name and value are encoded in add_metastring() |
|
121 | - $value_type = \ElggExtender::detectValueType($value, trim($value_type)); |
|
122 | - $time = $this->getCurrentTime()->getTimestamp(); |
|
123 | - $owner_guid = (int)$owner_guid; |
|
124 | - $allow_multiple = (boolean)$allow_multiple; |
|
119 | + $entity_guid = (int)$entity_guid; |
|
120 | + // name and value are encoded in add_metastring() |
|
121 | + $value_type = \ElggExtender::detectValueType($value, trim($value_type)); |
|
122 | + $time = $this->getCurrentTime()->getTimestamp(); |
|
123 | + $owner_guid = (int)$owner_guid; |
|
124 | + $allow_multiple = (boolean)$allow_multiple; |
|
125 | 125 | |
126 | - if (!isset($value)) { |
|
127 | - return false; |
|
128 | - } |
|
126 | + if (!isset($value)) { |
|
127 | + return false; |
|
128 | + } |
|
129 | 129 | |
130 | - if ($owner_guid == 0) { |
|
131 | - $owner_guid = $this->session->getLoggedInUserGuid(); |
|
132 | - } |
|
130 | + if ($owner_guid == 0) { |
|
131 | + $owner_guid = $this->session->getLoggedInUserGuid(); |
|
132 | + } |
|
133 | 133 | |
134 | - $access_id = (int) $access_id; |
|
134 | + $access_id = (int) $access_id; |
|
135 | 135 | |
136 | - $query = "SELECT * FROM {$this->table} |
|
136 | + $query = "SELECT * FROM {$this->table} |
|
137 | 137 | WHERE entity_guid = :entity_guid and name_id = :name_id LIMIT 1"; |
138 | 138 | |
139 | - $params = [ |
|
140 | - ':entity_guid' => $entity_guid, |
|
141 | - ':name_id' => $this->metastringsTable->getId($name) |
|
142 | - ]; |
|
139 | + $params = [ |
|
140 | + ':entity_guid' => $entity_guid, |
|
141 | + ':name_id' => $this->metastringsTable->getId($name) |
|
142 | + ]; |
|
143 | 143 | |
144 | - $existing = $this->db->getDataRow($query, null, $params); |
|
145 | - if ($existing && !$allow_multiple) { |
|
146 | - $id = (int)$existing->id; |
|
147 | - $result = $this->update($id, $name, $value, $value_type, $owner_guid, $access_id); |
|
148 | - |
|
149 | - if (!$result) { |
|
150 | - return false; |
|
151 | - } |
|
152 | - } else { |
|
153 | - // Support boolean types |
|
154 | - if (is_bool($value)) { |
|
155 | - $value = (int)$value; |
|
156 | - } |
|
157 | - |
|
158 | - // Add the metastrings |
|
159 | - $value_id = $this->metastringsTable->getId($value); |
|
160 | - if (!$value_id) { |
|
161 | - return false; |
|
162 | - } |
|
163 | - |
|
164 | - $name_id = $this->metastringsTable->getId($name); |
|
165 | - if (!$name_id) { |
|
166 | - return false; |
|
167 | - } |
|
168 | - |
|
169 | - // If ok then add it |
|
170 | - $query = "INSERT INTO {$this->table} |
|
144 | + $existing = $this->db->getDataRow($query, null, $params); |
|
145 | + if ($existing && !$allow_multiple) { |
|
146 | + $id = (int)$existing->id; |
|
147 | + $result = $this->update($id, $name, $value, $value_type, $owner_guid, $access_id); |
|
148 | + |
|
149 | + if (!$result) { |
|
150 | + return false; |
|
151 | + } |
|
152 | + } else { |
|
153 | + // Support boolean types |
|
154 | + if (is_bool($value)) { |
|
155 | + $value = (int)$value; |
|
156 | + } |
|
157 | + |
|
158 | + // Add the metastrings |
|
159 | + $value_id = $this->metastringsTable->getId($value); |
|
160 | + if (!$value_id) { |
|
161 | + return false; |
|
162 | + } |
|
163 | + |
|
164 | + $name_id = $this->metastringsTable->getId($name); |
|
165 | + if (!$name_id) { |
|
166 | + return false; |
|
167 | + } |
|
168 | + |
|
169 | + // If ok then add it |
|
170 | + $query = "INSERT INTO {$this->table} |
|
171 | 171 | (entity_guid, name_id, value_id, value_type, owner_guid, time_created, access_id) |
172 | 172 | VALUES (:entity_guid, :name_id, :value_id, :value_type, :owner_guid, :time_created, :access_id)"; |
173 | 173 | |
174 | - $params = [ |
|
175 | - ':entity_guid' => $entity_guid, |
|
176 | - ':name_id' => $name_id, |
|
177 | - ':value_id' => $value_id, |
|
178 | - ':value_type' => $value_type, |
|
179 | - ':owner_guid' => $owner_guid, |
|
180 | - ':time_created' => $time, |
|
181 | - ':access_id' => $access_id, |
|
182 | - ]; |
|
174 | + $params = [ |
|
175 | + ':entity_guid' => $entity_guid, |
|
176 | + ':name_id' => $name_id, |
|
177 | + ':value_id' => $value_id, |
|
178 | + ':value_type' => $value_type, |
|
179 | + ':owner_guid' => $owner_guid, |
|
180 | + ':time_created' => $time, |
|
181 | + ':access_id' => $access_id, |
|
182 | + ]; |
|
183 | 183 | |
184 | - $id = $this->db->insertData($query, $params); |
|
184 | + $id = $this->db->insertData($query, $params); |
|
185 | 185 | |
186 | - if ($id !== false) { |
|
187 | - $obj = $this->get($id); |
|
188 | - if ($this->events->trigger('create', 'metadata', $obj)) { |
|
186 | + if ($id !== false) { |
|
187 | + $obj = $this->get($id); |
|
188 | + if ($this->events->trigger('create', 'metadata', $obj)) { |
|
189 | 189 | |
190 | - $this->cache->clear($entity_guid); |
|
191 | - |
|
192 | - return $id; |
|
193 | - } else { |
|
194 | - $this->delete($id); |
|
195 | - } |
|
196 | - } |
|
197 | - } |
|
198 | - |
|
199 | - return $id; |
|
200 | - } |
|
201 | - |
|
202 | - /** |
|
203 | - * Update a specific piece of metadata. |
|
204 | - * |
|
205 | - * @param int $id ID of the metadata to update |
|
206 | - * @param string $name Metadata name |
|
207 | - * @param string $value Metadata value |
|
208 | - * @param string $value_type Value type |
|
209 | - * @param int $owner_guid Owner guid |
|
210 | - * @param int $access_id Access ID |
|
211 | - * |
|
212 | - * @return bool |
|
213 | - */ |
|
214 | - function update($id, $name, $value, $value_type, $owner_guid, $access_id) { |
|
215 | - $id = (int)$id; |
|
216 | - |
|
217 | - if (!$md = $this->get($id)) { |
|
218 | - return false; |
|
219 | - } |
|
220 | - if (!$md->canEdit()) { |
|
221 | - return false; |
|
222 | - } |
|
223 | - |
|
224 | - $value_type = \ElggExtender::detectValueType($value, trim($value_type)); |
|
225 | - |
|
226 | - $owner_guid = (int)$owner_guid; |
|
227 | - if ($owner_guid == 0) { |
|
228 | - $owner_guid = $this->session->getLoggedInUserGuid(); |
|
229 | - } |
|
230 | - |
|
231 | - $access_id = (int)$access_id; |
|
232 | - |
|
233 | - // Support boolean types (as integers) |
|
234 | - if (is_bool($value)) { |
|
235 | - $value = (int)$value; |
|
236 | - } |
|
237 | - |
|
238 | - $value_id = $this->metastringsTable->getId($value); |
|
239 | - if (!$value_id) { |
|
240 | - return false; |
|
241 | - } |
|
242 | - |
|
243 | - $name_id = $this->metastringsTable->getId($name); |
|
244 | - if (!$name_id) { |
|
245 | - return false; |
|
246 | - } |
|
247 | - |
|
248 | - // If ok then add it |
|
249 | - $query = "UPDATE {$this->table} |
|
190 | + $this->cache->clear($entity_guid); |
|
191 | + |
|
192 | + return $id; |
|
193 | + } else { |
|
194 | + $this->delete($id); |
|
195 | + } |
|
196 | + } |
|
197 | + } |
|
198 | + |
|
199 | + return $id; |
|
200 | + } |
|
201 | + |
|
202 | + /** |
|
203 | + * Update a specific piece of metadata. |
|
204 | + * |
|
205 | + * @param int $id ID of the metadata to update |
|
206 | + * @param string $name Metadata name |
|
207 | + * @param string $value Metadata value |
|
208 | + * @param string $value_type Value type |
|
209 | + * @param int $owner_guid Owner guid |
|
210 | + * @param int $access_id Access ID |
|
211 | + * |
|
212 | + * @return bool |
|
213 | + */ |
|
214 | + function update($id, $name, $value, $value_type, $owner_guid, $access_id) { |
|
215 | + $id = (int)$id; |
|
216 | + |
|
217 | + if (!$md = $this->get($id)) { |
|
218 | + return false; |
|
219 | + } |
|
220 | + if (!$md->canEdit()) { |
|
221 | + return false; |
|
222 | + } |
|
223 | + |
|
224 | + $value_type = \ElggExtender::detectValueType($value, trim($value_type)); |
|
225 | + |
|
226 | + $owner_guid = (int)$owner_guid; |
|
227 | + if ($owner_guid == 0) { |
|
228 | + $owner_guid = $this->session->getLoggedInUserGuid(); |
|
229 | + } |
|
230 | + |
|
231 | + $access_id = (int)$access_id; |
|
232 | + |
|
233 | + // Support boolean types (as integers) |
|
234 | + if (is_bool($value)) { |
|
235 | + $value = (int)$value; |
|
236 | + } |
|
237 | + |
|
238 | + $value_id = $this->metastringsTable->getId($value); |
|
239 | + if (!$value_id) { |
|
240 | + return false; |
|
241 | + } |
|
242 | + |
|
243 | + $name_id = $this->metastringsTable->getId($name); |
|
244 | + if (!$name_id) { |
|
245 | + return false; |
|
246 | + } |
|
247 | + |
|
248 | + // If ok then add it |
|
249 | + $query = "UPDATE {$this->table} |
|
250 | 250 | SET name_id = :name_id, |
251 | 251 | value_id = :value_id, |
252 | 252 | value_type = :value_type, |
@@ -254,603 +254,603 @@ discard block |
||
254 | 254 | owner_guid = :owner_guid |
255 | 255 | WHERE id = :id"; |
256 | 256 | |
257 | - $params = [ |
|
258 | - ':name_id' => $name_id, |
|
259 | - ':value_id' => $value_id, |
|
260 | - ':value_type' => $value_type, |
|
261 | - ':access_id' => $access_id, |
|
262 | - ':owner_guid' => $owner_guid, |
|
263 | - ':id' => $id, |
|
264 | - ]; |
|
257 | + $params = [ |
|
258 | + ':name_id' => $name_id, |
|
259 | + ':value_id' => $value_id, |
|
260 | + ':value_type' => $value_type, |
|
261 | + ':access_id' => $access_id, |
|
262 | + ':owner_guid' => $owner_guid, |
|
263 | + ':id' => $id, |
|
264 | + ]; |
|
265 | 265 | |
266 | - $result = $this->db->updateData($query, false, $params); |
|
266 | + $result = $this->db->updateData($query, false, $params); |
|
267 | 267 | |
268 | - if ($result !== false) { |
|
269 | - |
|
270 | - $this->cache->clear($md->entity_guid); |
|
271 | - |
|
272 | - // @todo this event tells you the metadata has been updated, but does not |
|
273 | - // let you do anything about it. What is needed is a plugin hook before |
|
274 | - // the update that passes old and new values. |
|
275 | - $obj = $this->get($id); |
|
276 | - $this->events->trigger('update', 'metadata', $obj); |
|
277 | - } |
|
278 | - |
|
279 | - return $result; |
|
280 | - } |
|
281 | - |
|
282 | - /** |
|
283 | - * This function creates metadata from an associative array of "key => value" pairs. |
|
284 | - * |
|
285 | - * To achieve an array for a single key, pass in the same key multiple times with |
|
286 | - * allow_multiple set to true. This creates an indexed array. It does not support |
|
287 | - * associative arrays and there is no guarantee on the ordering in the array. |
|
288 | - * |
|
289 | - * @param int $entity_guid The entity to attach the metadata to |
|
290 | - * @param array $name_and_values Associative array - a value can be a string, number, bool |
|
291 | - * @param string $value_type 'text', 'integer', or '' for automatic detection |
|
292 | - * @param int $owner_guid GUID of entity that owns the metadata |
|
293 | - * @param int $access_id Default is ACCESS_PRIVATE |
|
294 | - * @param bool $allow_multiple Allow multiple values for one key. Default is false |
|
295 | - * |
|
296 | - * @return bool |
|
297 | - */ |
|
298 | - function createFromArray($entity_guid, array $name_and_values, $value_type, $owner_guid, |
|
299 | - $access_id = ACCESS_PRIVATE, $allow_multiple = false) { |
|
300 | - |
|
301 | - foreach ($name_and_values as $k => $v) { |
|
302 | - $result = $this->create($entity_guid, $k, $v, $value_type, $owner_guid, |
|
303 | - $access_id, $allow_multiple); |
|
304 | - if (!$result) { |
|
305 | - return false; |
|
306 | - } |
|
307 | - } |
|
308 | - return true; |
|
309 | - } |
|
310 | - |
|
311 | - /** |
|
312 | - * Returns metadata. Accepts all elgg_get_entities() options for entity |
|
313 | - * restraints. |
|
314 | - * |
|
315 | - * @see elgg_get_entities |
|
316 | - * |
|
317 | - * @warning 1.7's find_metadata() didn't support limits and returned all metadata. |
|
318 | - * This function defaults to a limit of 25. There is probably not a reason |
|
319 | - * for you to return all metadata unless you're exporting an entity, |
|
320 | - * have other restraints in place, or are doing something horribly |
|
321 | - * wrong in your code. |
|
322 | - * |
|
323 | - * @param array $options Array in format: |
|
324 | - * |
|
325 | - * metadata_names => null|ARR metadata names |
|
326 | - * metadata_values => null|ARR metadata values |
|
327 | - * metadata_ids => null|ARR metadata ids |
|
328 | - * metadata_case_sensitive => BOOL Overall Case sensitive |
|
329 | - * metadata_owner_guids => null|ARR guids for metadata owners |
|
330 | - * metadata_created_time_lower => INT Lower limit for created time. |
|
331 | - * metadata_created_time_upper => INT Upper limit for created time. |
|
332 | - * metadata_calculation => STR Perform the MySQL function on the metadata values returned. |
|
333 | - * The "metadata_calculation" option causes this function to |
|
334 | - * return the result of performing a mathematical calculation on |
|
335 | - * all metadata that match the query instead of returning |
|
336 | - * \ElggMetadata objects. |
|
337 | - * |
|
338 | - * @return \ElggMetadata[]|mixed |
|
339 | - */ |
|
340 | - function getAll(array $options = array()) { |
|
341 | - |
|
342 | - // @todo remove support for count shortcut - see #4393 |
|
343 | - // support shortcut of 'count' => true for 'metadata_calculation' => 'count' |
|
344 | - if (isset($options['count']) && $options['count']) { |
|
345 | - $options['metadata_calculation'] = 'count'; |
|
346 | - unset($options['count']); |
|
347 | - } |
|
348 | - |
|
349 | - $options['metastring_type'] = 'metadata'; |
|
350 | - return _elgg_get_metastring_based_objects($options); |
|
351 | - } |
|
352 | - |
|
353 | - /** |
|
354 | - * Deletes metadata based on $options. |
|
355 | - * |
|
356 | - * @warning Unlike elgg_get_metadata() this will not accept an empty options array! |
|
357 | - * This requires at least one constraint: metadata_owner_guid(s), |
|
358 | - * metadata_name(s), metadata_value(s), or guid(s) must be set. |
|
359 | - * |
|
360 | - * @param array $options An options array. {@link elgg_get_metadata()} |
|
361 | - * @return bool|null true on success, false on failure, null if no metadata to delete. |
|
362 | - */ |
|
363 | - function deleteAll(array $options) { |
|
364 | - if (!_elgg_is_valid_options_for_batch_operation($options, 'metadata')) { |
|
365 | - return false; |
|
366 | - } |
|
367 | - $options['metastring_type'] = 'metadata'; |
|
368 | - $result = _elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false); |
|
369 | - |
|
370 | - // This moved last in case an object's constructor sets metadata. Currently the batch |
|
371 | - // delete process has to create the entity to delete its metadata. See #5214 |
|
372 | - $this->cache->invalidateByOptions($options); |
|
373 | - |
|
374 | - return $result; |
|
375 | - } |
|
376 | - |
|
377 | - /** |
|
378 | - * Disables metadata based on $options. |
|
379 | - * |
|
380 | - * @warning Unlike elgg_get_metadata() this will not accept an empty options array! |
|
381 | - * |
|
382 | - * @param array $options An options array. {@link elgg_get_metadata()} |
|
383 | - * @return bool|null true on success, false on failure, null if no metadata disabled. |
|
384 | - */ |
|
385 | - function disableAll(array $options) { |
|
386 | - if (!_elgg_is_valid_options_for_batch_operation($options, 'metadata')) { |
|
387 | - return false; |
|
388 | - } |
|
389 | - |
|
390 | - $this->cache->invalidateByOptions($options); |
|
391 | - |
|
392 | - // if we can see hidden (disabled) we need to use the offset |
|
393 | - // otherwise we risk an infinite loop if there are more than 50 |
|
394 | - $inc_offset = access_get_show_hidden_status(); |
|
395 | - |
|
396 | - $options['metastring_type'] = 'metadata'; |
|
397 | - return _elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset); |
|
398 | - } |
|
399 | - |
|
400 | - /** |
|
401 | - * Enables metadata based on $options. |
|
402 | - * |
|
403 | - * @warning Unlike elgg_get_metadata() this will not accept an empty options array! |
|
404 | - * |
|
405 | - * @warning In order to enable metadata, you must first use |
|
406 | - * {@link access_show_hidden_entities()}. |
|
407 | - * |
|
408 | - * @param array $options An options array. {@link elgg_get_metadata()} |
|
409 | - * @return bool|null true on success, false on failure, null if no metadata enabled. |
|
410 | - */ |
|
411 | - function enableAll(array $options) { |
|
412 | - if (!$options || !is_array($options)) { |
|
413 | - return false; |
|
414 | - } |
|
415 | - |
|
416 | - $this->cache->invalidateByOptions($options); |
|
417 | - |
|
418 | - $options['metastring_type'] = 'metadata'; |
|
419 | - return _elgg_batch_metastring_based_objects($options, 'elgg_batch_enable_callback'); |
|
420 | - } |
|
421 | - |
|
422 | - /** |
|
423 | - * Returns entities based upon metadata. Also accepts all |
|
424 | - * options available to elgg_get_entities(). Supports |
|
425 | - * the singular option shortcut. |
|
426 | - * |
|
427 | - * @note Using metadata_names and metadata_values results in a |
|
428 | - * "names IN (...) AND values IN (...)" clause. This is subtly |
|
429 | - * differently than default multiple metadata_name_value_pairs, which use |
|
430 | - * "(name = value) AND (name = value)" clauses. |
|
431 | - * |
|
432 | - * When in doubt, use name_value_pairs. |
|
433 | - * |
|
434 | - * To ask for entities that do not have a metadata value, use a custom |
|
435 | - * where clause like this: |
|
436 | - * |
|
437 | - * $options['wheres'][] = "NOT EXISTS ( |
|
438 | - * SELECT 1 FROM {$dbprefix}metadata md |
|
439 | - * WHERE md.entity_guid = e.guid |
|
440 | - * AND md.name_id = $name_metastring_id |
|
441 | - * AND md.value_id = $value_metastring_id)"; |
|
442 | - * |
|
443 | - * Note the metadata name and value has been denormalized in the above example. |
|
444 | - * |
|
445 | - * @see elgg_get_entities |
|
446 | - * |
|
447 | - * @param array $options Array in format: |
|
448 | - * |
|
449 | - * metadata_names => null|ARR metadata names |
|
450 | - * |
|
451 | - * metadata_values => null|ARR metadata values |
|
452 | - * |
|
453 | - * metadata_name_value_pairs => null|ARR ( |
|
454 | - * name => 'name', |
|
455 | - * value => 'value', |
|
456 | - * 'operand' => '=', |
|
457 | - * 'case_sensitive' => true |
|
458 | - * ) |
|
459 | - * Currently if multiple values are sent via |
|
460 | - * an array (value => array('value1', 'value2') |
|
461 | - * the pair's operand will be forced to "IN". |
|
462 | - * If passing "IN" as the operand and a string as the value, |
|
463 | - * the value must be a properly quoted and escaped string. |
|
464 | - * |
|
465 | - * metadata_name_value_pairs_operator => null|STR The operator to use for combining |
|
466 | - * (name = value) OPERATOR (name = value); default AND |
|
467 | - * |
|
468 | - * metadata_case_sensitive => BOOL Overall Case sensitive |
|
469 | - * |
|
470 | - * order_by_metadata => null|ARR array( |
|
471 | - * 'name' => 'metadata_text1', |
|
472 | - * 'direction' => ASC|DESC, |
|
473 | - * 'as' => text|integer |
|
474 | - * ) |
|
475 | - * Also supports array('name' => 'metadata_text1') |
|
476 | - * |
|
477 | - * metadata_owner_guids => null|ARR guids for metadata owners |
|
478 | - * |
|
479 | - * @return \ElggEntity[]|mixed If count, int. If not count, array. false on errors. |
|
480 | - */ |
|
481 | - function getEntities(array $options = array()) { |
|
482 | - $defaults = array( |
|
483 | - 'metadata_names' => ELGG_ENTITIES_ANY_VALUE, |
|
484 | - 'metadata_values' => ELGG_ENTITIES_ANY_VALUE, |
|
485 | - 'metadata_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
486 | - |
|
487 | - 'metadata_name_value_pairs_operator' => 'AND', |
|
488 | - 'metadata_case_sensitive' => true, |
|
489 | - 'order_by_metadata' => array(), |
|
490 | - |
|
491 | - 'metadata_owner_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
492 | - ); |
|
493 | - |
|
494 | - $options = array_merge($defaults, $options); |
|
495 | - |
|
496 | - $singulars = array('metadata_name', 'metadata_value', |
|
497 | - 'metadata_name_value_pair', 'metadata_owner_guid'); |
|
498 | - |
|
499 | - $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
500 | - |
|
501 | - if (!$options = _elgg_entities_get_metastrings_options('metadata', $options)) { |
|
502 | - return false; |
|
503 | - } |
|
504 | - |
|
505 | - return $this->entityTable->getEntities($options); |
|
506 | - } |
|
507 | - |
|
508 | - /** |
|
509 | - * Returns metadata name and value SQL where for entities. |
|
510 | - * NB: $names and $values are not paired. Use $pairs for this. |
|
511 | - * Pairs default to '=' operand. |
|
512 | - * |
|
513 | - * This function is reused for annotations because the tables are |
|
514 | - * exactly the same. |
|
515 | - * |
|
516 | - * @param string $e_table Entities table name |
|
517 | - * @param string $n_table Normalized metastrings table name (Where entities, |
|
518 | - * values, and names are joined. annotations / metadata) |
|
519 | - * @param array|null $names Array of names |
|
520 | - * @param array|null $values Array of values |
|
521 | - * @param array|null $pairs Array of names / values / operands |
|
522 | - * @param string $pair_operator ("AND" or "OR") Operator to use to join the where clauses for pairs |
|
523 | - * @param bool $case_sensitive Case sensitive metadata names? |
|
524 | - * @param array|null $order_by_metadata Array of names / direction |
|
525 | - * @param array|null $owner_guids Array of owner GUIDs |
|
526 | - * |
|
527 | - * @return false|array False on fail, array('joins', 'wheres') |
|
528 | - * @access private |
|
529 | - */ |
|
530 | - function getEntityMetadataWhereSql($e_table, $n_table, $names = null, $values = null, |
|
531 | - $pairs = null, $pair_operator = 'AND', $case_sensitive = true, $order_by_metadata = null, |
|
532 | - $owner_guids = null) { |
|
533 | - // short circuit if nothing requested |
|
534 | - // 0 is a valid (if not ill-conceived) metadata name. |
|
535 | - // 0 is also a valid metadata value for false, null, or 0 |
|
536 | - // 0 is also a valid(ish) owner_guid |
|
537 | - if ((!$names && $names !== 0) |
|
538 | - && (!$values && $values !== 0) |
|
539 | - && (!$pairs && $pairs !== 0) |
|
540 | - && (!$owner_guids && $owner_guids !== 0) |
|
541 | - && !$order_by_metadata) { |
|
542 | - return ''; |
|
543 | - } |
|
544 | - |
|
545 | - // join counter for incremental joins. |
|
546 | - $i = 1; |
|
547 | - |
|
548 | - // binary forces byte-to-byte comparision of strings, making |
|
549 | - // it case- and diacritical-mark- sensitive. |
|
550 | - // only supported on values. |
|
551 | - $binary = ($case_sensitive) ? ' BINARY ' : ''; |
|
552 | - |
|
553 | - $access = _elgg_get_access_where_sql(array( |
|
554 | - 'table_alias' => 'n_table', |
|
555 | - 'guid_column' => 'entity_guid', |
|
556 | - )); |
|
557 | - |
|
558 | - $return = array ( |
|
559 | - 'joins' => array (), |
|
560 | - 'wheres' => array(), |
|
561 | - 'orders' => array() |
|
562 | - ); |
|
563 | - |
|
564 | - // will always want to join these tables if pulling metastrings. |
|
565 | - $return['joins'][] = "JOIN {$this->db->prefix}{$n_table} n_table on |
|
268 | + if ($result !== false) { |
|
269 | + |
|
270 | + $this->cache->clear($md->entity_guid); |
|
271 | + |
|
272 | + // @todo this event tells you the metadata has been updated, but does not |
|
273 | + // let you do anything about it. What is needed is a plugin hook before |
|
274 | + // the update that passes old and new values. |
|
275 | + $obj = $this->get($id); |
|
276 | + $this->events->trigger('update', 'metadata', $obj); |
|
277 | + } |
|
278 | + |
|
279 | + return $result; |
|
280 | + } |
|
281 | + |
|
282 | + /** |
|
283 | + * This function creates metadata from an associative array of "key => value" pairs. |
|
284 | + * |
|
285 | + * To achieve an array for a single key, pass in the same key multiple times with |
|
286 | + * allow_multiple set to true. This creates an indexed array. It does not support |
|
287 | + * associative arrays and there is no guarantee on the ordering in the array. |
|
288 | + * |
|
289 | + * @param int $entity_guid The entity to attach the metadata to |
|
290 | + * @param array $name_and_values Associative array - a value can be a string, number, bool |
|
291 | + * @param string $value_type 'text', 'integer', or '' for automatic detection |
|
292 | + * @param int $owner_guid GUID of entity that owns the metadata |
|
293 | + * @param int $access_id Default is ACCESS_PRIVATE |
|
294 | + * @param bool $allow_multiple Allow multiple values for one key. Default is false |
|
295 | + * |
|
296 | + * @return bool |
|
297 | + */ |
|
298 | + function createFromArray($entity_guid, array $name_and_values, $value_type, $owner_guid, |
|
299 | + $access_id = ACCESS_PRIVATE, $allow_multiple = false) { |
|
300 | + |
|
301 | + foreach ($name_and_values as $k => $v) { |
|
302 | + $result = $this->create($entity_guid, $k, $v, $value_type, $owner_guid, |
|
303 | + $access_id, $allow_multiple); |
|
304 | + if (!$result) { |
|
305 | + return false; |
|
306 | + } |
|
307 | + } |
|
308 | + return true; |
|
309 | + } |
|
310 | + |
|
311 | + /** |
|
312 | + * Returns metadata. Accepts all elgg_get_entities() options for entity |
|
313 | + * restraints. |
|
314 | + * |
|
315 | + * @see elgg_get_entities |
|
316 | + * |
|
317 | + * @warning 1.7's find_metadata() didn't support limits and returned all metadata. |
|
318 | + * This function defaults to a limit of 25. There is probably not a reason |
|
319 | + * for you to return all metadata unless you're exporting an entity, |
|
320 | + * have other restraints in place, or are doing something horribly |
|
321 | + * wrong in your code. |
|
322 | + * |
|
323 | + * @param array $options Array in format: |
|
324 | + * |
|
325 | + * metadata_names => null|ARR metadata names |
|
326 | + * metadata_values => null|ARR metadata values |
|
327 | + * metadata_ids => null|ARR metadata ids |
|
328 | + * metadata_case_sensitive => BOOL Overall Case sensitive |
|
329 | + * metadata_owner_guids => null|ARR guids for metadata owners |
|
330 | + * metadata_created_time_lower => INT Lower limit for created time. |
|
331 | + * metadata_created_time_upper => INT Upper limit for created time. |
|
332 | + * metadata_calculation => STR Perform the MySQL function on the metadata values returned. |
|
333 | + * The "metadata_calculation" option causes this function to |
|
334 | + * return the result of performing a mathematical calculation on |
|
335 | + * all metadata that match the query instead of returning |
|
336 | + * \ElggMetadata objects. |
|
337 | + * |
|
338 | + * @return \ElggMetadata[]|mixed |
|
339 | + */ |
|
340 | + function getAll(array $options = array()) { |
|
341 | + |
|
342 | + // @todo remove support for count shortcut - see #4393 |
|
343 | + // support shortcut of 'count' => true for 'metadata_calculation' => 'count' |
|
344 | + if (isset($options['count']) && $options['count']) { |
|
345 | + $options['metadata_calculation'] = 'count'; |
|
346 | + unset($options['count']); |
|
347 | + } |
|
348 | + |
|
349 | + $options['metastring_type'] = 'metadata'; |
|
350 | + return _elgg_get_metastring_based_objects($options); |
|
351 | + } |
|
352 | + |
|
353 | + /** |
|
354 | + * Deletes metadata based on $options. |
|
355 | + * |
|
356 | + * @warning Unlike elgg_get_metadata() this will not accept an empty options array! |
|
357 | + * This requires at least one constraint: metadata_owner_guid(s), |
|
358 | + * metadata_name(s), metadata_value(s), or guid(s) must be set. |
|
359 | + * |
|
360 | + * @param array $options An options array. {@link elgg_get_metadata()} |
|
361 | + * @return bool|null true on success, false on failure, null if no metadata to delete. |
|
362 | + */ |
|
363 | + function deleteAll(array $options) { |
|
364 | + if (!_elgg_is_valid_options_for_batch_operation($options, 'metadata')) { |
|
365 | + return false; |
|
366 | + } |
|
367 | + $options['metastring_type'] = 'metadata'; |
|
368 | + $result = _elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false); |
|
369 | + |
|
370 | + // This moved last in case an object's constructor sets metadata. Currently the batch |
|
371 | + // delete process has to create the entity to delete its metadata. See #5214 |
|
372 | + $this->cache->invalidateByOptions($options); |
|
373 | + |
|
374 | + return $result; |
|
375 | + } |
|
376 | + |
|
377 | + /** |
|
378 | + * Disables metadata based on $options. |
|
379 | + * |
|
380 | + * @warning Unlike elgg_get_metadata() this will not accept an empty options array! |
|
381 | + * |
|
382 | + * @param array $options An options array. {@link elgg_get_metadata()} |
|
383 | + * @return bool|null true on success, false on failure, null if no metadata disabled. |
|
384 | + */ |
|
385 | + function disableAll(array $options) { |
|
386 | + if (!_elgg_is_valid_options_for_batch_operation($options, 'metadata')) { |
|
387 | + return false; |
|
388 | + } |
|
389 | + |
|
390 | + $this->cache->invalidateByOptions($options); |
|
391 | + |
|
392 | + // if we can see hidden (disabled) we need to use the offset |
|
393 | + // otherwise we risk an infinite loop if there are more than 50 |
|
394 | + $inc_offset = access_get_show_hidden_status(); |
|
395 | + |
|
396 | + $options['metastring_type'] = 'metadata'; |
|
397 | + return _elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset); |
|
398 | + } |
|
399 | + |
|
400 | + /** |
|
401 | + * Enables metadata based on $options. |
|
402 | + * |
|
403 | + * @warning Unlike elgg_get_metadata() this will not accept an empty options array! |
|
404 | + * |
|
405 | + * @warning In order to enable metadata, you must first use |
|
406 | + * {@link access_show_hidden_entities()}. |
|
407 | + * |
|
408 | + * @param array $options An options array. {@link elgg_get_metadata()} |
|
409 | + * @return bool|null true on success, false on failure, null if no metadata enabled. |
|
410 | + */ |
|
411 | + function enableAll(array $options) { |
|
412 | + if (!$options || !is_array($options)) { |
|
413 | + return false; |
|
414 | + } |
|
415 | + |
|
416 | + $this->cache->invalidateByOptions($options); |
|
417 | + |
|
418 | + $options['metastring_type'] = 'metadata'; |
|
419 | + return _elgg_batch_metastring_based_objects($options, 'elgg_batch_enable_callback'); |
|
420 | + } |
|
421 | + |
|
422 | + /** |
|
423 | + * Returns entities based upon metadata. Also accepts all |
|
424 | + * options available to elgg_get_entities(). Supports |
|
425 | + * the singular option shortcut. |
|
426 | + * |
|
427 | + * @note Using metadata_names and metadata_values results in a |
|
428 | + * "names IN (...) AND values IN (...)" clause. This is subtly |
|
429 | + * differently than default multiple metadata_name_value_pairs, which use |
|
430 | + * "(name = value) AND (name = value)" clauses. |
|
431 | + * |
|
432 | + * When in doubt, use name_value_pairs. |
|
433 | + * |
|
434 | + * To ask for entities that do not have a metadata value, use a custom |
|
435 | + * where clause like this: |
|
436 | + * |
|
437 | + * $options['wheres'][] = "NOT EXISTS ( |
|
438 | + * SELECT 1 FROM {$dbprefix}metadata md |
|
439 | + * WHERE md.entity_guid = e.guid |
|
440 | + * AND md.name_id = $name_metastring_id |
|
441 | + * AND md.value_id = $value_metastring_id)"; |
|
442 | + * |
|
443 | + * Note the metadata name and value has been denormalized in the above example. |
|
444 | + * |
|
445 | + * @see elgg_get_entities |
|
446 | + * |
|
447 | + * @param array $options Array in format: |
|
448 | + * |
|
449 | + * metadata_names => null|ARR metadata names |
|
450 | + * |
|
451 | + * metadata_values => null|ARR metadata values |
|
452 | + * |
|
453 | + * metadata_name_value_pairs => null|ARR ( |
|
454 | + * name => 'name', |
|
455 | + * value => 'value', |
|
456 | + * 'operand' => '=', |
|
457 | + * 'case_sensitive' => true |
|
458 | + * ) |
|
459 | + * Currently if multiple values are sent via |
|
460 | + * an array (value => array('value1', 'value2') |
|
461 | + * the pair's operand will be forced to "IN". |
|
462 | + * If passing "IN" as the operand and a string as the value, |
|
463 | + * the value must be a properly quoted and escaped string. |
|
464 | + * |
|
465 | + * metadata_name_value_pairs_operator => null|STR The operator to use for combining |
|
466 | + * (name = value) OPERATOR (name = value); default AND |
|
467 | + * |
|
468 | + * metadata_case_sensitive => BOOL Overall Case sensitive |
|
469 | + * |
|
470 | + * order_by_metadata => null|ARR array( |
|
471 | + * 'name' => 'metadata_text1', |
|
472 | + * 'direction' => ASC|DESC, |
|
473 | + * 'as' => text|integer |
|
474 | + * ) |
|
475 | + * Also supports array('name' => 'metadata_text1') |
|
476 | + * |
|
477 | + * metadata_owner_guids => null|ARR guids for metadata owners |
|
478 | + * |
|
479 | + * @return \ElggEntity[]|mixed If count, int. If not count, array. false on errors. |
|
480 | + */ |
|
481 | + function getEntities(array $options = array()) { |
|
482 | + $defaults = array( |
|
483 | + 'metadata_names' => ELGG_ENTITIES_ANY_VALUE, |
|
484 | + 'metadata_values' => ELGG_ENTITIES_ANY_VALUE, |
|
485 | + 'metadata_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, |
|
486 | + |
|
487 | + 'metadata_name_value_pairs_operator' => 'AND', |
|
488 | + 'metadata_case_sensitive' => true, |
|
489 | + 'order_by_metadata' => array(), |
|
490 | + |
|
491 | + 'metadata_owner_guids' => ELGG_ENTITIES_ANY_VALUE, |
|
492 | + ); |
|
493 | + |
|
494 | + $options = array_merge($defaults, $options); |
|
495 | + |
|
496 | + $singulars = array('metadata_name', 'metadata_value', |
|
497 | + 'metadata_name_value_pair', 'metadata_owner_guid'); |
|
498 | + |
|
499 | + $options = _elgg_normalize_plural_options_array($options, $singulars); |
|
500 | + |
|
501 | + if (!$options = _elgg_entities_get_metastrings_options('metadata', $options)) { |
|
502 | + return false; |
|
503 | + } |
|
504 | + |
|
505 | + return $this->entityTable->getEntities($options); |
|
506 | + } |
|
507 | + |
|
508 | + /** |
|
509 | + * Returns metadata name and value SQL where for entities. |
|
510 | + * NB: $names and $values are not paired. Use $pairs for this. |
|
511 | + * Pairs default to '=' operand. |
|
512 | + * |
|
513 | + * This function is reused for annotations because the tables are |
|
514 | + * exactly the same. |
|
515 | + * |
|
516 | + * @param string $e_table Entities table name |
|
517 | + * @param string $n_table Normalized metastrings table name (Where entities, |
|
518 | + * values, and names are joined. annotations / metadata) |
|
519 | + * @param array|null $names Array of names |
|
520 | + * @param array|null $values Array of values |
|
521 | + * @param array|null $pairs Array of names / values / operands |
|
522 | + * @param string $pair_operator ("AND" or "OR") Operator to use to join the where clauses for pairs |
|
523 | + * @param bool $case_sensitive Case sensitive metadata names? |
|
524 | + * @param array|null $order_by_metadata Array of names / direction |
|
525 | + * @param array|null $owner_guids Array of owner GUIDs |
|
526 | + * |
|
527 | + * @return false|array False on fail, array('joins', 'wheres') |
|
528 | + * @access private |
|
529 | + */ |
|
530 | + function getEntityMetadataWhereSql($e_table, $n_table, $names = null, $values = null, |
|
531 | + $pairs = null, $pair_operator = 'AND', $case_sensitive = true, $order_by_metadata = null, |
|
532 | + $owner_guids = null) { |
|
533 | + // short circuit if nothing requested |
|
534 | + // 0 is a valid (if not ill-conceived) metadata name. |
|
535 | + // 0 is also a valid metadata value for false, null, or 0 |
|
536 | + // 0 is also a valid(ish) owner_guid |
|
537 | + if ((!$names && $names !== 0) |
|
538 | + && (!$values && $values !== 0) |
|
539 | + && (!$pairs && $pairs !== 0) |
|
540 | + && (!$owner_guids && $owner_guids !== 0) |
|
541 | + && !$order_by_metadata) { |
|
542 | + return ''; |
|
543 | + } |
|
544 | + |
|
545 | + // join counter for incremental joins. |
|
546 | + $i = 1; |
|
547 | + |
|
548 | + // binary forces byte-to-byte comparision of strings, making |
|
549 | + // it case- and diacritical-mark- sensitive. |
|
550 | + // only supported on values. |
|
551 | + $binary = ($case_sensitive) ? ' BINARY ' : ''; |
|
552 | + |
|
553 | + $access = _elgg_get_access_where_sql(array( |
|
554 | + 'table_alias' => 'n_table', |
|
555 | + 'guid_column' => 'entity_guid', |
|
556 | + )); |
|
557 | + |
|
558 | + $return = array ( |
|
559 | + 'joins' => array (), |
|
560 | + 'wheres' => array(), |
|
561 | + 'orders' => array() |
|
562 | + ); |
|
563 | + |
|
564 | + // will always want to join these tables if pulling metastrings. |
|
565 | + $return['joins'][] = "JOIN {$this->db->prefix}{$n_table} n_table on |
|
566 | 566 | {$e_table}.guid = n_table.entity_guid"; |
567 | 567 | |
568 | - $wheres = array(); |
|
569 | - |
|
570 | - // get names wheres and joins |
|
571 | - $names_where = ''; |
|
572 | - if ($names !== null) { |
|
573 | - if (!is_array($names)) { |
|
574 | - $names = array($names); |
|
575 | - } |
|
576 | - |
|
577 | - $sanitised_names = array(); |
|
578 | - foreach ($names as $name) { |
|
579 | - // normalise to 0. |
|
580 | - if (!$name) { |
|
581 | - $name = '0'; |
|
582 | - } |
|
583 | - $sanitised_names[] = '\'' . $this->db->sanitizeString($name) . '\''; |
|
584 | - } |
|
585 | - |
|
586 | - if ($names_str = implode(',', $sanitised_names)) { |
|
587 | - $return['joins'][] = "JOIN {$this->metastringsTable->getTableName()} msn on n_table.name_id = msn.id"; |
|
588 | - $names_where = "(msn.string IN ($names_str))"; |
|
589 | - } |
|
590 | - } |
|
591 | - |
|
592 | - // get values wheres and joins |
|
593 | - $values_where = ''; |
|
594 | - if ($values !== null) { |
|
595 | - if (!is_array($values)) { |
|
596 | - $values = array($values); |
|
597 | - } |
|
598 | - |
|
599 | - $sanitised_values = array(); |
|
600 | - foreach ($values as $value) { |
|
601 | - // normalize to 0 |
|
602 | - if (!$value) { |
|
603 | - $value = 0; |
|
604 | - } |
|
605 | - $sanitised_values[] = '\'' . $this->db->sanitizeString($value) . '\''; |
|
606 | - } |
|
607 | - |
|
608 | - if ($values_str = implode(',', $sanitised_values)) { |
|
609 | - $return['joins'][] = "JOIN {$this->metastringsTable->getTableName()} msv on n_table.value_id = msv.id"; |
|
610 | - $values_where = "({$binary}msv.string IN ($values_str))"; |
|
611 | - } |
|
612 | - } |
|
613 | - |
|
614 | - if ($names_where && $values_where) { |
|
615 | - $wheres[] = "($names_where AND $values_where AND $access)"; |
|
616 | - } elseif ($names_where) { |
|
617 | - $wheres[] = "($names_where AND $access)"; |
|
618 | - } elseif ($values_where) { |
|
619 | - $wheres[] = "($values_where AND $access)"; |
|
620 | - } |
|
621 | - |
|
622 | - // add pairs |
|
623 | - // pairs must be in arrays. |
|
624 | - if (is_array($pairs)) { |
|
625 | - // check if this is an array of pairs or just a single pair. |
|
626 | - if (isset($pairs['name']) || isset($pairs['value'])) { |
|
627 | - $pairs = array($pairs); |
|
628 | - } |
|
629 | - |
|
630 | - $pair_wheres = array(); |
|
631 | - |
|
632 | - // @todo when the pairs are > 3 should probably split the query up to |
|
633 | - // denormalize the strings table. |
|
634 | - |
|
635 | - foreach ($pairs as $index => $pair) { |
|
636 | - // @todo move this elsewhere? |
|
637 | - // support shortcut 'n' => 'v' method. |
|
638 | - if (!is_array($pair)) { |
|
639 | - $pair = array( |
|
640 | - 'name' => $index, |
|
641 | - 'value' => $pair |
|
642 | - ); |
|
643 | - } |
|
644 | - |
|
645 | - // must have at least a name and value |
|
646 | - if (!isset($pair['name']) || !isset($pair['value'])) { |
|
647 | - // @todo should probably return false. |
|
648 | - continue; |
|
649 | - } |
|
650 | - |
|
651 | - // case sensitivity can be specified per pair. |
|
652 | - // default to higher level setting. |
|
653 | - if (isset($pair['case_sensitive'])) { |
|
654 | - $pair_binary = ($pair['case_sensitive']) ? ' BINARY ' : ''; |
|
655 | - } else { |
|
656 | - $pair_binary = $binary; |
|
657 | - } |
|
658 | - |
|
659 | - if (isset($pair['operand'])) { |
|
660 | - $operand = $this->db->sanitizeString($pair['operand']); |
|
661 | - } else { |
|
662 | - $operand = ' = '; |
|
663 | - } |
|
664 | - |
|
665 | - // for comparing |
|
666 | - $trimmed_operand = trim(strtolower($operand)); |
|
667 | - |
|
668 | - $access = _elgg_get_access_where_sql(array( |
|
669 | - 'table_alias' => "n_table{$i}", |
|
670 | - 'guid_column' => 'entity_guid', |
|
671 | - )); |
|
568 | + $wheres = array(); |
|
569 | + |
|
570 | + // get names wheres and joins |
|
571 | + $names_where = ''; |
|
572 | + if ($names !== null) { |
|
573 | + if (!is_array($names)) { |
|
574 | + $names = array($names); |
|
575 | + } |
|
576 | + |
|
577 | + $sanitised_names = array(); |
|
578 | + foreach ($names as $name) { |
|
579 | + // normalise to 0. |
|
580 | + if (!$name) { |
|
581 | + $name = '0'; |
|
582 | + } |
|
583 | + $sanitised_names[] = '\'' . $this->db->sanitizeString($name) . '\''; |
|
584 | + } |
|
585 | + |
|
586 | + if ($names_str = implode(',', $sanitised_names)) { |
|
587 | + $return['joins'][] = "JOIN {$this->metastringsTable->getTableName()} msn on n_table.name_id = msn.id"; |
|
588 | + $names_where = "(msn.string IN ($names_str))"; |
|
589 | + } |
|
590 | + } |
|
591 | + |
|
592 | + // get values wheres and joins |
|
593 | + $values_where = ''; |
|
594 | + if ($values !== null) { |
|
595 | + if (!is_array($values)) { |
|
596 | + $values = array($values); |
|
597 | + } |
|
598 | + |
|
599 | + $sanitised_values = array(); |
|
600 | + foreach ($values as $value) { |
|
601 | + // normalize to 0 |
|
602 | + if (!$value) { |
|
603 | + $value = 0; |
|
604 | + } |
|
605 | + $sanitised_values[] = '\'' . $this->db->sanitizeString($value) . '\''; |
|
606 | + } |
|
607 | + |
|
608 | + if ($values_str = implode(',', $sanitised_values)) { |
|
609 | + $return['joins'][] = "JOIN {$this->metastringsTable->getTableName()} msv on n_table.value_id = msv.id"; |
|
610 | + $values_where = "({$binary}msv.string IN ($values_str))"; |
|
611 | + } |
|
612 | + } |
|
613 | + |
|
614 | + if ($names_where && $values_where) { |
|
615 | + $wheres[] = "($names_where AND $values_where AND $access)"; |
|
616 | + } elseif ($names_where) { |
|
617 | + $wheres[] = "($names_where AND $access)"; |
|
618 | + } elseif ($values_where) { |
|
619 | + $wheres[] = "($values_where AND $access)"; |
|
620 | + } |
|
621 | + |
|
622 | + // add pairs |
|
623 | + // pairs must be in arrays. |
|
624 | + if (is_array($pairs)) { |
|
625 | + // check if this is an array of pairs or just a single pair. |
|
626 | + if (isset($pairs['name']) || isset($pairs['value'])) { |
|
627 | + $pairs = array($pairs); |
|
628 | + } |
|
629 | + |
|
630 | + $pair_wheres = array(); |
|
631 | + |
|
632 | + // @todo when the pairs are > 3 should probably split the query up to |
|
633 | + // denormalize the strings table. |
|
634 | + |
|
635 | + foreach ($pairs as $index => $pair) { |
|
636 | + // @todo move this elsewhere? |
|
637 | + // support shortcut 'n' => 'v' method. |
|
638 | + if (!is_array($pair)) { |
|
639 | + $pair = array( |
|
640 | + 'name' => $index, |
|
641 | + 'value' => $pair |
|
642 | + ); |
|
643 | + } |
|
644 | + |
|
645 | + // must have at least a name and value |
|
646 | + if (!isset($pair['name']) || !isset($pair['value'])) { |
|
647 | + // @todo should probably return false. |
|
648 | + continue; |
|
649 | + } |
|
650 | + |
|
651 | + // case sensitivity can be specified per pair. |
|
652 | + // default to higher level setting. |
|
653 | + if (isset($pair['case_sensitive'])) { |
|
654 | + $pair_binary = ($pair['case_sensitive']) ? ' BINARY ' : ''; |
|
655 | + } else { |
|
656 | + $pair_binary = $binary; |
|
657 | + } |
|
658 | + |
|
659 | + if (isset($pair['operand'])) { |
|
660 | + $operand = $this->db->sanitizeString($pair['operand']); |
|
661 | + } else { |
|
662 | + $operand = ' = '; |
|
663 | + } |
|
664 | + |
|
665 | + // for comparing |
|
666 | + $trimmed_operand = trim(strtolower($operand)); |
|
667 | + |
|
668 | + $access = _elgg_get_access_where_sql(array( |
|
669 | + 'table_alias' => "n_table{$i}", |
|
670 | + 'guid_column' => 'entity_guid', |
|
671 | + )); |
|
672 | 672 | |
673 | - // certain operands can't work well with strings that can be interpreted as numbers |
|
674 | - // for direct comparisons like IN, =, != we treat them as strings |
|
675 | - // gt/lt comparisons need to stay unencapsulated because strings '5' > '15' |
|
676 | - // see https://github.com/Elgg/Elgg/issues/7009 |
|
677 | - $num_safe_operands = array('>', '<', '>=', '<='); |
|
678 | - $num_test_operand = trim(strtoupper($operand)); |
|
679 | - |
|
680 | - if (is_numeric($pair['value']) && in_array($num_test_operand, $num_safe_operands)) { |
|
681 | - $value = $this->db->sanitizeString($pair['value']); |
|
682 | - } else if (is_bool($pair['value'])) { |
|
683 | - $value = (int)$pair['value']; |
|
684 | - } else if (is_array($pair['value'])) { |
|
685 | - $values_array = array(); |
|
686 | - |
|
687 | - foreach ($pair['value'] as $pair_value) { |
|
688 | - if (is_numeric($pair_value) && !in_array($num_test_operand, $num_safe_operands)) { |
|
689 | - $values_array[] = $this->db->sanitizeString($pair_value); |
|
690 | - } else { |
|
691 | - $values_array[] = "'" . $this->db->sanitizeString($pair_value) . "'"; |
|
692 | - } |
|
693 | - } |
|
694 | - |
|
695 | - if ($values_array) { |
|
696 | - $value = '(' . implode(', ', $values_array) . ')'; |
|
697 | - } |
|
698 | - |
|
699 | - // @todo allow support for non IN operands with array of values. |
|
700 | - // will have to do more silly joins. |
|
701 | - $operand = 'IN'; |
|
702 | - } else if ($trimmed_operand == 'in') { |
|
703 | - $value = "({$pair['value']})"; |
|
704 | - } else { |
|
705 | - $value = "'" . $this->db->sanitizeString($pair['value']) . "'"; |
|
706 | - } |
|
707 | - |
|
708 | - $name = $this->db->sanitizeString($pair['name']); |
|
709 | - |
|
710 | - // @todo The multiple joins are only needed when the operator is AND |
|
711 | - $return['joins'][] = "JOIN {$this->db->prefix}{$n_table} n_table{$i} |
|
673 | + // certain operands can't work well with strings that can be interpreted as numbers |
|
674 | + // for direct comparisons like IN, =, != we treat them as strings |
|
675 | + // gt/lt comparisons need to stay unencapsulated because strings '5' > '15' |
|
676 | + // see https://github.com/Elgg/Elgg/issues/7009 |
|
677 | + $num_safe_operands = array('>', '<', '>=', '<='); |
|
678 | + $num_test_operand = trim(strtoupper($operand)); |
|
679 | + |
|
680 | + if (is_numeric($pair['value']) && in_array($num_test_operand, $num_safe_operands)) { |
|
681 | + $value = $this->db->sanitizeString($pair['value']); |
|
682 | + } else if (is_bool($pair['value'])) { |
|
683 | + $value = (int)$pair['value']; |
|
684 | + } else if (is_array($pair['value'])) { |
|
685 | + $values_array = array(); |
|
686 | + |
|
687 | + foreach ($pair['value'] as $pair_value) { |
|
688 | + if (is_numeric($pair_value) && !in_array($num_test_operand, $num_safe_operands)) { |
|
689 | + $values_array[] = $this->db->sanitizeString($pair_value); |
|
690 | + } else { |
|
691 | + $values_array[] = "'" . $this->db->sanitizeString($pair_value) . "'"; |
|
692 | + } |
|
693 | + } |
|
694 | + |
|
695 | + if ($values_array) { |
|
696 | + $value = '(' . implode(', ', $values_array) . ')'; |
|
697 | + } |
|
698 | + |
|
699 | + // @todo allow support for non IN operands with array of values. |
|
700 | + // will have to do more silly joins. |
|
701 | + $operand = 'IN'; |
|
702 | + } else if ($trimmed_operand == 'in') { |
|
703 | + $value = "({$pair['value']})"; |
|
704 | + } else { |
|
705 | + $value = "'" . $this->db->sanitizeString($pair['value']) . "'"; |
|
706 | + } |
|
707 | + |
|
708 | + $name = $this->db->sanitizeString($pair['name']); |
|
709 | + |
|
710 | + // @todo The multiple joins are only needed when the operator is AND |
|
711 | + $return['joins'][] = "JOIN {$this->db->prefix}{$n_table} n_table{$i} |
|
712 | 712 | on {$e_table}.guid = n_table{$i}.entity_guid"; |
713 | - $return['joins'][] = "JOIN {$this->metastringsTable->getTableName()} msn{$i} |
|
713 | + $return['joins'][] = "JOIN {$this->metastringsTable->getTableName()} msn{$i} |
|
714 | 714 | on n_table{$i}.name_id = msn{$i}.id"; |
715 | - $return['joins'][] = "JOIN {$this->metastringsTable->getTableName()} msv{$i} |
|
715 | + $return['joins'][] = "JOIN {$this->metastringsTable->getTableName()} msv{$i} |
|
716 | 716 | on n_table{$i}.value_id = msv{$i}.id"; |
717 | 717 | |
718 | - $pair_wheres[] = "(msn{$i}.string = '$name' AND {$pair_binary}msv{$i}.string |
|
718 | + $pair_wheres[] = "(msn{$i}.string = '$name' AND {$pair_binary}msv{$i}.string |
|
719 | 719 | $operand $value AND $access)"; |
720 | 720 | |
721 | - $i++; |
|
722 | - } |
|
723 | - |
|
724 | - if ($where = implode(" $pair_operator ", $pair_wheres)) { |
|
725 | - $wheres[] = "($where)"; |
|
726 | - } |
|
727 | - } |
|
728 | - |
|
729 | - // add owner_guids |
|
730 | - if ($owner_guids) { |
|
731 | - if (is_array($owner_guids)) { |
|
732 | - $sanitised = array_map('sanitise_int', $owner_guids); |
|
733 | - $owner_str = implode(',', $sanitised); |
|
734 | - } else { |
|
735 | - $owner_str = (int)$owner_guids; |
|
736 | - } |
|
737 | - |
|
738 | - $wheres[] = "(n_table.owner_guid IN ($owner_str))"; |
|
739 | - } |
|
740 | - |
|
741 | - if ($where = implode(' AND ', $wheres)) { |
|
742 | - $return['wheres'][] = "($where)"; |
|
743 | - } |
|
744 | - |
|
745 | - if (is_array($order_by_metadata)) { |
|
746 | - if ((count($order_by_metadata) > 0) && !isset($order_by_metadata[0])) { |
|
747 | - // singleton, so fix |
|
748 | - $order_by_metadata = array($order_by_metadata); |
|
749 | - } |
|
750 | - foreach ($order_by_metadata as $order_by) { |
|
751 | - if (is_array($order_by) && isset($order_by['name'])) { |
|
752 | - $name = $this->db->sanitizeString($order_by['name']); |
|
753 | - if (isset($order_by['direction'])) { |
|
754 | - $direction = $this->db->sanitizeString($order_by['direction']); |
|
755 | - } else { |
|
756 | - $direction = 'ASC'; |
|
757 | - } |
|
758 | - $return['joins'][] = "JOIN {$this->db->prefix}{$n_table} n_table{$i} |
|
721 | + $i++; |
|
722 | + } |
|
723 | + |
|
724 | + if ($where = implode(" $pair_operator ", $pair_wheres)) { |
|
725 | + $wheres[] = "($where)"; |
|
726 | + } |
|
727 | + } |
|
728 | + |
|
729 | + // add owner_guids |
|
730 | + if ($owner_guids) { |
|
731 | + if (is_array($owner_guids)) { |
|
732 | + $sanitised = array_map('sanitise_int', $owner_guids); |
|
733 | + $owner_str = implode(',', $sanitised); |
|
734 | + } else { |
|
735 | + $owner_str = (int)$owner_guids; |
|
736 | + } |
|
737 | + |
|
738 | + $wheres[] = "(n_table.owner_guid IN ($owner_str))"; |
|
739 | + } |
|
740 | + |
|
741 | + if ($where = implode(' AND ', $wheres)) { |
|
742 | + $return['wheres'][] = "($where)"; |
|
743 | + } |
|
744 | + |
|
745 | + if (is_array($order_by_metadata)) { |
|
746 | + if ((count($order_by_metadata) > 0) && !isset($order_by_metadata[0])) { |
|
747 | + // singleton, so fix |
|
748 | + $order_by_metadata = array($order_by_metadata); |
|
749 | + } |
|
750 | + foreach ($order_by_metadata as $order_by) { |
|
751 | + if (is_array($order_by) && isset($order_by['name'])) { |
|
752 | + $name = $this->db->sanitizeString($order_by['name']); |
|
753 | + if (isset($order_by['direction'])) { |
|
754 | + $direction = $this->db->sanitizeString($order_by['direction']); |
|
755 | + } else { |
|
756 | + $direction = 'ASC'; |
|
757 | + } |
|
758 | + $return['joins'][] = "JOIN {$this->db->prefix}{$n_table} n_table{$i} |
|
759 | 759 | on {$e_table}.guid = n_table{$i}.entity_guid"; |
760 | - $return['joins'][] = "JOIN {$this->metastringsTable->getTableName()} msn{$i} |
|
760 | + $return['joins'][] = "JOIN {$this->metastringsTable->getTableName()} msn{$i} |
|
761 | 761 | on n_table{$i}.name_id = msn{$i}.id"; |
762 | - $return['joins'][] = "JOIN {$this->metastringsTable->getTableName()} msv{$i} |
|
762 | + $return['joins'][] = "JOIN {$this->metastringsTable->getTableName()} msv{$i} |
|
763 | 763 | on n_table{$i}.value_id = msv{$i}.id"; |
764 | 764 | |
765 | - $access = _elgg_get_access_where_sql(array( |
|
766 | - 'table_alias' => "n_table{$i}", |
|
767 | - 'guid_column' => 'entity_guid', |
|
768 | - )); |
|
769 | - |
|
770 | - $return['wheres'][] = "(msn{$i}.string = '$name' AND $access)"; |
|
771 | - if (isset($order_by['as']) && $order_by['as'] == 'integer') { |
|
772 | - $return['orders'][] = "CAST(msv{$i}.string AS SIGNED) $direction"; |
|
773 | - } else { |
|
774 | - $return['orders'][] = "msv{$i}.string $direction"; |
|
775 | - } |
|
776 | - $i++; |
|
777 | - } |
|
778 | - } |
|
779 | - } |
|
780 | - |
|
781 | - return $return; |
|
782 | - } |
|
783 | - |
|
784 | - /** |
|
785 | - * Get the URL for this metadata |
|
786 | - * |
|
787 | - * By default this links to the export handler in the current view. |
|
788 | - * |
|
789 | - * @param int $id Metadata ID |
|
790 | - * |
|
791 | - * @return mixed |
|
792 | - */ |
|
793 | - function getUrl($id) { |
|
794 | - $extender = $this->get($id); |
|
765 | + $access = _elgg_get_access_where_sql(array( |
|
766 | + 'table_alias' => "n_table{$i}", |
|
767 | + 'guid_column' => 'entity_guid', |
|
768 | + )); |
|
769 | + |
|
770 | + $return['wheres'][] = "(msn{$i}.string = '$name' AND $access)"; |
|
771 | + if (isset($order_by['as']) && $order_by['as'] == 'integer') { |
|
772 | + $return['orders'][] = "CAST(msv{$i}.string AS SIGNED) $direction"; |
|
773 | + } else { |
|
774 | + $return['orders'][] = "msv{$i}.string $direction"; |
|
775 | + } |
|
776 | + $i++; |
|
777 | + } |
|
778 | + } |
|
779 | + } |
|
780 | + |
|
781 | + return $return; |
|
782 | + } |
|
783 | + |
|
784 | + /** |
|
785 | + * Get the URL for this metadata |
|
786 | + * |
|
787 | + * By default this links to the export handler in the current view. |
|
788 | + * |
|
789 | + * @param int $id Metadata ID |
|
790 | + * |
|
791 | + * @return mixed |
|
792 | + */ |
|
793 | + function getUrl($id) { |
|
794 | + $extender = $this->get($id); |
|
795 | 795 | |
796 | - return $extender ? $extender->getURL() : false; |
|
797 | - } |
|
798 | - |
|
799 | - /** |
|
800 | - * Mark entities with a particular type and subtype as having access permissions |
|
801 | - * that can be changed independently from their parent entity |
|
802 | - * |
|
803 | - * @param string $type The type - object, user, etc |
|
804 | - * @param string $subtype The subtype; all subtypes by default |
|
805 | - * |
|
806 | - * @return void |
|
807 | - */ |
|
808 | - function registerMetadataAsIndependent($type, $subtype = '*') { |
|
809 | - if (!isset($this->independents[$type])) { |
|
810 | - $this->independents[$type] = array(); |
|
811 | - } |
|
796 | + return $extender ? $extender->getURL() : false; |
|
797 | + } |
|
798 | + |
|
799 | + /** |
|
800 | + * Mark entities with a particular type and subtype as having access permissions |
|
801 | + * that can be changed independently from their parent entity |
|
802 | + * |
|
803 | + * @param string $type The type - object, user, etc |
|
804 | + * @param string $subtype The subtype; all subtypes by default |
|
805 | + * |
|
806 | + * @return void |
|
807 | + */ |
|
808 | + function registerMetadataAsIndependent($type, $subtype = '*') { |
|
809 | + if (!isset($this->independents[$type])) { |
|
810 | + $this->independents[$type] = array(); |
|
811 | + } |
|
812 | 812 | |
813 | - $this->independents[$type][$subtype] = true; |
|
814 | - } |
|
815 | - |
|
816 | - /** |
|
817 | - * Determines whether entities of a given type and subtype should not change |
|
818 | - * their metadata in line with their parent entity |
|
819 | - * |
|
820 | - * @param string $type The type - object, user, etc |
|
821 | - * @param string $subtype The entity subtype |
|
822 | - * |
|
823 | - * @return bool |
|
824 | - */ |
|
825 | - function isMetadataIndependent($type, $subtype) { |
|
826 | - if (empty($this->independents[$type])) { |
|
827 | - return false; |
|
828 | - } |
|
813 | + $this->independents[$type][$subtype] = true; |
|
814 | + } |
|
815 | + |
|
816 | + /** |
|
817 | + * Determines whether entities of a given type and subtype should not change |
|
818 | + * their metadata in line with their parent entity |
|
819 | + * |
|
820 | + * @param string $type The type - object, user, etc |
|
821 | + * @param string $subtype The entity subtype |
|
822 | + * |
|
823 | + * @return bool |
|
824 | + */ |
|
825 | + function isMetadataIndependent($type, $subtype) { |
|
826 | + if (empty($this->independents[$type])) { |
|
827 | + return false; |
|
828 | + } |
|
829 | 829 | |
830 | - return !empty($this->independents[$type][$subtype]) |
|
831 | - || !empty($this->independents[$type]['*']); |
|
832 | - } |
|
833 | - |
|
834 | - /** |
|
835 | - * When an entity is updated, resets the access ID on all of its child metadata |
|
836 | - * |
|
837 | - * @param string $event The name of the event |
|
838 | - * @param string $object_type The type of object |
|
839 | - * @param \ElggEntity $object The entity itself |
|
840 | - * |
|
841 | - * @return true |
|
842 | - * @access private Set as private in 1.9.0 |
|
843 | - */ |
|
844 | - function handleUpdate($event, $object_type, $object) { |
|
845 | - if ($object instanceof \ElggEntity) { |
|
846 | - if (!$this->isMetadataIndependent($object->getType(), $object->getSubtype())) { |
|
847 | - $access_id = (int)$object->access_id; |
|
848 | - $guid = (int)$object->getGUID(); |
|
849 | - $query = "update {$this->table} set access_id = {$access_id} where entity_guid = {$guid}"; |
|
850 | - $this->db->updateData($query); |
|
851 | - } |
|
852 | - } |
|
853 | - return true; |
|
854 | - } |
|
830 | + return !empty($this->independents[$type][$subtype]) |
|
831 | + || !empty($this->independents[$type]['*']); |
|
832 | + } |
|
833 | + |
|
834 | + /** |
|
835 | + * When an entity is updated, resets the access ID on all of its child metadata |
|
836 | + * |
|
837 | + * @param string $event The name of the event |
|
838 | + * @param string $object_type The type of object |
|
839 | + * @param \ElggEntity $object The entity itself |
|
840 | + * |
|
841 | + * @return true |
|
842 | + * @access private Set as private in 1.9.0 |
|
843 | + */ |
|
844 | + function handleUpdate($event, $object_type, $object) { |
|
845 | + if ($object instanceof \ElggEntity) { |
|
846 | + if (!$this->isMetadataIndependent($object->getType(), $object->getSubtype())) { |
|
847 | + $access_id = (int)$object->access_id; |
|
848 | + $guid = (int)$object->getGUID(); |
|
849 | + $query = "update {$this->table} set access_id = {$access_id} where entity_guid = {$guid}"; |
|
850 | + $this->db->updateData($query); |
|
851 | + } |
|
852 | + } |
|
853 | + return true; |
|
854 | + } |
|
855 | 855 | |
856 | 856 | } |
@@ -69,7 +69,7 @@ discard block |
||
69 | 69 | $this->events = $events; |
70 | 70 | $this->metastringsTable = $metastringsTable; |
71 | 71 | $this->session = $session; |
72 | - $this->table = $this->db->prefix . "metadata"; |
|
72 | + $this->table = $this->db->prefix."metadata"; |
|
73 | 73 | } |
74 | 74 | |
75 | 75 | /** |
@@ -116,12 +116,12 @@ discard block |
||
116 | 116 | function create($entity_guid, $name, $value, $value_type = '', $owner_guid = 0, |
117 | 117 | $access_id = ACCESS_PRIVATE, $allow_multiple = false) { |
118 | 118 | |
119 | - $entity_guid = (int)$entity_guid; |
|
119 | + $entity_guid = (int) $entity_guid; |
|
120 | 120 | // name and value are encoded in add_metastring() |
121 | 121 | $value_type = \ElggExtender::detectValueType($value, trim($value_type)); |
122 | 122 | $time = $this->getCurrentTime()->getTimestamp(); |
123 | - $owner_guid = (int)$owner_guid; |
|
124 | - $allow_multiple = (boolean)$allow_multiple; |
|
123 | + $owner_guid = (int) $owner_guid; |
|
124 | + $allow_multiple = (boolean) $allow_multiple; |
|
125 | 125 | |
126 | 126 | if (!isset($value)) { |
127 | 127 | return false; |
@@ -143,7 +143,7 @@ discard block |
||
143 | 143 | |
144 | 144 | $existing = $this->db->getDataRow($query, null, $params); |
145 | 145 | if ($existing && !$allow_multiple) { |
146 | - $id = (int)$existing->id; |
|
146 | + $id = (int) $existing->id; |
|
147 | 147 | $result = $this->update($id, $name, $value, $value_type, $owner_guid, $access_id); |
148 | 148 | |
149 | 149 | if (!$result) { |
@@ -152,7 +152,7 @@ discard block |
||
152 | 152 | } else { |
153 | 153 | // Support boolean types |
154 | 154 | if (is_bool($value)) { |
155 | - $value = (int)$value; |
|
155 | + $value = (int) $value; |
|
156 | 156 | } |
157 | 157 | |
158 | 158 | // Add the metastrings |
@@ -212,7 +212,7 @@ discard block |
||
212 | 212 | * @return bool |
213 | 213 | */ |
214 | 214 | function update($id, $name, $value, $value_type, $owner_guid, $access_id) { |
215 | - $id = (int)$id; |
|
215 | + $id = (int) $id; |
|
216 | 216 | |
217 | 217 | if (!$md = $this->get($id)) { |
218 | 218 | return false; |
@@ -223,16 +223,16 @@ discard block |
||
223 | 223 | |
224 | 224 | $value_type = \ElggExtender::detectValueType($value, trim($value_type)); |
225 | 225 | |
226 | - $owner_guid = (int)$owner_guid; |
|
226 | + $owner_guid = (int) $owner_guid; |
|
227 | 227 | if ($owner_guid == 0) { |
228 | 228 | $owner_guid = $this->session->getLoggedInUserGuid(); |
229 | 229 | } |
230 | 230 | |
231 | - $access_id = (int)$access_id; |
|
231 | + $access_id = (int) $access_id; |
|
232 | 232 | |
233 | 233 | // Support boolean types (as integers) |
234 | 234 | if (is_bool($value)) { |
235 | - $value = (int)$value; |
|
235 | + $value = (int) $value; |
|
236 | 236 | } |
237 | 237 | |
238 | 238 | $value_id = $this->metastringsTable->getId($value); |
@@ -555,8 +555,8 @@ discard block |
||
555 | 555 | 'guid_column' => 'entity_guid', |
556 | 556 | )); |
557 | 557 | |
558 | - $return = array ( |
|
559 | - 'joins' => array (), |
|
558 | + $return = array( |
|
559 | + 'joins' => array(), |
|
560 | 560 | 'wheres' => array(), |
561 | 561 | 'orders' => array() |
562 | 562 | ); |
@@ -580,7 +580,7 @@ discard block |
||
580 | 580 | if (!$name) { |
581 | 581 | $name = '0'; |
582 | 582 | } |
583 | - $sanitised_names[] = '\'' . $this->db->sanitizeString($name) . '\''; |
|
583 | + $sanitised_names[] = '\''.$this->db->sanitizeString($name).'\''; |
|
584 | 584 | } |
585 | 585 | |
586 | 586 | if ($names_str = implode(',', $sanitised_names)) { |
@@ -602,7 +602,7 @@ discard block |
||
602 | 602 | if (!$value) { |
603 | 603 | $value = 0; |
604 | 604 | } |
605 | - $sanitised_values[] = '\'' . $this->db->sanitizeString($value) . '\''; |
|
605 | + $sanitised_values[] = '\''.$this->db->sanitizeString($value).'\''; |
|
606 | 606 | } |
607 | 607 | |
608 | 608 | if ($values_str = implode(',', $sanitised_values)) { |
@@ -680,7 +680,7 @@ discard block |
||
680 | 680 | if (is_numeric($pair['value']) && in_array($num_test_operand, $num_safe_operands)) { |
681 | 681 | $value = $this->db->sanitizeString($pair['value']); |
682 | 682 | } else if (is_bool($pair['value'])) { |
683 | - $value = (int)$pair['value']; |
|
683 | + $value = (int) $pair['value']; |
|
684 | 684 | } else if (is_array($pair['value'])) { |
685 | 685 | $values_array = array(); |
686 | 686 | |
@@ -688,12 +688,12 @@ discard block |
||
688 | 688 | if (is_numeric($pair_value) && !in_array($num_test_operand, $num_safe_operands)) { |
689 | 689 | $values_array[] = $this->db->sanitizeString($pair_value); |
690 | 690 | } else { |
691 | - $values_array[] = "'" . $this->db->sanitizeString($pair_value) . "'"; |
|
691 | + $values_array[] = "'".$this->db->sanitizeString($pair_value)."'"; |
|
692 | 692 | } |
693 | 693 | } |
694 | 694 | |
695 | 695 | if ($values_array) { |
696 | - $value = '(' . implode(', ', $values_array) . ')'; |
|
696 | + $value = '('.implode(', ', $values_array).')'; |
|
697 | 697 | } |
698 | 698 | |
699 | 699 | // @todo allow support for non IN operands with array of values. |
@@ -702,7 +702,7 @@ discard block |
||
702 | 702 | } else if ($trimmed_operand == 'in') { |
703 | 703 | $value = "({$pair['value']})"; |
704 | 704 | } else { |
705 | - $value = "'" . $this->db->sanitizeString($pair['value']) . "'"; |
|
705 | + $value = "'".$this->db->sanitizeString($pair['value'])."'"; |
|
706 | 706 | } |
707 | 707 | |
708 | 708 | $name = $this->db->sanitizeString($pair['name']); |
@@ -732,7 +732,7 @@ discard block |
||
732 | 732 | $sanitised = array_map('sanitise_int', $owner_guids); |
733 | 733 | $owner_str = implode(',', $sanitised); |
734 | 734 | } else { |
735 | - $owner_str = (int)$owner_guids; |
|
735 | + $owner_str = (int) $owner_guids; |
|
736 | 736 | } |
737 | 737 | |
738 | 738 | $wheres[] = "(n_table.owner_guid IN ($owner_str))"; |
@@ -844,8 +844,8 @@ discard block |
||
844 | 844 | function handleUpdate($event, $object_type, $object) { |
845 | 845 | if ($object instanceof \ElggEntity) { |
846 | 846 | if (!$this->isMetadataIndependent($object->getType(), $object->getSubtype())) { |
847 | - $access_id = (int)$object->access_id; |
|
848 | - $guid = (int)$object->getGUID(); |
|
847 | + $access_id = (int) $object->access_id; |
|
848 | + $guid = (int) $object->getGUID(); |
|
849 | 849 | $query = "update {$this->table} set access_id = {$access_id} where entity_guid = {$guid}"; |
850 | 850 | $this->db->updateData($query); |
851 | 851 | } |
@@ -28,7 +28,7 @@ |
||
28 | 28 | * |
29 | 29 | * Note: Old secrets were hex encoded. |
30 | 30 | * |
31 | - * @return mixed The site secret hash or false |
|
31 | + * @return string|false The site secret hash or false |
|
32 | 32 | * @access private |
33 | 33 | */ |
34 | 34 | function init() { |
@@ -21,114 +21,114 @@ |
||
21 | 21 | */ |
22 | 22 | class SiteSecret { |
23 | 23 | |
24 | - /** |
|
25 | - * @var Datalist |
|
26 | - */ |
|
27 | - private $datalist; |
|
24 | + /** |
|
25 | + * @var Datalist |
|
26 | + */ |
|
27 | + private $datalist; |
|
28 | 28 | |
29 | - /** |
|
30 | - * Constructor |
|
31 | - * |
|
32 | - * @param Datalist $datalist Datalist table |
|
33 | - */ |
|
34 | - public function __construct(Datalist $datalist) { |
|
35 | - $this->datalist = $datalist; |
|
36 | - } |
|
29 | + /** |
|
30 | + * Constructor |
|
31 | + * |
|
32 | + * @param Datalist $datalist Datalist table |
|
33 | + */ |
|
34 | + public function __construct(Datalist $datalist) { |
|
35 | + $this->datalist = $datalist; |
|
36 | + } |
|
37 | 37 | |
38 | - /** |
|
39 | - * @var string |
|
40 | - */ |
|
41 | - private $test_secret = ''; |
|
38 | + /** |
|
39 | + * @var string |
|
40 | + */ |
|
41 | + private $test_secret = ''; |
|
42 | 42 | |
43 | - /** |
|
44 | - * Set a secret to be used in testing |
|
45 | - * |
|
46 | - * @param string $secret Testing site secret. 32 alphanums starting with "z" |
|
47 | - * @return void |
|
48 | - */ |
|
49 | - public function setTestingSecret($secret) { |
|
50 | - $this->test_secret = $secret; |
|
51 | - } |
|
43 | + /** |
|
44 | + * Set a secret to be used in testing |
|
45 | + * |
|
46 | + * @param string $secret Testing site secret. 32 alphanums starting with "z" |
|
47 | + * @return void |
|
48 | + */ |
|
49 | + public function setTestingSecret($secret) { |
|
50 | + $this->test_secret = $secret; |
|
51 | + } |
|
52 | 52 | |
53 | - /** |
|
54 | - * Initialise the site secret (32 bytes: "z" to indicate format + 186-bit key in Base64 URL). |
|
55 | - * |
|
56 | - * Used during installation and saves as a datalist. |
|
57 | - * |
|
58 | - * Note: Old secrets were hex encoded. |
|
59 | - * |
|
60 | - * @return mixed The site secret hash or false |
|
61 | - * @access private |
|
62 | - */ |
|
63 | - function init() { |
|
64 | - $secret = 'z' . _elgg_services()->crypto->getRandomString(31); |
|
53 | + /** |
|
54 | + * Initialise the site secret (32 bytes: "z" to indicate format + 186-bit key in Base64 URL). |
|
55 | + * |
|
56 | + * Used during installation and saves as a datalist. |
|
57 | + * |
|
58 | + * Note: Old secrets were hex encoded. |
|
59 | + * |
|
60 | + * @return mixed The site secret hash or false |
|
61 | + * @access private |
|
62 | + */ |
|
63 | + function init() { |
|
64 | + $secret = 'z' . _elgg_services()->crypto->getRandomString(31); |
|
65 | 65 | |
66 | - if ($this->datalist->set('__site_secret__', $secret)) { |
|
67 | - return $secret; |
|
68 | - } |
|
66 | + if ($this->datalist->set('__site_secret__', $secret)) { |
|
67 | + return $secret; |
|
68 | + } |
|
69 | 69 | |
70 | - return false; |
|
71 | - } |
|
70 | + return false; |
|
71 | + } |
|
72 | 72 | |
73 | - /** |
|
74 | - * Returns the site secret. |
|
75 | - * |
|
76 | - * Used to generate difficult to guess hashes for sessions and action tokens. |
|
77 | - * |
|
78 | - * @param bool $raw If true, a binary key will be returned |
|
79 | - * |
|
80 | - * @return string Site secret. |
|
81 | - * @access private |
|
82 | - */ |
|
83 | - function get($raw = false) { |
|
84 | - if ($this->test_secret) { |
|
85 | - $secret = $this->test_secret; |
|
86 | - } else { |
|
87 | - $secret = $this->datalist->get('__site_secret__'); |
|
88 | - } |
|
89 | - if (!$secret) { |
|
90 | - $secret = $this->init(); |
|
91 | - } |
|
73 | + /** |
|
74 | + * Returns the site secret. |
|
75 | + * |
|
76 | + * Used to generate difficult to guess hashes for sessions and action tokens. |
|
77 | + * |
|
78 | + * @param bool $raw If true, a binary key will be returned |
|
79 | + * |
|
80 | + * @return string Site secret. |
|
81 | + * @access private |
|
82 | + */ |
|
83 | + function get($raw = false) { |
|
84 | + if ($this->test_secret) { |
|
85 | + $secret = $this->test_secret; |
|
86 | + } else { |
|
87 | + $secret = $this->datalist->get('__site_secret__'); |
|
88 | + } |
|
89 | + if (!$secret) { |
|
90 | + $secret = $this->init(); |
|
91 | + } |
|
92 | 92 | |
93 | - if ($raw) { |
|
94 | - // try to return binary key |
|
95 | - if ($secret[0] === 'z') { |
|
96 | - // new keys are "z" + base64URL |
|
97 | - $base64 = strtr(substr($secret, 1), '-_', '+/'); |
|
98 | - $key = base64_decode($base64); |
|
99 | - if ($key !== false) { |
|
100 | - // on failure, at least return string key :/ |
|
101 | - return $key; |
|
102 | - } |
|
103 | - } else { |
|
104 | - // old keys are hex |
|
105 | - return hex2bin($secret); |
|
106 | - } |
|
107 | - } |
|
93 | + if ($raw) { |
|
94 | + // try to return binary key |
|
95 | + if ($secret[0] === 'z') { |
|
96 | + // new keys are "z" + base64URL |
|
97 | + $base64 = strtr(substr($secret, 1), '-_', '+/'); |
|
98 | + $key = base64_decode($base64); |
|
99 | + if ($key !== false) { |
|
100 | + // on failure, at least return string key :/ |
|
101 | + return $key; |
|
102 | + } |
|
103 | + } else { |
|
104 | + // old keys are hex |
|
105 | + return hex2bin($secret); |
|
106 | + } |
|
107 | + } |
|
108 | 108 | |
109 | - return $secret; |
|
110 | - } |
|
109 | + return $secret; |
|
110 | + } |
|
111 | 111 | |
112 | - /** |
|
113 | - * Get the strength of the site secret |
|
114 | - * |
|
115 | - * If "weak" or "moderate" is returned, this assumes we're running on the same system that created |
|
116 | - * the key. |
|
117 | - * |
|
118 | - * @return string "strong", "moderate", or "weak" |
|
119 | - * @access private |
|
120 | - */ |
|
121 | - function getStrength() { |
|
122 | - $secret = $this->get(); |
|
123 | - if ($secret[0] !== 'z') { |
|
124 | - $rand_max = getrandmax(); |
|
125 | - if ($rand_max < pow(2, 16)) { |
|
126 | - return 'weak'; |
|
127 | - } |
|
128 | - if ($rand_max < pow(2, 32)) { |
|
129 | - return 'moderate'; |
|
130 | - } |
|
131 | - } |
|
132 | - return 'strong'; |
|
133 | - } |
|
112 | + /** |
|
113 | + * Get the strength of the site secret |
|
114 | + * |
|
115 | + * If "weak" or "moderate" is returned, this assumes we're running on the same system that created |
|
116 | + * the key. |
|
117 | + * |
|
118 | + * @return string "strong", "moderate", or "weak" |
|
119 | + * @access private |
|
120 | + */ |
|
121 | + function getStrength() { |
|
122 | + $secret = $this->get(); |
|
123 | + if ($secret[0] !== 'z') { |
|
124 | + $rand_max = getrandmax(); |
|
125 | + if ($rand_max < pow(2, 16)) { |
|
126 | + return 'weak'; |
|
127 | + } |
|
128 | + if ($rand_max < pow(2, 32)) { |
|
129 | + return 'moderate'; |
|
130 | + } |
|
131 | + } |
|
132 | + return 'strong'; |
|
133 | + } |
|
134 | 134 | } |
@@ -61,7 +61,7 @@ |
||
61 | 61 | * @access private |
62 | 62 | */ |
63 | 63 | function init() { |
64 | - $secret = 'z' . _elgg_services()->crypto->getRandomString(31); |
|
64 | + $secret = 'z'._elgg_services()->crypto->getRandomString(31); |
|
65 | 65 | |
66 | 66 | if ($this->datalist->set('__site_secret__', $secret)) { |
67 | 67 | return $secret; |
@@ -105,6 +105,7 @@ |
||
105 | 105 | |
106 | 106 | /** |
107 | 107 | * {@inheritdoc} |
108 | + * @return string |
|
108 | 109 | */ |
109 | 110 | public function getClientIp() { |
110 | 111 | $ip = parent::getClientIp(); |
@@ -39,7 +39,7 @@ |
||
39 | 39 | */ |
40 | 40 | public function setUrlSegments(array $segments) { |
41 | 41 | $query = $this->query->all(); |
42 | - $query[Application::GET_PATH_KEY] = '/' . implode('/', $segments); |
|
42 | + $query[Application::GET_PATH_KEY] = '/'.implode('/', $segments); |
|
43 | 43 | return $this->duplicate($query); |
44 | 44 | } |
45 | 45 |
@@ -11,79 +11,79 @@ |
||
11 | 11 | */ |
12 | 12 | class Request extends SymfonyRequest { |
13 | 13 | |
14 | - /** |
|
15 | - * Get the Elgg URL segments |
|
16 | - * |
|
17 | - * @param bool $raw If true, the segments will not be HTML escaped |
|
18 | - * |
|
19 | - * @return string[] |
|
20 | - */ |
|
21 | - public function getUrlSegments($raw = false) { |
|
22 | - $path = trim($this->query->get(Application::GET_PATH_KEY), '/'); |
|
23 | - if (!$raw) { |
|
24 | - $path = htmlspecialchars($path, ENT_QUOTES, 'UTF-8'); |
|
25 | - } |
|
26 | - if (!$path) { |
|
27 | - return array(); |
|
28 | - } |
|
14 | + /** |
|
15 | + * Get the Elgg URL segments |
|
16 | + * |
|
17 | + * @param bool $raw If true, the segments will not be HTML escaped |
|
18 | + * |
|
19 | + * @return string[] |
|
20 | + */ |
|
21 | + public function getUrlSegments($raw = false) { |
|
22 | + $path = trim($this->query->get(Application::GET_PATH_KEY), '/'); |
|
23 | + if (!$raw) { |
|
24 | + $path = htmlspecialchars($path, ENT_QUOTES, 'UTF-8'); |
|
25 | + } |
|
26 | + if (!$path) { |
|
27 | + return array(); |
|
28 | + } |
|
29 | 29 | |
30 | - return explode('/', $path); |
|
31 | - } |
|
30 | + return explode('/', $path); |
|
31 | + } |
|
32 | 32 | |
33 | - /** |
|
34 | - * Get a cloned request with new Elgg URL segments |
|
35 | - * |
|
36 | - * @param string[] $segments URL segments |
|
37 | - * |
|
38 | - * @return Request |
|
39 | - */ |
|
40 | - public function setUrlSegments(array $segments) { |
|
41 | - $query = $this->query->all(); |
|
42 | - $query[Application::GET_PATH_KEY] = '/' . implode('/', $segments); |
|
43 | - return $this->duplicate($query); |
|
44 | - } |
|
33 | + /** |
|
34 | + * Get a cloned request with new Elgg URL segments |
|
35 | + * |
|
36 | + * @param string[] $segments URL segments |
|
37 | + * |
|
38 | + * @return Request |
|
39 | + */ |
|
40 | + public function setUrlSegments(array $segments) { |
|
41 | + $query = $this->query->all(); |
|
42 | + $query[Application::GET_PATH_KEY] = '/' . implode('/', $segments); |
|
43 | + return $this->duplicate($query); |
|
44 | + } |
|
45 | 45 | |
46 | - /** |
|
47 | - * Get first Elgg URL segment |
|
48 | - * |
|
49 | - * @see \Elgg\Http\Request::getUrlSegments() |
|
50 | - * |
|
51 | - * @return string |
|
52 | - */ |
|
53 | - public function getFirstUrlSegment() { |
|
54 | - $segments = $this->getUrlSegments(); |
|
55 | - if ($segments) { |
|
56 | - return array_shift($segments); |
|
57 | - } else { |
|
58 | - return ''; |
|
59 | - } |
|
60 | - } |
|
46 | + /** |
|
47 | + * Get first Elgg URL segment |
|
48 | + * |
|
49 | + * @see \Elgg\Http\Request::getUrlSegments() |
|
50 | + * |
|
51 | + * @return string |
|
52 | + */ |
|
53 | + public function getFirstUrlSegment() { |
|
54 | + $segments = $this->getUrlSegments(); |
|
55 | + if ($segments) { |
|
56 | + return array_shift($segments); |
|
57 | + } else { |
|
58 | + return ''; |
|
59 | + } |
|
60 | + } |
|
61 | 61 | |
62 | - /** |
|
63 | - * {@inheritdoc} |
|
64 | - */ |
|
65 | - public function getClientIp() { |
|
66 | - $ip = parent::getClientIp(); |
|
62 | + /** |
|
63 | + * {@inheritdoc} |
|
64 | + */ |
|
65 | + public function getClientIp() { |
|
66 | + $ip = parent::getClientIp(); |
|
67 | 67 | |
68 | - if ($ip == $this->server->get('REMOTE_ADDR')) { |
|
69 | - // try one more |
|
70 | - $ip_addresses = $this->server->get('HTTP_X_REAL_IP'); |
|
71 | - if ($ip_addresses) { |
|
72 | - $ip_addresses = explode(',', $ip_addresses); |
|
73 | - return array_pop($ip_addresses); |
|
74 | - } |
|
75 | - } |
|
68 | + if ($ip == $this->server->get('REMOTE_ADDR')) { |
|
69 | + // try one more |
|
70 | + $ip_addresses = $this->server->get('HTTP_X_REAL_IP'); |
|
71 | + if ($ip_addresses) { |
|
72 | + $ip_addresses = explode(',', $ip_addresses); |
|
73 | + return array_pop($ip_addresses); |
|
74 | + } |
|
75 | + } |
|
76 | 76 | |
77 | - return $ip; |
|
78 | - } |
|
77 | + return $ip; |
|
78 | + } |
|
79 | 79 | |
80 | - /** |
|
81 | - * {@inheritdoc} |
|
82 | - */ |
|
83 | - public function isXmlHttpRequest() { |
|
84 | - return (strtolower($this->headers->get('X-Requested-With')) === 'xmlhttprequest' |
|
85 | - || $this->query->get('X-Requested-With') === 'XMLHttpRequest' |
|
86 | - || $this->request->get('X-Requested-With') === 'XMLHttpRequest'); |
|
87 | - // GET/POST check is necessary for jQuery.form and other iframe-based "ajax". #8735 |
|
88 | - } |
|
80 | + /** |
|
81 | + * {@inheritdoc} |
|
82 | + */ |
|
83 | + public function isXmlHttpRequest() { |
|
84 | + return (strtolower($this->headers->get('X-Requested-With')) === 'xmlhttprequest' |
|
85 | + || $this->query->get('X-Requested-With') === 'XMLHttpRequest' |
|
86 | + || $this->request->get('X-Requested-With') === 'XMLHttpRequest'); |
|
87 | + // GET/POST check is necessary for jQuery.form and other iframe-based "ajax". #8735 |
|
88 | + } |
|
89 | 89 | } |
@@ -59,6 +59,8 @@ discard block |
||
59 | 59 | /** |
60 | 60 | * @see elgg_register_notification_event() |
61 | 61 | * @access private |
62 | + * @param string $type |
|
63 | + * @param string $subtype |
|
62 | 64 | */ |
63 | 65 | public function registerEvent($type, $subtype, array $actions = array()) { |
64 | 66 | |
@@ -80,6 +82,8 @@ discard block |
||
80 | 82 | /** |
81 | 83 | * @see elgg_unregister_notification_event() |
82 | 84 | * @access private |
85 | + * @param string $type |
|
86 | + * @param string $subtype |
|
83 | 87 | */ |
84 | 88 | public function unregisterEvent($type, $subtype) { |
85 | 89 | |
@@ -102,6 +106,7 @@ discard block |
||
102 | 106 | /** |
103 | 107 | * @see elgg_register_notification_method() |
104 | 108 | * @access private |
109 | + * @param string $name |
|
105 | 110 | */ |
106 | 111 | public function registerMethod($name) { |
107 | 112 | $this->methods[$name] = $name; |
@@ -110,6 +115,7 @@ discard block |
||
110 | 115 | /** |
111 | 116 | * @see elgg_unregister_notification_method() |
112 | 117 | * @access private |
118 | + * @param string $name |
|
113 | 119 | */ |
114 | 120 | public function unregisterMethod($name) { |
115 | 121 | if (isset($this->methods[$name])) { |
@@ -25,746 +25,746 @@ |
||
25 | 25 | */ |
26 | 26 | class NotificationsService { |
27 | 27 | |
28 | - const QUEUE_NAME = 'notifications'; |
|
28 | + const QUEUE_NAME = 'notifications'; |
|
29 | 29 | |
30 | - /** @var SubscriptionsService */ |
|
31 | - protected $subscriptions; |
|
30 | + /** @var SubscriptionsService */ |
|
31 | + protected $subscriptions; |
|
32 | 32 | |
33 | - /** @var Queue */ |
|
34 | - protected $queue; |
|
33 | + /** @var Queue */ |
|
34 | + protected $queue; |
|
35 | 35 | |
36 | - /** @var PluginHooksService */ |
|
37 | - protected $hooks; |
|
36 | + /** @var PluginHooksService */ |
|
37 | + protected $hooks; |
|
38 | 38 | |
39 | - /** @var ElggSession */ |
|
40 | - protected $session; |
|
39 | + /** @var ElggSession */ |
|
40 | + protected $session; |
|
41 | 41 | |
42 | - /** @var Translator */ |
|
43 | - protected $translator; |
|
42 | + /** @var Translator */ |
|
43 | + protected $translator; |
|
44 | 44 | |
45 | - /** @var EntityTable */ |
|
46 | - protected $entities; |
|
45 | + /** @var EntityTable */ |
|
46 | + protected $entities; |
|
47 | 47 | |
48 | - /** @var Logger */ |
|
49 | - protected $logger; |
|
48 | + /** @var Logger */ |
|
49 | + protected $logger; |
|
50 | 50 | |
51 | - /** @var array Registered notification events */ |
|
52 | - protected $events = array(); |
|
53 | - |
|
54 | - /** @var array Registered notification methods */ |
|
55 | - protected $methods = array(); |
|
56 | - |
|
57 | - /** @var array Deprecated notification handlers */ |
|
58 | - protected $deprHandlers = array(); |
|
59 | - |
|
60 | - /** @var array Deprecated message subjects */ |
|
61 | - protected $deprSubjects = array(); |
|
62 | - |
|
63 | - /** |
|
64 | - * Constructor |
|
65 | - * |
|
66 | - * @param SubscriptionsService $subscriptions Subscription service |
|
67 | - * @param Queue $queue Queue |
|
68 | - * @param PluginHooksService $hooks Plugin hook service |
|
69 | - * @param ElggSession $session Session service |
|
70 | - * @param Translator $translator Translator |
|
71 | - * @param EntityTable $entities Entity table |
|
72 | - * @param Logger $logger Logger |
|
73 | - */ |
|
74 | - public function __construct( |
|
75 | - SubscriptionsService $subscriptions, |
|
76 | - Queue $queue, PluginHooksService $hooks, |
|
77 | - ElggSession $session, |
|
78 | - Translator $translator, |
|
79 | - EntityTable $entities, |
|
80 | - Logger $logger) { |
|
81 | - |
|
82 | - $this->subscriptions = $subscriptions; |
|
83 | - $this->queue = $queue; |
|
84 | - $this->hooks = $hooks; |
|
85 | - $this->session = $session; |
|
86 | - $this->translator = $translator; |
|
87 | - $this->entities = $entities; |
|
88 | - $this->logger = $logger; |
|
89 | - } |
|
90 | - |
|
91 | - /** |
|
92 | - * @see elgg_register_notification_event() |
|
93 | - * @access private |
|
94 | - */ |
|
95 | - public function registerEvent($type, $subtype, array $actions = array()) { |
|
96 | - |
|
97 | - if (!isset($this->events[$type])) { |
|
98 | - $this->events[$type] = array(); |
|
99 | - } |
|
100 | - if (!isset($this->events[$type][$subtype])) { |
|
101 | - $this->events[$type][$subtype] = array(); |
|
102 | - } |
|
103 | - |
|
104 | - $action_list =& $this->events[$type][$subtype]; |
|
105 | - if ($actions) { |
|
106 | - $action_list = array_unique(array_merge($action_list, $actions)); |
|
107 | - } elseif (!in_array('create', $action_list)) { |
|
108 | - $action_list[] = 'create'; |
|
109 | - } |
|
110 | - } |
|
111 | - |
|
112 | - /** |
|
113 | - * @see elgg_unregister_notification_event() |
|
114 | - * @access private |
|
115 | - */ |
|
116 | - public function unregisterEvent($type, $subtype) { |
|
117 | - |
|
118 | - if (!isset($this->events[$type]) || !isset($this->events[$type][$subtype])) { |
|
119 | - return false; |
|
120 | - } |
|
121 | - |
|
122 | - unset($this->events[$type][$subtype]); |
|
123 | - |
|
124 | - return true; |
|
125 | - } |
|
126 | - |
|
127 | - /** |
|
128 | - * @access private |
|
129 | - */ |
|
130 | - public function getEvents() { |
|
131 | - return $this->events; |
|
132 | - } |
|
133 | - |
|
134 | - /** |
|
135 | - * @see elgg_register_notification_method() |
|
136 | - * @access private |
|
137 | - */ |
|
138 | - public function registerMethod($name) { |
|
139 | - $this->methods[$name] = $name; |
|
140 | - } |
|
141 | - |
|
142 | - /** |
|
143 | - * @see elgg_unregister_notification_method() |
|
144 | - * @access private |
|
145 | - */ |
|
146 | - public function unregisterMethod($name) { |
|
147 | - if (isset($this->methods[$name])) { |
|
148 | - unset($this->methods[$name]); |
|
149 | - return true; |
|
150 | - } |
|
151 | - return false; |
|
152 | - } |
|
153 | - |
|
154 | - /** |
|
155 | - * @access private |
|
156 | - */ |
|
157 | - public function getMethods() { |
|
158 | - return $this->methods; |
|
159 | - } |
|
160 | - |
|
161 | - /** |
|
162 | - * Add a notification event to the queue |
|
163 | - * |
|
164 | - * @param string $action Action name |
|
165 | - * @param string $type Type of the object of the action |
|
166 | - * @param ElggData $object The object of the action |
|
167 | - * @return void |
|
168 | - * @access private |
|
169 | - */ |
|
170 | - public function enqueueEvent($action, $type, $object) { |
|
51 | + /** @var array Registered notification events */ |
|
52 | + protected $events = array(); |
|
53 | + |
|
54 | + /** @var array Registered notification methods */ |
|
55 | + protected $methods = array(); |
|
56 | + |
|
57 | + /** @var array Deprecated notification handlers */ |
|
58 | + protected $deprHandlers = array(); |
|
59 | + |
|
60 | + /** @var array Deprecated message subjects */ |
|
61 | + protected $deprSubjects = array(); |
|
62 | + |
|
63 | + /** |
|
64 | + * Constructor |
|
65 | + * |
|
66 | + * @param SubscriptionsService $subscriptions Subscription service |
|
67 | + * @param Queue $queue Queue |
|
68 | + * @param PluginHooksService $hooks Plugin hook service |
|
69 | + * @param ElggSession $session Session service |
|
70 | + * @param Translator $translator Translator |
|
71 | + * @param EntityTable $entities Entity table |
|
72 | + * @param Logger $logger Logger |
|
73 | + */ |
|
74 | + public function __construct( |
|
75 | + SubscriptionsService $subscriptions, |
|
76 | + Queue $queue, PluginHooksService $hooks, |
|
77 | + ElggSession $session, |
|
78 | + Translator $translator, |
|
79 | + EntityTable $entities, |
|
80 | + Logger $logger) { |
|
81 | + |
|
82 | + $this->subscriptions = $subscriptions; |
|
83 | + $this->queue = $queue; |
|
84 | + $this->hooks = $hooks; |
|
85 | + $this->session = $session; |
|
86 | + $this->translator = $translator; |
|
87 | + $this->entities = $entities; |
|
88 | + $this->logger = $logger; |
|
89 | + } |
|
90 | + |
|
91 | + /** |
|
92 | + * @see elgg_register_notification_event() |
|
93 | + * @access private |
|
94 | + */ |
|
95 | + public function registerEvent($type, $subtype, array $actions = array()) { |
|
96 | + |
|
97 | + if (!isset($this->events[$type])) { |
|
98 | + $this->events[$type] = array(); |
|
99 | + } |
|
100 | + if (!isset($this->events[$type][$subtype])) { |
|
101 | + $this->events[$type][$subtype] = array(); |
|
102 | + } |
|
103 | + |
|
104 | + $action_list =& $this->events[$type][$subtype]; |
|
105 | + if ($actions) { |
|
106 | + $action_list = array_unique(array_merge($action_list, $actions)); |
|
107 | + } elseif (!in_array('create', $action_list)) { |
|
108 | + $action_list[] = 'create'; |
|
109 | + } |
|
110 | + } |
|
111 | + |
|
112 | + /** |
|
113 | + * @see elgg_unregister_notification_event() |
|
114 | + * @access private |
|
115 | + */ |
|
116 | + public function unregisterEvent($type, $subtype) { |
|
117 | + |
|
118 | + if (!isset($this->events[$type]) || !isset($this->events[$type][$subtype])) { |
|
119 | + return false; |
|
120 | + } |
|
121 | + |
|
122 | + unset($this->events[$type][$subtype]); |
|
123 | + |
|
124 | + return true; |
|
125 | + } |
|
126 | + |
|
127 | + /** |
|
128 | + * @access private |
|
129 | + */ |
|
130 | + public function getEvents() { |
|
131 | + return $this->events; |
|
132 | + } |
|
133 | + |
|
134 | + /** |
|
135 | + * @see elgg_register_notification_method() |
|
136 | + * @access private |
|
137 | + */ |
|
138 | + public function registerMethod($name) { |
|
139 | + $this->methods[$name] = $name; |
|
140 | + } |
|
141 | + |
|
142 | + /** |
|
143 | + * @see elgg_unregister_notification_method() |
|
144 | + * @access private |
|
145 | + */ |
|
146 | + public function unregisterMethod($name) { |
|
147 | + if (isset($this->methods[$name])) { |
|
148 | + unset($this->methods[$name]); |
|
149 | + return true; |
|
150 | + } |
|
151 | + return false; |
|
152 | + } |
|
153 | + |
|
154 | + /** |
|
155 | + * @access private |
|
156 | + */ |
|
157 | + public function getMethods() { |
|
158 | + return $this->methods; |
|
159 | + } |
|
160 | + |
|
161 | + /** |
|
162 | + * Add a notification event to the queue |
|
163 | + * |
|
164 | + * @param string $action Action name |
|
165 | + * @param string $type Type of the object of the action |
|
166 | + * @param ElggData $object The object of the action |
|
167 | + * @return void |
|
168 | + * @access private |
|
169 | + */ |
|
170 | + public function enqueueEvent($action, $type, $object) { |
|
171 | 171 | |
172 | - if ($object instanceof ElggData) { |
|
173 | - $object_type = $object->getType(); |
|
174 | - $object_subtype = $object->getSubtype(); |
|
175 | - |
|
176 | - $registered = false; |
|
177 | - if (!empty($this->events[$object_type][$object_subtype]) && in_array($action, $this->events[$object_type][$object_subtype])) { |
|
178 | - $registered = true; |
|
179 | - } |
|
180 | - |
|
181 | - if ($registered) { |
|
182 | - $params = array( |
|
183 | - 'action' => $action, |
|
184 | - 'object' => $object, |
|
185 | - ); |
|
186 | - $registered = $this->hooks->trigger('enqueue', 'notification', $params, $registered); |
|
187 | - } |
|
188 | - |
|
189 | - if ($registered) { |
|
190 | - $this->queue->enqueue(new SubscriptionNotificationEvent($object, $action)); |
|
191 | - } |
|
192 | - } |
|
193 | - } |
|
194 | - |
|
195 | - /** |
|
196 | - * Pull notification events from queue until stop time is reached |
|
197 | - * |
|
198 | - * @param int $stopTime The Unix time to stop sending notifications |
|
199 | - * @param bool $matrix If true, will return delivery matrix instead of a notifications event count |
|
200 | - * @return int|array The number of notification events handled, or a delivery matrix |
|
201 | - * @access private |
|
202 | - */ |
|
203 | - public function processQueue($stopTime, $matrix = false) { |
|
204 | - |
|
205 | - $this->subscriptions->methods = $this->methods; |
|
206 | - |
|
207 | - $delivery_matrix = []; |
|
208 | - |
|
209 | - $count = 0; |
|
210 | - |
|
211 | - // @todo grab mutex |
|
212 | - |
|
213 | - $ia = $this->session->setIgnoreAccess(true); |
|
214 | - |
|
215 | - while (time() < $stopTime) { |
|
216 | - // dequeue notification event |
|
217 | - $event = $this->queue->dequeue(); |
|
218 | - /* @var $event NotificationEvent */ |
|
219 | - |
|
220 | - if (!$event) { |
|
221 | - // queue is empty |
|
222 | - break; |
|
223 | - } |
|
224 | - |
|
225 | - if (!$event instanceof NotificationEvent || !$event->getObject() || !$event->getActor()) { |
|
226 | - // event object or actor have been deleted since the event was enqueued |
|
227 | - continue; |
|
228 | - } |
|
172 | + if ($object instanceof ElggData) { |
|
173 | + $object_type = $object->getType(); |
|
174 | + $object_subtype = $object->getSubtype(); |
|
175 | + |
|
176 | + $registered = false; |
|
177 | + if (!empty($this->events[$object_type][$object_subtype]) && in_array($action, $this->events[$object_type][$object_subtype])) { |
|
178 | + $registered = true; |
|
179 | + } |
|
180 | + |
|
181 | + if ($registered) { |
|
182 | + $params = array( |
|
183 | + 'action' => $action, |
|
184 | + 'object' => $object, |
|
185 | + ); |
|
186 | + $registered = $this->hooks->trigger('enqueue', 'notification', $params, $registered); |
|
187 | + } |
|
188 | + |
|
189 | + if ($registered) { |
|
190 | + $this->queue->enqueue(new SubscriptionNotificationEvent($object, $action)); |
|
191 | + } |
|
192 | + } |
|
193 | + } |
|
194 | + |
|
195 | + /** |
|
196 | + * Pull notification events from queue until stop time is reached |
|
197 | + * |
|
198 | + * @param int $stopTime The Unix time to stop sending notifications |
|
199 | + * @param bool $matrix If true, will return delivery matrix instead of a notifications event count |
|
200 | + * @return int|array The number of notification events handled, or a delivery matrix |
|
201 | + * @access private |
|
202 | + */ |
|
203 | + public function processQueue($stopTime, $matrix = false) { |
|
204 | + |
|
205 | + $this->subscriptions->methods = $this->methods; |
|
206 | + |
|
207 | + $delivery_matrix = []; |
|
208 | + |
|
209 | + $count = 0; |
|
210 | + |
|
211 | + // @todo grab mutex |
|
212 | + |
|
213 | + $ia = $this->session->setIgnoreAccess(true); |
|
214 | + |
|
215 | + while (time() < $stopTime) { |
|
216 | + // dequeue notification event |
|
217 | + $event = $this->queue->dequeue(); |
|
218 | + /* @var $event NotificationEvent */ |
|
219 | + |
|
220 | + if (!$event) { |
|
221 | + // queue is empty |
|
222 | + break; |
|
223 | + } |
|
224 | + |
|
225 | + if (!$event instanceof NotificationEvent || !$event->getObject() || !$event->getActor()) { |
|
226 | + // event object or actor have been deleted since the event was enqueued |
|
227 | + continue; |
|
228 | + } |
|
229 | 229 | |
230 | - // test for usage of the deprecated override hook |
|
231 | - if ($this->existsDeprecatedNotificationOverride($event)) { |
|
232 | - continue; |
|
233 | - } |
|
230 | + // test for usage of the deprecated override hook |
|
231 | + if ($this->existsDeprecatedNotificationOverride($event)) { |
|
232 | + continue; |
|
233 | + } |
|
234 | 234 | |
235 | - $subscriptions = $this->subscriptions->getSubscriptions($event); |
|
235 | + $subscriptions = $this->subscriptions->getSubscriptions($event); |
|
236 | 236 | |
237 | - // return false to stop the default notification sender |
|
238 | - $params = [ |
|
239 | - 'event' => $event, |
|
240 | - 'subscriptions' => $subscriptions |
|
241 | - ]; |
|
237 | + // return false to stop the default notification sender |
|
238 | + $params = [ |
|
239 | + 'event' => $event, |
|
240 | + 'subscriptions' => $subscriptions |
|
241 | + ]; |
|
242 | 242 | |
243 | - $deliveries = []; |
|
244 | - if ($this->hooks->trigger('send:before', 'notifications', $params, true)) { |
|
245 | - $deliveries = $this->sendNotifications($event, $subscriptions); |
|
246 | - } |
|
247 | - $params['deliveries'] = $deliveries; |
|
248 | - $this->hooks->trigger('send:after', 'notifications', $params); |
|
249 | - $count++; |
|
250 | - |
|
251 | - $delivery_matrix[$event->getDescription()] = $deliveries; |
|
252 | - } |
|
253 | - |
|
254 | - // release mutex |
|
255 | - |
|
256 | - $this->session->setIgnoreAccess($ia); |
|
257 | - |
|
258 | - return $matrix ? $delivery_matrix : $count; |
|
259 | - } |
|
260 | - |
|
261 | - /** |
|
262 | - * Sends the notifications based on subscriptions |
|
263 | - * |
|
264 | - * Returns an array in the form: |
|
265 | - * <code> |
|
266 | - * [ |
|
267 | - * 25 => [ |
|
268 | - * 'email' => true, |
|
269 | - * 'sms' => false, |
|
270 | - * ], |
|
271 | - * 55 => [], |
|
272 | - * ] |
|
273 | - * </code> |
|
274 | - * |
|
275 | - * @param NotificationEvent $event Notification event |
|
276 | - * @param array $subscriptions Subscriptions for this event |
|
277 | - * @param array $params Default notification parameters |
|
278 | - * @return array |
|
279 | - * @access private |
|
280 | - */ |
|
281 | - protected function sendNotifications($event, $subscriptions, array $params = []) { |
|
282 | - |
|
283 | - if (!$this->methods) { |
|
284 | - return 0; |
|
285 | - } |
|
286 | - |
|
287 | - $result = []; |
|
288 | - foreach ($subscriptions as $guid => $methods) { |
|
289 | - foreach ($methods as $method) { |
|
290 | - $result[$guid][$method] = false; |
|
291 | - if (in_array($method, $this->methods)) { |
|
292 | - $result[$guid][$method] = $this->sendNotification($event, $guid, $method, $params); |
|
293 | - } |
|
294 | - } |
|
295 | - } |
|
296 | - |
|
297 | - $this->logger->notice("Results for the notification event {$event->getDescription()}: " . print_r($result, true)); |
|
298 | - return $result; |
|
299 | - } |
|
300 | - |
|
301 | - /** |
|
302 | - * Notify a user via their preferences. |
|
303 | - * |
|
304 | - * Returns an array in the form: |
|
305 | - * <code> |
|
306 | - * [ |
|
307 | - * 25 => [ |
|
308 | - * 'email' => true, |
|
309 | - * 'sms' => false, |
|
310 | - * ], |
|
311 | - * 55 => [], |
|
312 | - * ] |
|
313 | - * </code> |
|
314 | - * |
|
315 | - * @param ElggEntity $sender Sender of the notification |
|
316 | - * @param ElggUser[] $recipients An array of entities to notify |
|
317 | - * @param array $params Notification parameters |
|
318 | - * |
|
319 | - * @uses $params['subject'] string |
|
320 | - * Default message subject |
|
321 | - * @uses $params['body'] string |
|
322 | - * Default message body |
|
323 | - * @uses $params['object'] null|\ElggEntity|\ElggAnnotation |
|
324 | - * The object that is triggering the notification. |
|
325 | - * @uses $params['action'] null|string |
|
326 | - * Word that describes the action that is triggering the notification |
|
327 | - * (e.g. "create" or "update"). Defaults to "notify_user" |
|
328 | - * @uses $params['summary'] null|string |
|
329 | - * Summary that notification plugins can use alongside the notification title and body. |
|
330 | - * @uses $params['methods_override'] string|array |
|
331 | - * A string, or an array of strings specifying the delivery |
|
332 | - * methods to use - or leave blank for delivery using the |
|
333 | - * user's chosen delivery methods. |
|
334 | - * |
|
335 | - * @return array |
|
336 | - * @access private |
|
337 | - */ |
|
338 | - public function sendInstantNotifications(\ElggEntity $sender, array $recipients = [], array $params = []) { |
|
339 | - |
|
340 | - if (!$sender instanceof \ElggEntity) { |
|
341 | - throw new InvalidArgumentException("Notification sender must be a valid entity"); |
|
342 | - } |
|
243 | + $deliveries = []; |
|
244 | + if ($this->hooks->trigger('send:before', 'notifications', $params, true)) { |
|
245 | + $deliveries = $this->sendNotifications($event, $subscriptions); |
|
246 | + } |
|
247 | + $params['deliveries'] = $deliveries; |
|
248 | + $this->hooks->trigger('send:after', 'notifications', $params); |
|
249 | + $count++; |
|
250 | + |
|
251 | + $delivery_matrix[$event->getDescription()] = $deliveries; |
|
252 | + } |
|
253 | + |
|
254 | + // release mutex |
|
255 | + |
|
256 | + $this->session->setIgnoreAccess($ia); |
|
257 | + |
|
258 | + return $matrix ? $delivery_matrix : $count; |
|
259 | + } |
|
260 | + |
|
261 | + /** |
|
262 | + * Sends the notifications based on subscriptions |
|
263 | + * |
|
264 | + * Returns an array in the form: |
|
265 | + * <code> |
|
266 | + * [ |
|
267 | + * 25 => [ |
|
268 | + * 'email' => true, |
|
269 | + * 'sms' => false, |
|
270 | + * ], |
|
271 | + * 55 => [], |
|
272 | + * ] |
|
273 | + * </code> |
|
274 | + * |
|
275 | + * @param NotificationEvent $event Notification event |
|
276 | + * @param array $subscriptions Subscriptions for this event |
|
277 | + * @param array $params Default notification parameters |
|
278 | + * @return array |
|
279 | + * @access private |
|
280 | + */ |
|
281 | + protected function sendNotifications($event, $subscriptions, array $params = []) { |
|
282 | + |
|
283 | + if (!$this->methods) { |
|
284 | + return 0; |
|
285 | + } |
|
286 | + |
|
287 | + $result = []; |
|
288 | + foreach ($subscriptions as $guid => $methods) { |
|
289 | + foreach ($methods as $method) { |
|
290 | + $result[$guid][$method] = false; |
|
291 | + if (in_array($method, $this->methods)) { |
|
292 | + $result[$guid][$method] = $this->sendNotification($event, $guid, $method, $params); |
|
293 | + } |
|
294 | + } |
|
295 | + } |
|
296 | + |
|
297 | + $this->logger->notice("Results for the notification event {$event->getDescription()}: " . print_r($result, true)); |
|
298 | + return $result; |
|
299 | + } |
|
300 | + |
|
301 | + /** |
|
302 | + * Notify a user via their preferences. |
|
303 | + * |
|
304 | + * Returns an array in the form: |
|
305 | + * <code> |
|
306 | + * [ |
|
307 | + * 25 => [ |
|
308 | + * 'email' => true, |
|
309 | + * 'sms' => false, |
|
310 | + * ], |
|
311 | + * 55 => [], |
|
312 | + * ] |
|
313 | + * </code> |
|
314 | + * |
|
315 | + * @param ElggEntity $sender Sender of the notification |
|
316 | + * @param ElggUser[] $recipients An array of entities to notify |
|
317 | + * @param array $params Notification parameters |
|
318 | + * |
|
319 | + * @uses $params['subject'] string |
|
320 | + * Default message subject |
|
321 | + * @uses $params['body'] string |
|
322 | + * Default message body |
|
323 | + * @uses $params['object'] null|\ElggEntity|\ElggAnnotation |
|
324 | + * The object that is triggering the notification. |
|
325 | + * @uses $params['action'] null|string |
|
326 | + * Word that describes the action that is triggering the notification |
|
327 | + * (e.g. "create" or "update"). Defaults to "notify_user" |
|
328 | + * @uses $params['summary'] null|string |
|
329 | + * Summary that notification plugins can use alongside the notification title and body. |
|
330 | + * @uses $params['methods_override'] string|array |
|
331 | + * A string, or an array of strings specifying the delivery |
|
332 | + * methods to use - or leave blank for delivery using the |
|
333 | + * user's chosen delivery methods. |
|
334 | + * |
|
335 | + * @return array |
|
336 | + * @access private |
|
337 | + */ |
|
338 | + public function sendInstantNotifications(\ElggEntity $sender, array $recipients = [], array $params = []) { |
|
339 | + |
|
340 | + if (!$sender instanceof \ElggEntity) { |
|
341 | + throw new InvalidArgumentException("Notification sender must be a valid entity"); |
|
342 | + } |
|
343 | 343 | |
344 | - $deliveries = []; |
|
344 | + $deliveries = []; |
|
345 | 345 | |
346 | - if (!$this->methods) { |
|
347 | - return $deliveries; |
|
348 | - } |
|
346 | + if (!$this->methods) { |
|
347 | + return $deliveries; |
|
348 | + } |
|
349 | 349 | |
350 | - $recipients = array_filter($recipients, function($e) { |
|
351 | - return ($e instanceof \ElggUser); |
|
352 | - }); |
|
350 | + $recipients = array_filter($recipients, function($e) { |
|
351 | + return ($e instanceof \ElggUser); |
|
352 | + }); |
|
353 | 353 | |
354 | - $object = elgg_extract('object', $params); |
|
355 | - $action = elgg_extract('action', $params); |
|
356 | - |
|
357 | - $methods_override = elgg_extract('methods_override', $params); |
|
358 | - unset($params['methods_override']); |
|
359 | - if ($methods_override && !is_array($methods_override)) { |
|
360 | - $methods_override = [$methods_override]; |
|
361 | - } |
|
362 | - |
|
363 | - $event = new InstantNotificationEvent($object, $action, $sender); |
|
364 | - |
|
365 | - $params['event'] = $event; |
|
366 | - $params['origin'] = Notification::ORIGIN_INSTANT; |
|
367 | - |
|
368 | - $subscriptions = []; |
|
369 | - |
|
370 | - foreach ($recipients as $recipient) { |
|
371 | - |
|
372 | - // Are we overriding delivery? |
|
373 | - $methods = $methods_override; |
|
374 | - if (empty($methods)) { |
|
375 | - $methods = []; |
|
376 | - $user_settings = $recipient->getNotificationSettings(); |
|
377 | - foreach ($user_settings as $method => $enabled) { |
|
378 | - if ($enabled) { |
|
379 | - $methods[] = $method; |
|
380 | - } |
|
381 | - } |
|
382 | - } |
|
383 | - |
|
384 | - $subscriptions[$recipient->guid] = $methods; |
|
385 | - } |
|
386 | - |
|
387 | - $hook_params = [ |
|
388 | - 'event' => $params['event'], |
|
389 | - 'origin' => $params['origin'], |
|
390 | - 'methods_override' => $methods_override, |
|
391 | - ]; |
|
392 | - $subscriptions = $this->hooks->trigger('get', 'subscriptions', $hook_params, $subscriptions); |
|
354 | + $object = elgg_extract('object', $params); |
|
355 | + $action = elgg_extract('action', $params); |
|
356 | + |
|
357 | + $methods_override = elgg_extract('methods_override', $params); |
|
358 | + unset($params['methods_override']); |
|
359 | + if ($methods_override && !is_array($methods_override)) { |
|
360 | + $methods_override = [$methods_override]; |
|
361 | + } |
|
362 | + |
|
363 | + $event = new InstantNotificationEvent($object, $action, $sender); |
|
364 | + |
|
365 | + $params['event'] = $event; |
|
366 | + $params['origin'] = Notification::ORIGIN_INSTANT; |
|
367 | + |
|
368 | + $subscriptions = []; |
|
369 | + |
|
370 | + foreach ($recipients as $recipient) { |
|
371 | + |
|
372 | + // Are we overriding delivery? |
|
373 | + $methods = $methods_override; |
|
374 | + if (empty($methods)) { |
|
375 | + $methods = []; |
|
376 | + $user_settings = $recipient->getNotificationSettings(); |
|
377 | + foreach ($user_settings as $method => $enabled) { |
|
378 | + if ($enabled) { |
|
379 | + $methods[] = $method; |
|
380 | + } |
|
381 | + } |
|
382 | + } |
|
383 | + |
|
384 | + $subscriptions[$recipient->guid] = $methods; |
|
385 | + } |
|
386 | + |
|
387 | + $hook_params = [ |
|
388 | + 'event' => $params['event'], |
|
389 | + 'origin' => $params['origin'], |
|
390 | + 'methods_override' => $methods_override, |
|
391 | + ]; |
|
392 | + $subscriptions = $this->hooks->trigger('get', 'subscriptions', $hook_params, $subscriptions); |
|
393 | 393 | |
394 | - $params['subscriptions'] = $subscriptions; |
|
395 | - |
|
396 | - // return false to stop the default notification sender |
|
397 | - if ($this->hooks->trigger('send:before', 'notifications', $params, true)) { |
|
398 | - $deliveries = $this->sendNotifications($event, $subscriptions, $params); |
|
399 | - } |
|
400 | - $params['deliveries'] = $deliveries; |
|
401 | - $this->hooks->trigger('send:after', 'notifications', $params); |
|
402 | - |
|
403 | - return $deliveries; |
|
404 | - } |
|
405 | - |
|
406 | - /** |
|
407 | - * Send a notification to a subscriber |
|
408 | - * |
|
409 | - * @param NotificationEvent $event The notification event |
|
410 | - * @param int $guid The guid of the subscriber |
|
411 | - * @param string $method The notification method |
|
412 | - * @param array $params Default notification params |
|
413 | - * @return bool |
|
414 | - * @access private |
|
415 | - */ |
|
416 | - protected function sendNotification(NotificationEvent $event, $guid, $method, array $params = []) { |
|
417 | - |
|
418 | - $actor = $event->getActor(); |
|
419 | - $object = $event->getObject(); |
|
420 | - |
|
421 | - if ($event instanceof InstantNotificationEvent) { |
|
422 | - $recipient = $this->entities->get($guid); |
|
423 | - /* @var \ElggEntity $recipient */ |
|
424 | - $subject = elgg_extract('subject', $params, ''); |
|
425 | - $body = elgg_extract('body', $params, ''); |
|
426 | - $summary = elgg_extract('summary', $params, ''); |
|
427 | - } else { |
|
428 | - $recipient = $this->entities->get($guid, 'user'); |
|
429 | - /* @var \ElggUser $recipient */ |
|
430 | - if (!$recipient || $recipient->isBanned()) { |
|
431 | - return false; |
|
432 | - } |
|
394 | + $params['subscriptions'] = $subscriptions; |
|
395 | + |
|
396 | + // return false to stop the default notification sender |
|
397 | + if ($this->hooks->trigger('send:before', 'notifications', $params, true)) { |
|
398 | + $deliveries = $this->sendNotifications($event, $subscriptions, $params); |
|
399 | + } |
|
400 | + $params['deliveries'] = $deliveries; |
|
401 | + $this->hooks->trigger('send:after', 'notifications', $params); |
|
402 | + |
|
403 | + return $deliveries; |
|
404 | + } |
|
405 | + |
|
406 | + /** |
|
407 | + * Send a notification to a subscriber |
|
408 | + * |
|
409 | + * @param NotificationEvent $event The notification event |
|
410 | + * @param int $guid The guid of the subscriber |
|
411 | + * @param string $method The notification method |
|
412 | + * @param array $params Default notification params |
|
413 | + * @return bool |
|
414 | + * @access private |
|
415 | + */ |
|
416 | + protected function sendNotification(NotificationEvent $event, $guid, $method, array $params = []) { |
|
417 | + |
|
418 | + $actor = $event->getActor(); |
|
419 | + $object = $event->getObject(); |
|
420 | + |
|
421 | + if ($event instanceof InstantNotificationEvent) { |
|
422 | + $recipient = $this->entities->get($guid); |
|
423 | + /* @var \ElggEntity $recipient */ |
|
424 | + $subject = elgg_extract('subject', $params, ''); |
|
425 | + $body = elgg_extract('body', $params, ''); |
|
426 | + $summary = elgg_extract('summary', $params, ''); |
|
427 | + } else { |
|
428 | + $recipient = $this->entities->get($guid, 'user'); |
|
429 | + /* @var \ElggUser $recipient */ |
|
430 | + if (!$recipient || $recipient->isBanned()) { |
|
431 | + return false; |
|
432 | + } |
|
433 | 433 | |
434 | - if ($recipient->getGUID() == $event->getActorGUID()) { |
|
435 | - // Content creators should not be receiving subscription |
|
436 | - // notifications about their own content |
|
437 | - return false; |
|
438 | - } |
|
434 | + if ($recipient->getGUID() == $event->getActorGUID()) { |
|
435 | + // Content creators should not be receiving subscription |
|
436 | + // notifications about their own content |
|
437 | + return false; |
|
438 | + } |
|
439 | 439 | |
440 | - if (!$actor || !$object) { |
|
441 | - return false; |
|
442 | - } |
|
443 | - |
|
444 | - if ($object instanceof ElggEntity && !has_access_to_entity($object, $recipient)) { |
|
445 | - // Recipient does not have access to the notification object |
|
446 | - // The access level may have changed since the event was enqueued |
|
447 | - return false; |
|
448 | - } |
|
449 | - |
|
450 | - $subject = $this->getNotificationSubject($event, $recipient); |
|
451 | - $body = $this->getNotificationBody($event, $recipient); |
|
452 | - $summary = ''; |
|
440 | + if (!$actor || !$object) { |
|
441 | + return false; |
|
442 | + } |
|
443 | + |
|
444 | + if ($object instanceof ElggEntity && !has_access_to_entity($object, $recipient)) { |
|
445 | + // Recipient does not have access to the notification object |
|
446 | + // The access level may have changed since the event was enqueued |
|
447 | + return false; |
|
448 | + } |
|
449 | + |
|
450 | + $subject = $this->getNotificationSubject($event, $recipient); |
|
451 | + $body = $this->getNotificationBody($event, $recipient); |
|
452 | + $summary = ''; |
|
453 | 453 | |
454 | - $params['origin'] = Notification::ORIGIN_SUBSCRIPTIONS; |
|
455 | - } |
|
456 | - |
|
457 | - $language = $recipient->language; |
|
458 | - $params['event'] = $event; |
|
459 | - $params['method'] = $method; |
|
460 | - $params['sender'] = $actor; |
|
461 | - $params['recipient'] = $recipient; |
|
462 | - $params['language'] = $language; |
|
463 | - $params['object'] = $object; |
|
464 | - $params['action'] = $event->getAction(); |
|
465 | - |
|
466 | - $notification = new Notification($actor, $recipient, $language, $subject, $body, $summary, $params); |
|
467 | - |
|
468 | - $notification = $this->hooks->trigger('prepare', 'notification', $params, $notification); |
|
469 | - if (!$notification instanceof Notification) { |
|
470 | - throw new RuntimeException("'prepare','notification' hook must return an instance of " . Notification::class); |
|
471 | - } |
|
472 | - |
|
473 | - $type = 'notification:' . $event->getDescription(); |
|
474 | - if ($this->hooks->hasHandler('prepare', $type)) { |
|
475 | - $notification = $this->hooks->trigger('prepare', $type, $params, $notification); |
|
476 | - if (!$notification instanceof Notification) { |
|
477 | - throw new RuntimeException("'prepare','$type' hook must return an instance of " . Notification::class); |
|
478 | - } |
|
479 | - } else { |
|
480 | - // pre Elgg 1.9 notification message generation |
|
481 | - $notification = $this->getDeprecatedNotificationBody($notification, $event, $method); |
|
482 | - } |
|
483 | - |
|
484 | - $notification = $this->hooks->trigger('format', "notification:$method", [], $notification); |
|
485 | - if (!$notification instanceof Notification) { |
|
486 | - throw new RuntimeException("'format','notification:$method' hook must return an instance of " . Notification::class); |
|
487 | - } |
|
488 | - |
|
489 | - if ($this->hooks->hasHandler('send', "notification:$method")) { |
|
490 | - // return true to indicate the notification has been sent |
|
491 | - $params = array( |
|
492 | - 'notification' => $notification, |
|
493 | - 'event' => $event, |
|
494 | - ); |
|
495 | - |
|
496 | - $result = $this->hooks->trigger('send', "notification:$method", $params, false); |
|
497 | - if ($this->logger->getLevel() == Logger::INFO) { |
|
498 | - $logger_data = print_r((array) $notification->toObject(), true); |
|
499 | - if ($result) { |
|
500 | - $this->logger->info("Notification sent: " . $logger_data); |
|
501 | - } else { |
|
502 | - $this->logger->info("Notification was not sent: " . $logger_data); |
|
503 | - } |
|
504 | - } |
|
505 | - return $result; |
|
506 | - } else { |
|
507 | - // pre Elgg 1.9 notification handler |
|
508 | - $userGuid = $notification->getRecipientGUID(); |
|
509 | - $senderGuid = $notification->getSenderGUID(); |
|
510 | - $subject = $notification->subject; |
|
511 | - $body = $notification->body; |
|
512 | - $params = $notification->params; |
|
513 | - return (bool) _elgg_notify_user($userGuid, $senderGuid, $subject, $body, $params, array($method)); |
|
514 | - } |
|
515 | - } |
|
516 | - |
|
517 | - /** |
|
518 | - * Get subject for the notification |
|
519 | - * |
|
520 | - * Plugins can define a subtype specific subject simply by providing a |
|
521 | - * translation for the string "notification:subject:<action>:<type>:<subtype". |
|
522 | - * |
|
523 | - * For example in mod/blog/languages/en.php: |
|
524 | - * |
|
525 | - * 'notification:subject:publish:object:blog' => '%s published a blog called %s' |
|
526 | - * |
|
527 | - * @param NotificationEvent $event Notification event |
|
528 | - * @param ElggUser $recipient Notification recipient |
|
529 | - * @return string Notification subject in the recipient's language |
|
530 | - */ |
|
531 | - private function getNotificationSubject(NotificationEvent $event, ElggUser $recipient) { |
|
532 | - $actor = $event->getActor(); |
|
533 | - $object = $event->getObject(); |
|
534 | - /* @var \ElggObject $object */ |
|
535 | - $language = $recipient->language; |
|
536 | - |
|
537 | - // Check custom notification subject for the action/type/subtype combination |
|
538 | - $subject_key = "notification:{$event->getDescription()}:subject"; |
|
539 | - if ($this->translator->languageKeyExists($subject_key, $language)) { |
|
540 | - if ($object instanceof \ElggEntity) { |
|
541 | - $display_name = $object->getDisplayName(); |
|
542 | - } else { |
|
543 | - $display_name = ''; |
|
544 | - } |
|
545 | - return $this->translator->translate($subject_key, array( |
|
546 | - $actor->name, |
|
547 | - $display_name, |
|
548 | - ), $language); |
|
549 | - } |
|
550 | - |
|
551 | - // Fall back to default subject |
|
552 | - return $this->translator->translate('notification:subject', array($actor->name), $language); |
|
553 | - } |
|
554 | - |
|
555 | - /** |
|
556 | - * Get body for the notification |
|
557 | - * |
|
558 | - * Plugin can define a subtype specific body simply by providing a |
|
559 | - * translation for the string "notification:body:<action>:<type>:<subtype". |
|
560 | - * |
|
561 | - * For example in mod/blog/languages/en.php: |
|
562 | - * |
|
563 | - * 'notification:body:publish:object:blog' => ' |
|
564 | - * Hi %s! |
|
565 | - * |
|
566 | - * %s has created a new post called "%s" in the group %s. |
|
567 | - * |
|
568 | - * It says: |
|
569 | - * |
|
570 | - * "%s" |
|
571 | - * |
|
572 | - * You can comment the post here: |
|
573 | - * %s |
|
574 | - * ', |
|
575 | - * |
|
576 | - * The arguments passed into the translation are: |
|
577 | - * 1. Recipient's name |
|
578 | - * 2. Name of the user who triggered the notification |
|
579 | - * 3. Title of the content |
|
580 | - * 4. Name of the content's container |
|
581 | - * 5. The actual content (entity's 'description' field) |
|
582 | - * 6. URL to the content |
|
583 | - * |
|
584 | - * Argument swapping can be used to change the order of the parameters. |
|
585 | - * See http://php.net/manual/en/function.sprintf.php#example-5427 |
|
586 | - * |
|
587 | - * @param NotificationEvent $event Notification event |
|
588 | - * @param ElggUser $recipient Notification recipient |
|
589 | - * @return string Notification body in the recipient's language |
|
590 | - */ |
|
591 | - private function getNotificationBody(NotificationEvent $event, ElggUser $recipient) { |
|
592 | - $actor = $event->getActor(); |
|
593 | - $object = $event->getObject(); |
|
594 | - /* @var \ElggObject $object */ |
|
595 | - $language = $recipient->language; |
|
596 | - |
|
597 | - // Check custom notification body for the action/type/subtype combination |
|
598 | - $body_key = "notification:{$event->getDescription()}:body"; |
|
599 | - if ($this->translator->languageKeyExists($body_key, $language)) { |
|
600 | - if ($object instanceof \ElggEntity) { |
|
601 | - $display_name = $object->getDisplayName(); |
|
602 | - $container_name = ''; |
|
603 | - $container = $object->getContainerEntity(); |
|
604 | - if ($container) { |
|
605 | - $container_name = $container->getDisplayName(); |
|
606 | - } |
|
607 | - } else { |
|
608 | - $display_name = ''; |
|
609 | - $container_name = ''; |
|
610 | - } |
|
611 | - |
|
612 | - return $this->translator->translate($body_key, array( |
|
613 | - $recipient->name, |
|
614 | - $actor->name, |
|
615 | - $display_name, |
|
616 | - $container_name, |
|
617 | - $object->description, |
|
618 | - $object->getURL(), |
|
619 | - ), $language); |
|
620 | - } |
|
621 | - |
|
622 | - // Fall back to default body |
|
623 | - return $this->translator->translate('notification:body', array($object->getURL()), $language); |
|
624 | - } |
|
625 | - |
|
626 | - /** |
|
627 | - * Register a deprecated notification handler |
|
628 | - * |
|
629 | - * @param string $method Method name |
|
630 | - * @param string $handler Handler callback |
|
631 | - * @return void |
|
632 | - */ |
|
633 | - public function registerDeprecatedHandler($method, $handler) { |
|
634 | - $this->deprHandlers[$method] = $handler; |
|
635 | - } |
|
636 | - |
|
637 | - /** |
|
638 | - * Get a deprecated notification handler callback |
|
639 | - * |
|
640 | - * @param string $method Method name |
|
641 | - * @return callback|null |
|
642 | - */ |
|
643 | - public function getDeprecatedHandler($method) { |
|
644 | - if (isset($this->deprHandlers[$method])) { |
|
645 | - return $this->deprHandlers[$method]; |
|
646 | - } else { |
|
647 | - return null; |
|
648 | - } |
|
649 | - } |
|
650 | - |
|
651 | - /** |
|
652 | - * Provides a way to incrementally wean Elgg's notifications code from the |
|
653 | - * global $NOTIFICATION_HANDLERS |
|
654 | - * |
|
655 | - * @return array |
|
656 | - */ |
|
657 | - public function getMethodsAsDeprecatedGlobal() { |
|
658 | - $data = array(); |
|
659 | - foreach ($this->methods as $method) { |
|
660 | - $data[$method] = 'empty'; |
|
661 | - } |
|
662 | - return $data; |
|
663 | - } |
|
664 | - |
|
665 | - /** |
|
666 | - * Get the notification body using a pre-Elgg 1.9 plugin hook |
|
667 | - * |
|
668 | - * @param Notification $notification Notification |
|
669 | - * @param NotificationEvent $event Event |
|
670 | - * @param string $method Method |
|
671 | - * @return Notification |
|
672 | - */ |
|
673 | - protected function getDeprecatedNotificationBody(Notification $notification, NotificationEvent $event, $method) { |
|
674 | - $entity = $event->getObject(); |
|
675 | - if (!$entity) { |
|
676 | - return $notification; |
|
677 | - } |
|
678 | - $params = array( |
|
679 | - 'entity' => $entity, |
|
680 | - 'to_entity' => $notification->getRecipient(), |
|
681 | - 'method' => $method, |
|
682 | - ); |
|
683 | - $subject = $this->getDeprecatedNotificationSubject($entity->getType(), $entity->getSubtype()); |
|
684 | - $string = $subject . ": " . $entity->getURL(); |
|
685 | - $body = $this->hooks->trigger('notify:entity:message', $entity->getType(), $params, $string); |
|
686 | - |
|
687 | - if ($subject) { |
|
688 | - $notification->subject = $subject; |
|
689 | - $notification->body = $body; |
|
690 | - } |
|
691 | - |
|
692 | - return $notification; |
|
693 | - } |
|
694 | - |
|
695 | - /** |
|
696 | - * Set message subject for deprecated notification code |
|
697 | - * |
|
698 | - * @param string $type Entity type |
|
699 | - * @param string $subtype Entity subtype |
|
700 | - * @param string $subject Subject line |
|
701 | - * @return void |
|
702 | - */ |
|
703 | - public function setDeprecatedNotificationSubject($type, $subtype, $subject) { |
|
704 | - if ($type == '') { |
|
705 | - $type = '__BLANK__'; |
|
706 | - } |
|
707 | - if ($subtype == '') { |
|
708 | - $subtype = '__BLANK__'; |
|
709 | - } |
|
710 | - |
|
711 | - if (!isset($this->deprSubjects[$type])) { |
|
712 | - $this->deprSubjects[$type] = array(); |
|
713 | - } |
|
714 | - |
|
715 | - $this->deprSubjects[$type][$subtype] = $subject; |
|
716 | - } |
|
717 | - |
|
718 | - /** |
|
719 | - * Get the deprecated subject |
|
720 | - * |
|
721 | - * @param string $type Entity type |
|
722 | - * @param string $subtype Entity subtype |
|
723 | - * @return string |
|
724 | - */ |
|
725 | - protected function getDeprecatedNotificationSubject($type, $subtype) { |
|
726 | - if ($type == '') { |
|
727 | - $type = '__BLANK__'; |
|
728 | - } |
|
729 | - if ($subtype == '') { |
|
730 | - $subtype = '__BLANK__'; |
|
731 | - } |
|
732 | - |
|
733 | - if (!isset($this->deprSubjects[$type])) { |
|
734 | - return ''; |
|
735 | - } |
|
736 | - |
|
737 | - if (!isset($this->deprSubjects[$type][$subtype])) { |
|
738 | - return ''; |
|
739 | - } |
|
740 | - |
|
741 | - return $this->deprSubjects[$type][$subtype]; |
|
742 | - } |
|
743 | - |
|
744 | - /** |
|
745 | - * Is someone using the deprecated override |
|
746 | - * |
|
747 | - * @param NotificationEvent $event Event |
|
748 | - * @return boolean |
|
749 | - */ |
|
750 | - protected function existsDeprecatedNotificationOverride(NotificationEvent $event) { |
|
751 | - $entity = $event->getObject(); |
|
752 | - if (!elgg_instanceof($entity)) { |
|
753 | - return false; |
|
754 | - } |
|
755 | - $params = array( |
|
756 | - 'event' => $event->getAction(), |
|
757 | - 'object_type' => $entity->getType(), |
|
758 | - 'object' => $entity, |
|
759 | - ); |
|
760 | - $hookresult = $this->hooks->trigger('object:notifications', $entity->getType(), $params, false); |
|
761 | - if ($hookresult === true) { |
|
762 | - elgg_deprecated_notice("Using the plugin hook 'object:notifications' has been deprecated " |
|
763 | - . "by the hook 'send:before', 'notifications'", 1.9); |
|
764 | - return true; |
|
765 | - } else { |
|
766 | - return false; |
|
767 | - } |
|
768 | - } |
|
454 | + $params['origin'] = Notification::ORIGIN_SUBSCRIPTIONS; |
|
455 | + } |
|
456 | + |
|
457 | + $language = $recipient->language; |
|
458 | + $params['event'] = $event; |
|
459 | + $params['method'] = $method; |
|
460 | + $params['sender'] = $actor; |
|
461 | + $params['recipient'] = $recipient; |
|
462 | + $params['language'] = $language; |
|
463 | + $params['object'] = $object; |
|
464 | + $params['action'] = $event->getAction(); |
|
465 | + |
|
466 | + $notification = new Notification($actor, $recipient, $language, $subject, $body, $summary, $params); |
|
467 | + |
|
468 | + $notification = $this->hooks->trigger('prepare', 'notification', $params, $notification); |
|
469 | + if (!$notification instanceof Notification) { |
|
470 | + throw new RuntimeException("'prepare','notification' hook must return an instance of " . Notification::class); |
|
471 | + } |
|
472 | + |
|
473 | + $type = 'notification:' . $event->getDescription(); |
|
474 | + if ($this->hooks->hasHandler('prepare', $type)) { |
|
475 | + $notification = $this->hooks->trigger('prepare', $type, $params, $notification); |
|
476 | + if (!$notification instanceof Notification) { |
|
477 | + throw new RuntimeException("'prepare','$type' hook must return an instance of " . Notification::class); |
|
478 | + } |
|
479 | + } else { |
|
480 | + // pre Elgg 1.9 notification message generation |
|
481 | + $notification = $this->getDeprecatedNotificationBody($notification, $event, $method); |
|
482 | + } |
|
483 | + |
|
484 | + $notification = $this->hooks->trigger('format', "notification:$method", [], $notification); |
|
485 | + if (!$notification instanceof Notification) { |
|
486 | + throw new RuntimeException("'format','notification:$method' hook must return an instance of " . Notification::class); |
|
487 | + } |
|
488 | + |
|
489 | + if ($this->hooks->hasHandler('send', "notification:$method")) { |
|
490 | + // return true to indicate the notification has been sent |
|
491 | + $params = array( |
|
492 | + 'notification' => $notification, |
|
493 | + 'event' => $event, |
|
494 | + ); |
|
495 | + |
|
496 | + $result = $this->hooks->trigger('send', "notification:$method", $params, false); |
|
497 | + if ($this->logger->getLevel() == Logger::INFO) { |
|
498 | + $logger_data = print_r((array) $notification->toObject(), true); |
|
499 | + if ($result) { |
|
500 | + $this->logger->info("Notification sent: " . $logger_data); |
|
501 | + } else { |
|
502 | + $this->logger->info("Notification was not sent: " . $logger_data); |
|
503 | + } |
|
504 | + } |
|
505 | + return $result; |
|
506 | + } else { |
|
507 | + // pre Elgg 1.9 notification handler |
|
508 | + $userGuid = $notification->getRecipientGUID(); |
|
509 | + $senderGuid = $notification->getSenderGUID(); |
|
510 | + $subject = $notification->subject; |
|
511 | + $body = $notification->body; |
|
512 | + $params = $notification->params; |
|
513 | + return (bool) _elgg_notify_user($userGuid, $senderGuid, $subject, $body, $params, array($method)); |
|
514 | + } |
|
515 | + } |
|
516 | + |
|
517 | + /** |
|
518 | + * Get subject for the notification |
|
519 | + * |
|
520 | + * Plugins can define a subtype specific subject simply by providing a |
|
521 | + * translation for the string "notification:subject:<action>:<type>:<subtype". |
|
522 | + * |
|
523 | + * For example in mod/blog/languages/en.php: |
|
524 | + * |
|
525 | + * 'notification:subject:publish:object:blog' => '%s published a blog called %s' |
|
526 | + * |
|
527 | + * @param NotificationEvent $event Notification event |
|
528 | + * @param ElggUser $recipient Notification recipient |
|
529 | + * @return string Notification subject in the recipient's language |
|
530 | + */ |
|
531 | + private function getNotificationSubject(NotificationEvent $event, ElggUser $recipient) { |
|
532 | + $actor = $event->getActor(); |
|
533 | + $object = $event->getObject(); |
|
534 | + /* @var \ElggObject $object */ |
|
535 | + $language = $recipient->language; |
|
536 | + |
|
537 | + // Check custom notification subject for the action/type/subtype combination |
|
538 | + $subject_key = "notification:{$event->getDescription()}:subject"; |
|
539 | + if ($this->translator->languageKeyExists($subject_key, $language)) { |
|
540 | + if ($object instanceof \ElggEntity) { |
|
541 | + $display_name = $object->getDisplayName(); |
|
542 | + } else { |
|
543 | + $display_name = ''; |
|
544 | + } |
|
545 | + return $this->translator->translate($subject_key, array( |
|
546 | + $actor->name, |
|
547 | + $display_name, |
|
548 | + ), $language); |
|
549 | + } |
|
550 | + |
|
551 | + // Fall back to default subject |
|
552 | + return $this->translator->translate('notification:subject', array($actor->name), $language); |
|
553 | + } |
|
554 | + |
|
555 | + /** |
|
556 | + * Get body for the notification |
|
557 | + * |
|
558 | + * Plugin can define a subtype specific body simply by providing a |
|
559 | + * translation for the string "notification:body:<action>:<type>:<subtype". |
|
560 | + * |
|
561 | + * For example in mod/blog/languages/en.php: |
|
562 | + * |
|
563 | + * 'notification:body:publish:object:blog' => ' |
|
564 | + * Hi %s! |
|
565 | + * |
|
566 | + * %s has created a new post called "%s" in the group %s. |
|
567 | + * |
|
568 | + * It says: |
|
569 | + * |
|
570 | + * "%s" |
|
571 | + * |
|
572 | + * You can comment the post here: |
|
573 | + * %s |
|
574 | + * ', |
|
575 | + * |
|
576 | + * The arguments passed into the translation are: |
|
577 | + * 1. Recipient's name |
|
578 | + * 2. Name of the user who triggered the notification |
|
579 | + * 3. Title of the content |
|
580 | + * 4. Name of the content's container |
|
581 | + * 5. The actual content (entity's 'description' field) |
|
582 | + * 6. URL to the content |
|
583 | + * |
|
584 | + * Argument swapping can be used to change the order of the parameters. |
|
585 | + * See http://php.net/manual/en/function.sprintf.php#example-5427 |
|
586 | + * |
|
587 | + * @param NotificationEvent $event Notification event |
|
588 | + * @param ElggUser $recipient Notification recipient |
|
589 | + * @return string Notification body in the recipient's language |
|
590 | + */ |
|
591 | + private function getNotificationBody(NotificationEvent $event, ElggUser $recipient) { |
|
592 | + $actor = $event->getActor(); |
|
593 | + $object = $event->getObject(); |
|
594 | + /* @var \ElggObject $object */ |
|
595 | + $language = $recipient->language; |
|
596 | + |
|
597 | + // Check custom notification body for the action/type/subtype combination |
|
598 | + $body_key = "notification:{$event->getDescription()}:body"; |
|
599 | + if ($this->translator->languageKeyExists($body_key, $language)) { |
|
600 | + if ($object instanceof \ElggEntity) { |
|
601 | + $display_name = $object->getDisplayName(); |
|
602 | + $container_name = ''; |
|
603 | + $container = $object->getContainerEntity(); |
|
604 | + if ($container) { |
|
605 | + $container_name = $container->getDisplayName(); |
|
606 | + } |
|
607 | + } else { |
|
608 | + $display_name = ''; |
|
609 | + $container_name = ''; |
|
610 | + } |
|
611 | + |
|
612 | + return $this->translator->translate($body_key, array( |
|
613 | + $recipient->name, |
|
614 | + $actor->name, |
|
615 | + $display_name, |
|
616 | + $container_name, |
|
617 | + $object->description, |
|
618 | + $object->getURL(), |
|
619 | + ), $language); |
|
620 | + } |
|
621 | + |
|
622 | + // Fall back to default body |
|
623 | + return $this->translator->translate('notification:body', array($object->getURL()), $language); |
|
624 | + } |
|
625 | + |
|
626 | + /** |
|
627 | + * Register a deprecated notification handler |
|
628 | + * |
|
629 | + * @param string $method Method name |
|
630 | + * @param string $handler Handler callback |
|
631 | + * @return void |
|
632 | + */ |
|
633 | + public function registerDeprecatedHandler($method, $handler) { |
|
634 | + $this->deprHandlers[$method] = $handler; |
|
635 | + } |
|
636 | + |
|
637 | + /** |
|
638 | + * Get a deprecated notification handler callback |
|
639 | + * |
|
640 | + * @param string $method Method name |
|
641 | + * @return callback|null |
|
642 | + */ |
|
643 | + public function getDeprecatedHandler($method) { |
|
644 | + if (isset($this->deprHandlers[$method])) { |
|
645 | + return $this->deprHandlers[$method]; |
|
646 | + } else { |
|
647 | + return null; |
|
648 | + } |
|
649 | + } |
|
650 | + |
|
651 | + /** |
|
652 | + * Provides a way to incrementally wean Elgg's notifications code from the |
|
653 | + * global $NOTIFICATION_HANDLERS |
|
654 | + * |
|
655 | + * @return array |
|
656 | + */ |
|
657 | + public function getMethodsAsDeprecatedGlobal() { |
|
658 | + $data = array(); |
|
659 | + foreach ($this->methods as $method) { |
|
660 | + $data[$method] = 'empty'; |
|
661 | + } |
|
662 | + return $data; |
|
663 | + } |
|
664 | + |
|
665 | + /** |
|
666 | + * Get the notification body using a pre-Elgg 1.9 plugin hook |
|
667 | + * |
|
668 | + * @param Notification $notification Notification |
|
669 | + * @param NotificationEvent $event Event |
|
670 | + * @param string $method Method |
|
671 | + * @return Notification |
|
672 | + */ |
|
673 | + protected function getDeprecatedNotificationBody(Notification $notification, NotificationEvent $event, $method) { |
|
674 | + $entity = $event->getObject(); |
|
675 | + if (!$entity) { |
|
676 | + return $notification; |
|
677 | + } |
|
678 | + $params = array( |
|
679 | + 'entity' => $entity, |
|
680 | + 'to_entity' => $notification->getRecipient(), |
|
681 | + 'method' => $method, |
|
682 | + ); |
|
683 | + $subject = $this->getDeprecatedNotificationSubject($entity->getType(), $entity->getSubtype()); |
|
684 | + $string = $subject . ": " . $entity->getURL(); |
|
685 | + $body = $this->hooks->trigger('notify:entity:message', $entity->getType(), $params, $string); |
|
686 | + |
|
687 | + if ($subject) { |
|
688 | + $notification->subject = $subject; |
|
689 | + $notification->body = $body; |
|
690 | + } |
|
691 | + |
|
692 | + return $notification; |
|
693 | + } |
|
694 | + |
|
695 | + /** |
|
696 | + * Set message subject for deprecated notification code |
|
697 | + * |
|
698 | + * @param string $type Entity type |
|
699 | + * @param string $subtype Entity subtype |
|
700 | + * @param string $subject Subject line |
|
701 | + * @return void |
|
702 | + */ |
|
703 | + public function setDeprecatedNotificationSubject($type, $subtype, $subject) { |
|
704 | + if ($type == '') { |
|
705 | + $type = '__BLANK__'; |
|
706 | + } |
|
707 | + if ($subtype == '') { |
|
708 | + $subtype = '__BLANK__'; |
|
709 | + } |
|
710 | + |
|
711 | + if (!isset($this->deprSubjects[$type])) { |
|
712 | + $this->deprSubjects[$type] = array(); |
|
713 | + } |
|
714 | + |
|
715 | + $this->deprSubjects[$type][$subtype] = $subject; |
|
716 | + } |
|
717 | + |
|
718 | + /** |
|
719 | + * Get the deprecated subject |
|
720 | + * |
|
721 | + * @param string $type Entity type |
|
722 | + * @param string $subtype Entity subtype |
|
723 | + * @return string |
|
724 | + */ |
|
725 | + protected function getDeprecatedNotificationSubject($type, $subtype) { |
|
726 | + if ($type == '') { |
|
727 | + $type = '__BLANK__'; |
|
728 | + } |
|
729 | + if ($subtype == '') { |
|
730 | + $subtype = '__BLANK__'; |
|
731 | + } |
|
732 | + |
|
733 | + if (!isset($this->deprSubjects[$type])) { |
|
734 | + return ''; |
|
735 | + } |
|
736 | + |
|
737 | + if (!isset($this->deprSubjects[$type][$subtype])) { |
|
738 | + return ''; |
|
739 | + } |
|
740 | + |
|
741 | + return $this->deprSubjects[$type][$subtype]; |
|
742 | + } |
|
743 | + |
|
744 | + /** |
|
745 | + * Is someone using the deprecated override |
|
746 | + * |
|
747 | + * @param NotificationEvent $event Event |
|
748 | + * @return boolean |
|
749 | + */ |
|
750 | + protected function existsDeprecatedNotificationOverride(NotificationEvent $event) { |
|
751 | + $entity = $event->getObject(); |
|
752 | + if (!elgg_instanceof($entity)) { |
|
753 | + return false; |
|
754 | + } |
|
755 | + $params = array( |
|
756 | + 'event' => $event->getAction(), |
|
757 | + 'object_type' => $entity->getType(), |
|
758 | + 'object' => $entity, |
|
759 | + ); |
|
760 | + $hookresult = $this->hooks->trigger('object:notifications', $entity->getType(), $params, false); |
|
761 | + if ($hookresult === true) { |
|
762 | + elgg_deprecated_notice("Using the plugin hook 'object:notifications' has been deprecated " |
|
763 | + . "by the hook 'send:before', 'notifications'", 1.9); |
|
764 | + return true; |
|
765 | + } else { |
|
766 | + return false; |
|
767 | + } |
|
768 | + } |
|
769 | 769 | |
770 | 770 | } |
@@ -101,7 +101,7 @@ discard block |
||
101 | 101 | $this->events[$type][$subtype] = array(); |
102 | 102 | } |
103 | 103 | |
104 | - $action_list =& $this->events[$type][$subtype]; |
|
104 | + $action_list = & $this->events[$type][$subtype]; |
|
105 | 105 | if ($actions) { |
106 | 106 | $action_list = array_unique(array_merge($action_list, $actions)); |
107 | 107 | } elseif (!in_array('create', $action_list)) { |
@@ -294,7 +294,7 @@ discard block |
||
294 | 294 | } |
295 | 295 | } |
296 | 296 | |
297 | - $this->logger->notice("Results for the notification event {$event->getDescription()}: " . print_r($result, true)); |
|
297 | + $this->logger->notice("Results for the notification event {$event->getDescription()}: ".print_r($result, true)); |
|
298 | 298 | return $result; |
299 | 299 | } |
300 | 300 | |
@@ -467,14 +467,14 @@ discard block |
||
467 | 467 | |
468 | 468 | $notification = $this->hooks->trigger('prepare', 'notification', $params, $notification); |
469 | 469 | if (!$notification instanceof Notification) { |
470 | - throw new RuntimeException("'prepare','notification' hook must return an instance of " . Notification::class); |
|
470 | + throw new RuntimeException("'prepare','notification' hook must return an instance of ".Notification::class); |
|
471 | 471 | } |
472 | 472 | |
473 | - $type = 'notification:' . $event->getDescription(); |
|
473 | + $type = 'notification:'.$event->getDescription(); |
|
474 | 474 | if ($this->hooks->hasHandler('prepare', $type)) { |
475 | 475 | $notification = $this->hooks->trigger('prepare', $type, $params, $notification); |
476 | 476 | if (!$notification instanceof Notification) { |
477 | - throw new RuntimeException("'prepare','$type' hook must return an instance of " . Notification::class); |
|
477 | + throw new RuntimeException("'prepare','$type' hook must return an instance of ".Notification::class); |
|
478 | 478 | } |
479 | 479 | } else { |
480 | 480 | // pre Elgg 1.9 notification message generation |
@@ -483,7 +483,7 @@ discard block |
||
483 | 483 | |
484 | 484 | $notification = $this->hooks->trigger('format', "notification:$method", [], $notification); |
485 | 485 | if (!$notification instanceof Notification) { |
486 | - throw new RuntimeException("'format','notification:$method' hook must return an instance of " . Notification::class); |
|
486 | + throw new RuntimeException("'format','notification:$method' hook must return an instance of ".Notification::class); |
|
487 | 487 | } |
488 | 488 | |
489 | 489 | if ($this->hooks->hasHandler('send', "notification:$method")) { |
@@ -497,9 +497,9 @@ discard block |
||
497 | 497 | if ($this->logger->getLevel() == Logger::INFO) { |
498 | 498 | $logger_data = print_r((array) $notification->toObject(), true); |
499 | 499 | if ($result) { |
500 | - $this->logger->info("Notification sent: " . $logger_data); |
|
500 | + $this->logger->info("Notification sent: ".$logger_data); |
|
501 | 501 | } else { |
502 | - $this->logger->info("Notification was not sent: " . $logger_data); |
|
502 | + $this->logger->info("Notification was not sent: ".$logger_data); |
|
503 | 503 | } |
504 | 504 | } |
505 | 505 | return $result; |
@@ -681,7 +681,7 @@ discard block |
||
681 | 681 | 'method' => $method, |
682 | 682 | ); |
683 | 683 | $subject = $this->getDeprecatedNotificationSubject($entity->getType(), $entity->getSubtype()); |
684 | - $string = $subject . ": " . $entity->getURL(); |
|
684 | + $string = $subject.": ".$entity->getURL(); |
|
685 | 685 | $body = $this->hooks->trigger('notify:entity:message', $entity->getType(), $params, $string); |
686 | 686 | |
687 | 687 | if ($subject) { |
@@ -223,7 +223,7 @@ |
||
223 | 223 | * @param \DatabaseException $exception The exception to handle |
224 | 224 | * @param string $default The value to return if the table doesn't exist yet |
225 | 225 | * |
226 | - * @return mixed |
|
226 | + * @return string|null |
|
227 | 227 | * |
228 | 228 | * @throws \DatabaseException |
229 | 229 | */ |
@@ -25,354 +25,354 @@ |
||
25 | 25 | */ |
26 | 26 | class PersistentLoginService { |
27 | 27 | |
28 | - /** |
|
29 | - * Constructor |
|
30 | - * |
|
31 | - * @param Database $db The DB service |
|
32 | - * @param \ElggSession $session The Elgg session |
|
33 | - * @param \ElggCrypto $crypto The cryptography service |
|
34 | - * @param array $cookie_config The persistent login cookie settings |
|
35 | - * @param string $cookie_token The token from the request cookie |
|
36 | - * @param int $time The current time |
|
37 | - */ |
|
38 | - public function __construct( |
|
39 | - Database $db, |
|
40 | - \ElggSession $session, |
|
41 | - \ElggCrypto $crypto, |
|
42 | - array $cookie_config, |
|
43 | - $cookie_token, |
|
44 | - $time = null) { |
|
45 | - $this->db = $db; |
|
46 | - $this->session = $session; |
|
47 | - $this->crypto = $crypto; |
|
48 | - $this->cookie_config = $cookie_config; |
|
49 | - $this->cookie_token = $cookie_token; |
|
50 | - |
|
51 | - $prefix = $this->db->prefix; |
|
52 | - $this->table = "{$prefix}users_remember_me_cookies"; |
|
53 | - $this->time = is_numeric($time) ? (int)$time : time(); |
|
54 | - } |
|
55 | - |
|
56 | - /** |
|
57 | - * Make the user's login persistent |
|
58 | - * |
|
59 | - * @param \ElggUser $user The user who logged in |
|
60 | - * |
|
61 | - * @return void |
|
62 | - */ |
|
63 | - public function makeLoginPersistent(\ElggUser $user) { |
|
64 | - $token = $this->generateToken(); |
|
65 | - $hash = $this->hashToken($token); |
|
66 | - |
|
67 | - $this->storeHash($user, $hash); |
|
68 | - $this->setCookie($token); |
|
69 | - $this->setSession($token); |
|
70 | - } |
|
71 | - |
|
72 | - /** |
|
73 | - * Remove the persisted login token from client and server |
|
74 | - * |
|
75 | - * @return void |
|
76 | - */ |
|
77 | - public function removePersistentLogin() { |
|
78 | - if ($this->cookie_token) { |
|
79 | - $client_hash = $this->hashToken($this->cookie_token); |
|
80 | - $this->removeHash($client_hash); |
|
81 | - } |
|
82 | - |
|
83 | - $this->setCookie(""); |
|
84 | - $this->setSession(""); |
|
85 | - } |
|
86 | - |
|
87 | - /** |
|
88 | - * Handle a password change |
|
89 | - * |
|
90 | - * @param \ElggUser $subject The user whose password changed |
|
91 | - * @param \ElggUser $modifier The user who changed the password |
|
92 | - * |
|
93 | - * @return void |
|
94 | - */ |
|
95 | - public function handlePasswordChange(\ElggUser $subject, \ElggUser $modifier = null) { |
|
96 | - $this->removeAllHashes($subject); |
|
97 | - if (!$modifier || ($modifier->guid !== $subject->guid) || !$this->cookie_token) { |
|
98 | - return; |
|
99 | - } |
|
100 | - |
|
101 | - $this->makeLoginPersistent($modifier); |
|
102 | - } |
|
103 | - |
|
104 | - /** |
|
105 | - * Boot the persistent login session, possibly returning the user who should be |
|
106 | - * silently logged in. |
|
107 | - * |
|
108 | - * @return \ElggUser|null |
|
109 | - */ |
|
110 | - public function bootSession() { |
|
111 | - if (!$this->cookie_token) { |
|
112 | - return null; |
|
113 | - } |
|
114 | - |
|
115 | - // is this token good? |
|
116 | - $cookie_hash = $this->hashToken($this->cookie_token); |
|
117 | - $user = $this->getUserFromHash($cookie_hash); |
|
118 | - if ($user) { |
|
119 | - $this->setSession($this->cookie_token); |
|
120 | - // note: if the token is legacy, we don't both replacing it here because |
|
121 | - // it will be replaced during the next request boot |
|
122 | - return $user; |
|
123 | - } else { |
|
124 | - if ($this->isLegacyToken($this->cookie_token)) { |
|
125 | - // may be attempt to brute force legacy low-entropy tokens |
|
126 | - call_user_func($this->_callable_sleep, 1); |
|
127 | - } |
|
128 | - $this->setCookie(''); |
|
129 | - } |
|
130 | - } |
|
131 | - |
|
132 | - /** |
|
133 | - * Replace the user's token if it's a legacy hexadecimal token |
|
134 | - * |
|
135 | - * @param \ElggUser $logged_in_user The logged in user |
|
136 | - * |
|
137 | - * @return void |
|
138 | - */ |
|
139 | - public function replaceLegacyToken(\ElggUser $logged_in_user) { |
|
140 | - if (!$this->cookie_token || !$this->isLegacyToken($this->cookie_token)) { |
|
141 | - return; |
|
142 | - } |
|
143 | - |
|
144 | - // replace user's old weaker-entropy code with new one |
|
145 | - $this->removeHash($this->hashToken($this->cookie_token)); |
|
146 | - $this->makeLoginPersistent($logged_in_user); |
|
147 | - } |
|
148 | - |
|
149 | - /** |
|
150 | - * Find a user with the given hash |
|
151 | - * |
|
152 | - * @param string $hash The hashed token |
|
153 | - * |
|
154 | - * @return \ElggUser|null |
|
155 | - */ |
|
156 | - public function getUserFromHash($hash) { |
|
157 | - if (!$hash) { |
|
158 | - return null; |
|
159 | - } |
|
160 | - |
|
161 | - $hash = $this->db->sanitizeString($hash); |
|
162 | - $query = "SELECT guid FROM {$this->table} WHERE code = '$hash'"; |
|
163 | - try { |
|
164 | - $user_row = $this->db->getDataRow($query); |
|
165 | - } catch (\DatabaseException $e) { |
|
166 | - return $this->handleDbException($e); |
|
167 | - } |
|
168 | - if (!$user_row) { |
|
169 | - return null; |
|
170 | - } |
|
171 | - |
|
172 | - $user = call_user_func($this->_callable_get_user, $user_row->guid); |
|
173 | - return $user ? $user : null; |
|
174 | - } |
|
175 | - |
|
176 | - /** |
|
177 | - * Store a hash in the DB |
|
178 | - * |
|
179 | - * @param \ElggUser $user The user for whom we're storing the hash |
|
180 | - * @param string $hash The hashed token |
|
181 | - * |
|
182 | - * @return void |
|
183 | - */ |
|
184 | - protected function storeHash(\ElggUser $user, $hash) { |
|
185 | - // This prevents inserting the same hash twice, which seems to be happening in some rare cases |
|
186 | - // and for unknown reasons. See https://github.com/Elgg/Elgg/issues/8104 |
|
187 | - $this->removeHash($hash); |
|
188 | - |
|
189 | - $time = time(); |
|
190 | - $hash = $this->db->sanitizeString($hash); |
|
191 | - |
|
192 | - $query = " |
|
28 | + /** |
|
29 | + * Constructor |
|
30 | + * |
|
31 | + * @param Database $db The DB service |
|
32 | + * @param \ElggSession $session The Elgg session |
|
33 | + * @param \ElggCrypto $crypto The cryptography service |
|
34 | + * @param array $cookie_config The persistent login cookie settings |
|
35 | + * @param string $cookie_token The token from the request cookie |
|
36 | + * @param int $time The current time |
|
37 | + */ |
|
38 | + public function __construct( |
|
39 | + Database $db, |
|
40 | + \ElggSession $session, |
|
41 | + \ElggCrypto $crypto, |
|
42 | + array $cookie_config, |
|
43 | + $cookie_token, |
|
44 | + $time = null) { |
|
45 | + $this->db = $db; |
|
46 | + $this->session = $session; |
|
47 | + $this->crypto = $crypto; |
|
48 | + $this->cookie_config = $cookie_config; |
|
49 | + $this->cookie_token = $cookie_token; |
|
50 | + |
|
51 | + $prefix = $this->db->prefix; |
|
52 | + $this->table = "{$prefix}users_remember_me_cookies"; |
|
53 | + $this->time = is_numeric($time) ? (int)$time : time(); |
|
54 | + } |
|
55 | + |
|
56 | + /** |
|
57 | + * Make the user's login persistent |
|
58 | + * |
|
59 | + * @param \ElggUser $user The user who logged in |
|
60 | + * |
|
61 | + * @return void |
|
62 | + */ |
|
63 | + public function makeLoginPersistent(\ElggUser $user) { |
|
64 | + $token = $this->generateToken(); |
|
65 | + $hash = $this->hashToken($token); |
|
66 | + |
|
67 | + $this->storeHash($user, $hash); |
|
68 | + $this->setCookie($token); |
|
69 | + $this->setSession($token); |
|
70 | + } |
|
71 | + |
|
72 | + /** |
|
73 | + * Remove the persisted login token from client and server |
|
74 | + * |
|
75 | + * @return void |
|
76 | + */ |
|
77 | + public function removePersistentLogin() { |
|
78 | + if ($this->cookie_token) { |
|
79 | + $client_hash = $this->hashToken($this->cookie_token); |
|
80 | + $this->removeHash($client_hash); |
|
81 | + } |
|
82 | + |
|
83 | + $this->setCookie(""); |
|
84 | + $this->setSession(""); |
|
85 | + } |
|
86 | + |
|
87 | + /** |
|
88 | + * Handle a password change |
|
89 | + * |
|
90 | + * @param \ElggUser $subject The user whose password changed |
|
91 | + * @param \ElggUser $modifier The user who changed the password |
|
92 | + * |
|
93 | + * @return void |
|
94 | + */ |
|
95 | + public function handlePasswordChange(\ElggUser $subject, \ElggUser $modifier = null) { |
|
96 | + $this->removeAllHashes($subject); |
|
97 | + if (!$modifier || ($modifier->guid !== $subject->guid) || !$this->cookie_token) { |
|
98 | + return; |
|
99 | + } |
|
100 | + |
|
101 | + $this->makeLoginPersistent($modifier); |
|
102 | + } |
|
103 | + |
|
104 | + /** |
|
105 | + * Boot the persistent login session, possibly returning the user who should be |
|
106 | + * silently logged in. |
|
107 | + * |
|
108 | + * @return \ElggUser|null |
|
109 | + */ |
|
110 | + public function bootSession() { |
|
111 | + if (!$this->cookie_token) { |
|
112 | + return null; |
|
113 | + } |
|
114 | + |
|
115 | + // is this token good? |
|
116 | + $cookie_hash = $this->hashToken($this->cookie_token); |
|
117 | + $user = $this->getUserFromHash($cookie_hash); |
|
118 | + if ($user) { |
|
119 | + $this->setSession($this->cookie_token); |
|
120 | + // note: if the token is legacy, we don't both replacing it here because |
|
121 | + // it will be replaced during the next request boot |
|
122 | + return $user; |
|
123 | + } else { |
|
124 | + if ($this->isLegacyToken($this->cookie_token)) { |
|
125 | + // may be attempt to brute force legacy low-entropy tokens |
|
126 | + call_user_func($this->_callable_sleep, 1); |
|
127 | + } |
|
128 | + $this->setCookie(''); |
|
129 | + } |
|
130 | + } |
|
131 | + |
|
132 | + /** |
|
133 | + * Replace the user's token if it's a legacy hexadecimal token |
|
134 | + * |
|
135 | + * @param \ElggUser $logged_in_user The logged in user |
|
136 | + * |
|
137 | + * @return void |
|
138 | + */ |
|
139 | + public function replaceLegacyToken(\ElggUser $logged_in_user) { |
|
140 | + if (!$this->cookie_token || !$this->isLegacyToken($this->cookie_token)) { |
|
141 | + return; |
|
142 | + } |
|
143 | + |
|
144 | + // replace user's old weaker-entropy code with new one |
|
145 | + $this->removeHash($this->hashToken($this->cookie_token)); |
|
146 | + $this->makeLoginPersistent($logged_in_user); |
|
147 | + } |
|
148 | + |
|
149 | + /** |
|
150 | + * Find a user with the given hash |
|
151 | + * |
|
152 | + * @param string $hash The hashed token |
|
153 | + * |
|
154 | + * @return \ElggUser|null |
|
155 | + */ |
|
156 | + public function getUserFromHash($hash) { |
|
157 | + if (!$hash) { |
|
158 | + return null; |
|
159 | + } |
|
160 | + |
|
161 | + $hash = $this->db->sanitizeString($hash); |
|
162 | + $query = "SELECT guid FROM {$this->table} WHERE code = '$hash'"; |
|
163 | + try { |
|
164 | + $user_row = $this->db->getDataRow($query); |
|
165 | + } catch (\DatabaseException $e) { |
|
166 | + return $this->handleDbException($e); |
|
167 | + } |
|
168 | + if (!$user_row) { |
|
169 | + return null; |
|
170 | + } |
|
171 | + |
|
172 | + $user = call_user_func($this->_callable_get_user, $user_row->guid); |
|
173 | + return $user ? $user : null; |
|
174 | + } |
|
175 | + |
|
176 | + /** |
|
177 | + * Store a hash in the DB |
|
178 | + * |
|
179 | + * @param \ElggUser $user The user for whom we're storing the hash |
|
180 | + * @param string $hash The hashed token |
|
181 | + * |
|
182 | + * @return void |
|
183 | + */ |
|
184 | + protected function storeHash(\ElggUser $user, $hash) { |
|
185 | + // This prevents inserting the same hash twice, which seems to be happening in some rare cases |
|
186 | + // and for unknown reasons. See https://github.com/Elgg/Elgg/issues/8104 |
|
187 | + $this->removeHash($hash); |
|
188 | + |
|
189 | + $time = time(); |
|
190 | + $hash = $this->db->sanitizeString($hash); |
|
191 | + |
|
192 | + $query = " |
|
193 | 193 | INSERT INTO {$this->table} (code, guid, timestamp) |
194 | 194 | VALUES ('$hash', {$user->guid}, $time) |
195 | 195 | "; |
196 | - try { |
|
197 | - $this->db->insertData($query); |
|
198 | - } catch (\DatabaseException $e) { |
|
199 | - $this->handleDbException($e); |
|
200 | - } |
|
201 | - } |
|
202 | - |
|
203 | - /** |
|
204 | - * Remove a hash from the DB |
|
205 | - * |
|
206 | - * @param string $hash The hashed token to remove (unused before 1.9) |
|
207 | - * @return void |
|
208 | - */ |
|
209 | - protected function removeHash($hash) { |
|
210 | - $hash = $this->db->sanitizeString($hash); |
|
211 | - |
|
212 | - $query = "DELETE FROM {$this->table} WHERE code = '$hash'"; |
|
213 | - try { |
|
214 | - $this->db->deleteData($query); |
|
215 | - } catch (\DatabaseException $e) { |
|
216 | - $this->handleDbException($e); |
|
217 | - } |
|
218 | - } |
|
219 | - |
|
220 | - /** |
|
221 | - * Swallow a schema not upgraded exception, otherwise rethrow it |
|
222 | - * |
|
223 | - * @param \DatabaseException $exception The exception to handle |
|
224 | - * @param string $default The value to return if the table doesn't exist yet |
|
225 | - * |
|
226 | - * @return mixed |
|
227 | - * |
|
228 | - * @throws \DatabaseException |
|
229 | - */ |
|
230 | - protected function handleDbException(\DatabaseException $exception, $default = null) { |
|
231 | - if (false !== strpos($exception->getMessage(), "users_remember_me_cookies' doesn't exist")) { |
|
232 | - // schema has not been updated so we swallow this exception |
|
233 | - return $default; |
|
234 | - } else { |
|
235 | - throw $exception; |
|
236 | - } |
|
237 | - } |
|
238 | - |
|
239 | - /** |
|
240 | - * Remove all the hashes associated with a user |
|
241 | - * |
|
242 | - * @param \ElggUser $user The user for whom we're removing hashes |
|
243 | - * |
|
244 | - * @return void |
|
245 | - */ |
|
246 | - protected function removeAllHashes(\ElggUser $user) { |
|
247 | - $query = "DELETE FROM {$this->table} WHERE guid = '{$user->guid}'"; |
|
248 | - try { |
|
249 | - $this->db->deleteData($query); |
|
250 | - } catch (\DatabaseException $e) { |
|
251 | - $this->handleDbException($e); |
|
252 | - } |
|
253 | - } |
|
254 | - |
|
255 | - /** |
|
256 | - * Create a hash from the token |
|
257 | - * |
|
258 | - * @param string $token The token to hash |
|
259 | - * |
|
260 | - * @return string |
|
261 | - */ |
|
262 | - protected function hashToken($token) { |
|
263 | - // note: with user passwords, you'd want legit password hashing, but since these are randomly |
|
264 | - // generated and long tokens, rainbow tables aren't any help. |
|
265 | - return md5($token); |
|
266 | - } |
|
267 | - |
|
268 | - /** |
|
269 | - * Store the token in the client cookie (or remove the cookie) |
|
270 | - * |
|
271 | - * @param string $token Empty string to remove cookie |
|
272 | - * |
|
273 | - * @return void |
|
274 | - */ |
|
275 | - protected function setCookie($token) { |
|
276 | - $cookie = new \ElggCookie($this->cookie_config['name']); |
|
277 | - foreach (array('expire', 'path', 'domain', 'secure', 'httponly') as $key) { |
|
278 | - $cookie->$key = $this->cookie_config[$key]; |
|
279 | - } |
|
280 | - $cookie->value = $token; |
|
281 | - if (!$token) { |
|
282 | - $cookie->expire = $this->time - (86400 * 30); |
|
283 | - } |
|
284 | - call_user_func($this->_callable_elgg_set_cookie, $cookie); |
|
285 | - } |
|
286 | - |
|
287 | - /** |
|
288 | - * Store the token in the session (or remove it from the session) |
|
289 | - * |
|
290 | - * @param string $token The token to store in session. Empty string to remove. |
|
291 | - * |
|
292 | - * @return void |
|
293 | - */ |
|
294 | - protected function setSession($token) { |
|
295 | - if ($token) { |
|
296 | - $this->session->set('code', $token); |
|
297 | - } else { |
|
298 | - $this->session->remove('code'); |
|
299 | - } |
|
300 | - } |
|
301 | - |
|
302 | - /** |
|
303 | - * Generate a random token (base 64 URL) |
|
304 | - * |
|
305 | - * The first char is always "z" to indicate the value has more entropy than the |
|
306 | - * previously generated ones. |
|
307 | - * |
|
308 | - * @return string |
|
309 | - */ |
|
310 | - protected function generateToken() { |
|
311 | - return 'z' . $this->crypto->getRandomString(31); |
|
312 | - } |
|
313 | - |
|
314 | - /** |
|
315 | - * Is the given token a legacy MD5 hash? |
|
316 | - * |
|
317 | - * @param string $token The token to analyze |
|
318 | - * |
|
319 | - * @return bool |
|
320 | - */ |
|
321 | - protected function isLegacyToken($token) { |
|
322 | - return (isset($token[0]) && $token[0] !== 'z'); |
|
323 | - } |
|
324 | - |
|
325 | - /** |
|
326 | - * @var Database |
|
327 | - */ |
|
328 | - protected $db; |
|
329 | - |
|
330 | - /** |
|
331 | - * @var string |
|
332 | - */ |
|
333 | - protected $table; |
|
334 | - |
|
335 | - /** |
|
336 | - * @var array |
|
337 | - */ |
|
338 | - protected $cookie_config; |
|
339 | - |
|
340 | - /** |
|
341 | - * @var string |
|
342 | - */ |
|
343 | - protected $cookie_token; |
|
344 | - |
|
345 | - /** |
|
346 | - * @var \ElggSession |
|
347 | - */ |
|
348 | - protected $session; |
|
349 | - |
|
350 | - /** |
|
351 | - * @var \ElggCrypto |
|
352 | - */ |
|
353 | - protected $crypto; |
|
354 | - |
|
355 | - /** |
|
356 | - * @var int |
|
357 | - */ |
|
358 | - protected $time; |
|
359 | - |
|
360 | - /** |
|
361 | - * DO NOT USE. For unit test mocking |
|
362 | - * @access private |
|
363 | - */ |
|
364 | - public $_callable_get_user = 'get_user'; |
|
365 | - |
|
366 | - /** |
|
367 | - * DO NOT USE. For unit test mocking |
|
368 | - * @access private |
|
369 | - */ |
|
370 | - public $_callable_elgg_set_cookie = 'elgg_set_cookie'; |
|
371 | - |
|
372 | - /** |
|
373 | - * DO NOT USE. For unit test mocking |
|
374 | - * @access private |
|
375 | - */ |
|
376 | - public $_callable_sleep = 'sleep'; |
|
196 | + try { |
|
197 | + $this->db->insertData($query); |
|
198 | + } catch (\DatabaseException $e) { |
|
199 | + $this->handleDbException($e); |
|
200 | + } |
|
201 | + } |
|
202 | + |
|
203 | + /** |
|
204 | + * Remove a hash from the DB |
|
205 | + * |
|
206 | + * @param string $hash The hashed token to remove (unused before 1.9) |
|
207 | + * @return void |
|
208 | + */ |
|
209 | + protected function removeHash($hash) { |
|
210 | + $hash = $this->db->sanitizeString($hash); |
|
211 | + |
|
212 | + $query = "DELETE FROM {$this->table} WHERE code = '$hash'"; |
|
213 | + try { |
|
214 | + $this->db->deleteData($query); |
|
215 | + } catch (\DatabaseException $e) { |
|
216 | + $this->handleDbException($e); |
|
217 | + } |
|
218 | + } |
|
219 | + |
|
220 | + /** |
|
221 | + * Swallow a schema not upgraded exception, otherwise rethrow it |
|
222 | + * |
|
223 | + * @param \DatabaseException $exception The exception to handle |
|
224 | + * @param string $default The value to return if the table doesn't exist yet |
|
225 | + * |
|
226 | + * @return mixed |
|
227 | + * |
|
228 | + * @throws \DatabaseException |
|
229 | + */ |
|
230 | + protected function handleDbException(\DatabaseException $exception, $default = null) { |
|
231 | + if (false !== strpos($exception->getMessage(), "users_remember_me_cookies' doesn't exist")) { |
|
232 | + // schema has not been updated so we swallow this exception |
|
233 | + return $default; |
|
234 | + } else { |
|
235 | + throw $exception; |
|
236 | + } |
|
237 | + } |
|
238 | + |
|
239 | + /** |
|
240 | + * Remove all the hashes associated with a user |
|
241 | + * |
|
242 | + * @param \ElggUser $user The user for whom we're removing hashes |
|
243 | + * |
|
244 | + * @return void |
|
245 | + */ |
|
246 | + protected function removeAllHashes(\ElggUser $user) { |
|
247 | + $query = "DELETE FROM {$this->table} WHERE guid = '{$user->guid}'"; |
|
248 | + try { |
|
249 | + $this->db->deleteData($query); |
|
250 | + } catch (\DatabaseException $e) { |
|
251 | + $this->handleDbException($e); |
|
252 | + } |
|
253 | + } |
|
254 | + |
|
255 | + /** |
|
256 | + * Create a hash from the token |
|
257 | + * |
|
258 | + * @param string $token The token to hash |
|
259 | + * |
|
260 | + * @return string |
|
261 | + */ |
|
262 | + protected function hashToken($token) { |
|
263 | + // note: with user passwords, you'd want legit password hashing, but since these are randomly |
|
264 | + // generated and long tokens, rainbow tables aren't any help. |
|
265 | + return md5($token); |
|
266 | + } |
|
267 | + |
|
268 | + /** |
|
269 | + * Store the token in the client cookie (or remove the cookie) |
|
270 | + * |
|
271 | + * @param string $token Empty string to remove cookie |
|
272 | + * |
|
273 | + * @return void |
|
274 | + */ |
|
275 | + protected function setCookie($token) { |
|
276 | + $cookie = new \ElggCookie($this->cookie_config['name']); |
|
277 | + foreach (array('expire', 'path', 'domain', 'secure', 'httponly') as $key) { |
|
278 | + $cookie->$key = $this->cookie_config[$key]; |
|
279 | + } |
|
280 | + $cookie->value = $token; |
|
281 | + if (!$token) { |
|
282 | + $cookie->expire = $this->time - (86400 * 30); |
|
283 | + } |
|
284 | + call_user_func($this->_callable_elgg_set_cookie, $cookie); |
|
285 | + } |
|
286 | + |
|
287 | + /** |
|
288 | + * Store the token in the session (or remove it from the session) |
|
289 | + * |
|
290 | + * @param string $token The token to store in session. Empty string to remove. |
|
291 | + * |
|
292 | + * @return void |
|
293 | + */ |
|
294 | + protected function setSession($token) { |
|
295 | + if ($token) { |
|
296 | + $this->session->set('code', $token); |
|
297 | + } else { |
|
298 | + $this->session->remove('code'); |
|
299 | + } |
|
300 | + } |
|
301 | + |
|
302 | + /** |
|
303 | + * Generate a random token (base 64 URL) |
|
304 | + * |
|
305 | + * The first char is always "z" to indicate the value has more entropy than the |
|
306 | + * previously generated ones. |
|
307 | + * |
|
308 | + * @return string |
|
309 | + */ |
|
310 | + protected function generateToken() { |
|
311 | + return 'z' . $this->crypto->getRandomString(31); |
|
312 | + } |
|
313 | + |
|
314 | + /** |
|
315 | + * Is the given token a legacy MD5 hash? |
|
316 | + * |
|
317 | + * @param string $token The token to analyze |
|
318 | + * |
|
319 | + * @return bool |
|
320 | + */ |
|
321 | + protected function isLegacyToken($token) { |
|
322 | + return (isset($token[0]) && $token[0] !== 'z'); |
|
323 | + } |
|
324 | + |
|
325 | + /** |
|
326 | + * @var Database |
|
327 | + */ |
|
328 | + protected $db; |
|
329 | + |
|
330 | + /** |
|
331 | + * @var string |
|
332 | + */ |
|
333 | + protected $table; |
|
334 | + |
|
335 | + /** |
|
336 | + * @var array |
|
337 | + */ |
|
338 | + protected $cookie_config; |
|
339 | + |
|
340 | + /** |
|
341 | + * @var string |
|
342 | + */ |
|
343 | + protected $cookie_token; |
|
344 | + |
|
345 | + /** |
|
346 | + * @var \ElggSession |
|
347 | + */ |
|
348 | + protected $session; |
|
349 | + |
|
350 | + /** |
|
351 | + * @var \ElggCrypto |
|
352 | + */ |
|
353 | + protected $crypto; |
|
354 | + |
|
355 | + /** |
|
356 | + * @var int |
|
357 | + */ |
|
358 | + protected $time; |
|
359 | + |
|
360 | + /** |
|
361 | + * DO NOT USE. For unit test mocking |
|
362 | + * @access private |
|
363 | + */ |
|
364 | + public $_callable_get_user = 'get_user'; |
|
365 | + |
|
366 | + /** |
|
367 | + * DO NOT USE. For unit test mocking |
|
368 | + * @access private |
|
369 | + */ |
|
370 | + public $_callable_elgg_set_cookie = 'elgg_set_cookie'; |
|
371 | + |
|
372 | + /** |
|
373 | + * DO NOT USE. For unit test mocking |
|
374 | + * @access private |
|
375 | + */ |
|
376 | + public $_callable_sleep = 'sleep'; |
|
377 | 377 | } |
378 | 378 |
@@ -50,7 +50,7 @@ discard block |
||
50 | 50 | |
51 | 51 | $prefix = $this->db->prefix; |
52 | 52 | $this->table = "{$prefix}users_remember_me_cookies"; |
53 | - $this->time = is_numeric($time) ? (int)$time : time(); |
|
53 | + $this->time = is_numeric($time) ? (int) $time : time(); |
|
54 | 54 | } |
55 | 55 | |
56 | 56 | /** |
@@ -308,7 +308,7 @@ discard block |
||
308 | 308 | * @return string |
309 | 309 | */ |
310 | 310 | protected function generateToken() { |
311 | - return 'z' . $this->crypto->getRandomString(31); |
|
311 | + return 'z'.$this->crypto->getRandomString(31); |
|
312 | 312 | } |
313 | 313 | |
314 | 314 | /** |
@@ -120,7 +120,7 @@ discard block |
||
120 | 120 | /** |
121 | 121 | * Get array of UTF-8 (NFC) character replacements. |
122 | 122 | * |
123 | - * @return array |
|
123 | + * @return string |
|
124 | 124 | */ |
125 | 125 | static public function getAsciiTranslitMap() { |
126 | 126 | return array( |
@@ -253,7 +253,7 @@ discard block |
||
253 | 253 | /** |
254 | 254 | * Tests that "normalizer_normalize" exists and works |
255 | 255 | * |
256 | - * @return bool |
|
256 | + * @return boolean|null |
|
257 | 257 | */ |
258 | 258 | static public function hasNormalizerSupport() { |
259 | 259 | static $ret = null; |
@@ -30,240 +30,240 @@ |
||
30 | 30 | */ |
31 | 31 | class Translit { |
32 | 32 | |
33 | - /** |
|
34 | - * Create a version of a string for embedding in a URL |
|
35 | - * |
|
36 | - * @param string $string A UTF-8 string |
|
37 | - * @param string $separator The character to separate words with |
|
38 | - * @return string |
|
39 | - */ |
|
40 | - static public function urlize($string, $separator = '-') { |
|
41 | - // Iñtërnâtiônàlizætiøn, AND 日本語! |
|
33 | + /** |
|
34 | + * Create a version of a string for embedding in a URL |
|
35 | + * |
|
36 | + * @param string $string A UTF-8 string |
|
37 | + * @param string $separator The character to separate words with |
|
38 | + * @return string |
|
39 | + */ |
|
40 | + static public function urlize($string, $separator = '-') { |
|
41 | + // Iñtërnâtiônàlizætiøn, AND 日本語! |
|
42 | 42 | |
43 | - // try to force combined chars because the translit map and others expect it |
|
44 | - if (self::hasNormalizerSupport()) { |
|
45 | - $nfc = normalizer_normalize($string); |
|
46 | - if (is_string($nfc)) { |
|
47 | - $string = $nfc; |
|
48 | - } |
|
49 | - } |
|
50 | - // Internationalization, AND 日本語! |
|
51 | - $string = self::transliterateAscii($string); |
|
43 | + // try to force combined chars because the translit map and others expect it |
|
44 | + if (self::hasNormalizerSupport()) { |
|
45 | + $nfc = normalizer_normalize($string); |
|
46 | + if (is_string($nfc)) { |
|
47 | + $string = $nfc; |
|
48 | + } |
|
49 | + } |
|
50 | + // Internationalization, AND 日本語! |
|
51 | + $string = self::transliterateAscii($string); |
|
52 | 52 | |
53 | - // allow HTML tags in titles |
|
54 | - $string = preg_replace('~<([a-zA-Z][^>]*)>~', ' $1 ', $string); |
|
53 | + // allow HTML tags in titles |
|
54 | + $string = preg_replace('~<([a-zA-Z][^>]*)>~', ' $1 ', $string); |
|
55 | 55 | |
56 | - // more substitutions |
|
57 | - // @todo put these somewhere else |
|
58 | - $string = strtr($string, array( |
|
59 | - // currency |
|
60 | - "\xE2\x82\xAC" /* € */ => ' E ', |
|
61 | - "\xC2\xA3" /* £ */ => ' GBP ', |
|
62 | - )); |
|
56 | + // more substitutions |
|
57 | + // @todo put these somewhere else |
|
58 | + $string = strtr($string, array( |
|
59 | + // currency |
|
60 | + "\xE2\x82\xAC" /* € */ => ' E ', |
|
61 | + "\xC2\xA3" /* £ */ => ' GBP ', |
|
62 | + )); |
|
63 | 63 | |
64 | - // remove all ASCII except 0-9a-zA-Z, hyphen, underscore, and whitespace |
|
65 | - // note: "x" modifier did not work with this pattern. |
|
66 | - $string = preg_replace('~[' |
|
67 | - . '\x00-\x08' // control chars |
|
68 | - . '\x0b\x0c' // vert tab, form feed |
|
69 | - . '\x0e-\x1f' // control chars |
|
70 | - . '\x21-\x2c' // ! ... , |
|
71 | - . '\x2e\x2f' // . slash |
|
72 | - . '\x3a-\x40' // : ... @ |
|
73 | - . '\x5b-\x5e' // [ ... ^ |
|
74 | - . '\x60' // ` |
|
75 | - . '\x7b-\x7f' // { ... DEL |
|
76 | - . ']~', '', $string); |
|
77 | - $string = strtr($string, '', ''); |
|
64 | + // remove all ASCII except 0-9a-zA-Z, hyphen, underscore, and whitespace |
|
65 | + // note: "x" modifier did not work with this pattern. |
|
66 | + $string = preg_replace('~[' |
|
67 | + . '\x00-\x08' // control chars |
|
68 | + . '\x0b\x0c' // vert tab, form feed |
|
69 | + . '\x0e-\x1f' // control chars |
|
70 | + . '\x21-\x2c' // ! ... , |
|
71 | + . '\x2e\x2f' // . slash |
|
72 | + . '\x3a-\x40' // : ... @ |
|
73 | + . '\x5b-\x5e' // [ ... ^ |
|
74 | + . '\x60' // ` |
|
75 | + . '\x7b-\x7f' // { ... DEL |
|
76 | + . ']~', '', $string); |
|
77 | + $string = strtr($string, '', ''); |
|
78 | 78 | |
79 | - // internationalization, and 日本語! |
|
80 | - // note: not using elgg_strtolower to keep this class portable |
|
81 | - $string = is_callable('mb_strtolower') ? mb_strtolower($string, 'UTF-8') : strtolower($string); |
|
79 | + // internationalization, and 日本語! |
|
80 | + // note: not using elgg_strtolower to keep this class portable |
|
81 | + $string = is_callable('mb_strtolower') ? mb_strtolower($string, 'UTF-8') : strtolower($string); |
|
82 | 82 | |
83 | - // split by ASCII chars not in 0-9a-zA-Z |
|
84 | - // note: we cannot use [^0-9a-zA-Z] because that matches multibyte chars. |
|
85 | - // note: "x" modifier did not work with this pattern. |
|
86 | - $pattern = '~[' |
|
87 | - . '\x00-\x2f' // controls ... slash |
|
88 | - . '\x3a-\x40' // : ... @ |
|
89 | - . '\x5b-\x60' // [ ... ` |
|
90 | - . '\x7b-\x7f' // { ... DEL |
|
91 | - . ']+~x'; |
|
83 | + // split by ASCII chars not in 0-9a-zA-Z |
|
84 | + // note: we cannot use [^0-9a-zA-Z] because that matches multibyte chars. |
|
85 | + // note: "x" modifier did not work with this pattern. |
|
86 | + $pattern = '~[' |
|
87 | + . '\x00-\x2f' // controls ... slash |
|
88 | + . '\x3a-\x40' // : ... @ |
|
89 | + . '\x5b-\x60' // [ ... ` |
|
90 | + . '\x7b-\x7f' // { ... DEL |
|
91 | + . ']+~x'; |
|
92 | 92 | |
93 | - // ['internationalization', 'and', '日本語'] |
|
94 | - $words = preg_split($pattern, $string, -1, PREG_SPLIT_NO_EMPTY); |
|
93 | + // ['internationalization', 'and', '日本語'] |
|
94 | + $words = preg_split($pattern, $string, -1, PREG_SPLIT_NO_EMPTY); |
|
95 | 95 | |
96 | - // ['internationalization', 'and', '%E6%97%A5%E6%9C%AC%E8%AA%9E'] |
|
97 | - $words = array_map('urlencode', $words); |
|
96 | + // ['internationalization', 'and', '%E6%97%A5%E6%9C%AC%E8%AA%9E'] |
|
97 | + $words = array_map('urlencode', $words); |
|
98 | 98 | |
99 | - // internationalization-and-%E6%97%A5%E6%9C%AC%E8%AA%9E |
|
100 | - return implode($separator, $words); |
|
101 | - } |
|
99 | + // internationalization-and-%E6%97%A5%E6%9C%AC%E8%AA%9E |
|
100 | + return implode($separator, $words); |
|
101 | + } |
|
102 | 102 | |
103 | - /** |
|
104 | - * Transliterate Western multibyte chars to ASCII |
|
105 | - * |
|
106 | - * @param string $utf8 a UTF-8 string |
|
107 | - * @return string |
|
108 | - */ |
|
109 | - static public function transliterateAscii($utf8) { |
|
110 | - static $map = null; |
|
111 | - if (!preg_match('/[\x80-\xff]/', $utf8)) { |
|
112 | - return $utf8; |
|
113 | - } |
|
114 | - if (null === $map) { |
|
115 | - $map = self::getAsciiTranslitMap(); |
|
116 | - } |
|
117 | - return strtr($utf8, $map); |
|
118 | - } |
|
103 | + /** |
|
104 | + * Transliterate Western multibyte chars to ASCII |
|
105 | + * |
|
106 | + * @param string $utf8 a UTF-8 string |
|
107 | + * @return string |
|
108 | + */ |
|
109 | + static public function transliterateAscii($utf8) { |
|
110 | + static $map = null; |
|
111 | + if (!preg_match('/[\x80-\xff]/', $utf8)) { |
|
112 | + return $utf8; |
|
113 | + } |
|
114 | + if (null === $map) { |
|
115 | + $map = self::getAsciiTranslitMap(); |
|
116 | + } |
|
117 | + return strtr($utf8, $map); |
|
118 | + } |
|
119 | 119 | |
120 | - /** |
|
121 | - * Get array of UTF-8 (NFC) character replacements. |
|
122 | - * |
|
123 | - * @return array |
|
124 | - */ |
|
125 | - static public function getAsciiTranslitMap() { |
|
126 | - return array( |
|
127 | - // Decompositions for Latin-1 Supplement |
|
128 | - "\xC2\xAA" /* ª */ => 'a', "\xC2\xBA" /* º */ => 'o', "\xC3\x80" /* À */ => 'A', |
|
129 | - "\xC3\x81" /* Á */ => 'A', "\xC3\x82" /* Â */ => 'A', "\xC3\x83" /* Ã */ => 'A', |
|
130 | - "\xC3\x84" /* Ä */ => 'A', "\xC3\x85" /* Å */ => 'A', "\xC3\x86" /* Æ */ => 'AE', |
|
131 | - "\xC3\x87" /* Ç */ => 'C', "\xC3\x88" /* È */ => 'E', "\xC3\x89" /* É */ => 'E', |
|
132 | - "\xC3\x8A" /* Ê */ => 'E', "\xC3\x8B" /* Ë */ => 'E', "\xC3\x8C" /* Ì */ => 'I', |
|
133 | - "\xC3\x8D" /* Í */ => 'I', "\xC3\x8E" /* Î */ => 'I', "\xC3\x8F" /* Ï */ => 'I', |
|
134 | - "\xC3\x90" /* Ð */ => 'D', "\xC3\x91" /* Ñ */ => 'N', "\xC3\x92" /* Ò */ => 'O', |
|
135 | - "\xC3\x93" /* Ó */ => 'O', "\xC3\x94" /* Ô */ => 'O', "\xC3\x95" /* Õ */ => 'O', |
|
136 | - "\xC3\x96" /* Ö */ => 'O', "\xC3\x99" /* Ù */ => 'U', "\xC3\x9A" /* Ú */ => 'U', |
|
137 | - "\xC3\x9B" /* Û */ => 'U', "\xC3\x9C" /* Ü */ => 'U', "\xC3\x9D" /* Ý */ => 'Y', |
|
138 | - "\xC3\x9E" /* Þ */ => 'TH', "\xC3\x9F" /* ß */ => 'ss', "\xC3\xA0" /* à */ => 'a', |
|
139 | - "\xC3\xA1" /* á */ => 'a', "\xC3\xA2" /* â */ => 'a', "\xC3\xA3" /* ã */ => 'a', |
|
140 | - "\xC3\xA4" /* ä */ => 'a', "\xC3\xA5" /* å */ => 'a', "\xC3\xA6" /* æ */ => 'ae', |
|
141 | - "\xC3\xA7" /* ç */ => 'c', "\xC3\xA8" /* è */ => 'e', "\xC3\xA9" /* é */ => 'e', |
|
142 | - "\xC3\xAA" /* ê */ => 'e', "\xC3\xAB" /* ë */ => 'e', "\xC3\xAC" /* ì */ => 'i', |
|
143 | - "\xC3\xAD" /* í */ => 'i', "\xC3\xAE" /* î */ => 'i', "\xC3\xAF" /* ï */ => 'i', |
|
144 | - "\xC3\xB0" /* ð */ => 'd', "\xC3\xB1" /* ñ */ => 'n', "\xC3\xB2" /* ò */ => 'o', |
|
145 | - "\xC3\xB3" /* ó */ => 'o', "\xC3\xB4" /* ô */ => 'o', "\xC3\xB5" /* õ */ => 'o', |
|
146 | - "\xC3\xB6" /* ö */ => 'o', "\xC3\xB8" /* ø */ => 'o', "\xC3\xB9" /* ù */ => 'u', |
|
147 | - "\xC3\xBA" /* ú */ => 'u', "\xC3\xBB" /* û */ => 'u', "\xC3\xBC" /* ü */ => 'u', |
|
148 | - "\xC3\xBD" /* ý */ => 'y', "\xC3\xBE" /* þ */ => 'th', "\xC3\xBF" /* ÿ */ => 'y', |
|
149 | - "\xC3\x98" /* Ø */ => 'O', |
|
150 | - // Decompositions for Latin Extended-A |
|
151 | - "\xC4\x80" /* Ā */ => 'A', "\xC4\x81" /* ā */ => 'a', "\xC4\x82" /* Ă */ => 'A', |
|
152 | - "\xC4\x83" /* ă */ => 'a', "\xC4\x84" /* Ą */ => 'A', "\xC4\x85" /* ą */ => 'a', |
|
153 | - "\xC4\x86" /* Ć */ => 'C', "\xC4\x87" /* ć */ => 'c', "\xC4\x88" /* Ĉ */ => 'C', |
|
154 | - "\xC4\x89" /* ĉ */ => 'c', "\xC4\x8A" /* Ċ */ => 'C', "\xC4\x8B" /* ċ */ => 'c', |
|
155 | - "\xC4\x8C" /* Č */ => 'C', "\xC4\x8D" /* č */ => 'c', "\xC4\x8E" /* Ď */ => 'D', |
|
156 | - "\xC4\x8F" /* ď */ => 'd', "\xC4\x90" /* Đ */ => 'D', "\xC4\x91" /* đ */ => 'd', |
|
157 | - "\xC4\x92" /* Ē */ => 'E', "\xC4\x93" /* ē */ => 'e', "\xC4\x94" /* Ĕ */ => 'E', |
|
158 | - "\xC4\x95" /* ĕ */ => 'e', "\xC4\x96" /* Ė */ => 'E', "\xC4\x97" /* ė */ => 'e', |
|
159 | - "\xC4\x98" /* Ę */ => 'E', "\xC4\x99" /* ę */ => 'e', "\xC4\x9A" /* Ě */ => 'E', |
|
160 | - "\xC4\x9B" /* ě */ => 'e', "\xC4\x9C" /* Ĝ */ => 'G', "\xC4\x9D" /* ĝ */ => 'g', |
|
161 | - "\xC4\x9E" /* Ğ */ => 'G', "\xC4\x9F" /* ğ */ => 'g', "\xC4\xA0" /* Ġ */ => 'G', |
|
162 | - "\xC4\xA1" /* ġ */ => 'g', "\xC4\xA2" /* Ģ */ => 'G', "\xC4\xA3" /* ģ */ => 'g', |
|
163 | - "\xC4\xA4" /* Ĥ */ => 'H', "\xC4\xA5" /* ĥ */ => 'h', "\xC4\xA6" /* Ħ */ => 'H', |
|
164 | - "\xC4\xA7" /* ħ */ => 'h', "\xC4\xA8" /* Ĩ */ => 'I', "\xC4\xA9" /* ĩ */ => 'i', |
|
165 | - "\xC4\xAA" /* Ī */ => 'I', "\xC4\xAB" /* ī */ => 'i', "\xC4\xAC" /* Ĭ */ => 'I', |
|
166 | - "\xC4\xAD" /* ĭ */ => 'i', "\xC4\xAE" /* Į */ => 'I', "\xC4\xAF" /* į */ => 'i', |
|
167 | - "\xC4\xB0" /* İ */ => 'I', "\xC4\xB1" /* ı */ => 'i', "\xC4\xB2" /* IJ */ => 'IJ', |
|
168 | - "\xC4\xB3" /* ij */ => 'ij', "\xC4\xB4" /* Ĵ */ => 'J', "\xC4\xB5" /* ĵ */ => 'j', |
|
169 | - "\xC4\xB6" /* Ķ */ => 'K', "\xC4\xB7" /* ķ */ => 'k', "\xC4\xB8" /* ĸ */ => 'k', |
|
170 | - "\xC4\xB9" /* Ĺ */ => 'L', "\xC4\xBA" /* ĺ */ => 'l', "\xC4\xBB" /* Ļ */ => 'L', |
|
171 | - "\xC4\xBC" /* ļ */ => 'l', "\xC4\xBD" /* Ľ */ => 'L', "\xC4\xBE" /* ľ */ => 'l', |
|
172 | - "\xC4\xBF" /* Ŀ */ => 'L', "\xC5\x80" /* ŀ */ => 'l', "\xC5\x81" /* Ł */ => 'L', |
|
173 | - "\xC5\x82" /* ł */ => 'l', "\xC5\x83" /* Ń */ => 'N', "\xC5\x84" /* ń */ => 'n', |
|
174 | - "\xC5\x85" /* Ņ */ => 'N', "\xC5\x86" /* ņ */ => 'n', "\xC5\x87" /* Ň */ => 'N', |
|
175 | - "\xC5\x88" /* ň */ => 'n', "\xC5\x89" /* ʼn */ => 'N', "\xC5\x8A" /* Ŋ */ => 'n', |
|
176 | - "\xC5\x8B" /* ŋ */ => 'N', "\xC5\x8C" /* Ō */ => 'O', "\xC5\x8D" /* ō */ => 'o', |
|
177 | - "\xC5\x8E" /* Ŏ */ => 'O', "\xC5\x8F" /* ŏ */ => 'o', "\xC5\x90" /* Ő */ => 'O', |
|
178 | - "\xC5\x91" /* ő */ => 'o', "\xC5\x92" /* Œ */ => 'OE', "\xC5\x93" /* œ */ => 'oe', |
|
179 | - "\xC5\x94" /* Ŕ */ => 'R', "\xC5\x95" /* ŕ */ => 'r', "\xC5\x96" /* Ŗ */ => 'R', |
|
180 | - "\xC5\x97" /* ŗ */ => 'r', "\xC5\x98" /* Ř */ => 'R', "\xC5\x99" /* ř */ => 'r', |
|
181 | - "\xC5\x9A" /* Ś */ => 'S', "\xC5\x9B" /* ś */ => 's', "\xC5\x9C" /* Ŝ */ => 'S', |
|
182 | - "\xC5\x9D" /* ŝ */ => 's', "\xC5\x9E" /* Ş */ => 'S', "\xC5\x9F" /* ş */ => 's', |
|
183 | - "\xC5\xA0" /* Š */ => 'S', "\xC5\xA1" /* š */ => 's', "\xC5\xA2" /* Ţ */ => 'T', |
|
184 | - "\xC5\xA3" /* ţ */ => 't', "\xC5\xA4" /* Ť */ => 'T', "\xC5\xA5" /* ť */ => 't', |
|
185 | - "\xC5\xA6" /* Ŧ */ => 'T', "\xC5\xA7" /* ŧ */ => 't', "\xC5\xA8" /* Ũ */ => 'U', |
|
186 | - "\xC5\xA9" /* ũ */ => 'u', "\xC5\xAA" /* Ū */ => 'U', "\xC5\xAB" /* ū */ => 'u', |
|
187 | - "\xC5\xAC" /* Ŭ */ => 'U', "\xC5\xAD" /* ŭ */ => 'u', "\xC5\xAE" /* Ů */ => 'U', |
|
188 | - "\xC5\xAF" /* ů */ => 'u', "\xC5\xB0" /* Ű */ => 'U', "\xC5\xB1" /* ű */ => 'u', |
|
189 | - "\xC5\xB2" /* Ų */ => 'U', "\xC5\xB3" /* ų */ => 'u', "\xC5\xB4" /* Ŵ */ => 'W', |
|
190 | - "\xC5\xB5" /* ŵ */ => 'w', "\xC5\xB6" /* Ŷ */ => 'Y', "\xC5\xB7" /* ŷ */ => 'y', |
|
191 | - "\xC5\xB8" /* Ÿ */ => 'Y', "\xC5\xB9" /* Ź */ => 'Z', "\xC5\xBA" /* ź */ => 'z', |
|
192 | - "\xC5\xBB" /* Ż */ => 'Z', "\xC5\xBC" /* ż */ => 'z', "\xC5\xBD" /* Ž */ => 'Z', |
|
193 | - "\xC5\xBE" /* ž */ => 'z', "\xC5\xBF" /* ſ */ => 's', |
|
194 | - // Decompositions for Latin Extended-B |
|
195 | - "\xC8\x98" /* Ș */ => 'S', "\xC8\x99" /* ș */ => 's', |
|
196 | - "\xC8\x9A" /* Ț */ => 'T', "\xC8\x9B" /* ț */ => 't', |
|
197 | - // unmarked |
|
198 | - "\xC6\xA0" /* Ơ */ => 'O', "\xC6\xA1" /* ơ */ => 'o', |
|
199 | - "\xC6\xAF" /* Ư */ => 'U', "\xC6\xB0" /* ư */ => 'u', |
|
200 | - // grave accent |
|
201 | - "\xE1\xBA\xA6" /* Ầ */ => 'A', "\xE1\xBA\xA7" /* ầ */ => 'a', |
|
202 | - "\xE1\xBA\xB0" /* Ằ */ => 'A', "\xE1\xBA\xB1" /* ằ */ => 'a', |
|
203 | - "\xE1\xBB\x80" /* Ề */ => 'E', "\xE1\xBB\x81" /* ề */ => 'e', |
|
204 | - "\xE1\xBB\x92" /* Ồ */ => 'O', "\xE1\xBB\x93" /* ồ */ => 'o', |
|
205 | - "\xE1\xBB\x9C" /* Ờ */ => 'O', "\xE1\xBB\x9D" /* ờ */ => 'o', |
|
206 | - "\xE1\xBB\xAA" /* Ừ */ => 'U', "\xE1\xBB\xAB" /* ừ */ => 'u', |
|
207 | - "\xE1\xBB\xB2" /* Ỳ */ => 'Y', "\xE1\xBB\xB3" /* ỳ */ => 'y', |
|
208 | - // hook |
|
209 | - "\xE1\xBA\xA2" /* Ả */ => 'A', "\xE1\xBA\xA3" /* ả */ => 'a', |
|
210 | - "\xE1\xBA\xA8" /* Ẩ */ => 'A', "\xE1\xBA\xA9" /* ẩ */ => 'a', |
|
211 | - "\xE1\xBA\xB2" /* Ẳ */ => 'A', "\xE1\xBA\xB3" /* ẳ */ => 'a', |
|
212 | - "\xE1\xBA\xBA" /* Ẻ */ => 'E', "\xE1\xBA\xBB" /* ẻ */ => 'e', |
|
213 | - "\xE1\xBB\x82" /* Ể */ => 'E', "\xE1\xBB\x83" /* ể */ => 'e', |
|
214 | - "\xE1\xBB\x88" /* Ỉ */ => 'I', "\xE1\xBB\x89" /* ỉ */ => 'i', |
|
215 | - "\xE1\xBB\x8E" /* Ỏ */ => 'O', "\xE1\xBB\x8F" /* ỏ */ => 'o', |
|
216 | - "\xE1\xBB\x94" /* Ổ */ => 'O', "\xE1\xBB\x95" /* ổ */ => 'o', |
|
217 | - "\xE1\xBB\x9E" /* Ở */ => 'O', "\xE1\xBB\x9F" /* ở */ => 'o', |
|
218 | - "\xE1\xBB\xA6" /* Ủ */ => 'U', "\xE1\xBB\xA7" /* ủ */ => 'u', |
|
219 | - "\xE1\xBB\xAC" /* Ử */ => 'U', "\xE1\xBB\xAD" /* ử */ => 'u', |
|
220 | - "\xE1\xBB\xB6" /* Ỷ */ => 'Y', "\xE1\xBB\xB7" /* ỷ */ => 'y', |
|
221 | - // tilde |
|
222 | - "\xE1\xBA\xAA" /* Ẫ */ => 'A', "\xE1\xBA\xAB" /* ẫ */ => 'a', |
|
223 | - "\xE1\xBA\xB4" /* Ẵ */ => 'A', "\xE1\xBA\xB5" /* ẵ */ => 'a', |
|
224 | - "\xE1\xBA\xBC" /* Ẽ */ => 'E', "\xE1\xBA\xBD" /* ẽ */ => 'e', |
|
225 | - "\xE1\xBB\x84" /* Ễ */ => 'E', "\xE1\xBB\x85" /* ễ */ => 'e', |
|
226 | - "\xE1\xBB\x96" /* Ỗ */ => 'O', "\xE1\xBB\x97" /* ỗ */ => 'o', |
|
227 | - "\xE1\xBB\xA0" /* Ỡ */ => 'O', "\xE1\xBB\xA1" /* ỡ */ => 'o', |
|
228 | - "\xE1\xBB\xAE" /* Ữ */ => 'U', "\xE1\xBB\xAF" /* ữ */ => 'u', |
|
229 | - "\xE1\xBB\xB8" /* Ỹ */ => 'Y', "\xE1\xBB\xB9" /* ỹ */ => 'y', |
|
230 | - // acute accent |
|
231 | - "\xE1\xBA\xA4" /* Ấ */ => 'A', "\xE1\xBA\xA5" /* ấ */ => 'a', |
|
232 | - "\xE1\xBA\xAE" /* Ắ */ => 'A', "\xE1\xBA\xAF" /* ắ */ => 'a', |
|
233 | - "\xE1\xBA\xBE" /* Ế */ => 'E', "\xE1\xBA\xBF" /* ế */ => 'e', |
|
234 | - "\xE1\xBB\x90" /* Ố */ => 'O', "\xE1\xBB\x91" /* ố */ => 'o', |
|
235 | - "\xE1\xBB\x9A" /* Ớ */ => 'O', "\xE1\xBB\x9B" /* ớ */ => 'o', |
|
236 | - "\xE1\xBB\xA8" /* Ứ */ => 'U', "\xE1\xBB\xA9" /* ứ */ => 'u', |
|
237 | - // dot below |
|
238 | - "\xE1\xBA\xA0" /* Ạ */ => 'A', "\xE1\xBA\xA1" /* ạ */ => 'a', |
|
239 | - "\xE1\xBA\xAC" /* Ậ */ => 'A', "\xE1\xBA\xAD" /* ậ */ => 'a', |
|
240 | - "\xE1\xBA\xB6" /* Ặ */ => 'A', "\xE1\xBA\xB7" /* ặ */ => 'a', |
|
241 | - "\xE1\xBA\xB8" /* Ẹ */ => 'E', "\xE1\xBA\xB9" /* ẹ */ => 'e', |
|
242 | - "\xE1\xBB\x86" /* Ệ */ => 'E', "\xE1\xBB\x87" /* ệ */ => 'e', |
|
243 | - "\xE1\xBB\x8A" /* Ị */ => 'I', "\xE1\xBB\x8B" /* ị */ => 'i', |
|
244 | - "\xE1\xBB\x8C" /* Ọ */ => 'O', "\xE1\xBB\x8D" /* ọ */ => 'o', |
|
245 | - "\xE1\xBB\x98" /* Ộ */ => 'O', "\xE1\xBB\x99" /* ộ */ => 'o', |
|
246 | - "\xE1\xBB\xA2" /* Ợ */ => 'O', "\xE1\xBB\xA3" /* ợ */ => 'o', |
|
247 | - "\xE1\xBB\xA4" /* Ụ */ => 'U', "\xE1\xBB\xA5" /* ụ */ => 'u', |
|
248 | - "\xE1\xBB\xB0" /* Ự */ => 'U', "\xE1\xBB\xB1" /* ự */ => 'u', |
|
249 | - "\xE1\xBB\xB4" /* Ỵ */ => 'Y', "\xE1\xBB\xB5" /* ỵ */ => 'y', |
|
250 | - ); |
|
251 | - } |
|
120 | + /** |
|
121 | + * Get array of UTF-8 (NFC) character replacements. |
|
122 | + * |
|
123 | + * @return array |
|
124 | + */ |
|
125 | + static public function getAsciiTranslitMap() { |
|
126 | + return array( |
|
127 | + // Decompositions for Latin-1 Supplement |
|
128 | + "\xC2\xAA" /* ª */ => 'a', "\xC2\xBA" /* º */ => 'o', "\xC3\x80" /* À */ => 'A', |
|
129 | + "\xC3\x81" /* Á */ => 'A', "\xC3\x82" /* Â */ => 'A', "\xC3\x83" /* Ã */ => 'A', |
|
130 | + "\xC3\x84" /* Ä */ => 'A', "\xC3\x85" /* Å */ => 'A', "\xC3\x86" /* Æ */ => 'AE', |
|
131 | + "\xC3\x87" /* Ç */ => 'C', "\xC3\x88" /* È */ => 'E', "\xC3\x89" /* É */ => 'E', |
|
132 | + "\xC3\x8A" /* Ê */ => 'E', "\xC3\x8B" /* Ë */ => 'E', "\xC3\x8C" /* Ì */ => 'I', |
|
133 | + "\xC3\x8D" /* Í */ => 'I', "\xC3\x8E" /* Î */ => 'I', "\xC3\x8F" /* Ï */ => 'I', |
|
134 | + "\xC3\x90" /* Ð */ => 'D', "\xC3\x91" /* Ñ */ => 'N', "\xC3\x92" /* Ò */ => 'O', |
|
135 | + "\xC3\x93" /* Ó */ => 'O', "\xC3\x94" /* Ô */ => 'O', "\xC3\x95" /* Õ */ => 'O', |
|
136 | + "\xC3\x96" /* Ö */ => 'O', "\xC3\x99" /* Ù */ => 'U', "\xC3\x9A" /* Ú */ => 'U', |
|
137 | + "\xC3\x9B" /* Û */ => 'U', "\xC3\x9C" /* Ü */ => 'U', "\xC3\x9D" /* Ý */ => 'Y', |
|
138 | + "\xC3\x9E" /* Þ */ => 'TH', "\xC3\x9F" /* ß */ => 'ss', "\xC3\xA0" /* à */ => 'a', |
|
139 | + "\xC3\xA1" /* á */ => 'a', "\xC3\xA2" /* â */ => 'a', "\xC3\xA3" /* ã */ => 'a', |
|
140 | + "\xC3\xA4" /* ä */ => 'a', "\xC3\xA5" /* å */ => 'a', "\xC3\xA6" /* æ */ => 'ae', |
|
141 | + "\xC3\xA7" /* ç */ => 'c', "\xC3\xA8" /* è */ => 'e', "\xC3\xA9" /* é */ => 'e', |
|
142 | + "\xC3\xAA" /* ê */ => 'e', "\xC3\xAB" /* ë */ => 'e', "\xC3\xAC" /* ì */ => 'i', |
|
143 | + "\xC3\xAD" /* í */ => 'i', "\xC3\xAE" /* î */ => 'i', "\xC3\xAF" /* ï */ => 'i', |
|
144 | + "\xC3\xB0" /* ð */ => 'd', "\xC3\xB1" /* ñ */ => 'n', "\xC3\xB2" /* ò */ => 'o', |
|
145 | + "\xC3\xB3" /* ó */ => 'o', "\xC3\xB4" /* ô */ => 'o', "\xC3\xB5" /* õ */ => 'o', |
|
146 | + "\xC3\xB6" /* ö */ => 'o', "\xC3\xB8" /* ø */ => 'o', "\xC3\xB9" /* ù */ => 'u', |
|
147 | + "\xC3\xBA" /* ú */ => 'u', "\xC3\xBB" /* û */ => 'u', "\xC3\xBC" /* ü */ => 'u', |
|
148 | + "\xC3\xBD" /* ý */ => 'y', "\xC3\xBE" /* þ */ => 'th', "\xC3\xBF" /* ÿ */ => 'y', |
|
149 | + "\xC3\x98" /* Ø */ => 'O', |
|
150 | + // Decompositions for Latin Extended-A |
|
151 | + "\xC4\x80" /* Ā */ => 'A', "\xC4\x81" /* ā */ => 'a', "\xC4\x82" /* Ă */ => 'A', |
|
152 | + "\xC4\x83" /* ă */ => 'a', "\xC4\x84" /* Ą */ => 'A', "\xC4\x85" /* ą */ => 'a', |
|
153 | + "\xC4\x86" /* Ć */ => 'C', "\xC4\x87" /* ć */ => 'c', "\xC4\x88" /* Ĉ */ => 'C', |
|
154 | + "\xC4\x89" /* ĉ */ => 'c', "\xC4\x8A" /* Ċ */ => 'C', "\xC4\x8B" /* ċ */ => 'c', |
|
155 | + "\xC4\x8C" /* Č */ => 'C', "\xC4\x8D" /* č */ => 'c', "\xC4\x8E" /* Ď */ => 'D', |
|
156 | + "\xC4\x8F" /* ď */ => 'd', "\xC4\x90" /* Đ */ => 'D', "\xC4\x91" /* đ */ => 'd', |
|
157 | + "\xC4\x92" /* Ē */ => 'E', "\xC4\x93" /* ē */ => 'e', "\xC4\x94" /* Ĕ */ => 'E', |
|
158 | + "\xC4\x95" /* ĕ */ => 'e', "\xC4\x96" /* Ė */ => 'E', "\xC4\x97" /* ė */ => 'e', |
|
159 | + "\xC4\x98" /* Ę */ => 'E', "\xC4\x99" /* ę */ => 'e', "\xC4\x9A" /* Ě */ => 'E', |
|
160 | + "\xC4\x9B" /* ě */ => 'e', "\xC4\x9C" /* Ĝ */ => 'G', "\xC4\x9D" /* ĝ */ => 'g', |
|
161 | + "\xC4\x9E" /* Ğ */ => 'G', "\xC4\x9F" /* ğ */ => 'g', "\xC4\xA0" /* Ġ */ => 'G', |
|
162 | + "\xC4\xA1" /* ġ */ => 'g', "\xC4\xA2" /* Ģ */ => 'G', "\xC4\xA3" /* ģ */ => 'g', |
|
163 | + "\xC4\xA4" /* Ĥ */ => 'H', "\xC4\xA5" /* ĥ */ => 'h', "\xC4\xA6" /* Ħ */ => 'H', |
|
164 | + "\xC4\xA7" /* ħ */ => 'h', "\xC4\xA8" /* Ĩ */ => 'I', "\xC4\xA9" /* ĩ */ => 'i', |
|
165 | + "\xC4\xAA" /* Ī */ => 'I', "\xC4\xAB" /* ī */ => 'i', "\xC4\xAC" /* Ĭ */ => 'I', |
|
166 | + "\xC4\xAD" /* ĭ */ => 'i', "\xC4\xAE" /* Į */ => 'I', "\xC4\xAF" /* į */ => 'i', |
|
167 | + "\xC4\xB0" /* İ */ => 'I', "\xC4\xB1" /* ı */ => 'i', "\xC4\xB2" /* IJ */ => 'IJ', |
|
168 | + "\xC4\xB3" /* ij */ => 'ij', "\xC4\xB4" /* Ĵ */ => 'J', "\xC4\xB5" /* ĵ */ => 'j', |
|
169 | + "\xC4\xB6" /* Ķ */ => 'K', "\xC4\xB7" /* ķ */ => 'k', "\xC4\xB8" /* ĸ */ => 'k', |
|
170 | + "\xC4\xB9" /* Ĺ */ => 'L', "\xC4\xBA" /* ĺ */ => 'l', "\xC4\xBB" /* Ļ */ => 'L', |
|
171 | + "\xC4\xBC" /* ļ */ => 'l', "\xC4\xBD" /* Ľ */ => 'L', "\xC4\xBE" /* ľ */ => 'l', |
|
172 | + "\xC4\xBF" /* Ŀ */ => 'L', "\xC5\x80" /* ŀ */ => 'l', "\xC5\x81" /* Ł */ => 'L', |
|
173 | + "\xC5\x82" /* ł */ => 'l', "\xC5\x83" /* Ń */ => 'N', "\xC5\x84" /* ń */ => 'n', |
|
174 | + "\xC5\x85" /* Ņ */ => 'N', "\xC5\x86" /* ņ */ => 'n', "\xC5\x87" /* Ň */ => 'N', |
|
175 | + "\xC5\x88" /* ň */ => 'n', "\xC5\x89" /* ʼn */ => 'N', "\xC5\x8A" /* Ŋ */ => 'n', |
|
176 | + "\xC5\x8B" /* ŋ */ => 'N', "\xC5\x8C" /* Ō */ => 'O', "\xC5\x8D" /* ō */ => 'o', |
|
177 | + "\xC5\x8E" /* Ŏ */ => 'O', "\xC5\x8F" /* ŏ */ => 'o', "\xC5\x90" /* Ő */ => 'O', |
|
178 | + "\xC5\x91" /* ő */ => 'o', "\xC5\x92" /* Œ */ => 'OE', "\xC5\x93" /* œ */ => 'oe', |
|
179 | + "\xC5\x94" /* Ŕ */ => 'R', "\xC5\x95" /* ŕ */ => 'r', "\xC5\x96" /* Ŗ */ => 'R', |
|
180 | + "\xC5\x97" /* ŗ */ => 'r', "\xC5\x98" /* Ř */ => 'R', "\xC5\x99" /* ř */ => 'r', |
|
181 | + "\xC5\x9A" /* Ś */ => 'S', "\xC5\x9B" /* ś */ => 's', "\xC5\x9C" /* Ŝ */ => 'S', |
|
182 | + "\xC5\x9D" /* ŝ */ => 's', "\xC5\x9E" /* Ş */ => 'S', "\xC5\x9F" /* ş */ => 's', |
|
183 | + "\xC5\xA0" /* Š */ => 'S', "\xC5\xA1" /* š */ => 's', "\xC5\xA2" /* Ţ */ => 'T', |
|
184 | + "\xC5\xA3" /* ţ */ => 't', "\xC5\xA4" /* Ť */ => 'T', "\xC5\xA5" /* ť */ => 't', |
|
185 | + "\xC5\xA6" /* Ŧ */ => 'T', "\xC5\xA7" /* ŧ */ => 't', "\xC5\xA8" /* Ũ */ => 'U', |
|
186 | + "\xC5\xA9" /* ũ */ => 'u', "\xC5\xAA" /* Ū */ => 'U', "\xC5\xAB" /* ū */ => 'u', |
|
187 | + "\xC5\xAC" /* Ŭ */ => 'U', "\xC5\xAD" /* ŭ */ => 'u', "\xC5\xAE" /* Ů */ => 'U', |
|
188 | + "\xC5\xAF" /* ů */ => 'u', "\xC5\xB0" /* Ű */ => 'U', "\xC5\xB1" /* ű */ => 'u', |
|
189 | + "\xC5\xB2" /* Ų */ => 'U', "\xC5\xB3" /* ų */ => 'u', "\xC5\xB4" /* Ŵ */ => 'W', |
|
190 | + "\xC5\xB5" /* ŵ */ => 'w', "\xC5\xB6" /* Ŷ */ => 'Y', "\xC5\xB7" /* ŷ */ => 'y', |
|
191 | + "\xC5\xB8" /* Ÿ */ => 'Y', "\xC5\xB9" /* Ź */ => 'Z', "\xC5\xBA" /* ź */ => 'z', |
|
192 | + "\xC5\xBB" /* Ż */ => 'Z', "\xC5\xBC" /* ż */ => 'z', "\xC5\xBD" /* Ž */ => 'Z', |
|
193 | + "\xC5\xBE" /* ž */ => 'z', "\xC5\xBF" /* ſ */ => 's', |
|
194 | + // Decompositions for Latin Extended-B |
|
195 | + "\xC8\x98" /* Ș */ => 'S', "\xC8\x99" /* ș */ => 's', |
|
196 | + "\xC8\x9A" /* Ț */ => 'T', "\xC8\x9B" /* ț */ => 't', |
|
197 | + // unmarked |
|
198 | + "\xC6\xA0" /* Ơ */ => 'O', "\xC6\xA1" /* ơ */ => 'o', |
|
199 | + "\xC6\xAF" /* Ư */ => 'U', "\xC6\xB0" /* ư */ => 'u', |
|
200 | + // grave accent |
|
201 | + "\xE1\xBA\xA6" /* Ầ */ => 'A', "\xE1\xBA\xA7" /* ầ */ => 'a', |
|
202 | + "\xE1\xBA\xB0" /* Ằ */ => 'A', "\xE1\xBA\xB1" /* ằ */ => 'a', |
|
203 | + "\xE1\xBB\x80" /* Ề */ => 'E', "\xE1\xBB\x81" /* ề */ => 'e', |
|
204 | + "\xE1\xBB\x92" /* Ồ */ => 'O', "\xE1\xBB\x93" /* ồ */ => 'o', |
|
205 | + "\xE1\xBB\x9C" /* Ờ */ => 'O', "\xE1\xBB\x9D" /* ờ */ => 'o', |
|
206 | + "\xE1\xBB\xAA" /* Ừ */ => 'U', "\xE1\xBB\xAB" /* ừ */ => 'u', |
|
207 | + "\xE1\xBB\xB2" /* Ỳ */ => 'Y', "\xE1\xBB\xB3" /* ỳ */ => 'y', |
|
208 | + // hook |
|
209 | + "\xE1\xBA\xA2" /* Ả */ => 'A', "\xE1\xBA\xA3" /* ả */ => 'a', |
|
210 | + "\xE1\xBA\xA8" /* Ẩ */ => 'A', "\xE1\xBA\xA9" /* ẩ */ => 'a', |
|
211 | + "\xE1\xBA\xB2" /* Ẳ */ => 'A', "\xE1\xBA\xB3" /* ẳ */ => 'a', |
|
212 | + "\xE1\xBA\xBA" /* Ẻ */ => 'E', "\xE1\xBA\xBB" /* ẻ */ => 'e', |
|
213 | + "\xE1\xBB\x82" /* Ể */ => 'E', "\xE1\xBB\x83" /* ể */ => 'e', |
|
214 | + "\xE1\xBB\x88" /* Ỉ */ => 'I', "\xE1\xBB\x89" /* ỉ */ => 'i', |
|
215 | + "\xE1\xBB\x8E" /* Ỏ */ => 'O', "\xE1\xBB\x8F" /* ỏ */ => 'o', |
|
216 | + "\xE1\xBB\x94" /* Ổ */ => 'O', "\xE1\xBB\x95" /* ổ */ => 'o', |
|
217 | + "\xE1\xBB\x9E" /* Ở */ => 'O', "\xE1\xBB\x9F" /* ở */ => 'o', |
|
218 | + "\xE1\xBB\xA6" /* Ủ */ => 'U', "\xE1\xBB\xA7" /* ủ */ => 'u', |
|
219 | + "\xE1\xBB\xAC" /* Ử */ => 'U', "\xE1\xBB\xAD" /* ử */ => 'u', |
|
220 | + "\xE1\xBB\xB6" /* Ỷ */ => 'Y', "\xE1\xBB\xB7" /* ỷ */ => 'y', |
|
221 | + // tilde |
|
222 | + "\xE1\xBA\xAA" /* Ẫ */ => 'A', "\xE1\xBA\xAB" /* ẫ */ => 'a', |
|
223 | + "\xE1\xBA\xB4" /* Ẵ */ => 'A', "\xE1\xBA\xB5" /* ẵ */ => 'a', |
|
224 | + "\xE1\xBA\xBC" /* Ẽ */ => 'E', "\xE1\xBA\xBD" /* ẽ */ => 'e', |
|
225 | + "\xE1\xBB\x84" /* Ễ */ => 'E', "\xE1\xBB\x85" /* ễ */ => 'e', |
|
226 | + "\xE1\xBB\x96" /* Ỗ */ => 'O', "\xE1\xBB\x97" /* ỗ */ => 'o', |
|
227 | + "\xE1\xBB\xA0" /* Ỡ */ => 'O', "\xE1\xBB\xA1" /* ỡ */ => 'o', |
|
228 | + "\xE1\xBB\xAE" /* Ữ */ => 'U', "\xE1\xBB\xAF" /* ữ */ => 'u', |
|
229 | + "\xE1\xBB\xB8" /* Ỹ */ => 'Y', "\xE1\xBB\xB9" /* ỹ */ => 'y', |
|
230 | + // acute accent |
|
231 | + "\xE1\xBA\xA4" /* Ấ */ => 'A', "\xE1\xBA\xA5" /* ấ */ => 'a', |
|
232 | + "\xE1\xBA\xAE" /* Ắ */ => 'A', "\xE1\xBA\xAF" /* ắ */ => 'a', |
|
233 | + "\xE1\xBA\xBE" /* Ế */ => 'E', "\xE1\xBA\xBF" /* ế */ => 'e', |
|
234 | + "\xE1\xBB\x90" /* Ố */ => 'O', "\xE1\xBB\x91" /* ố */ => 'o', |
|
235 | + "\xE1\xBB\x9A" /* Ớ */ => 'O', "\xE1\xBB\x9B" /* ớ */ => 'o', |
|
236 | + "\xE1\xBB\xA8" /* Ứ */ => 'U', "\xE1\xBB\xA9" /* ứ */ => 'u', |
|
237 | + // dot below |
|
238 | + "\xE1\xBA\xA0" /* Ạ */ => 'A', "\xE1\xBA\xA1" /* ạ */ => 'a', |
|
239 | + "\xE1\xBA\xAC" /* Ậ */ => 'A', "\xE1\xBA\xAD" /* ậ */ => 'a', |
|
240 | + "\xE1\xBA\xB6" /* Ặ */ => 'A', "\xE1\xBA\xB7" /* ặ */ => 'a', |
|
241 | + "\xE1\xBA\xB8" /* Ẹ */ => 'E', "\xE1\xBA\xB9" /* ẹ */ => 'e', |
|
242 | + "\xE1\xBB\x86" /* Ệ */ => 'E', "\xE1\xBB\x87" /* ệ */ => 'e', |
|
243 | + "\xE1\xBB\x8A" /* Ị */ => 'I', "\xE1\xBB\x8B" /* ị */ => 'i', |
|
244 | + "\xE1\xBB\x8C" /* Ọ */ => 'O', "\xE1\xBB\x8D" /* ọ */ => 'o', |
|
245 | + "\xE1\xBB\x98" /* Ộ */ => 'O', "\xE1\xBB\x99" /* ộ */ => 'o', |
|
246 | + "\xE1\xBB\xA2" /* Ợ */ => 'O', "\xE1\xBB\xA3" /* ợ */ => 'o', |
|
247 | + "\xE1\xBB\xA4" /* Ụ */ => 'U', "\xE1\xBB\xA5" /* ụ */ => 'u', |
|
248 | + "\xE1\xBB\xB0" /* Ự */ => 'U', "\xE1\xBB\xB1" /* ự */ => 'u', |
|
249 | + "\xE1\xBB\xB4" /* Ỵ */ => 'Y', "\xE1\xBB\xB5" /* ỵ */ => 'y', |
|
250 | + ); |
|
251 | + } |
|
252 | 252 | |
253 | - /** |
|
254 | - * Tests that "normalizer_normalize" exists and works |
|
255 | - * |
|
256 | - * @return bool |
|
257 | - */ |
|
258 | - static public function hasNormalizerSupport() { |
|
259 | - static $ret = null; |
|
260 | - if (null === $ret) { |
|
261 | - $form_c = "\xC3\x85"; // 'LATIN CAPITAL LETTER A WITH RING ABOVE' (U+00C5) |
|
262 | - $form_d = "A\xCC\x8A"; // A followed by 'COMBINING RING ABOVE' (U+030A) |
|
263 | - $ret = (function_exists('normalizer_normalize') |
|
264 | - && $form_c === normalizer_normalize($form_d)); |
|
265 | - } |
|
266 | - return $ret; |
|
267 | - } |
|
253 | + /** |
|
254 | + * Tests that "normalizer_normalize" exists and works |
|
255 | + * |
|
256 | + * @return bool |
|
257 | + */ |
|
258 | + static public function hasNormalizerSupport() { |
|
259 | + static $ret = null; |
|
260 | + if (null === $ret) { |
|
261 | + $form_c = "\xC3\x85"; // 'LATIN CAPITAL LETTER A WITH RING ABOVE' (U+00C5) |
|
262 | + $form_d = "A\xCC\x8A"; // A followed by 'COMBINING RING ABOVE' (U+030A) |
|
263 | + $ret = (function_exists('normalizer_normalize') |
|
264 | + && $form_c === normalizer_normalize($form_d)); |
|
265 | + } |
|
266 | + return $ret; |
|
267 | + } |
|
268 | 268 | } |
269 | 269 |
@@ -174,7 +174,7 @@ |
||
174 | 174 | * @param array $options The options array to pass to the getter function. If limit is |
175 | 175 | * not set, 10 is used as the default. In most cases that is not |
176 | 176 | * what you want. |
177 | - * @param mixed $callback An optional callback function that all results will be passed |
|
177 | + * @param null|string $callback An optional callback function that all results will be passed |
|
178 | 178 | * to upon load. The callback needs to accept $result, $getter, |
179 | 179 | * $options. |
180 | 180 | * @param int $chunk_size The number of entities to pull in before requesting more. |
@@ -72,422 +72,422 @@ |
||
72 | 72 | */ |
73 | 73 | class ElggBatch implements BatchResult { |
74 | 74 | |
75 | - /** |
|
76 | - * The objects to iterate over. |
|
77 | - * |
|
78 | - * @var array |
|
79 | - */ |
|
80 | - private $results = array(); |
|
81 | - |
|
82 | - /** |
|
83 | - * The function used to get results. |
|
84 | - * |
|
85 | - * @var callable |
|
86 | - */ |
|
87 | - private $getter = null; |
|
88 | - |
|
89 | - /** |
|
90 | - * The given $options to alter and pass to the getter. |
|
91 | - * |
|
92 | - * @var array |
|
93 | - */ |
|
94 | - private $options = array(); |
|
95 | - |
|
96 | - /** |
|
97 | - * The number of results to grab at a time. |
|
98 | - * |
|
99 | - * @var int |
|
100 | - */ |
|
101 | - private $chunkSize = 25; |
|
102 | - |
|
103 | - /** |
|
104 | - * A callback function to pass results through. |
|
105 | - * |
|
106 | - * @var callable |
|
107 | - */ |
|
108 | - private $callback = null; |
|
109 | - |
|
110 | - /** |
|
111 | - * Start after this many results. |
|
112 | - * |
|
113 | - * @var int |
|
114 | - */ |
|
115 | - private $offset = 0; |
|
116 | - |
|
117 | - /** |
|
118 | - * Stop after this many results. |
|
119 | - * |
|
120 | - * @var int |
|
121 | - */ |
|
122 | - private $limit = 0; |
|
123 | - |
|
124 | - /** |
|
125 | - * Number of processed results. |
|
126 | - * |
|
127 | - * @var int |
|
128 | - */ |
|
129 | - private $retrievedResults = 0; |
|
130 | - |
|
131 | - /** |
|
132 | - * The index of the current result within the current chunk |
|
133 | - * |
|
134 | - * @var int |
|
135 | - */ |
|
136 | - private $resultIndex = 0; |
|
137 | - |
|
138 | - /** |
|
139 | - * The index of the current chunk |
|
140 | - * |
|
141 | - * @var int |
|
142 | - */ |
|
143 | - private $chunkIndex = 0; |
|
144 | - |
|
145 | - /** |
|
146 | - * The number of results iterated through |
|
147 | - * |
|
148 | - * @var int |
|
149 | - */ |
|
150 | - private $processedResults = 0; |
|
151 | - |
|
152 | - /** |
|
153 | - * Is the getter a valid callback |
|
154 | - * |
|
155 | - * @var bool |
|
156 | - */ |
|
157 | - private $validGetter = null; |
|
158 | - |
|
159 | - /** |
|
160 | - * The result of running all entities through the callback function. |
|
161 | - * |
|
162 | - * @var mixed |
|
163 | - */ |
|
164 | - public $callbackResult = null; |
|
165 | - |
|
166 | - /** |
|
167 | - * If false, offset will not be incremented. This is used for callbacks/loops that delete. |
|
168 | - * |
|
169 | - * @var bool |
|
170 | - */ |
|
171 | - private $incrementOffset = true; |
|
172 | - |
|
173 | - /** |
|
174 | - * Entities that could not be instantiated during a fetch |
|
175 | - * |
|
176 | - * @var \stdClass[] |
|
177 | - */ |
|
178 | - private $incompleteEntities = array(); |
|
179 | - |
|
180 | - /** |
|
181 | - * Total number of incomplete entities fetched |
|
182 | - * |
|
183 | - * @var int |
|
184 | - */ |
|
185 | - private $totalIncompletes = 0; |
|
186 | - |
|
187 | - /** |
|
188 | - * Batches operations on any elgg_get_*() or compatible function that supports |
|
189 | - * an options array. |
|
190 | - * |
|
191 | - * Instead of returning all objects in memory, it goes through $chunk_size |
|
192 | - * objects, then requests more from the server. This avoids OOM errors. |
|
193 | - * |
|
194 | - * @param string $getter The function used to get objects. Usually |
|
195 | - * an elgg_get_*() function, but can be any valid PHP callback. |
|
196 | - * @param array $options The options array to pass to the getter function. If limit is |
|
197 | - * not set, 10 is used as the default. In most cases that is not |
|
198 | - * what you want. |
|
199 | - * @param mixed $callback An optional callback function that all results will be passed |
|
200 | - * to upon load. The callback needs to accept $result, $getter, |
|
201 | - * $options. |
|
202 | - * @param int $chunk_size The number of entities to pull in before requesting more. |
|
203 | - * You have to balance this between running out of memory in PHP |
|
204 | - * and hitting the db server too often. |
|
205 | - * @param bool $inc_offset Increment the offset on each fetch. This must be false for |
|
206 | - * callbacks that delete rows. You can set this after the |
|
207 | - * object is created with {@link \ElggBatch::setIncrementOffset()}. |
|
208 | - */ |
|
209 | - public function __construct($getter, $options, $callback = null, $chunk_size = 25, |
|
210 | - $inc_offset = true) { |
|
75 | + /** |
|
76 | + * The objects to iterate over. |
|
77 | + * |
|
78 | + * @var array |
|
79 | + */ |
|
80 | + private $results = array(); |
|
81 | + |
|
82 | + /** |
|
83 | + * The function used to get results. |
|
84 | + * |
|
85 | + * @var callable |
|
86 | + */ |
|
87 | + private $getter = null; |
|
88 | + |
|
89 | + /** |
|
90 | + * The given $options to alter and pass to the getter. |
|
91 | + * |
|
92 | + * @var array |
|
93 | + */ |
|
94 | + private $options = array(); |
|
95 | + |
|
96 | + /** |
|
97 | + * The number of results to grab at a time. |
|
98 | + * |
|
99 | + * @var int |
|
100 | + */ |
|
101 | + private $chunkSize = 25; |
|
102 | + |
|
103 | + /** |
|
104 | + * A callback function to pass results through. |
|
105 | + * |
|
106 | + * @var callable |
|
107 | + */ |
|
108 | + private $callback = null; |
|
109 | + |
|
110 | + /** |
|
111 | + * Start after this many results. |
|
112 | + * |
|
113 | + * @var int |
|
114 | + */ |
|
115 | + private $offset = 0; |
|
116 | + |
|
117 | + /** |
|
118 | + * Stop after this many results. |
|
119 | + * |
|
120 | + * @var int |
|
121 | + */ |
|
122 | + private $limit = 0; |
|
123 | + |
|
124 | + /** |
|
125 | + * Number of processed results. |
|
126 | + * |
|
127 | + * @var int |
|
128 | + */ |
|
129 | + private $retrievedResults = 0; |
|
130 | + |
|
131 | + /** |
|
132 | + * The index of the current result within the current chunk |
|
133 | + * |
|
134 | + * @var int |
|
135 | + */ |
|
136 | + private $resultIndex = 0; |
|
137 | + |
|
138 | + /** |
|
139 | + * The index of the current chunk |
|
140 | + * |
|
141 | + * @var int |
|
142 | + */ |
|
143 | + private $chunkIndex = 0; |
|
144 | + |
|
145 | + /** |
|
146 | + * The number of results iterated through |
|
147 | + * |
|
148 | + * @var int |
|
149 | + */ |
|
150 | + private $processedResults = 0; |
|
151 | + |
|
152 | + /** |
|
153 | + * Is the getter a valid callback |
|
154 | + * |
|
155 | + * @var bool |
|
156 | + */ |
|
157 | + private $validGetter = null; |
|
158 | + |
|
159 | + /** |
|
160 | + * The result of running all entities through the callback function. |
|
161 | + * |
|
162 | + * @var mixed |
|
163 | + */ |
|
164 | + public $callbackResult = null; |
|
165 | + |
|
166 | + /** |
|
167 | + * If false, offset will not be incremented. This is used for callbacks/loops that delete. |
|
168 | + * |
|
169 | + * @var bool |
|
170 | + */ |
|
171 | + private $incrementOffset = true; |
|
172 | + |
|
173 | + /** |
|
174 | + * Entities that could not be instantiated during a fetch |
|
175 | + * |
|
176 | + * @var \stdClass[] |
|
177 | + */ |
|
178 | + private $incompleteEntities = array(); |
|
179 | + |
|
180 | + /** |
|
181 | + * Total number of incomplete entities fetched |
|
182 | + * |
|
183 | + * @var int |
|
184 | + */ |
|
185 | + private $totalIncompletes = 0; |
|
186 | + |
|
187 | + /** |
|
188 | + * Batches operations on any elgg_get_*() or compatible function that supports |
|
189 | + * an options array. |
|
190 | + * |
|
191 | + * Instead of returning all objects in memory, it goes through $chunk_size |
|
192 | + * objects, then requests more from the server. This avoids OOM errors. |
|
193 | + * |
|
194 | + * @param string $getter The function used to get objects. Usually |
|
195 | + * an elgg_get_*() function, but can be any valid PHP callback. |
|
196 | + * @param array $options The options array to pass to the getter function. If limit is |
|
197 | + * not set, 10 is used as the default. In most cases that is not |
|
198 | + * what you want. |
|
199 | + * @param mixed $callback An optional callback function that all results will be passed |
|
200 | + * to upon load. The callback needs to accept $result, $getter, |
|
201 | + * $options. |
|
202 | + * @param int $chunk_size The number of entities to pull in before requesting more. |
|
203 | + * You have to balance this between running out of memory in PHP |
|
204 | + * and hitting the db server too often. |
|
205 | + * @param bool $inc_offset Increment the offset on each fetch. This must be false for |
|
206 | + * callbacks that delete rows. You can set this after the |
|
207 | + * object is created with {@link \ElggBatch::setIncrementOffset()}. |
|
208 | + */ |
|
209 | + public function __construct($getter, $options, $callback = null, $chunk_size = 25, |
|
210 | + $inc_offset = true) { |
|
211 | 211 | |
212 | - $this->getter = $getter; |
|
213 | - $this->options = $options; |
|
214 | - $this->callback = $callback; |
|
215 | - $this->chunkSize = $chunk_size; |
|
216 | - $this->setIncrementOffset($inc_offset); |
|
217 | - |
|
218 | - if ($this->chunkSize <= 0) { |
|
219 | - $this->chunkSize = 25; |
|
220 | - } |
|
221 | - |
|
222 | - // store these so we can compare later |
|
223 | - $this->offset = elgg_extract('offset', $options, 0); |
|
224 | - $this->limit = elgg_extract('limit', $options, elgg_get_config('default_limit')); |
|
225 | - |
|
226 | - // if passed a callback, create a new \ElggBatch with the same options |
|
227 | - // and pass each to the callback. |
|
228 | - if ($callback && is_callable($callback)) { |
|
229 | - $batch = new \ElggBatch($getter, $options, null, $chunk_size, $inc_offset); |
|
230 | - |
|
231 | - $all_results = null; |
|
232 | - |
|
233 | - foreach ($batch as $result) { |
|
234 | - $result = call_user_func($callback, $result, $getter, $options); |
|
235 | - |
|
236 | - if (!isset($all_results)) { |
|
237 | - if ($result === true || $result === false || $result === null) { |
|
238 | - $all_results = $result; |
|
239 | - } else { |
|
240 | - $all_results = array(); |
|
241 | - } |
|
242 | - } |
|
243 | - |
|
244 | - if (($result === true || $result === false || $result === null) && !is_array($all_results)) { |
|
245 | - $all_results = $result && $all_results; |
|
246 | - } else { |
|
247 | - $all_results[] = $result; |
|
248 | - } |
|
249 | - } |
|
250 | - |
|
251 | - $this->callbackResult = $all_results; |
|
252 | - } |
|
253 | - } |
|
254 | - |
|
255 | - /** |
|
256 | - * Tell the process that an entity was incomplete during a fetch |
|
257 | - * |
|
258 | - * @param \stdClass $row |
|
259 | - * |
|
260 | - * @access private |
|
261 | - */ |
|
262 | - public function reportIncompleteEntity(\stdClass $row) { |
|
263 | - $this->incompleteEntities[] = $row; |
|
264 | - } |
|
265 | - |
|
266 | - /** |
|
267 | - * Fetches the next chunk of results |
|
268 | - * |
|
269 | - * @return bool |
|
270 | - */ |
|
271 | - private function getNextResultsChunk() { |
|
272 | - |
|
273 | - // always reset results. |
|
274 | - $this->results = array(); |
|
275 | - |
|
276 | - if (!isset($this->validGetter)) { |
|
277 | - $this->validGetter = is_callable($this->getter); |
|
278 | - } |
|
279 | - |
|
280 | - if (!$this->validGetter) { |
|
281 | - return false; |
|
282 | - } |
|
283 | - |
|
284 | - $limit = $this->chunkSize; |
|
285 | - |
|
286 | - // if someone passed limit = 0 they want everything. |
|
287 | - if ($this->limit != 0) { |
|
288 | - if ($this->retrievedResults >= $this->limit) { |
|
289 | - return false; |
|
290 | - } |
|
291 | - |
|
292 | - // if original limit < chunk size, set limit to original limit |
|
293 | - // else if the number of results we'll fetch if greater than the original limit |
|
294 | - if ($this->limit < $this->chunkSize) { |
|
295 | - $limit = $this->limit; |
|
296 | - } elseif ($this->retrievedResults + $this->chunkSize > $this->limit) { |
|
297 | - // set the limit to the number of results remaining in the original limit |
|
298 | - $limit = $this->limit - $this->retrievedResults; |
|
299 | - } |
|
300 | - } |
|
301 | - |
|
302 | - if ($this->incrementOffset) { |
|
303 | - $offset = $this->offset + $this->retrievedResults; |
|
304 | - } else { |
|
305 | - $offset = $this->offset + $this->totalIncompletes; |
|
306 | - } |
|
307 | - |
|
308 | - $current_options = array( |
|
309 | - 'limit' => $limit, |
|
310 | - 'offset' => $offset, |
|
311 | - '__ElggBatch' => $this, |
|
312 | - ); |
|
313 | - |
|
314 | - $options = array_merge($this->options, $current_options); |
|
315 | - |
|
316 | - $this->incompleteEntities = array(); |
|
317 | - $this->results = call_user_func($this->getter, $options); |
|
318 | - |
|
319 | - // batch result sets tend to be large; we don't want to cache these. |
|
320 | - _elgg_services()->db->disableQueryCache(); |
|
321 | - |
|
322 | - $num_results = count($this->results); |
|
323 | - $num_incomplete = count($this->incompleteEntities); |
|
324 | - |
|
325 | - $this->totalIncompletes += $num_incomplete; |
|
326 | - |
|
327 | - if ($this->incompleteEntities) { |
|
328 | - // pad the front of the results with nulls representing the incompletes |
|
329 | - array_splice($this->results, 0, 0, array_pad(array(), $num_incomplete, null)); |
|
330 | - // ...and skip past them |
|
331 | - reset($this->results); |
|
332 | - for ($i = 0; $i < $num_incomplete; $i++) { |
|
333 | - next($this->results); |
|
334 | - } |
|
335 | - } |
|
336 | - |
|
337 | - if ($this->results) { |
|
338 | - $this->chunkIndex++; |
|
339 | - |
|
340 | - // let the system know we've jumped past the nulls |
|
341 | - $this->resultIndex = $num_incomplete; |
|
342 | - |
|
343 | - $this->retrievedResults += ($num_results + $num_incomplete); |
|
344 | - if ($num_results == 0) { |
|
345 | - // This fetch was *all* incompletes! We need to fetch until we can either |
|
346 | - // offer at least one row to iterate over, or give up. |
|
347 | - return $this->getNextResultsChunk(); |
|
348 | - } |
|
349 | - _elgg_services()->db->enableQueryCache(); |
|
350 | - return true; |
|
351 | - } else { |
|
352 | - _elgg_services()->db->enableQueryCache(); |
|
353 | - return false; |
|
354 | - } |
|
355 | - } |
|
356 | - |
|
357 | - /** |
|
358 | - * Increment the offset from the original options array? Setting to |
|
359 | - * false is required for callbacks that delete rows. |
|
360 | - * |
|
361 | - * @param bool $increment Set to false when deleting data |
|
362 | - * @return void |
|
363 | - */ |
|
364 | - public function setIncrementOffset($increment = true) { |
|
365 | - $this->incrementOffset = (bool) $increment; |
|
366 | - } |
|
367 | - |
|
368 | - /** |
|
369 | - * Implements Iterator |
|
370 | - */ |
|
371 | - |
|
372 | - /** |
|
373 | - * {@inheritdoc} |
|
374 | - */ |
|
375 | - public function rewind() { |
|
376 | - $this->resultIndex = 0; |
|
377 | - $this->retrievedResults = 0; |
|
378 | - $this->processedResults = 0; |
|
379 | - |
|
380 | - // only grab results if we haven't yet or we're crossing chunks |
|
381 | - if ($this->chunkIndex == 0 || $this->limit > $this->chunkSize) { |
|
382 | - $this->chunkIndex = 0; |
|
383 | - $this->getNextResultsChunk(); |
|
384 | - } |
|
385 | - } |
|
386 | - |
|
387 | - /** |
|
388 | - * {@inheritdoc} |
|
389 | - */ |
|
390 | - public function current() { |
|
391 | - return current($this->results); |
|
392 | - } |
|
393 | - |
|
394 | - /** |
|
395 | - * {@inheritdoc} |
|
396 | - */ |
|
397 | - public function key() { |
|
398 | - return $this->processedResults; |
|
399 | - } |
|
400 | - |
|
401 | - /** |
|
402 | - * {@inheritdoc} |
|
403 | - */ |
|
404 | - public function next() { |
|
405 | - // if we'll be at the end. |
|
406 | - if (($this->processedResults + 1) >= $this->limit && $this->limit > 0) { |
|
407 | - $this->results = array(); |
|
408 | - return false; |
|
409 | - } |
|
410 | - |
|
411 | - // if we'll need new results. |
|
412 | - if (($this->resultIndex + 1) >= $this->chunkSize) { |
|
413 | - if (!$this->getNextResultsChunk()) { |
|
414 | - $this->results = array(); |
|
415 | - return false; |
|
416 | - } |
|
417 | - |
|
418 | - $result = current($this->results); |
|
419 | - } else { |
|
420 | - // the function above resets the indexes, so only inc if not |
|
421 | - // getting new set |
|
422 | - $this->resultIndex++; |
|
423 | - $result = next($this->results); |
|
424 | - } |
|
425 | - |
|
426 | - $this->processedResults++; |
|
427 | - return $result; |
|
428 | - } |
|
429 | - |
|
430 | - /** |
|
431 | - * {@inheritdoc} |
|
432 | - */ |
|
433 | - public function valid() { |
|
434 | - if (!is_array($this->results)) { |
|
435 | - return false; |
|
436 | - } |
|
437 | - $key = key($this->results); |
|
438 | - return ($key !== null && $key !== false); |
|
439 | - } |
|
440 | - |
|
441 | - /** |
|
442 | - * Count the total results available at this moment. |
|
443 | - * |
|
444 | - * As this performs a separate query, the count returned may not match the number of results you can |
|
445 | - * fetch via iteration on a very active DB. |
|
446 | - * |
|
447 | - * @see Countable::count() |
|
448 | - * @return int |
|
449 | - */ |
|
450 | - public function count() { |
|
451 | - if (!is_callable($this->getter)) { |
|
452 | - $inspector = new \Elgg\Debug\Inspector(); |
|
453 | - throw new RuntimeException("Getter is not callable: " . $inspector->describeCallable($this->getter)); |
|
454 | - } |
|
455 | - |
|
456 | - $options = array_merge($this->options, ['count' => true]); |
|
457 | - |
|
458 | - return call_user_func($this->getter, $options); |
|
459 | - } |
|
460 | - |
|
461 | - /** |
|
462 | - * Read a property |
|
463 | - * |
|
464 | - * @param string $name |
|
465 | - * @return mixed |
|
466 | - * @access private |
|
467 | - */ |
|
468 | - public function __get($name) { |
|
469 | - if ($name === 'options') { |
|
470 | - elgg_deprecated_notice("The ElggBatch 'options' property is private and should not be used", "2.3"); |
|
471 | - return $this->options; |
|
472 | - } |
|
473 | - |
|
474 | - _elgg_services()->logger->warn("Read of non-existent property '$name'"); |
|
475 | - return null; |
|
476 | - } |
|
477 | - |
|
478 | - /** |
|
479 | - * Write a property |
|
480 | - * |
|
481 | - * @param string $name |
|
482 | - * @param mixed $value |
|
483 | - * @return void |
|
484 | - * @access private |
|
485 | - */ |
|
486 | - public function __set($name, $value) { |
|
487 | - if ($name === 'options') { |
|
488 | - elgg_deprecated_notice("The ElggBatch 'options' property is private and should not be used", "2.3"); |
|
489 | - } |
|
490 | - |
|
491 | - $this->{$name} = $value; |
|
492 | - } |
|
212 | + $this->getter = $getter; |
|
213 | + $this->options = $options; |
|
214 | + $this->callback = $callback; |
|
215 | + $this->chunkSize = $chunk_size; |
|
216 | + $this->setIncrementOffset($inc_offset); |
|
217 | + |
|
218 | + if ($this->chunkSize <= 0) { |
|
219 | + $this->chunkSize = 25; |
|
220 | + } |
|
221 | + |
|
222 | + // store these so we can compare later |
|
223 | + $this->offset = elgg_extract('offset', $options, 0); |
|
224 | + $this->limit = elgg_extract('limit', $options, elgg_get_config('default_limit')); |
|
225 | + |
|
226 | + // if passed a callback, create a new \ElggBatch with the same options |
|
227 | + // and pass each to the callback. |
|
228 | + if ($callback && is_callable($callback)) { |
|
229 | + $batch = new \ElggBatch($getter, $options, null, $chunk_size, $inc_offset); |
|
230 | + |
|
231 | + $all_results = null; |
|
232 | + |
|
233 | + foreach ($batch as $result) { |
|
234 | + $result = call_user_func($callback, $result, $getter, $options); |
|
235 | + |
|
236 | + if (!isset($all_results)) { |
|
237 | + if ($result === true || $result === false || $result === null) { |
|
238 | + $all_results = $result; |
|
239 | + } else { |
|
240 | + $all_results = array(); |
|
241 | + } |
|
242 | + } |
|
243 | + |
|
244 | + if (($result === true || $result === false || $result === null) && !is_array($all_results)) { |
|
245 | + $all_results = $result && $all_results; |
|
246 | + } else { |
|
247 | + $all_results[] = $result; |
|
248 | + } |
|
249 | + } |
|
250 | + |
|
251 | + $this->callbackResult = $all_results; |
|
252 | + } |
|
253 | + } |
|
254 | + |
|
255 | + /** |
|
256 | + * Tell the process that an entity was incomplete during a fetch |
|
257 | + * |
|
258 | + * @param \stdClass $row |
|
259 | + * |
|
260 | + * @access private |
|
261 | + */ |
|
262 | + public function reportIncompleteEntity(\stdClass $row) { |
|
263 | + $this->incompleteEntities[] = $row; |
|
264 | + } |
|
265 | + |
|
266 | + /** |
|
267 | + * Fetches the next chunk of results |
|
268 | + * |
|
269 | + * @return bool |
|
270 | + */ |
|
271 | + private function getNextResultsChunk() { |
|
272 | + |
|
273 | + // always reset results. |
|
274 | + $this->results = array(); |
|
275 | + |
|
276 | + if (!isset($this->validGetter)) { |
|
277 | + $this->validGetter = is_callable($this->getter); |
|
278 | + } |
|
279 | + |
|
280 | + if (!$this->validGetter) { |
|
281 | + return false; |
|
282 | + } |
|
283 | + |
|
284 | + $limit = $this->chunkSize; |
|
285 | + |
|
286 | + // if someone passed limit = 0 they want everything. |
|
287 | + if ($this->limit != 0) { |
|
288 | + if ($this->retrievedResults >= $this->limit) { |
|
289 | + return false; |
|
290 | + } |
|
291 | + |
|
292 | + // if original limit < chunk size, set limit to original limit |
|
293 | + // else if the number of results we'll fetch if greater than the original limit |
|
294 | + if ($this->limit < $this->chunkSize) { |
|
295 | + $limit = $this->limit; |
|
296 | + } elseif ($this->retrievedResults + $this->chunkSize > $this->limit) { |
|
297 | + // set the limit to the number of results remaining in the original limit |
|
298 | + $limit = $this->limit - $this->retrievedResults; |
|
299 | + } |
|
300 | + } |
|
301 | + |
|
302 | + if ($this->incrementOffset) { |
|
303 | + $offset = $this->offset + $this->retrievedResults; |
|
304 | + } else { |
|
305 | + $offset = $this->offset + $this->totalIncompletes; |
|
306 | + } |
|
307 | + |
|
308 | + $current_options = array( |
|
309 | + 'limit' => $limit, |
|
310 | + 'offset' => $offset, |
|
311 | + '__ElggBatch' => $this, |
|
312 | + ); |
|
313 | + |
|
314 | + $options = array_merge($this->options, $current_options); |
|
315 | + |
|
316 | + $this->incompleteEntities = array(); |
|
317 | + $this->results = call_user_func($this->getter, $options); |
|
318 | + |
|
319 | + // batch result sets tend to be large; we don't want to cache these. |
|
320 | + _elgg_services()->db->disableQueryCache(); |
|
321 | + |
|
322 | + $num_results = count($this->results); |
|
323 | + $num_incomplete = count($this->incompleteEntities); |
|
324 | + |
|
325 | + $this->totalIncompletes += $num_incomplete; |
|
326 | + |
|
327 | + if ($this->incompleteEntities) { |
|
328 | + // pad the front of the results with nulls representing the incompletes |
|
329 | + array_splice($this->results, 0, 0, array_pad(array(), $num_incomplete, null)); |
|
330 | + // ...and skip past them |
|
331 | + reset($this->results); |
|
332 | + for ($i = 0; $i < $num_incomplete; $i++) { |
|
333 | + next($this->results); |
|
334 | + } |
|
335 | + } |
|
336 | + |
|
337 | + if ($this->results) { |
|
338 | + $this->chunkIndex++; |
|
339 | + |
|
340 | + // let the system know we've jumped past the nulls |
|
341 | + $this->resultIndex = $num_incomplete; |
|
342 | + |
|
343 | + $this->retrievedResults += ($num_results + $num_incomplete); |
|
344 | + if ($num_results == 0) { |
|
345 | + // This fetch was *all* incompletes! We need to fetch until we can either |
|
346 | + // offer at least one row to iterate over, or give up. |
|
347 | + return $this->getNextResultsChunk(); |
|
348 | + } |
|
349 | + _elgg_services()->db->enableQueryCache(); |
|
350 | + return true; |
|
351 | + } else { |
|
352 | + _elgg_services()->db->enableQueryCache(); |
|
353 | + return false; |
|
354 | + } |
|
355 | + } |
|
356 | + |
|
357 | + /** |
|
358 | + * Increment the offset from the original options array? Setting to |
|
359 | + * false is required for callbacks that delete rows. |
|
360 | + * |
|
361 | + * @param bool $increment Set to false when deleting data |
|
362 | + * @return void |
|
363 | + */ |
|
364 | + public function setIncrementOffset($increment = true) { |
|
365 | + $this->incrementOffset = (bool) $increment; |
|
366 | + } |
|
367 | + |
|
368 | + /** |
|
369 | + * Implements Iterator |
|
370 | + */ |
|
371 | + |
|
372 | + /** |
|
373 | + * {@inheritdoc} |
|
374 | + */ |
|
375 | + public function rewind() { |
|
376 | + $this->resultIndex = 0; |
|
377 | + $this->retrievedResults = 0; |
|
378 | + $this->processedResults = 0; |
|
379 | + |
|
380 | + // only grab results if we haven't yet or we're crossing chunks |
|
381 | + if ($this->chunkIndex == 0 || $this->limit > $this->chunkSize) { |
|
382 | + $this->chunkIndex = 0; |
|
383 | + $this->getNextResultsChunk(); |
|
384 | + } |
|
385 | + } |
|
386 | + |
|
387 | + /** |
|
388 | + * {@inheritdoc} |
|
389 | + */ |
|
390 | + public function current() { |
|
391 | + return current($this->results); |
|
392 | + } |
|
393 | + |
|
394 | + /** |
|
395 | + * {@inheritdoc} |
|
396 | + */ |
|
397 | + public function key() { |
|
398 | + return $this->processedResults; |
|
399 | + } |
|
400 | + |
|
401 | + /** |
|
402 | + * {@inheritdoc} |
|
403 | + */ |
|
404 | + public function next() { |
|
405 | + // if we'll be at the end. |
|
406 | + if (($this->processedResults + 1) >= $this->limit && $this->limit > 0) { |
|
407 | + $this->results = array(); |
|
408 | + return false; |
|
409 | + } |
|
410 | + |
|
411 | + // if we'll need new results. |
|
412 | + if (($this->resultIndex + 1) >= $this->chunkSize) { |
|
413 | + if (!$this->getNextResultsChunk()) { |
|
414 | + $this->results = array(); |
|
415 | + return false; |
|
416 | + } |
|
417 | + |
|
418 | + $result = current($this->results); |
|
419 | + } else { |
|
420 | + // the function above resets the indexes, so only inc if not |
|
421 | + // getting new set |
|
422 | + $this->resultIndex++; |
|
423 | + $result = next($this->results); |
|
424 | + } |
|
425 | + |
|
426 | + $this->processedResults++; |
|
427 | + return $result; |
|
428 | + } |
|
429 | + |
|
430 | + /** |
|
431 | + * {@inheritdoc} |
|
432 | + */ |
|
433 | + public function valid() { |
|
434 | + if (!is_array($this->results)) { |
|
435 | + return false; |
|
436 | + } |
|
437 | + $key = key($this->results); |
|
438 | + return ($key !== null && $key !== false); |
|
439 | + } |
|
440 | + |
|
441 | + /** |
|
442 | + * Count the total results available at this moment. |
|
443 | + * |
|
444 | + * As this performs a separate query, the count returned may not match the number of results you can |
|
445 | + * fetch via iteration on a very active DB. |
|
446 | + * |
|
447 | + * @see Countable::count() |
|
448 | + * @return int |
|
449 | + */ |
|
450 | + public function count() { |
|
451 | + if (!is_callable($this->getter)) { |
|
452 | + $inspector = new \Elgg\Debug\Inspector(); |
|
453 | + throw new RuntimeException("Getter is not callable: " . $inspector->describeCallable($this->getter)); |
|
454 | + } |
|
455 | + |
|
456 | + $options = array_merge($this->options, ['count' => true]); |
|
457 | + |
|
458 | + return call_user_func($this->getter, $options); |
|
459 | + } |
|
460 | + |
|
461 | + /** |
|
462 | + * Read a property |
|
463 | + * |
|
464 | + * @param string $name |
|
465 | + * @return mixed |
|
466 | + * @access private |
|
467 | + */ |
|
468 | + public function __get($name) { |
|
469 | + if ($name === 'options') { |
|
470 | + elgg_deprecated_notice("The ElggBatch 'options' property is private and should not be used", "2.3"); |
|
471 | + return $this->options; |
|
472 | + } |
|
473 | + |
|
474 | + _elgg_services()->logger->warn("Read of non-existent property '$name'"); |
|
475 | + return null; |
|
476 | + } |
|
477 | + |
|
478 | + /** |
|
479 | + * Write a property |
|
480 | + * |
|
481 | + * @param string $name |
|
482 | + * @param mixed $value |
|
483 | + * @return void |
|
484 | + * @access private |
|
485 | + */ |
|
486 | + public function __set($name, $value) { |
|
487 | + if ($name === 'options') { |
|
488 | + elgg_deprecated_notice("The ElggBatch 'options' property is private and should not be used", "2.3"); |
|
489 | + } |
|
490 | + |
|
491 | + $this->{$name} = $value; |
|
492 | + } |
|
493 | 493 | } |
@@ -450,7 +450,7 @@ |
||
450 | 450 | public function count() { |
451 | 451 | if (!is_callable($this->getter)) { |
452 | 452 | $inspector = new \Elgg\Debug\Inspector(); |
453 | - throw new RuntimeException("Getter is not callable: " . $inspector->describeCallable($this->getter)); |
|
453 | + throw new RuntimeException("Getter is not callable: ".$inspector->describeCallable($this->getter)); |
|
454 | 454 | } |
455 | 455 | |
456 | 456 | $options = array_merge($this->options, ['count' => true]); |