2 * Copyright (C) 2016 Roland Haeder
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation, either version 3 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
14 * You should have received a copy of the GNU Affero General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 package org.mxchange.pizzaapplication.beans.user;
19 import java.text.MessageFormat;
20 import java.util.Collections;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Objects;
24 import javax.annotation.PostConstruct;
25 import javax.enterprise.context.SessionScoped;
26 import javax.enterprise.event.Event;
27 import javax.enterprise.event.Observes;
28 import javax.enterprise.inject.Any;
29 import javax.faces.context.FacesContext;
30 import javax.faces.view.facelets.FaceletException;
31 import javax.inject.Inject;
32 import javax.inject.Named;
33 import javax.naming.Context;
34 import javax.naming.InitialContext;
35 import javax.naming.NamingException;
36 import org.mxchange.jcontacts.contact.Contact;
37 import org.mxchange.jcontacts.contact.ContactSessionBeanRemote;
38 import org.mxchange.jcontacts.events.contact.add.AdminAddedContactEvent;
39 import org.mxchange.jusercore.events.login.UserLoggedInEvent;
40 import org.mxchange.jusercore.events.registration.UserRegisteredEvent;
41 import org.mxchange.jusercore.events.user.add.AdminAddedUserEvent;
42 import org.mxchange.jusercore.events.user.update.AdminUpdatedUserDataEvent;
43 import org.mxchange.jusercore.events.user.update.UpdatedUserPersonalDataEvent;
44 import org.mxchange.jusercore.events.user.update.UserUpdatedPersonalDataEvent;
45 import org.mxchange.jusercore.exceptions.UserEmailAddressNotFoundException;
46 import org.mxchange.jusercore.exceptions.UserNotFoundException;
47 import org.mxchange.jusercore.exceptions.UserPasswordMismatchException;
48 import org.mxchange.jusercore.model.user.LoginUser;
49 import org.mxchange.jusercore.model.user.User;
50 import org.mxchange.jusercore.model.user.UserSessionBeanRemote;
51 import org.mxchange.jusercore.model.user.UserUtils;
52 import org.mxchange.jusercore.model.user.profilemodes.ProfileMode;
53 import org.mxchange.pizzaapplication.beans.BasePizzaController;
54 import org.mxchange.pizzaapplication.beans.contact.PizzaContactWebSessionController;
55 import org.mxchange.pizzaapplication.beans.login.PizzaUserLoginWebSessionController;
56 import org.mxchange.pizzaapplication.beans.register.PizzaUserRegisterWebSessionController;
59 * A user bean (controller)
61 * @author Roland Haeder<roland@mxchange.org>
63 @Named ("userController")
65 public class PizzaUserWebSessionBean extends BasePizzaController implements PizzaUserWebSessionController {
70 private static final long serialVersionUID = 542_145_347_916L;
75 private ContactSessionBeanRemote contactBean;
78 * General contact controller
81 private PizzaContactWebSessionController contactController;
84 * Login bean (controller)
87 private PizzaUserLoginWebSessionController loginController;
90 * Registration controller
93 private PizzaUserRegisterWebSessionController
97 * A list of all selectable contacts
99 private List<Contact> selectableContacts;
102 * Event being fired when user updated personal data
106 private Event<UpdatedUserPersonalDataEvent> updatedPersonalDataEvent;
111 private final UserSessionBeanRemote userBean;
119 * A list of all user profiles
121 private List<User> userList;
124 * Login bean (controller)
127 private PizzaUserLoginWebSessionController userLoginController;
132 private String userName;
137 private List<String> userNameList;
140 * User password (unencrypted from web form)
142 private String userPassword;
145 * User password repeated (unencrypted from web form)
147 private String userPasswordRepeat;
150 * Whether the user wants a public profile
152 private ProfileMode userProfileMode;
155 * A list of all public user profiles
157 private List<User> visibleUserList;
160 * Default constructor
162 public PizzaUserWebSessionBean () {
165 // Get initial context
166 Context context = new InitialContext();
169 this.userBean = (UserSessionBeanRemote) context.lookup("java:global/pizzaservice-ejb/user!org.mxchange.jusercore.model.user.UserSessionBeanRemote"); //NOI18N
172 this.contactBean = (ContactSessionBeanRemote) context.lookup("java:global/pizzaservice-ejb/contact!org.mxchange.jcontacts.contact.ContactSessionBeanRemote"); //NOI18N
173 } catch (final NamingException e) {
175 throw new FaceletException(e);
180 public void afterAdminAddedContact (@Observes final AdminAddedContactEvent event) {
181 // The event must be valid
184 throw new NullPointerException("event is null"); //NOI18N
185 } else if (event.getAddedContact() == null) {
187 throw new NullPointerException("event.addedContact is null"); //NOI18N
188 } else if (event.getAddedContact().getContactId() == null) {
190 throw new NullPointerException("event.addedContact.customerId is null"); //NOI18N
191 } else if (event.getAddedContact().getContactId() < 1) {
193 throw new IllegalArgumentException(MessageFormat.format("event.addedContact.customerId={0} is not valid", event.getAddedContact().getContactId())); //NOI18N //NOI18N
197 this.selectableContacts.add(event.getAddedContact());
201 public void afterAdminAddedUserEvent (@Observes final AdminAddedUserEvent event) {
203 //* NOISY-DEBUG: */ System.out.println(MessageFormat.format("UserWebBean:afterAdminAddedUserEvent: event={0} - CALLED!", event)); //NOI18N
205 // event should not be null
208 throw new NullPointerException("event is null"); //NOI18N
209 } else if (event.getAddedUser() == null) {
211 throw new NullPointerException("event.addedUser is null"); //NOI18N
212 } else if (event.getAddedUser().getUserId() == null) {
214 throw new NullPointerException("event.addedUser.userId is null"); //NOI18N
215 } else if (event.getAddedUser().getUserId() < 1) {
217 throw new IllegalArgumentException(MessageFormat.format("userId of user={0} is not valid: {1}", event.getAddedUser(), event.getAddedUser().getUserId())); //NOI18N
220 // Add user to local list
221 this.userList.add(event.getAddedUser());
227 this.setUserId(event.getAddedUser().getUserId());
230 //* NOISY-DEBUG: */ System.out.println("UserWebBean:afterAdminAddedUserEvent: EXIT!"); //NOI18N
234 public void afterAdminUpdatedUserDataEvent (@Observes final AdminUpdatedUserDataEvent event) {
236 //* NOISY-DEBUG: */ System.out.println(MessageFormat.format("UserWebBean:afterAdminUpdatedUserEvent: event={0} - CALLED!", event)); //NOI18N
238 // event should not be null
241 throw new NullPointerException("event is null"); //NOI18N
242 } else if (event.getUpdatedUser() == null) {
244 throw new NullPointerException("event.updatedUser is null"); //NOI18N
245 } else if (event.getUpdatedUser().getUserId() == null) {
247 throw new NullPointerException("event.updatedUser.userId is null"); //NOI18N
248 } else if (event.getUpdatedUser().getUserId() < 1) {
250 throw new IllegalArgumentException(MessageFormat.format("userId of user={0} is not valid: {1}", event.getUpdatedUser(), event.getUpdatedUser().getUserId())); //NOI18N
254 this.updateList(event.getUpdatedUser());
260 //* NOISY-DEBUG: */ System.out.println("UserWebBean:afterAdminUpdatedUserEvent: EXIT!"); //NOI18N
264 public void afterRegistrationEvent (@Observes final UserRegisteredEvent event) {
266 //* NOISY-DEBUG: */ System.out.println(MessageFormat.format("UserWebBean:afterRegistration: event={0} - CALLED!", event)); //NOI18N
268 // event should not be null
271 throw new NullPointerException("event is null"); //NOI18N
272 } else if (event.getRegisteredUser() == null) {
274 throw new NullPointerException("event.registeredUser is null"); //NOI18N
275 } else if (event.getRegisteredUser().getUserId() == null) {
277 throw new NullPointerException("event.registeredUser.userId is null"); //NOI18N
278 } else if (event.getRegisteredUser().getUserId() < 1) {
280 throw new IllegalArgumentException(MessageFormat.format("userId of user={0} is not valid: {1}", event.getRegisteredUser(), event.getRegisteredUser().getUserId())); //NOI18N
284 User registeredUser = event.getRegisteredUser();
287 //* NOISY-DEBUG: */ System.out.println(MessageFormat.format("UserWebBean:afterRegistration: registeredUser={0}", registeredUser)); //NOI18N
289 // Copy all data from registered->user
290 this.copyUser(registeredUser);
295 // Add user to local list
296 this.userList.add(registeredUser);
299 this.addUserName(registeredUser);
301 // Is the account public?
302 if (Objects.equals(registeredUser.getUserProfileMode(), ProfileMode.PUBLIC)) {
303 // Also add it to this list
304 this.visibleUserList.add(registeredUser);
308 this.setUserId(registeredUser.getUserId());
311 //* NOISY-DEBUG: */ System.out.println("UserWebBean:afterRegistration: EXIT!"); //NOI18N
315 public void afterUserLogin (final @Observes UserLoggedInEvent event) {
317 //* NOISY-DEBUG: */ System.out.println(MessageFormat.format("UserWebBean:afterUserLogin: event={0} - CALLED!", event)); //NOI18N
319 // event should not be null
322 throw new NullPointerException("event is null"); //NOI18N
323 } else if (event.getLoggedInUser() == null) {
325 throw new NullPointerException("event.registeredUser is null"); //NOI18N
326 } else if (event.getLoggedInUser().getUserId() == null) {
328 throw new NullPointerException("event.registeredUser.userId is null"); //NOI18N
329 } else if (event.getLoggedInUser().getUserId() < 1) {
331 throw new IllegalArgumentException(MessageFormat.format("userId of user={0} is not valid: {1}", event.getLoggedInUser(), event.getLoggedInUser().getUserId())); //NOI18N
334 // Copy all data to this bean
335 this.copyUser(event.getLoggedInUser());
337 // Re-initialize list
338 this.visibleUserList = this.userBean.allMemberPublicVisibleUsers();
341 //* NOISY-DEBUG: */ System.out.println(MessageFormat.format("UserWebBean:afterUserLogin: this.visibleUserList.size()={0} - EXIT!", this.visibleUserList.size())); //NOI18N
345 public void afterUserUpdatedPersonalData (@Observes final UpdatedUserPersonalDataEvent event) {
349 throw new NullPointerException("event is null"); //NOI18N
350 } else if (event.getUpdatedUser() == null) {
352 throw new NullPointerException("event.updatedUser is null"); //NOI18N
353 } else if (event.getUpdatedUser().getUserId() == null) {
355 throw new NullPointerException("event.updatedUser.userId is null"); //NOI18N
356 } else if (event.getUpdatedUser().getUserId() < 1) {
358 throw new IllegalArgumentException(MessageFormat.format("event.updatedUser.userId={0} is in valid", event.getUpdatedUser().getUserId())); //NOI18N
361 // All fine, so update list
362 this.updateList(event.getUpdatedUser());
366 public List<User> allUsers () {
368 return Collections.unmodifiableList(this.userList);
372 public List<User> allVisibleUsers () {
374 return Collections.unmodifiableList(this.visibleUserList);
378 public User createUserInstance () {
380 //* NOISY-DEBUG: */ System.out.println(MessageFormat.format("{0}.createUserInstance: CALLED!", this.getClass().getSimpleName()));
382 // Required personal data must be set
383 assert (this.isRequiredPersonalDataSet()) : "not all personal data is set"; //NOI18N
385 // Create new user instance
386 User user = new LoginUser();
388 // Is user name required?
389 if (!this.isUserNameRequired()) {
390 // Generate pseudo-random user name
391 String randomName = this.userBean.generateRandomUserName();
393 // Set it and inivisible profile
394 this.setUserName(randomName);
395 this.setUserProfileMode(ProfileMode.INVISIBLE);
397 // Generate random password
398 String randomPassword = UserUtils.createRandomPassword(PizzaUserWebSessionController.MINIMUM_PASSWORD_LENGTH);
400 // Set random password
401 this.setUserPassword(randomPassword);
402 this.setUserPasswordRepeat(randomPassword);
405 // Set user name and mode
406 user.setUserName(this.getUserName());
407 user.setUserProfileMode(this.getUserProfileMode());
409 // Is multiple registration page
410 if (!this.registerController.isMultiplePageEnabled()) {
411 // Create contact instance
412 Contact contact = this.contactController.createContactInstance();
415 //* NOISY-DEBUG: */ System.out.println(MessageFormat.format("{0}.createUserInstance: contact={1}", this.getClass().getSimpleName(), contact));
417 // Set contact in user
418 user.setUserContact(contact);
422 //* NOISY-DEBUG: */ System.out.println(MessageFormat.format("{0}.createUserInstance: user={1} - EXIT!", this.getClass().getSimpleName(), user));
429 public User createUserLogin () {
431 //* NOISY-DEBUG */ System.out.println(MessageFormat.format("{0}.createUserLogin: CALLED!", this.getClass().getSimpleName()));
434 if (this.getUserName() == null) {
436 throw new NullPointerException("recruiterName is null"); //NOI18N
437 } else if (this.getUserName().isEmpty()) {
439 throw new IllegalStateException("recruiterName is empty."); //NOI18N
442 // Create new recruiter instance
443 User recruiter = new LoginUser();
445 // Update all data ...
446 recruiter.setUserName(this.getUserName());
449 //* NOISY-DEBUG */ System.out.println(MessageFormat.format("{0}.createUserLogin: recruiter={1} - EXIT!", this.getClass().getSimpleName(), recruiter));
451 // Return the new instance
456 public String doChangePersonalData () {
457 // This method shall only be called if the user is logged-in
458 if (!this.loginController.isUserLoggedIn()) {
460 throw new IllegalStateException("User is not logged-in"); //NOI18N
461 } else if (!this.isRequiredChangePersonalDataSet()) {
462 // Not all required fields are set
463 throw new FaceletException("Not all required fields are set."); //NOI18N
464 } else if (!this.loginController.ifCurrentPasswordMatches()) {
465 // Password not matching
466 throw new FaceletException(new UserPasswordMismatchException(this.loginController.getLoggedInUser()));
470 User user = this.loginController.getLoggedInUser();
472 // Copy contact data to contact instance
473 this.contactController.updateContactDataFromController(user.getUserContact());
475 // It should be there, so run some tests on it
476 assert (user instanceof User) : "Instance loginController.loggedInUser is null"; //NOI18N
477 assert (user.getUserId() instanceof Long) : "Instance loginController.loggedInUser.userId is null"; //NOI18N
478 assert (user.getUserId() > 0) : MessageFormat.format("loginController.loggedInUser.userId={0} is invalid", user.getUserId()); //NOI18N
479 assert (user.getUserContact() instanceof Contact) : "Instance loginController.loggedInUser.userContact is null"; //NOI18N
480 assert (user.getUserContact().getContactId() instanceof Long) : "Instance loginController.userContact.contactId is null"; //NOI18N
481 assert (user.getUserContact().getContactId() > 0) : MessageFormat.format("Instance loginController.userContact.contactId={0} is invalid", user.getUserContact().getContactId()); //NOI18N
484 user.setUserProfileMode(this.getUserProfileMode());
486 // Send it to the EJB
487 User updatedUser = this.userBean.updateUserPersonalData(user);
490 this.updatedPersonalDataEvent.fire(new UserUpdatedPersonalDataEvent(updatedUser));
493 return "user_data_saved"; //NOI18N
497 public Long getUserId () {
502 public void setUserId (final Long userId) {
503 this.userId = userId;
507 public String getUserName () {
508 return this.userName;
512 public void setUserName (final String userName) {
513 this.userName = userName;
517 public String getUserPassword () {
518 return this.userPassword;
522 public void setUserPassword (final String userPassword) {
523 this.userPassword = userPassword;
527 public String getUserPasswordRepeat () {
528 return this.userPasswordRepeat;
532 public void setUserPasswordRepeat (final String userPasswordRepeat) {
533 this.userPasswordRepeat = userPasswordRepeat;
537 public ProfileMode getUserProfileMode () {
538 return this.userProfileMode;
542 public void setUserProfileMode (final ProfileMode userProfileMode) {
543 this.userProfileMode = userProfileMode;
547 public boolean hasUsers () {
548 return (!this.allUsers().isEmpty());
552 * Post-initialization of this class
555 public void init () {
556 // Initialize user list
557 this.userList = this.userBean.allUsers();
559 // Get full user name list for reducing EJB calls
560 this.userNameList = this.userBean.getUserNameList();
562 // Is the user logged-in?
563 if (this.userLoginController.isUserLoggedIn()) {
564 // Is logged-in, so load also users visible to memebers
565 this.visibleUserList = this.userBean.allMemberPublicVisibleUsers();
567 // Initialize user list
568 this.visibleUserList = this.userBean.allPublicUsers();
572 List<User> allUsers = this.allUsers();
575 List<Contact> allContacts = this.contactBean.getAllContacts();
578 Iterator<Contact> iterator = allContacts.iterator();
581 while (iterator.hasNext()) {
583 Contact next = iterator.next();
586 Iterator<User> userIterator = allUsers.iterator();
588 // Loop through all users
589 while (userIterator.hasNext()) {
591 User nextUser = userIterator.next();
594 if (Objects.equals(next, nextUser.getUserContact())) {
603 this.selectableContacts = allContacts;
607 public boolean isContactFound (final Contact contact) {
608 // The contact must be valid
609 if (null == contact) {
611 throw new NullPointerException("contact is null"); //NOI18N
612 } else if (contact.getContactId() == null) {
614 throw new NullPointerException("contact.contactId is null"); //NOI18N
615 } else if (contact.getContactId() < 1) {
617 throw new IllegalArgumentException(MessageFormat.format("contact.contactId={0} is not valid", contact.getContactId())); //NOI18N
620 // Default is not found
621 boolean isFound = false;
624 Iterator<User> iterator = this.allUsers().iterator();
626 // Loop through all entries
627 while (iterator.hasNext()) {
629 User next = iterator.next();
631 // Compare both objects
632 if (Objects.equals(contact, next.getUserContact())) {
644 public boolean isPublicUserProfileEnabled () {
645 // Get context parameter
646 String contextParameter = FacesContext.getCurrentInstance().getExternalContext().getInitParameter("is_public_profile_enabled"); //NOI18N
649 boolean isEnabled = ((contextParameter instanceof String) && (contextParameter.toLowerCase().equals("true"))); //NOI18N
651 // This requires user names being enabled, too.
652 if ((isEnabled) && (!this.isUserNameRequired())) {
653 // Not valid state, users must be able to modify their profile, especially when it is public
654 throw new IllegalStateException("Public user profiles are enabled but user name requirement is disabled, this is not possible."); //NOI18N
662 public boolean isRequiredChangePersonalDataSet () {
663 return ((this.getUserProfileMode() != null) &&
664 (this.getUserName() != null) && (!this.getUserName().isEmpty()) &&
665 (this.contactController.isRequiredChangePersonalDataSet()));
669 public boolean isRequiredPersonalDataSet () {
670 if (this.registerController.isMultiplePageEnabled()) {
671 // Multiple registration page
672 return this.contactController.isRequiredPersonalDataSet();
674 // Single registration page
675 return (((this.getUserName() != null) || (!this.isUserNameRequired())) &&
676 (this.getUserProfileMode() != null) &&
677 (this.contactController.isRequiredPersonalDataSet()) &&
678 (this.getUserPassword() != null) &&
679 (this.getUserPasswordRepeat() != null));
684 public boolean isSamePasswordEntered () {
685 return ((!this.getUserPassword().isEmpty()) && (Objects.equals(this.getUserPassword(), this.getUserPasswordRepeat())));
689 public boolean isUserIdEmpty () {
690 return ((this.getUserId() == null) || (this.getUserId() == 0));
694 public boolean isUserNameRegistered (final User user) {
695 return ((this.userNameList instanceof List) && (this.userNameList.contains(user.getUserName())));
699 public boolean isUserNameRequired () {
700 // Get context parameter
701 String contextParameter = FacesContext.getCurrentInstance().getExternalContext().getInitParameter("is_user_name_required"); //NOI18N
704 boolean isRequired = ((contextParameter instanceof String) && (contextParameter.toLowerCase().equals("true"))); //NOI18N
711 public boolean isVisibleUserFound () {
712 return ((this.visibleUserList instanceof List) && (this.visibleUserList.size() > 0));
716 public User lookupUserById (final Long userId) throws UserNotFoundException {
717 // Parameter must be valid
718 if (null == userId) {
720 throw new NullPointerException("userId is null"); //NOI18N
721 } else if (userId < 1) {
723 throw new IllegalArgumentException(MessageFormat.format("userId={0} is not valid.", userId)); //NOI18N
729 // Try to lookup it in visible user list
730 for (final Iterator<User> iterator = this.userList.iterator(); iterator.hasNext();) {
732 User next = iterator.next();
734 // Is the user id found?
735 if (Objects.equals(next.getUserId(), userId)) {
736 // Copy to other variable
744 // Not visible for the current user
745 throw new UserNotFoundException(userId);
753 public User lookupUserByEmailAddress (final String emailAddress) throws UserEmailAddressNotFoundException {
754 // Parameter must be valid
755 if (null == emailAddress) {
757 throw new NullPointerException("emailAddress is null"); //NOI18N
758 } else if (emailAddress.isEmpty()) {
760 throw new IllegalArgumentException("emailAddress is empty"); //NOI18N
766 // Try to lookup it in visible user list
767 for (final Iterator<User> iterator = this.userList.iterator(); iterator.hasNext();) {
769 User next = iterator.next();
771 // Contact should be set
772 if (next.getUserContact() == null) {
774 throw new NullPointerException(MessageFormat.format("next.userContact is null for user id {0}", next.getUserId())); //NOI18N
775 } else if (next.getUserContact().getContactEmailAddress() == null) {
776 // Email address should be set
777 throw new NullPointerException(MessageFormat.format("next.userContact.contactEmailAddress is null for user id {0}", next.getUserId())); //NOI18N //NOI18N
780 // Is the email address found?
781 if (Objects.equals(next.getUserContact().getContactEmailAddress(), emailAddress)) {
782 // Copy to other variable
790 // Not visible for the current user
791 throw new UserEmailAddressNotFoundException(emailAddress);
799 public List<Contact> selectableContacts () {
800 return Collections.unmodifiableList(this.selectableContacts);
804 * Adds user's name to bean's internal list. It also updates the public user
805 * list if the user has decided to have a public account,
807 * @param user User instance
809 private void addUserName (final User user) {
810 // Make sure the entry is not added yet
811 if (this.userNameList.contains(user.getUserName())) {
813 throw new IllegalArgumentException(MessageFormat.format("User name {0} already added.", user.getUserName())); //NOI18N
814 } else if (this.contactController.isEmailAddressRegistered(user.getUserContact())) {
816 throw new IllegalArgumentException(MessageFormat.format("Email address {0} already added.", user.getUserContact().getContactEmailAddress())); //NOI18N
820 this.userNameList.add(user.getUserName());
826 private void clear () {
829 this.setUserId(null);
830 this.setUserProfileMode(null);
833 this.setUserName(null);
834 this.setUserPassword(null);
835 this.setUserPasswordRepeat(null);
839 * Copies given user into the controller
841 * @param user User instance
843 private void copyUser (final User user) {
844 // Make sure the instance is valid
847 throw new NullPointerException("user is null"); //NOI18N
848 } else if (user.getUserContact() == null) {
850 throw new NullPointerException("user.userContact is null"); //NOI18N
855 this.setUserId(user.getUserId());
856 this.setUserProfileMode(user.getUserProfileMode());
860 * Updates list with given user instance
862 * @param user User instance
864 private void updateList (final User user) {
865 // The user should be valid
868 throw new NullPointerException("user is null"); //NOI18N
869 } else if (user.getUserId() == null) {
871 throw new NullPointerException("user.userId is null"); //NOI18N
872 } else if (user.getUserId() < 1) {
874 throw new IllegalArgumentException(MessageFormat.format("user.userId={0} is invalid", user.getUserId())); //NOI18N
878 Iterator<User> iterator = this.userList.iterator();
881 while (iterator.hasNext()) {
883 User next = iterator.next();
885 // Is the same user id?
886 if (Objects.equals(user.getUserId(), next.getUserId())) {
887 // Found it, so remove it
888 this.userList.remove(next);
894 this.userList.add(user);