All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.yadaframework.security.persistence.entity.YadaUserMessage Maven / Gradle / Ivy

There is a newer version: 0.7.7.R4
Show newest version
package net.yadaframework.security.persistence.entity;

import java.io.Serializable;
import java.lang.reflect.Type;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;

import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.unbescape.html.HtmlEscape;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.PrePersist;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import jakarta.persistence.Transient;
import jakarta.persistence.Version;
import net.yadaframework.components.YadaUtil;
import net.yadaframework.core.YadaLocalEnum;
import net.yadaframework.exceptions.YadaInternalException;
import net.yadaframework.persistence.entity.YadaAttachedFile;
import net.yadaframework.persistence.entity.YadaPersistentEnum;
import net.yadaframework.security.persistence.repository.YadaUserMessageDao;
import net.yadaframework.web.YadaJsonDateTimeShortSerializer;

/**
 * A message sent to some user by another user or by the system.
 * Identical consecutive messages can be "stacked" on a single row by incrementing the "stackSize" and adding a new "created" date
 *
 * @param  a localized enum for the message type
 */
@Entity
// Use a joined table for inheritance so that the subclasses can independently add any attributes without interference
@Inheritance(strategy = InheritanceType.JOINED)
// Should actually be YadaUserMessage>
// so that every subclass of YadaPersistentEnum handles its own Enum type
// and I get compiler error on a mismatched setType/getType
// but apparently there is no way of doing this in Java 8.
// Using YadaUserMessage> fixes getType/setType but would cause a runtime error when E is a plain enum
// because the type attribute should be a YadaPersistentEnum as there is a foreign key in the database.
// So I use YadaUserMessage> but I may need to check the enum consistency at runtime
// because the compiler can't do that. There will also be a runtime error on a mismatched setType/getType.
public class YadaUserMessage> implements Serializable {
	private static final long serialVersionUID = 7008892353441772768L;
	private final transient Logger log = LoggerFactory.getLogger(getClass());
	@Version
	protected long version; // For optimistic locking

	@Id
	@GeneratedValue(strategy= GenerationType.IDENTITY)
	protected Long id;

	protected int priority; // Priority or severity, 0 is lowest

	protected boolean readByRecipient = false; // Read by recipient

	protected boolean emailed = false; // Emailed to recipient

	@ElementCollection(fetch = FetchType.EAGER)
	@Fetch(FetchMode.SELECT)
	@Temporal(TemporalType.TIMESTAMP)
	//@JsonView(YadaJsonView.WithEagerAttributes.class)
	//@JsonView(YadaJsonView.WithLazyAttributes.class)
	protected List created; // Creation date of the message, a new date is added for each stacked message

	@Column(insertable = false, updatable = false, columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP")
	@Temporal(TemporalType.TIMESTAMP)
	protected Date modified = new Date();

	protected int stackSize = 0; // Counter for identical messages (stacked)

	@OneToOne(fetch = FetchType.EAGER, optional=true)
	protected YadaPersistentEnum type;

	@Column(length=80)
	protected String title;

	@Column(length=12000) // Should it be Lob?
	protected String message;

	@ManyToOne(optional = true)
	protected YadaUserProfile sender;

	@ManyToOne(optional = true)
	protected YadaUserProfile recipient;

	//@JsonView(YadaJsonView.WithLazyAttributes.class)
	@OneToMany(cascade= CascadeType.ALL, orphanRemoval=true) // It was REMOVE - why?
	protected List attachment = new ArrayList<>();

	@Column(length=1024)
	protected String data; // Can store a url here, or the identity of a non-user sender

	protected int contentHash; // To check message equality for stackability

	protected Integer status; // Application-defined status for this message

	////////////////////////////////////////////

	@Transient
	protected boolean stackable; // true if same-content messages should be counted not added

	private Type getCurrentEnumType() {
		return ((java.lang.reflect.ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}

	/**
	 * Used in Datatables to define the row class.
	 * @See {@link https://datatables.net/manual/server-side}
	 */
	@JsonProperty("DT_RowClass")
	public String getDT_RowClass() {
		// We set the class according to the read state of the message
		return this.readByRecipient?"yadaUserMessage-read":"yadaUserMessage-unread";
	}

	/**
	 * Computes the content hash before persisting.
	 * The hash does not consider attachments, sender, receiver or status flags.
	 * Override this method if your subclass add more fields that should be checked when computing stackability of messages
	 */
	@PrePersist
	public void init() {
		computeHash();
		setInitialDate();
	}

	/**
	 * Returns the timestamp formatted as a relative time from now, in the recipient's timezone
	 * @param locale
	 * @return a relative time like "1 minute ago"
	 */
	public String getTimestampAsRelative(Locale locale) {
		// YadaUserMessageDao yadaUserMessageDao = YadaUtil.getBean(YadaUserMessageDao.class);
		TimeZone timezone = recipient.getTimezone();
		Date created = getLastDate();
		// Date created = yadaUserMessageDao.getLastDate(this);
		ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(created.toInstant(), timezone.toZoneId());
		return YadaUtil.INSTANCE.getTimestampAsRelative(zonedDateTime, locale, null);
	}

	/**
	 * Escape all markup-significant characters
	 * @param message
	 * @return
	 * @return the current instance
	 */
	public YadaUserMessage setMessageEscaped(String message) {
		this.message = HtmlEscape.escapeHtml5Xml(message);
		return this;
	}

	public void computeHash() {
		// The type is null when the YadaPersistentEnum has not been initialized in the database
		if (type==null) {
			// get the type of the enum: if it is a YadaPersistentEnum throw an exception with info
			Class genericClass = YadaUtil.INSTANCE.findGenericClass(this);
//			boolean isYadaLocalEnum = YadaLocalEnum.class.isAssignableFrom(genericClass);
//			if (isYadaLocalEnum) {
//				Type missingEnum = null;
//				try {
//					missingEnum = getCurrentEnumType();
//				} catch (Exception e) {
//					// Ignored
//				}
//				String enumName = missingEnum!=null?missingEnum.toString():"all YadaLocalEnum classes";
//			}
			throw new YadaInternalException("YadaUserMessage 'type' is null - did you remember to add " + genericClass + " to yadaPersistentEnumDao.initDatabase() during application setup?");
		}
		this.contentHash = Objects.hash(type==null?null:type.getEnum(), title, message, data);
	}

	public void setInitialDate() {
		if (created==null) {
			created = new ArrayList();
			created.add(new Date());
		}
	}

	/**
	 * Returns the most recent date of the message stack - which is the initial date if the message is not stackable
	 * @return
	 * @see {@link YadaUserMessageDao#getLastDate()}
	 */
	public Date getLastDate() {
		if (created!=null && created.size()>0) {
			return created.get(created.size()-1);
		}
		return null;
	}

	public void incrementStack() {
		this.stackSize++;
		this.created.add(new Date());
	}

	public void setType(YLE localEnum) {
		this.type = localEnum.toYadaPersistentEnum();
	}

	/***********************************************************************/
	/* DataTables                                                          */

	@Transient
	@JsonProperty("DT_RowId")
	@Deprecated // can be removed now that it is added automatically by YadaDataTableDao
	public String getDT_RowId() {
		return this.getClass().getSimpleName()+"#"+this.id; // YadaUserMessage#142
	}

	@Transient
	@JsonProperty
	public String getSenderName() {
		if (sender!=null) {
			return sender.getUserCredentials().getUsername();
		}
		return null;
	}

	@Transient
	@JsonProperty
	public String getReceiverName() {
		return recipient!=null?recipient.getUserCredentials().getUsername():"-";
	}

	/** Adds a new attachment to this message
	 *
	 */
	public void addAttachment(YadaAttachedFile newAttachment) {
		attachment.add(newAttachment);
	}

	/***********************************************************************/
	/* Plain getter / setter                                               */
	@JsonSerialize(using=YadaJsonDateTimeShortSerializer.class)
	public Date getModified() {
		return modified;
	}

	public void setModified(Date modified) {
		this.modified = modified;
	}

	public long getVersion() {
		return version;
	}

	public void setVersion(long version) {
		this.version = version;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public int getPriority() {
		return priority;
	}

	public void setPriority(int priority) {
		this.priority = priority;
	}

	public boolean isEmailed() {
		return emailed;
	}

	public void setEmailed(boolean emailed) {
		this.emailed = emailed;
	}

	public int getStackSize() {
		return stackSize;
	}

	public void setStackSize(int count) {
		this.stackSize = count;
	}

	public YadaPersistentEnum getType() {
		return type;
	}

	public void setType(YadaPersistentEnum type) {
		this.type = type;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public YadaUserProfile getSender() {
		return sender;
	}

	public void setSender(YadaUserProfile senderUser) {
		this.sender = senderUser;
	}

	public YadaUserProfile getRecipient() {
		return recipient;
	}

	public void setRecipient(YadaUserProfile recipient) {
		this.recipient = recipient;
	}

	public List getAttachment() {
		return attachment;
	}

	public void setAttachment(List attachment) {
		this.attachment = attachment;
	}

	public int getContentHash() {
		return contentHash;
	}

	public void setContentHash(int hash) {
		this.contentHash = hash;
	}

	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}

	public boolean isStackable() {
		return stackable;
	}

	public void setStackable(boolean stackable) {
		this.stackable = stackable;
	}

	public List getCreated() {
		return created;
	}

	public void setCreated(List created) {
		this.created = created;
	}

	public boolean isReadByRecipient() {
		return readByRecipient;
	}

	public void setReadByRecipient(boolean readByRecipient) {
		this.readByRecipient = readByRecipient;
	}

	public Integer getStatus() {
		return status;
	}

	public void setStatus(Integer status) {
		this.status = status;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy