/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.model.impl;
import com.liferay.portal.kernel.bean.AutoEscape;
import com.liferay.portal.kernel.dao.orm.QueryUtil;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.Address;
import com.liferay.portal.kernel.model.Company;
import com.liferay.portal.kernel.model.CompanyConstants;
import com.liferay.portal.kernel.model.Contact;
import com.liferay.portal.kernel.model.EmailAddress;
import com.liferay.portal.kernel.model.Group;
import com.liferay.portal.kernel.model.Organization;
import com.liferay.portal.kernel.model.PasswordPolicy;
import com.liferay.portal.kernel.model.Phone;
import com.liferay.portal.kernel.model.Role;
import com.liferay.portal.kernel.model.Team;
import com.liferay.portal.kernel.model.UserConstants;
import com.liferay.portal.kernel.model.UserGroup;
import com.liferay.portal.kernel.model.Website;
import com.liferay.portal.kernel.security.auth.EmailAddressGenerator;
import com.liferay.portal.kernel.security.auth.FullNameGenerator;
import com.liferay.portal.kernel.security.auth.FullNameGeneratorFactory;
import com.liferay.portal.kernel.security.auth.PrincipalThreadLocal;
import com.liferay.portal.kernel.service.AddressLocalServiceUtil;
import com.liferay.portal.kernel.service.CompanyLocalServiceUtil;
import com.liferay.portal.kernel.service.ContactLocalServiceUtil;
import com.liferay.portal.kernel.service.EmailAddressLocalServiceUtil;
import com.liferay.portal.kernel.service.GroupLocalServiceUtil;
import com.liferay.portal.kernel.service.GroupServiceUtil;
import com.liferay.portal.kernel.service.LayoutLocalServiceUtil;
import com.liferay.portal.kernel.service.OrganizationLocalServiceUtil;
import com.liferay.portal.kernel.service.PasswordPolicyLocalServiceUtil;
import com.liferay.portal.kernel.service.PhoneLocalServiceUtil;
import com.liferay.portal.kernel.service.RoleLocalServiceUtil;
import com.liferay.portal.kernel.service.TeamLocalServiceUtil;
import com.liferay.portal.kernel.service.UserGroupLocalServiceUtil;
import com.liferay.portal.kernel.service.UserLocalServiceUtil;
import com.liferay.portal.kernel.service.WebsiteLocalServiceUtil;
import com.liferay.portal.kernel.theme.ThemeDisplay;
import com.liferay.portal.kernel.util.Digester;
import com.liferay.portal.kernel.util.DigesterUtil;
import com.liferay.portal.kernel.util.HtmlUtil;
import com.liferay.portal.kernel.util.LocaleUtil;
import com.liferay.portal.kernel.util.Portal;
import com.liferay.portal.kernel.util.PortalUtil;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.RemotePreference;
import com.liferay.portal.kernel.util.SetUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.TimeZoneUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.workflow.WorkflowConstants;
import com.liferay.portal.security.auth.EmailAddressGeneratorFactory;
import com.liferay.portal.util.PrefsPropsUtil;
import com.liferay.portal.util.PropsUtil;
import com.liferay.portal.util.PropsValues;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
/**
* Represents a portal user, providing access to the user's contact information,
* groups, organizations, teams, user groups, roles, locale, timezone, and more.
*
* @author Brian Wing Shun Chan
* @author Jorge Ferrer
* @author Wesley Gong
*/
public class UserImpl extends UserBaseImpl {
@Override
public void addRemotePreference(RemotePreference remotePreference) {
_remotePreferences.put(remotePreference.getName(), remotePreference);
}
@Override
public Contact fetchContact() {
if (_contact == _NULL_CONTACT) {
return null;
}
if (_contact == null) {
Contact contact = ContactLocalServiceUtil.fetchContact(
getContactId());
if (contact == null) {
_contact = _NULL_CONTACT;
}
else {
_contact = contact;
}
}
return _contact;
}
/**
* Returns the user's addresses.
*
* @return the user's addresses
*/
@Override
public List<Address> getAddresses() {
return AddressLocalServiceUtil.getAddresses(
getCompanyId(), Contact.class.getName(), getContactId());
}
/**
* Returns the user's birth date.
*
* @return the user's birth date
*/
@Override
public Date getBirthday() throws PortalException {
return getContact().getBirthday();
}
/**
* Returns the user's company's mail domain.
*
* @return the user's company's mail domain
*/
@Override
public String getCompanyMx() throws PortalException {
Company company = CompanyLocalServiceUtil.getCompanyById(
getCompanyId());
return company.getMx();
}
/**
* Returns the user's associated contact.
*
* @return the user's associated contact
* @see Contact
*/
@Override
public Contact getContact() throws PortalException {
if ((_contact == null) || (_contact == _NULL_CONTACT)) {
_contact = ContactLocalServiceUtil.getContact(getContactId());
}
return _contact;
}
/**
* Returns the user's digest.
*
* @return the user's digest
*/
@Override
public String getDigest() {
String digest = super.getDigest();
if (Validator.isNull(digest) && !isPasswordEncrypted()) {
digest = getDigest(getPassword());
}
return digest;
}
/**
* Returns a digest for the user, incorporating the password.
*
* @param password a password to incorporate with the digest
* @return a digest for the user, incorporating the password
*/
@Override
public String getDigest(String password) {
if (Validator.isNull(getScreenName())) {
throw new IllegalStateException("Screen name is null");
}
else if (Validator.isNull(getEmailAddress())) {
throw new IllegalStateException("Email address is null");
}
StringBundler sb = new StringBundler(5);
String digest1 = DigesterUtil.digestHex(
Digester.MD5, getEmailAddress(), Portal.PORTAL_REALM, password);
sb.append(digest1);
sb.append(StringPool.COMMA);
String digest2 = DigesterUtil.digestHex(
Digester.MD5, getScreenName(), Portal.PORTAL_REALM, password);
sb.append(digest2);
sb.append(StringPool.COMMA);
String digest3 = DigesterUtil.digestHex(
Digester.MD5, String.valueOf(getUserId()), Portal.PORTAL_REALM,
password);
sb.append(digest3);
return sb.toString();
}
/**
* Returns the user's primary email address, or a blank string if the
* address is fake.
*
* @return the user's primary email address, or a blank string if the
* address is fake
*/
@Override
public String getDisplayEmailAddress() {
String emailAddress = super.getEmailAddress();
EmailAddressGenerator emailAddressGenerator =
EmailAddressGeneratorFactory.getInstance();
if (emailAddressGenerator.isFake(emailAddress)) {
emailAddress = StringPool.BLANK;
}
return emailAddress;
}
/**
* Returns the user's display URL, discounting the URL of the user's default
* intranet site home page.
*
* <p>
* The logic for the display URL to return is as follows:
* </p>
*
* <ol>
* <li>
* If the user is the guest user, return an empty string.
* </li>
* <li>
* Else, if a friendly URL is available for the user's profile, return that
* friendly URL.
* </li>
* <li>
* Otherwise, return the URL of the user's default extranet site home page.
* </li>
* </ol>
*
* @param portalURL the portal's URL
* @param mainPath the main path
* @return the user's display URL
* @deprecated As of 7.0.0, replaced by {@link #getDisplayURL(ThemeDisplay)}
*/
@Deprecated
@Override
public String getDisplayURL(String portalURL, String mainPath)
throws PortalException {
return getDisplayURL(portalURL, mainPath, false);
}
/**
* Returns the user's display URL.
*
* <p>
* The logic for the display URL to return is as follows:
* </p>
*
* <ol>
* <li>
* If the user is the guest user, return an empty string.
* </li>
* <li>
* Else, if a friendly URL is available for the user's profile, return that
* friendly URL.
* </li>
* <li>
* Else, if <code>privateLayout</code> is <code>true</code>, return the URL
* of the user's default intranet site home page.
* </li>
* <li>
* Otherwise, return the URL of the user's default extranet site home page.
* </li>
* </ol>
*
* @param portalURL the portal's URL
* @param mainPath the main path
* @param privateLayout whether to use the URL of the user's default
* intranet(versus extranet) site home page, if no friendly URL
* is available for the user's profile
* @return the user's display URL
* @throws PortalException
* @deprecated As of 7.0.0, replaced by {@link #getDisplayURL(ThemeDisplay)}
*/
@Deprecated
@Override
public String getDisplayURL(
String portalURL, String mainPath, boolean privateLayout)
throws PortalException {
if (isDefaultUser()) {
return StringPool.BLANK;
}
String profileFriendlyURL = getProfileFriendlyURL();
if (profileFriendlyURL != null) {
return portalURL.concat(PortalUtil.getPathContext()).concat(
profileFriendlyURL);
}
return StringPool.BLANK;
}
/**
* Returns the user's display URL based on the theme display, discounting
* the URL of the user's default intranet site home page.
*
* <p>
* The logic for the display URL to return is as follows:
* </p>
*
* <ol>
* <li>
* If the user is the guest user, return an empty string.
* </li>
* <li>
* Else, if a friendly URL is available for the user's profile, return that
* friendly URL.
* </li>
* <li>
* Otherwise, return the URL of the user's default extranet site home page.
* </li>
* </ol>
*
* @param themeDisplay the theme display
* @return the user's display URL
*/
@Override
public String getDisplayURL(ThemeDisplay themeDisplay)
throws PortalException {
return getDisplayURL(themeDisplay, false);
}
/**
* Returns the user's display URL based on the theme display.
*
* <p>
* The logic for the display URL to return is as follows:
* </p>
*
* <ol>
* <li>
* If the user is the guest user, return an empty string.
* </li>
* <li>
* Else, if a friendly URL is available for the user's profile, return that
* friendly URL.
* </li>
* <li>
* Else, if <code>privateLayout</code> is <code>true</code>, return the URL
* of the user's default intranet site home page.
* </li>
* <li>
* Otherwise, return the URL of the user's default extranet site home page.
* </li>
* </ol>
*
* @param themeDisplay the theme display
* @param privateLayout whether to use the URL of the user's default
* intranet (versus extranet) site home page, if no friendly URL is
* available for the user's profile
* @return the user's display URL
* @throws PortalException
*/
@Override
public String getDisplayURL(
ThemeDisplay themeDisplay, boolean privateLayout)
throws PortalException {
if (isDefaultUser() || (themeDisplay == null)) {
return StringPool.BLANK;
}
String portalURL = themeDisplay.getPortalURL();
String profileFriendlyURL = getProfileFriendlyURL();
if (profileFriendlyURL != null) {
return PortalUtil.addPreservedParameters(
themeDisplay,
portalURL.concat(
PortalUtil.getPathContext()).concat(profileFriendlyURL));
}
Group group = getGroup();
return group.getDisplayURL(themeDisplay, privateLayout);
}
/**
* Returns the user's email addresses.
*
* @return the user's email addresses
*/
@Override
public List<EmailAddress> getEmailAddresses() {
return EmailAddressLocalServiceUtil.getEmailAddresses(
getCompanyId(), Contact.class.getName(), getContactId());
}
/**
* Returns <code>true</code> if the user is female.
*
* @return <code>true</code> if the user is female; <code>false</code>
* otherwise
*/
@Override
public boolean getFemale() throws PortalException {
return !getMale();
}
/**
* Returns the user's full name.
*
* @return the user's full name
*/
@AutoEscape
@Override
public String getFullName() {
return getFullName(false, false);
}
/**
* Returns the user's full name.
*
* @return the user's full name
*/
@AutoEscape
@Override
public String getFullName(boolean usePrefix, boolean useSuffix) {
FullNameGenerator fullNameGenerator =
FullNameGeneratorFactory.getInstance();
long prefixId = 0;
if (usePrefix) {
Contact contact = fetchContact();
if (contact != null) {
prefixId = contact.getPrefixId();
}
}
long suffixId = 0;
if (useSuffix) {
Contact contact = fetchContact();
if (contact != null) {
suffixId = contact.getSuffixId();
}
}
return fullNameGenerator.getLocalizedFullName(
getFirstName(), getMiddleName(), getLastName(), getLocale(),
prefixId, suffixId);
}
@Override
public Group getGroup() {
return GroupLocalServiceUtil.fetchUserGroup(
getCompanyId(), getUserId());
}
@Override
public long getGroupId() {
Group group = getGroup();
return group.getGroupId();
}
@Override
public long[] getGroupIds() {
return UserLocalServiceUtil.getGroupPrimaryKeys(getUserId());
}
@Override
public List<Group> getGroups() {
return GroupLocalServiceUtil.getUserGroups(getUserId());
}
@Override
public String getInitials() {
String firstInitial = StringUtil.shorten(getFirstName(), 1);
String lastInitial = StringUtil.shorten(getLastName(), 1);
return StringUtil.toUpperCase(firstInitial.concat(lastInitial));
}
@Override
public Locale getLocale() {
return _locale;
}
@Override
public String getLogin() throws PortalException {
String login = null;
Company company = CompanyLocalServiceUtil.getCompanyById(
getCompanyId());
if (company.getAuthType().equals(CompanyConstants.AUTH_TYPE_EA)) {
login = getEmailAddress();
}
else if (company.getAuthType().equals(CompanyConstants.AUTH_TYPE_SN)) {
login = getScreenName();
}
else if (company.getAuthType().equals(CompanyConstants.AUTH_TYPE_ID)) {
login = String.valueOf(getUserId());
}
return login;
}
/**
* Returns <code>true</code> if the user is male.
*
* @return <code>true</code> if the user is male; <code>false</code>
* otherwise
*/
@Override
public boolean getMale() throws PortalException {
return getContact().getMale();
}
@Override
public List<Group> getMySiteGroups() throws PortalException {
return getMySiteGroups(null, QueryUtil.ALL_POS);
}
@Override
public List<Group> getMySiteGroups(int max) throws PortalException {
return getMySiteGroups(null, max);
}
@Override
public List<Group> getMySiteGroups(String[] classNames, int max)
throws PortalException {
return GroupServiceUtil.getUserSitesGroups(
getUserId(), classNames, max);
}
@Override
public long[] getOrganizationIds() throws PortalException {
return getOrganizationIds(false);
}
@Override
public long[] getOrganizationIds(boolean includeAdministrative)
throws PortalException {
return OrganizationLocalServiceUtil.getUserOrganizationIds(
getUserId(), includeAdministrative);
}
@Override
public List<Organization> getOrganizations() throws PortalException {
return getOrganizations(false);
}
@Override
public List<Organization> getOrganizations(boolean includeAdministrative)
throws PortalException {
return OrganizationLocalServiceUtil.getUserOrganizations(
getUserId(), includeAdministrative);
}
@Override
public String getOriginalEmailAddress() {
return super.getOriginalEmailAddress();
}
@Override
public boolean getPasswordModified() {
return _passwordModified;
}
@Override
public PasswordPolicy getPasswordPolicy() throws PortalException {
if (_passwordPolicy == null) {
_passwordPolicy =
PasswordPolicyLocalServiceUtil.getPasswordPolicyByUser(this);
}
return _passwordPolicy;
}
@Override
public String getPasswordUnencrypted() {
return _passwordUnencrypted;
}
@Override
public List<Phone> getPhones() {
return PhoneLocalServiceUtil.getPhones(
getCompanyId(), Contact.class.getName(), getContactId());
}
@Override
public String getPortraitURL(ThemeDisplay themeDisplay)
throws PortalException {
return UserConstants.getPortraitURL(
themeDisplay.getPathImage(), isMale(), getPortraitId(),
getUserUuid());
}
@Override
public int getPrivateLayoutsPageCount() throws PortalException {
return LayoutLocalServiceUtil.getLayoutsCount(this, true);
}
@Override
public int getPublicLayoutsPageCount() throws PortalException {
return LayoutLocalServiceUtil.getLayoutsCount(this, false);
}
@Override
public Set<String> getReminderQueryQuestions() throws PortalException {
Set<String> questions = new TreeSet<>();
List<Organization> organizations =
OrganizationLocalServiceUtil.getUserOrganizations(getUserId());
for (Organization organization : organizations) {
Set<String> organizationQuestions =
organization.getReminderQueryQuestions(getLanguageId());
if (organizationQuestions.isEmpty()) {
Organization parentOrganization =
organization.getParentOrganization();
while (organizationQuestions.isEmpty() &&
(parentOrganization != null)) {
organizationQuestions =
parentOrganization.getReminderQueryQuestions(
getLanguageId());
parentOrganization =
parentOrganization.getParentOrganization();
}
}
questions.addAll(organizationQuestions);
}
if (questions.isEmpty()) {
Set<String> defaultQuestions = SetUtil.fromArray(
PropsUtil.getArray(PropsKeys.USERS_REMINDER_QUERIES_QUESTIONS));
questions.addAll(defaultQuestions);
}
return questions;
}
@Override
public RemotePreference getRemotePreference(String name) {
return _remotePreferences.get(name);
}
@Override
public Iterable<RemotePreference> getRemotePreferences() {
Collection<RemotePreference> values = _remotePreferences.values();
return Collections.unmodifiableCollection(values);
}
@Override
public long[] getRoleIds() {
return UserLocalServiceUtil.getRolePrimaryKeys(getUserId());
}
@Override
public List<Role> getRoles() {
return RoleLocalServiceUtil.getUserRoles(getUserId());
}
@Override
public List<Group> getSiteGroups() throws PortalException {
return getSiteGroups(false);
}
@Override
public List<Group> getSiteGroups(boolean includeAdministrative)
throws PortalException {
return GroupLocalServiceUtil.getUserSitesGroups(
getUserId(), includeAdministrative);
}
@Override
public long[] getTeamIds() {
return UserLocalServiceUtil.getTeamPrimaryKeys(getUserId());
}
@Override
public List<Team> getTeams() {
return TeamLocalServiceUtil.getUserTeams(getUserId());
}
@Override
public TimeZone getTimeZone() {
return _timeZone;
}
@Override
public Date getUnlockDate() throws PortalException {
return getUnlockDate(getPasswordPolicy());
}
@Override
public Date getUnlockDate(PasswordPolicy passwordPolicy) {
Date lockoutDate = getLockoutDate();
return new Date(
lockoutDate.getTime() +
(passwordPolicy.getLockoutDuration() * 1000));
}
@Override
public long[] getUserGroupIds() {
return UserLocalServiceUtil.getUserGroupPrimaryKeys(getUserId());
}
@Override
public List<UserGroup> getUserGroups() {
return UserGroupLocalServiceUtil.getUserUserGroups(getUserId());
}
@Override
public List<Website> getWebsites() {
return WebsiteLocalServiceUtil.getWebsites(
getCompanyId(), Contact.class.getName(), getContactId());
}
@Override
public boolean hasCompanyMx() throws PortalException {
return hasCompanyMx(getEmailAddress());
}
@Override
public boolean hasCompanyMx(String emailAddress) throws PortalException {
if (Validator.isNull(emailAddress)) {
return false;
}
Company company = CompanyLocalServiceUtil.getCompanyById(
getCompanyId());
return company.hasCompanyMx(emailAddress);
}
@Override
public boolean hasMySites() throws PortalException {
if (isDefaultUser()) {
return false;
}
if ((PropsValues.LAYOUT_USER_PRIVATE_LAYOUTS_ENABLED ||
PropsValues.LAYOUT_USER_PUBLIC_LAYOUTS_ENABLED) &&
(getUserId() == PrincipalThreadLocal.getUserId())) {
return true;
}
List<Group> groups = getMySiteGroups(1);
return !groups.isEmpty();
}
@Override
public boolean hasOrganization() {
return OrganizationLocalServiceUtil.hasUserOrganizations(getUserId());
}
@Override
public boolean hasPrivateLayouts() throws PortalException {
return LayoutLocalServiceUtil.hasLayouts(this, true);
}
@Override
public boolean hasPublicLayouts() throws PortalException {
return LayoutLocalServiceUtil.hasLayouts(this, false);
}
@Override
public boolean hasReminderQuery() {
if (Validator.isNotNull(getReminderQueryQuestion()) &&
Validator.isNotNull(getReminderQueryAnswer())) {
return true;
}
else {
return false;
}
}
@Override
public boolean isActive() {
if (getStatus() == WorkflowConstants.STATUS_APPROVED) {
return true;
}
else {
return false;
}
}
@Override
public boolean isEmailAddressComplete() {
if (isDefaultUser()) {
return true;
}
if (Validator.isNull(getEmailAddress()) ||
(PropsValues.USERS_EMAIL_ADDRESS_REQUIRED &&
Validator.isNull(getDisplayEmailAddress()))) {
return false;
}
return true;
}
@Override
public boolean isEmailAddressVerificationComplete() {
if (isDefaultUser() || isEmailAddressVerified()) {
return true;
}
boolean emailAddressVerificationRequired = false;
try {
Company company = CompanyLocalServiceUtil.getCompany(
getCompanyId());
emailAddressVerificationRequired = company.isStrangersVerify();
}
catch (PortalException pe) {
_log.error(pe, pe);
}
if (emailAddressVerificationRequired) {
return false;
}
return true;
}
@Override
public boolean isFemale() throws PortalException {
return getFemale();
}
@Override
public boolean isMale() throws PortalException {
return getMale();
}
@Override
public boolean isPasswordModified() {
return _passwordModified;
}
@Override
public boolean isReminderQueryComplete() {
if (isDefaultUser()) {
return true;
}
if (PropsValues.USERS_REMINDER_QUERIES_ENABLED) {
if (Validator.isNull(getReminderQueryQuestion()) ||
Validator.isNull(getReminderQueryAnswer())) {
return false;
}
}
return true;
}
@Override
public boolean isSetupComplete() {
if (isDefaultUser()) {
return true;
}
if (isEmailAddressComplete() && isEmailAddressVerificationComplete() &&
!isPasswordReset() && isReminderQueryComplete() &&
isTermsOfUseComplete()) {
return true;
}
return false;
}
@Override
public boolean isTermsOfUseComplete() {
if (isDefaultUser() || isAgreedToTermsOfUse()) {
return true;
}
boolean termsOfUseRequired = PrefsPropsUtil.getBoolean(
getCompanyId(), PropsKeys.TERMS_OF_USE_REQUIRED,
PropsValues.TERMS_OF_USE_REQUIRED);
if (termsOfUseRequired) {
return false;
}
return true;
}
@Override
public void setLanguageId(String languageId) {
_locale = LocaleUtil.fromLanguageId(languageId);
super.setLanguageId(LocaleUtil.toLanguageId(_locale));
}
@Override
public void setPasswordModified(boolean passwordModified) {
_passwordModified = passwordModified;
}
@Override
public void setPasswordUnencrypted(String passwordUnencrypted) {
_passwordUnencrypted = passwordUnencrypted;
}
@Override
public void setTimeZoneId(String timeZoneId) {
if (Validator.isNull(timeZoneId)) {
timeZoneId = TimeZoneUtil.getDefault().getID();
}
_timeZone = TimeZoneUtil.getTimeZone(timeZoneId);
super.setTimeZoneId(timeZoneId);
}
protected String getProfileFriendlyURL() {
if (!_hasUsersProfileFriendlyURL) {
return null;
}
return StringUtil.replace(
PropsValues.USERS_PROFILE_FRIENDLY_URL,
new String[] {"${liferay:screenName}", "${liferay:userId}"},
new String[] {
HtmlUtil.escapeURL(getScreenName()), String.valueOf(getUserId())
});
}
private static final Contact _NULL_CONTACT = new ContactImpl();
private static final Log _log = LogFactoryUtil.getLog(UserImpl.class);
private static final boolean _hasUsersProfileFriendlyURL = Validator.isNull(
PropsValues.USERS_PROFILE_FRIENDLY_URL);
private Contact _contact;
private Locale _locale;
private boolean _passwordModified;
private PasswordPolicy _passwordPolicy;
private String _passwordUnencrypted;
private final transient Map<String, RemotePreference> _remotePreferences =
new HashMap<>();
private TimeZone _timeZone;
}