Commit | Line | Data |
---|---|---|
11ade432 AE |
1 | <?php |
2 | namespace wcf\data\user; | |
0dd6ea0c MW |
3 | use wcf\data\object\type\ObjectTypeCache; |
4 | use wcf\data\user\avatar\UserAvatarAction; | |
11ade432 | 5 | use wcf\data\user\group\UserGroup; |
931f6597 | 6 | use wcf\data\AbstractDatabaseObjectAction; |
7918ddba | 7 | use wcf\data\IClipboardAction; |
a427a8c8 | 8 | use wcf\data\ISearchAction; |
320f4a6d | 9 | use wcf\system\cache\builder\UserNotificationEventCacheBuilder; |
7f379ade | 10 | use wcf\system\clipboard\ClipboardHandler; |
11ade432 | 11 | use wcf\system\database\util\PreparedStatementConditionBuilder; |
a79cfb56 | 12 | use wcf\system\exception\PermissionDeniedException; |
3631f7bd | 13 | use wcf\system\exception\UserInputException; |
bae8dd1e | 14 | use wcf\system\request\RequestHandler; |
2bc9f31d | 15 | use wcf\system\WCF; |
d5cab442 | 16 | use wcf\util\StringUtil; |
2fe45e04 | 17 | use wcf\util\UserRegistrationUtil; |
11ade432 AE |
18 | |
19 | /** | |
20 | * Executes user-related actions. | |
21 | * | |
22 | * @author Alexander Ebert | |
fd1dc2fb | 23 | * @copyright 2001-2013 WoltLab GmbH |
11ade432 AE |
24 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
25 | * @package com.woltlab.wcf | |
26 | * @subpackage data.user | |
9f959ced | 27 | * @category Community Framework |
11ade432 | 28 | */ |
7918ddba | 29 | class UserAction extends AbstractDatabaseObjectAction implements IClipboardAction, ISearchAction { |
11ade432 | 30 | /** |
73df94ae | 31 | * @see wcf\data\AbstractDatabaseObjectAction::$className |
11ade432 AE |
32 | */ |
33 | public $className = 'wcf\data\user\UserEditor'; | |
34 | ||
8eb8876b MW |
35 | /** |
36 | * @see wcf\data\AbstractDatabaseObjectAction::$allowGuestAccess | |
37 | */ | |
38 | protected $allowGuestAccess = array('getSearchResultList'); | |
39 | ||
11ade432 | 40 | /** |
73df94ae | 41 | * @see wcf\data\AbstractDatabaseObjectAction::$permissionsCreate |
11ade432 AE |
42 | */ |
43 | protected $permissionsCreate = array('admin.user.canAddUser'); | |
44 | ||
45 | /** | |
73df94ae | 46 | * @see wcf\data\AbstractDatabaseObjectAction::$permissionsDelete |
11ade432 AE |
47 | */ |
48 | protected $permissionsDelete = array('admin.user.canDeleteUser'); | |
49 | ||
50 | /** | |
73df94ae | 51 | * @see wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate |
11ade432 AE |
52 | */ |
53 | protected $permissionsUpdate = array('admin.user.canEditUser'); | |
54 | ||
bae8dd1e AE |
55 | /** |
56 | * @see wcf\data\AbstractDatabaseObjectAction::$requireACP | |
57 | */ | |
58 | protected $requireACP = array('create', 'ban', 'delete', 'disable', 'enable', 'unban'); | |
59 | ||
11ade432 AE |
60 | /** |
61 | * Validates permissions and parameters. | |
62 | */ | |
63 | public function validateCreate() { | |
a54f8d8f | 64 | $this->readString('password', false, 'data'); |
11ade432 AE |
65 | } |
66 | ||
67 | /** | |
11cf19be MW |
68 | * Validates accessible groups. |
69 | * | |
70 | * @param boolean $ignoreOwnUser | |
11ade432 | 71 | */ |
11cf19be MW |
72 | protected function __validateAccessibleGroups($ignoreOwnUser = true) { |
73 | if ($ignoreOwnUser) { | |
74 | if (in_array(WCF::getUser()->userID, $this->objectIDs)) { | |
75 | unset($this->objectIDs[array_search(WCF::getUser()->userID, $this->objectIDs)]); | |
76 | if (isset($this->objects[WCF::getUser()->userID])) { | |
77 | unset($this->objects[WCF::getUser()->userID]); | |
78 | } | |
a7fd745e | 79 | } |
48f9369a | 80 | } |
11ade432 | 81 | |
a7fd745e | 82 | // list might be empty because only our own user id was given |
11cf19be | 83 | if (empty($this->objectIDs)) { |
3631f7bd | 84 | throw new UserInputException('objectIDs'); |
a7fd745e AE |
85 | } |
86 | ||
11ade432 AE |
87 | // validate groups |
88 | $conditions = new PreparedStatementConditionBuilder(); | |
11cf19be | 89 | $conditions->add("userID IN (?)", array($this->objectIDs)); |
11ade432 AE |
90 | |
91 | $sql = "SELECT DISTINCT groupID | |
92 | FROM wcf".WCF_N."_user_to_group | |
93 | ".$conditions; | |
94 | $statement = WCF::getDB()->prepareStatement($sql); | |
95 | $statement->execute($conditions->getParameters()); | |
96 | ||
97 | $groupIDs = array(); | |
98 | while ($row = $statement->fetchArray()) { | |
99 | $groupIDs[] = $row['groupID']; | |
100 | } | |
101 | ||
102 | if (!UserGroup::isAccessibleGroup($groupIDs)) { | |
3631f7bd | 103 | throw new PermissionDeniedException(); |
11ade432 AE |
104 | } |
105 | } | |
106 | ||
11cf19be MW |
107 | /** |
108 | * Validates permissions and parameters. | |
109 | */ | |
110 | public function validateDelete() { | |
111 | // read and validate user objects | |
112 | parent::validateDelete(); | |
113 | ||
114 | $this->__validateAccessibleGroups(); | |
115 | } | |
116 | ||
0dd6ea0c MW |
117 | /** |
118 | * @see wcf\data\IDeleteAction::delete() | |
119 | */ | |
120 | public function delete() { | |
121 | if (empty($this->objects)) { | |
122 | $this->readObjects(); | |
123 | } | |
124 | ||
125 | // delete avatars | |
126 | $avatarIDs = array(); | |
127 | foreach ($this->objects as $user) { | |
128 | if ($user->avatarID) $avatarIDs[] = $user->avatarID; | |
129 | } | |
130 | if (!empty($avatarIDs)) { | |
131 | $action = new UserAvatarAction($avatarIDs, 'delete'); | |
132 | $action->executeAction(); | |
133 | } | |
134 | ||
135 | // delete profile comments | |
136 | if (!empty($this->objectIDs)) { | |
137 | $objectType = ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.comment.commentableContent', 'com.woltlab.wcf.user.profileComment'); | |
138 | $conditionBuilder = new PreparedStatementConditionBuilder(); | |
139 | $conditionBuilder->add('objectTypeID = ?', array($objectType->objectTypeID)); | |
140 | $conditionBuilder->add('objectID IN (?)', array($this->objectIDs)); | |
141 | ||
142 | $sql = "DELETE FROM wcf".WCF_N."_comment | |
143 | ".$conditionBuilder; | |
144 | $statement = WCF::getDB()->prepareStatement($sql); | |
145 | $statement->execute($conditionBuilder->getParameters()); | |
146 | } | |
147 | ||
148 | $returnValue = parent::delete(); | |
149 | ||
150 | return $returnValue; | |
151 | } | |
152 | ||
11ade432 AE |
153 | /** |
154 | * Validates permissions and parameters. | |
11ade432 AE |
155 | */ |
156 | public function validateUpdate() { | |
a79cfb56 | 157 | // read objects |
15fa2802 | 158 | if (empty($this->objects)) { |
a79cfb56 | 159 | $this->readObjects(); |
15fa2802 MS |
160 | |
161 | if (empty($this->objects)) { | |
3631f7bd | 162 | throw new UserInputException('objectIDs'); |
15fa2802 | 163 | } |
a79cfb56 | 164 | } |
11ade432 | 165 | |
bae8dd1e AE |
166 | // disallow updating of anything except for options outside of ACP |
167 | if (RequestHandler::getInstance()->isACPRequest() && (count($this->parameters) != 1 || !isset($this->parameters['options']))) { | |
168 | throw new PermissionDeniedException(); | |
169 | } | |
170 | ||
a79cfb56 AE |
171 | try { |
172 | WCF::getSession()->checkPermissions($this->permissionsUpdate); | |
173 | } | |
174 | catch (PermissionDeniedException $e) { | |
175 | // check if we're editing ourselves | |
176 | if (count($this->objects) == 1 && ($this->objects[0]->userID == WCF::getUser()->userID)) { | |
67ca3261 AE |
177 | $count = count($this->parameters); |
178 | if ($count > 1 || ($count == 1 && !isset($this->parameters['options']))) { | |
3631f7bd | 179 | throw new PermissionDeniedException(); |
a79cfb56 AE |
180 | } |
181 | } | |
182 | ||
3631f7bd | 183 | throw new PermissionDeniedException(); |
a79cfb56 | 184 | } |
11ade432 AE |
185 | } |
186 | ||
11cf19be MW |
187 | /** |
188 | * Validates the ban action. | |
189 | */ | |
190 | public function validateBan() { | |
191 | WCF::getSession()->checkPermissions(array('admin.user.canBanUser')); | |
192 | ||
193 | $this->__validateAccessibleGroups(); | |
194 | } | |
195 | ||
196 | /** | |
197 | * Validates the unban action. | |
198 | */ | |
199 | public function validateUnban() { | |
200 | $this->validateBan(); | |
201 | } | |
202 | ||
203 | /** | |
204 | * Bans users. | |
205 | */ | |
206 | public function ban() { | |
207 | $conditionBuilder = new PreparedStatementConditionBuilder(); | |
208 | $conditionBuilder->add('userID IN (?)', array($this->objectIDs)); | |
209 | $sql = "UPDATE wcf".WCF_N."_user | |
210 | SET banned = ?, | |
211 | banReason = ? | |
212 | ".$conditionBuilder; | |
213 | $statement = WCF::getDB()->prepareStatement($sql); | |
214 | $statement->execute( | |
215 | array_merge(array(1, $this->parameters['banReason']), $conditionBuilder->getParameters()) | |
216 | ); | |
bbef7ed8 MW |
217 | |
218 | $this->unmarkItems(); | |
11cf19be MW |
219 | } |
220 | ||
221 | /** | |
222 | * Unbans users. | |
223 | */ | |
224 | public function unban() { | |
225 | $conditionBuilder = new PreparedStatementConditionBuilder(); | |
226 | $conditionBuilder->add('userID IN (?)', array($this->objectIDs)); | |
227 | $sql = "UPDATE wcf".WCF_N."_user | |
228 | SET banned = 0 | |
229 | ".$conditionBuilder; | |
230 | $statement = WCF::getDB()->prepareStatement($sql); | |
231 | $statement->execute($conditionBuilder->getParameters()); | |
232 | } | |
233 | ||
11ade432 AE |
234 | /** |
235 | * Creates a new user. | |
236 | * | |
237 | * @return User | |
238 | */ | |
239 | public function create() { | |
240 | $user = parent::create(); | |
241 | $userEditor = new UserEditor($user); | |
242 | ||
243 | // updates user options | |
244 | if (isset($this->parameters['options'])) { | |
245 | $userEditor->updateUserOptions($this->parameters['options']); | |
246 | } | |
247 | ||
248 | // insert user groups | |
2bb10466 | 249 | $addDefaultGroups = (isset($this->parameters['addDefaultGroups'])) ? $this->parameters['addDefaultGroups'] : true; |
11ade432 | 250 | $groupIDs = (isset($this->parameters['groups'])) ? $this->parameters['groups'] : array(); |
2bb10466 | 251 | $userEditor->addToGroups($groupIDs, false, $addDefaultGroups); |
11ade432 AE |
252 | |
253 | // insert visible languages | |
254 | $languageIDs = (isset($this->parameters['languages'])) ? $this->parameters['languages'] : array(); | |
695780d7 | 255 | $userEditor->addToLanguages($languageIDs, false); |
11ade432 | 256 | |
320f4a6d MW |
257 | if (PACKAGE_ID) { |
258 | // set default notifications | |
259 | $sql = "INSERT INTO wcf".WCF_N."_user_notification_event_to_user | |
260 | (userID, eventID) | |
695780d7 MW |
261 | SELECT ?, eventID |
262 | FROM wcf".WCF_N."_user_notification_event | |
263 | WHERE preset = ?"; | |
320f4a6d | 264 | $statement = WCF::getDB()->prepareStatement($sql); |
695780d7 | 265 | $statement->execute(array($user->userID, 1)); |
320f4a6d MW |
266 | } |
267 | ||
11ade432 AE |
268 | return $user; |
269 | } | |
835fa8c2 AE |
270 | |
271 | /** | |
272 | * @see wcf\data\AbstractDatabaseObjectAction::update() | |
273 | */ | |
274 | public function update() { | |
881246d6 AE |
275 | if (isset($this->parameters['data'])) { |
276 | parent::update(); | |
8a3258f5 MS |
277 | |
278 | if (isset($this->parameters['data']['languageID'])) { | |
279 | foreach ($this->objects as $object) { | |
280 | if ($object->userID == WCF::getUser()->userID) { | |
281 | if ($this->parameters['data']['languageID'] != WCF::getUser()->languageID) { | |
282 | WCF::setLanguage($this->parameters['data']['languageID']); | |
283 | } | |
284 | ||
285 | break; | |
286 | } | |
287 | } | |
288 | } | |
881246d6 AE |
289 | } |
290 | else { | |
15fa2802 | 291 | if (empty($this->objects)) { |
881246d6 AE |
292 | $this->readObjects(); |
293 | } | |
294 | } | |
835fa8c2 AE |
295 | |
296 | $groupIDs = (isset($this->parameters['groups'])) ? $this->parameters['groups'] : array(); | |
44adccf6 | 297 | $languageIDs = (isset($this->parameters['languageIDs'])) ? $this->parameters['languageIDs'] : array(); |
835fa8c2 | 298 | $removeGroups = (isset($this->parameters['removeGroups'])) ? $this->parameters['removeGroups'] : array(); |
f277d540 | 299 | $userOptions = (isset($this->parameters['options'])) ? $this->parameters['options'] : array(); |
835fa8c2 | 300 | |
c2000c5d | 301 | if (!empty($groupIDs)) { |
12f80a9d MW |
302 | $action = new UserAction($this->objects, 'addToGroups', array( |
303 | 'groups' => $groupIDs, | |
304 | 'addDefaultGroups' => false | |
305 | )); | |
c2000c5d MW |
306 | $action->executeAction(); |
307 | } | |
308 | ||
835fa8c2 | 309 | foreach ($this->objects as $userEditor) { |
f277d540 | 310 | if (!empty($removeGroups)) { |
835fa8c2 AE |
311 | $userEditor->removeFromGroups($removeGroups); |
312 | } | |
f277d540 AE |
313 | |
314 | if (!empty($userOptions)) { | |
315 | $userEditor->updateUserOptions($userOptions); | |
316 | } | |
44adccf6 AE |
317 | |
318 | if (!empty($languageIDs)) { | |
319 | $userEditor->addToLanguages($languageIDs); | |
320 | } | |
835fa8c2 AE |
321 | } |
322 | } | |
d5cab442 | 323 | |
0dd6ea0c MW |
324 | /** |
325 | * Add users to given groups. | |
326 | */ | |
c2000c5d MW |
327 | public function addToGroups() { |
328 | if (empty($this->objects)) { | |
329 | $this->readObjects(); | |
330 | } | |
331 | ||
332 | $groupIDs = $this->parameters['groups']; | |
333 | $deleteOldGroups = $addDefaultGroups = true; | |
334 | if (isset($this->parameters['deleteOldGroups'])) $deleteOldGroups = $this->parameters['deleteOldGroups']; | |
335 | if (isset($this->parameters['addDefaultGroups'])) $addDefaultGroups = $this->parameters['addDefaultGroups']; | |
336 | ||
337 | foreach ($this->objects as $userEditor) { | |
338 | $userEditor->addToGroups($groupIDs, $deleteOldGroups, $addDefaultGroups); | |
339 | } | |
320f4a6d MW |
340 | |
341 | if (MODULE_USER_RANK) { | |
342 | $action = new UserProfileAction($this->objects, 'updateUserRank'); | |
343 | $action->executeAction(); | |
344 | } | |
345 | if (MODULE_USERS_ONLINE) { | |
346 | $action = new UserProfileAction($this->objects, 'updateUserOnlineMarking'); | |
347 | $action->executeAction(); | |
348 | } | |
c2000c5d MW |
349 | } |
350 | ||
a7fd745e | 351 | /** |
a427a8c8 | 352 | * @see wcf\data\ISearchAction::validateGetSearchResultList() |
a7fd745e | 353 | */ |
a427a8c8 | 354 | public function validateGetSearchResultList() { |
a54f8d8f AE |
355 | $this->readBoolean('includeUserGroups', false, 'data'); |
356 | $this->readString('searchString', false, 'data'); | |
a7fd745e AE |
357 | |
358 | if (isset($this->parameters['data']['excludedSearchValues']) && !is_array($this->parameters['data']['excludedSearchValues'])) { | |
3631f7bd | 359 | throw new UserInputException('excludedSearchValues'); |
a7fd745e | 360 | } |
d5cab442 AE |
361 | } |
362 | ||
a7fd745e | 363 | /** |
a427a8c8 | 364 | * @see wcf\data\ISearchAction::getSearchResultList() |
a7fd745e | 365 | */ |
a427a8c8 | 366 | public function getSearchResultList() { |
d5cab442 | 367 | $searchString = $this->parameters['data']['searchString']; |
c000b08a MS |
368 | $excludedSearchValues = array(); |
369 | if (isset($this->parameters['data']['excludedSearchValues'])) { | |
370 | $excludedSearchValues = $this->parameters['data']['excludedSearchValues']; | |
371 | } | |
d5cab442 | 372 | $list = array(); |
9f959ced | 373 | |
d5cab442 AE |
374 | if ($this->parameters['data']['includeUserGroups']) { |
375 | $accessibleGroups = UserGroup::getAccessibleGroups(); | |
376 | foreach ($accessibleGroups as $group) { | |
18c05238 | 377 | $groupName = $group->getName(); |
c000b08a | 378 | if (!in_array($groupName, $excludedSearchValues)) { |
838e315b | 379 | $pos = mb_strripos($groupName, $searchString); |
c000b08a MS |
380 | if ($pos !== false && $pos == 0) { |
381 | $list[] = array( | |
382 | 'label' => $groupName, | |
383 | 'objectID' => $group->groupID, | |
384 | 'type' => 'group' | |
385 | ); | |
386 | } | |
d5cab442 AE |
387 | } |
388 | } | |
389 | } | |
c000b08a | 390 | |
c2d0b2d6 MS |
391 | // find users |
392 | $userProfileList = new UserProfileList(); | |
393 | $userProfileList->getConditionBuilder()->add("username LIKE ?", array($searchString.'%')); | |
15fa2802 | 394 | if (!empty($excludedSearchValues)) { |
c2d0b2d6 | 395 | $userProfileList->getConditionBuilder()->add("username NOT IN (?)", array($excludedSearchValues)); |
c000b08a | 396 | } |
c2d0b2d6 MS |
397 | $userProfileList->sqlLimit = 10; |
398 | $userProfileList->readObjects(); | |
9f959ced | 399 | |
c2d0b2d6 | 400 | foreach ($userProfileList as $userProfile) { |
d5cab442 | 401 | $list[] = array( |
c2d0b2d6 MS |
402 | 'icon' => $userProfile->getAvatar()->getImageTag(16), |
403 | 'label' => $userProfile->username, | |
404 | 'objectID' => $userProfile->userID, | |
d5cab442 AE |
405 | 'type' => 'user' |
406 | ); | |
407 | } | |
9f959ced | 408 | |
d5cab442 AE |
409 | return $list; |
410 | } | |
49c164a8 AE |
411 | |
412 | /** | |
7918ddba | 413 | * @see wcf\data\IClipboardAction::validateUnmarkAll() |
49c164a8 | 414 | */ |
fbb077d4 MS |
415 | public function validateUnmarkAll() { |
416 | // does nothing | |
417 | } | |
49c164a8 AE |
418 | |
419 | /** | |
7918ddba | 420 | * @see wcf\data\IClipboardAction::unmarkAll() |
49c164a8 AE |
421 | */ |
422 | public function unmarkAll() { | |
423 | ClipboardHandler::getInstance()->removeItems(ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.user')); | |
424 | } | |
bbef7ed8 MW |
425 | |
426 | /** | |
427 | * Unmarks users. | |
428 | * | |
429 | * @param array<integer> $userIDs | |
430 | */ | |
431 | protected function unmarkItems(array $userIDs = array()) { | |
432 | if (empty($userIDs)) { | |
433 | $userIDs = $this->objectIDs; | |
434 | } | |
e3369fd2 | 435 | |
bbef7ed8 MW |
436 | if (!empty($userIDs)) { |
437 | ClipboardHandler::getInstance()->unmark($userIDs, ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.user')); | |
438 | } | |
439 | } | |
2fe45e04 MW |
440 | |
441 | /** | |
442 | * Validates the enable action. | |
443 | */ | |
444 | public function validateEnable() { | |
445 | WCF::getSession()->checkPermissions(array('admin.user.canEnableUser')); | |
9927f711 MS |
446 | |
447 | $this->__validateAccessibleGroups(); | |
2fe45e04 MW |
448 | } |
449 | ||
450 | /** | |
451 | * Validates the disable action. | |
452 | */ | |
453 | public function validateDisable() { | |
454 | $this->validateEnable(); | |
455 | } | |
456 | ||
457 | /** | |
458 | * Enables users. | |
459 | */ | |
460 | public function enable() { | |
461 | if (empty($this->objects)) $this->readObjects(); | |
9927f711 | 462 | |
2fe45e04 MW |
463 | $action = new UserAction($this->objects, 'update', array( |
464 | 'data' => array( | |
465 | 'activationCode' => 0 | |
466 | ), | |
2818981f | 467 | 'removeGroups' => UserGroup::getGroupIDsByType(array(UserGroup::GUESTS)) |
2fe45e04 MW |
468 | )); |
469 | $action->executeAction(); | |
2818981f MW |
470 | $action = new UserAction($this->objects, 'addToGroups', array( |
471 | 'groups' => UserGroup::getGroupIDsByType(array(UserGroup::USERS)), | |
472 | 'deleteOldGroups' => false, | |
9927f711 | 473 | 'addDefaultGroups' => false |
2818981f | 474 | )); |
2fe45e04 MW |
475 | $action->executeAction(); |
476 | } | |
477 | ||
478 | /** | |
479 | * Disables users. | |
480 | */ | |
481 | public function disable() { | |
482 | if (empty($this->objects)) $this->readObjects(); | |
9927f711 | 483 | |
2fe45e04 MW |
484 | $action = new UserAction($this->objects, 'update', array( |
485 | 'data' => array( | |
486 | 'activationCode' => UserRegistrationUtil::getActivationCode() | |
487 | ), | |
2818981f | 488 | 'removeGroups' => UserGroup::getGroupIDsByType(array(UserGroup::USERS)), |
2fe45e04 MW |
489 | )); |
490 | $action->executeAction(); | |
2818981f MW |
491 | $action = new UserAction($this->objects, 'addToGroups', array( |
492 | 'groups' => UserGroup::getGroupIDsByType(array(UserGroup::GUESTS)), | |
493 | 'deleteOldGroups' => false, | |
494 | 'addDefaultGroups' => false | |
495 | )); | |
2fe45e04 MW |
496 | $action->executeAction(); |
497 | } | |
11ade432 | 498 | } |