GuestBook.java
/*
* Copyright (C) 2012-2025 Gerd Neugebauer
*
* This file is distributed under the 3-clause BSD license.
* See file LICENSE for details.
*/
package org.ctan.site.domain.site;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.index.CorruptIndexException;
import org.ctan.site.CtanConfiguration.CtanConfig;
import org.ctan.site.domain.AbstractEntity;
import org.ctan.site.domain.account.User;
import org.ctan.site.services.search.base.IndexType;
import org.ctan.site.services.search.base.IndexingSession;
import org.ctan.site.services.search.base.IndexingSession.IndexArgs;
import org.ctan.site.services.search.base.Searchable;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.google.common.collect.ImmutableMap;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder.Default;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
/**
* This domain class represents an entry in the guest book.
*
* @author <a href="mailto:gene@ctan.org">Gerd Neugebauer</a>
*/
@Entity
@Table(name = "guestbook")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@EqualsAndHashCode(callSuper = false)
@SuppressFBWarnings(value = "EI_EXPOSE_REP")
public class GuestBook extends AbstractEntity implements Searchable {
/**
* The field <code>title</code> contains the title of the guest book item.
*/
@Column(length = 128, nullable = false)
private String title;
/**
* The field <code>text</code> contains the text body of the guest book
* item.
*/
@Column(length = 2048, nullable = false)
private String text;
/**
* The field <code>name</code> contains the optional name of the author.
*/
@Column(length = 128, nullable = true)
private String name;
/**
* The field <code>email</code> contains the optional email address of the
* guest book item.
*/
@Column(length = 255, nullable = true)
private String email;
/**
* The field <code>hideEmail</code> contains the indicator to hide the email
* address.
*/
@Column(name = "hide_email")
private boolean hideEmail;
/**
* The field <code>hidden</code> contains the indicator to hide the complete
* item. This is used for spurious items.
*/
@Column
@Default
private boolean hidden = false;
/**
* The field <code>user</code> contains the optional user who has created
* the item.
*/
@ManyToOne
private User user;
/**
* The field <code>dateCreated</code> contains the time stamp when the item
* has been created.
*/
@Column(name = "date_created")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
@Default
private LocalDateTime dateCreated = LocalDateTime.now();
/**
* The field <code>lastUpdated</code> contains the time stamp when the item
* has been updated.
*/
@Column(name = "last_updated")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
@Default
private LocalDateTime lastUpdated = LocalDateTime.now();
/**
* The field <code>parent</code> contains the reference to the parent item.
*/
@ManyToOne
@Default
private GuestBook parent = null;
// static hasMany = [ followup: Guestbook ]
// static constraints = {
// lastUpdated()
// hidden()
// title maxSize: 128
// text blank: false, maxSize: 2048
// name nullable: true, maxSize: 128
// email nullable: true, maxSize: 255
// hideEmail()
// user nullable: true
// parent nullable: true
// dateCreated()
// }
/**
* This method <code>getCleanedText</code> returns the text property after
* it has applied some cleanup to it. Currently HTML links are removed.
*
* @return the cleaned text
*/
public String getCleanedText() {
return text.replaceAll("<a .*</a>", "");
}
/**
* The method <code>getIndexType</code> provides means to name the
* indexType.
*
* @return the index type
*/
public IndexType getIndexType() {
return IndexType.GUESTBOOK;
}
/**
* Getter for the list of parents of this instance.
*
* @return the list of parents
*/
public List<GuestBook> getParents() {
List<GuestBook> parents = new ArrayList<>();
// for (var p = this.parent; p != null; p = p.parent) {
// parents.add(p);
// }
// TODO
return parents; // .reverse();
}
/**
* {@inheritDoc}
*
* @see org.ctan.site.services.search.base.Searchable#indexPath()
*/
@Override
public String indexPath() {
return "/guestbook/" + getId();
}
/**
* The method <code>toMap</code> provides means to get the instance as an
* immutable Map.
*
* @return the Map
*/
public ImmutableMap<String, Object> toMap() {
return ImmutableMap.of("id", (Object) getId(),
"title", title,
"text", text,
"hidden", hidden,
"hideEmail", hideEmail,
"created", String.format("%tF", dateCreated));
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return text.length() < 24 ? text : text.substring(0, 24) + "...";
}
/**
* This method <code>toUrl</code> returns the URL for the search index.
*
* @return the URL
*/
public String toUrl() {
return "/guestbook/item/" + getId();
}
/**
* {@inheritDoc}
*
* @see org.ctan.site.services.search.base.Searchable#updateIndex(IndexingSession)
*/
@Override
public void updateIndex(IndexingSession session)
throws CorruptIndexException,
IOException {
IndexArgs args = IndexArgs.builder()
.type(IndexType.GUESTBOOK)
.title(title)
.display(title)
.content(new String[]{
title,
text
})
.build();
for (var locale : CtanConfig.LOCALES) {
args.setLocale(locale);
session.updateIndex(indexPath(), args);
}
}
}