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