global search fixed

This commit is contained in:
Radek Davidek 2026-03-20 15:33:33 +01:00
parent c98befaf26
commit 56deb56c72
6 changed files with 248 additions and 5 deletions

View File

@ -44,6 +44,7 @@ import cz.kamma.fabka.repository.model.PrivateMessageStats;
import cz.kamma.fabka.repository.model.PrivateThreadRoot;
import cz.kamma.fabka.repository.model.PrivateThreadSummary;
import cz.kamma.fabka.repository.model.QuotedTextItem;
import cz.kamma.fabka.repository.model.SearchResultItem;
import cz.kamma.fabka.repository.SettingsRepository;
import cz.kamma.fabka.repository.model.UserIcon;
import cz.kamma.fabka.repository.UserIconRepository;
@ -150,6 +151,48 @@ public class HttpServerApplication {
boolean showError = "1".equals(ctx.queryParam("error"));
Responses.html(ctx.exchange(), 200, Pages.loginPage(showError));
});
router.get("/search", ctx -> {
if (!isAuthenticated(ctx)) {
Responses.text(ctx.exchange(), 401, "Unauthorized");
return;
}
String searchText = valueOrDefault(ctx.queryParam("stext"), "");
String f = ctx.queryParam("fid");
long forumId = f != null && !f.isBlank() ? parseLong(f, 0) : 0;
SessionData session = ctx.getSession();
if (session == null) {
Responses.text(ctx.exchange(), 401, "Unauthorized");
return;
}
String username = String.valueOf(session.getAttribute(AUTH_USER_KEY));
long userId = parseLong(session.getAttribute(AUTH_USER_ID_KEY), -1L);
if (userId <= 0) {
Responses.text(ctx.exchange(), 401, "Unauthorized");
return;
}
List<SearchResultItem> results = forumRepository.searchMessages(userId, searchText, forumId);
int loggedUsersCount = sessionManager.countSessionsWithAttribute(AUTH_USER_KEY);
List<String> loggedUsers = sessionManager.sessionAttributeValues(AUTH_USER_KEY);
PrivateMessageStats pmStats = privateMessageRepository.stats(userId);
Responses.html(ctx.exchange(), 200, Pages.searchResultsPage(
username,
userId,
results,
searchText,
loggedUsersCount,
loggedUsers,
pmStats
));
});
router.get("/chat.jsp", ctx -> Responses.redirect(ctx.exchange(), "/chat"));
router.get("/private.jsp", ctx -> {
String oBy = ctx.queryParam("oBy");

View File

@ -24,6 +24,7 @@ import cz.kamma.fabka.repository.model.ForumDetail;
import cz.kamma.fabka.repository.model.ForumMessage;
import cz.kamma.fabka.repository.model.ForumSummary;
import cz.kamma.fabka.repository.model.QuotedTextItem;
import cz.kamma.fabka.repository.model.SearchResultItem;
import cz.kamma.fabka.repository.model.VoteStats;
public class ForumRepository {
@ -282,6 +283,7 @@ public class ForumRepository {
out.add(new ForumMessage(
messageId,
forumId,
authorIds.get(i),
valueOrDefault(usernames.get(i), "N/A"),
formatTs(createdTimes.get(i)),
@ -678,6 +680,50 @@ public class ForumRepository {
return value == null || value.isBlank() ? defaultValue : value;
}
public List<SearchResultItem> searchMessages(long userId, String searchText, long forumId) {
List<SearchResultItem> results = new ArrayList<>();
if (searchText == null || searchText.isBlank()) {
return results;
}
String sql = "SELECT fi.id, fi.forumid, fi.created, fi.createdby, fi.text, ua.username, ua.city, ua.created as createdua, f.name FROM forum_items fi, user_accounts ua, forum f WHERE " +
(forumId > 0 ? "fi.forumid=" + forumId + " AND " : "") +
"MATCH (fi.text) AGAINST (? IN BOOLEAN MODE) AND fi.deleted=0 AND fi.createdby=ua.id AND f.id=fi.forumid AND f.password IN (SELECT password FROM passwords WHERE userid=?)";
long startTime = System.nanoTime();
try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, searchText);
ps.setLong(2, userId);
System.out.println("[DEBUG] SEARCH SQL (forumId=" + forumId + "): " + sql);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
results.add(buildSearchResultFromResultSet(rs));
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
long durationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
System.out.println("[DEBUG] SEARCH_MESSAGES_SQL: searchText='" + searchText + "', forumId=" + forumId + ", count=" + results.size() + ", duration=" + durationMs + "ms");
return results;
}
private SearchResultItem buildSearchResultFromResultSet(ResultSet rs) throws Exception {
long id = rs.getLong("id");
long forumId = rs.getLong("forumid");
Timestamp created = rs.getTimestamp("created");
long createdBy = rs.getLong("createdby");
String text = rs.getString("text");
String username = rs.getString("username");
String city = rs.getString("city");
Timestamp createdua = rs.getTimestamp("createdua");
String forumName = rs.getString("name");
return new SearchResultItem(id, forumId, created, createdBy, text, username, city, createdua, forumName);
}
private static String formatTs(Timestamp ts) {
if (ts == null) {
return "N/A";

View File

@ -4,6 +4,7 @@ import java.util.List;
public class ForumMessage {
private final long id;
private final long forumId;
private final long authorId;
private final String author;
private final String authorJoinDate;
@ -19,11 +20,12 @@ public class ForumMessage {
private final QuotedTextItem quotedItem;
private final String createdAt;
private final String text;
private final List<ForumAttachment> attachments;
private List<ForumAttachment> attachments;
private final boolean sticky;
public ForumMessage(
long id,
long forumId,
long authorId,
String author,
String authorJoinDate,
@ -43,6 +45,7 @@ public class ForumMessage {
boolean sticky
) {
this.id = id;
this.forumId = forumId;
this.authorId = authorId;
this.author = author;
this.authorJoinDate = authorJoinDate;
@ -133,4 +136,12 @@ public class ForumMessage {
public boolean isSticky() {
return sticky;
}
public long getForumId() {
return forumId;
}
public void setAttachments(List<ForumAttachment> attachments) {
this.attachments = attachments;
}
}

View File

@ -0,0 +1,73 @@
package cz.kamma.fabka.repository.model;
import java.sql.Timestamp;
public class SearchResultItem {
private final long id;
private final long forumId;
private final Timestamp created;
private final long createdBy;
private final String text;
private final String username;
private final String city;
private final Timestamp createdua;
private final String forumName;
public SearchResultItem(
long id,
long forumId,
Timestamp created,
long createdBy,
String text,
String username,
String city,
Timestamp createdua,
String forumName
) {
this.id = id;
this.forumId = forumId;
this.created = created;
this.createdBy = createdBy;
this.text = text;
this.username = username;
this.city = city;
this.createdua = createdua;
this.forumName = forumName;
}
public long getId() {
return id;
}
public long getForumId() {
return forumId;
}
public Timestamp getCreated() {
return created;
}
public long getCreatedBy() {
return createdBy;
}
public String getText() {
return text;
}
public String getUsername() {
return username;
}
public String getCity() {
return city;
}
public Timestamp getCreatedua() {
return createdua;
}
public String getForumName() {
return forumName;
}
}

View File

@ -9,7 +9,6 @@ import java.util.Map;
import java.util.stream.Collectors;
import cz.kamma.fabka.repository.MysqlClientRepository;
import cz.kamma.fabka.repository.model.AttachmentData;
import cz.kamma.fabka.repository.model.ForumAttachment;
import cz.kamma.fabka.repository.model.ForumDetail;
import cz.kamma.fabka.repository.model.ForumDisplayView;
@ -22,7 +21,7 @@ import cz.kamma.fabka.repository.model.PrivateMessageStats;
import cz.kamma.fabka.repository.model.PrivateThreadRoot;
import cz.kamma.fabka.repository.model.PrivateThreadSummary;
import cz.kamma.fabka.repository.model.QuotedTextItem;
import cz.kamma.fabka.repository.model.UserIcon;
import cz.kamma.fabka.repository.model.SearchResultItem;
public final class Pages {
private static final String LOGIN_TEMPLATE = readTemplate("webapp/login.html");
@ -532,6 +531,68 @@ public final class Pages {
.replace("{{PAGE_LINKS}}", buildPageLinks(baseLink, currentPage, totalPages));
}
public static String searchResultsPage(
String username,
long currentUserId,
List<SearchResultItem> results,
String searchText,
int loggedUsersCount,
List<String> loggedUsers,
PrivateMessageStats pmStats
) {
StringBuilder messageRows = new StringBuilder();
if (results == null || results.isEmpty()) {
messageRows.append("<div style='color:red'>No message found.</div>");
} else {
for (SearchResultItem result : results) {
messageRows.append("<table class='tborder' cellpadding='6' cellspacing='1' border='0' width='100%' align='center'>")
.append("<tbody>")
.append("<tr><td class='thead'>")
.append("<div class='normal'>")
.append(formatTimestamp(result.getCreated()))
.append(" - <a href='/forumdisplay?f=").append(result.getForumId()).append("'>")
.append(escapeHtml(valueOrDefault(result.getForumName(), ""))).append("</a>")
.append("</div></td></tr>")
.append("<tr><td class='alt2' style='padding:0'>")
.append("<table cellpadding='0' cellspacing='6' border='0' width='100%'><tbody><tr>")
.append("<td valign='middle' nowrap='nowrap'><img src='/process/showimage?userIcon=yes&uid=").append(result.getCreatedBy()).append("' width='80'/></td>")
.append("<td valign='middle' nowrap='nowrap'><div style='color: blue; font-weight:bold'><a href='#'>")
.append(escapeHtml(result.getUsername())).append("</a></div></td>")
.append("<td width='100%'>&nbsp;</td>")
.append("<td valign='top' nowrap='nowrap'><div class='smallfont'>")
.append("<div>Join Date: ").append(formatTimestamp(result.getCreatedua())).append("</div>")
.append("<div>Location: ").append(escapeHtml(valueOrDefault(result.getCity(), ""))).append("</div>")
.append("</div></td>")
.append("</tr></tbody></table>")
.append("</td></tr>")
.append("<tr><td class='alt1'><div>")
.append(LegacyMessageFormatter.convertMessageToHtml(valueOrDefault(result.getText(), ""), null, null, null))
.append("</div></td></tr>")
.append("</tbody></table>\n");
}
}
String body = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
+ "<html xmlns='http://www.w3.org/1999/xhtml' dir='ltr' lang='en'><head>"
+ "<title>kAmMa's Forum - Search Results</title>"
+ "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>"
+ "<meta http-equiv='Expires' content='0'/>"
+ "<link rel='stylesheet' type='text/css' href='./css/all.css'/>"
+ "<script type='text/javascript' src='/client.js'></script>"
+ "</head><body>"
+ "<div align='center'>"
+ "<div class='page' style='width: 100%; text-align: left'>"
+ "<div style='padding: 0px 25px 0px 25px' align='left'><br/>"
+ renderCommonHeader(username, "Search Results", true, pmStats)
+ "<br/>"
+ messageRows
+ "<br/>"
+ renderCommonFooter(Math.max(0, loggedUsersCount), loggedUsers)
+ "</div></div></div><br/>"
+ "</body></html>";
return body;
}
public static String mysqlClientPage(
String username,
List<String> databases,
@ -908,7 +969,7 @@ public final class Pages {
.replace("'", "&#39;");
}
private static String readTemplate(String path) {
private static String readTemplate(String path) {
try (InputStream in = Pages.class.getClassLoader().getResourceAsStream(path)) {
if (in == null) {
throw new IllegalStateException("Template not found: " + path);
@ -918,4 +979,13 @@ public final class Pages {
throw new IllegalStateException("Failed to read template: " + path, ex);
}
}
private static String formatTimestamp(java.sql.Timestamp ts) {
if (ts == null) {
return "N/A";
}
java.time.ZoneId zone = java.time.ZoneId.of("Europe/Prague");
java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");
return ts.toLocalDateTime().atZone(zone).format(formatter);
}
}

View File

@ -17,7 +17,7 @@
<table class='tborder' cellpadding='3' cellspacing='1' border='0'>
<tbody><tr>
<td class='vbmenu_control' style='font-weight:normal'>
<form action='#' method='post'>
<form action='/search' method='get'>
Search in threads:
<input type='text' class='bginput' style='font-size:11px' name='stext' tabindex='1'/>
</form>