package org.sigmah.server.domain;
/*
* #%L
* Sigmah
* %%
* Copyright (C) 2010 - 2016 URD
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.sigmah.server.domain.base.AbstractEntityId;
import org.sigmah.server.domain.profile.OrgUnitProfile;
import org.sigmah.server.domain.util.EntityConstants;
import org.sigmah.shared.Language;
import org.sigmah.shared.util.Users;
/**
* <p>
* User domain entity.
* </p>
* <p>
* <em>We want to avoid calling this table {@code 'User'} as it is a reserved word in some dialects of SQL.</em>
* </p>
*
* @author Denis Colliot (dcolliot@ideia.fr)
*/
@Entity
@Table(name = EntityConstants.USER_TABLE)
public class User extends AbstractEntityId<Integer> {
/**
* Serial version UID.
*/
private static final long serialVersionUID = 1511545346659844141L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = EntityConstants.USER_COLUMN_ID, unique = true, nullable = false)
private Integer id;
@Column(name = EntityConstants.USER_COLUMN_EMAIL, nullable = false, length = EntityConstants.EMAIL_MAX_LENGTH, unique = true)
@NotNull
@Size(max = EntityConstants.EMAIL_MAX_LENGTH)
private String email;
@Column(name = EntityConstants.USER_COLUMN_FIRSTNAME, nullable = true, length = EntityConstants.NAME_MAX_LENGTH)
@Size(max = EntityConstants.NAME_MAX_LENGTH)
private String firstName;
@Column(name = EntityConstants.USER_COLUMN_NAME, nullable = false, length = EntityConstants.NAME_MAX_LENGTH)
@NotNull
@Size(max = EntityConstants.NAME_MAX_LENGTH)
private String name;
@Column(name = EntityConstants.USER_COLUMN_NEW_USER, nullable = false)
@NotNull
private boolean newUser;
@Column(name = EntityConstants.USER_COLUMN_LOCALE, nullable = false, length = EntityConstants.LOCALE_MAX_LENGTH)
@NotNull
@Size(max = EntityConstants.LOCALE_MAX_LENGTH)
private String locale;
/**
* The secure key required to change the user's password.
* This is a random 128-bit key that can be safely sent to the user by email.
*/
@Column(name = EntityConstants.USER_COLUMN_CHANGE_PASSWORD_KEY, nullable = true, length = EntityConstants.CHANGE_PASSWORD_KEY_MAX_LENGTH)
@Size(max = EntityConstants.CHANGE_PASSWORD_KEY_MAX_LENGTH)
private String changePasswordKey;
/**
* The date on which the password key was issued; the application should not let users change passwords with really
* old keys.
*/
@Column(name = EntityConstants.USER_COLUMN_DATE_CHANGE_PASSWORD_KEY_ISSUED)
@Temporal(javax.persistence.TemporalType.TIMESTAMP)
private Date dateChangePasswordKeyIssued;
/**
* The user's password, hashed with the BCrypt algorithm.
*/
@Column(name = EntityConstants.USER_COLUMN_PASSWORD, length = EntityConstants.PASSWORD_MAX_LENGTH)
@Size(max = EntityConstants.PASSWORD_MAX_LENGTH)
private String hashedPassword;
@Column(name = EntityConstants.USER_COLUMN_ACTIVE, nullable = true)
private Boolean active;
// --------------------------------------------------------------------------------
//
// FOREIGN KEYS.
//
// --------------------------------------------------------------------------------
@ManyToOne(optional = true, fetch = FetchType.LAZY)
@JoinColumn(name = EntityConstants.ORGANIZATION_COLUMN_ID, nullable = true)
private Organization organization;
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrgUnitProfile> orgUnitsWithProfiles = new ArrayList<>();
@OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
private Contact contact;
@Transient
private OrgUnitProfile mainOrgUnitWithProfiles;
@Transient
private List<OrgUnitProfile> secondaryOrgUnitsWithProfiles = new ArrayList<>();
public User() {
}
// --------------------------------------------------------------------------------
//
// METHODS.
//
// --------------------------------------------------------------------------------
/**
* <p>
* Overrides default behaviour to only display {@link #getUserCompleteName(User)} value.
* </p>
*
* @see #getUserCompleteName(User)
*/
@Override
public String toString() {
return getUserCompleteName(this);
}
/**
* Clears the user's change password key (as well as the change password date).
*/
public void clearChangePasswordKey() {
this.setChangePasswordKey(null);
this.setDateChangePasswordKeyIssued(null);
}
/**
* <p>
* Override default behaviour to rely on {@link #getEmail()} value.
* </p>
*/
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof User)) {
return false;
}
final User that = (User) other;
return this.getEmail().equals(that.getEmail());
}
/**
* <p>
* Override default behaviour to rely on {@link #getEmail()} value.
* </p>
*/
@Override
public int hashCode() {
return getEmail().hashCode();
}
// --------------------------------------------------------------------------------
//
// STATIC METHODS.
//
// --------------------------------------------------------------------------------
/**
* Gets the formatted complete name of a user.
* <ul>
* <li>If the user has a first name and a last name, returns '<i>John Doe</i>'.</li>
* <li>If the user hasn't a first name and has a last name, returns '<i>Doe</i>'.</li>
* <li>If the user has neither a first name or a last name, returns an empty string.</li>
* </ul>
*
* @param user
* The user
* @return The complete name.
*/
public static String getUserCompleteName(final User user) {
if (user == null) {
return Users.getUserCompleteName(null, null);
}
return Users.getUserCompleteName(user.firstName, user.name);
}
/**
* Gets the formatted short name of a user.
* <ul>
* <li>If the user has a first name and a last name, returns '<i>J. Doe</i>'.</li>
* <li>If the user hasn't a first name and has a last name, returns '<i>Doe</i>'.</li>
* <li>If the user has neither a first name or a last name, returns an empty string.</li>
* </ul>
*
* @param user
* The user
* @return The short name.
*/
public static String getUserShortName(final User user) {
if (user == null) {
return Users.getUserShortName(null, null);
}
return Users.getUserShortName(user.firstName, user.name);
}
// --------------------------------------------------------------------------------
//
// GETTERS & SETTERS.
//
// --------------------------------------------------------------------------------
@Override
public Integer getId() {
return this.id;
}
@Override
public void setId(Integer id) {
this.id = id;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Transient
public String getFullName() {
if (firstName == null) {
return name;
}
return firstName + " " + name.toUpperCase();
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public boolean isNewUser() {
return this.newUser;
}
public void setNewUser(boolean newUser) {
this.newUser = newUser;
}
public String getLocale() {
return this.locale;
}
public Locale getLocaleInstance() {
final Language language = Language.fromString(getLocale());
return new Locale(language.getLocale());
}
public void setLocale(String locale) {
this.locale = locale;
}
public String getHashedPassword() {
return this.hashedPassword;
}
public void setHashedPassword(String hashed) {
this.hashedPassword = hashed;
}
public String getChangePasswordKey() {
return changePasswordKey;
}
public void setChangePasswordKey(String changePasswordKey) {
this.changePasswordKey = changePasswordKey;
}
public void setActive(Boolean active) {
this.active = active;
}
public Boolean getActive() {
return active;
}
public Date getDateChangePasswordKeyIssued() {
return dateChangePasswordKeyIssued;
}
public void setDateChangePasswordKeyIssued(Date dateChangePasswordKeyIssued) {
this.dateChangePasswordKeyIssued = dateChangePasswordKeyIssued;
}
public Organization getOrganization() {
return organization;
}
public void setOrganization(Organization organization) {
this.organization = organization;
}
public List<OrgUnitProfile> getOrgUnitsWithProfiles() {
return orgUnitsWithProfiles;
}
public void setOrgUnitsWithProfiles(List<OrgUnitProfile> orgUnitsWithProfiles) {
this.orgUnitsWithProfiles = orgUnitsWithProfiles;
}
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public OrgUnitProfile getMainOrgUnitWithProfiles() {
if (mainOrgUnitWithProfiles == null) {
// FIXME: For some reason, the are some cases where postLoad is not called when getting this entity
// For the moment, let's execute postLoad manually in case it was not called
postLoad();
}
return mainOrgUnitWithProfiles;
}
public List<OrgUnitProfile> getSecondaryOrgUnitsWithProfiles() {
if (secondaryOrgUnitsWithProfiles == null) {
// FIXME: For some reason, the are some cases where postLoad is not called when getting this entity
// For the moment, let's execute postLoad manually in case it was not called
postLoad();
}
return secondaryOrgUnitsWithProfiles;
}
/*
* Helper for Dozer.
* Dozer can't map a User.secondaryOrgUnitsWithProfiles.*.orgUnit to a orgUnit list
*/
public List<OrgUnit> getSecondaryOrgUnits() {
if (secondaryOrgUnitsWithProfiles == null) {
return null;
}
List<OrgUnit> orgUnits = new ArrayList<OrgUnit>(secondaryOrgUnitsWithProfiles.size());
for (OrgUnitProfile orgUnitProfile : secondaryOrgUnitsWithProfiles) {
orgUnits.add(orgUnitProfile.getOrgUnit());
}
return orgUnits;
}
@PostLoad
void postLoad() {
List<OrgUnitProfile> secondaryOrgUnitProfiles = new ArrayList<>();
for (OrgUnitProfile orgUnitsWithProfile : orgUnitsWithProfiles) {
if (orgUnitsWithProfile.getType() == null) {
// In case the related user unit is not updated since last Sigmah update,
// this value can be null
// Let's prevent a NPE by making it the main OrgUnitProfile
orgUnitsWithProfile.setType(OrgUnitProfile.OrgUnitProfileType.MAIN);
}
switch (orgUnitsWithProfile.getType()) {
case MAIN:
mainOrgUnitWithProfiles = orgUnitsWithProfile;
break;
case SECONDARY:
secondaryOrgUnitProfiles.add(orgUnitsWithProfile);
break;
default:
throw new IllegalStateException("Unknown OrgUnitProfileType : " + orgUnitsWithProfile.getType());
}
}
this.secondaryOrgUnitsWithProfiles = Collections.unmodifiableList(secondaryOrgUnitProfiles);
}
}