diff --git a/tools/cldr-apps/js/src/esm/cldrErrorSubtypes.mjs b/tools/cldr-apps/js/src/esm/cldrErrorSubtypes.mjs
index 4e5f21d3a2e..4e4a88d281f 100644
--- a/tools/cldr-apps/js/src/esm/cldrErrorSubtypes.mjs
+++ b/tools/cldr-apps/js/src/esm/cldrErrorSubtypes.mjs
@@ -147,7 +147,7 @@ function reloadMapHandler(json) {
}
el.innerHTML = html;
if (json.err) {
- const b = cldrDom.createLinkToFn('special_error_subtypes', load, 'button');
+ const b = cldrDom.createLinkToFn("special_error_subtypes", load, "button");
el.appendChild(b);
}
}
diff --git a/tools/cldr-apps/js/src/esm/cldrTable.mjs b/tools/cldr-apps/js/src/esm/cldrTable.mjs
index d983f105687..b163b7b7a7f 100644
--- a/tools/cldr-apps/js/src/esm/cldrTable.mjs
+++ b/tools/cldr-apps/js/src/esm/cldrTable.mjs
@@ -763,7 +763,7 @@ function updateRowEnglishComparisonCell(tr, theRow, cell) {
}
const TRANS_HINT_ID = "en"; // expected to match SurveyMain.TRANS_HINT_ID
cldrSurvey.setLang(cell, TRANS_HINT_ID);
- if (theRow.displayExample || trHint) {
+ if (theRow.displayExample || trHint || theRow.forumStatus.hasPosts) {
const infos = document.createElement("div");
infos.className = "infos-code";
if (trHint) {
@@ -772,6 +772,9 @@ function updateRowEnglishComparisonCell(tr, theRow, cell) {
if (theRow.displayExample) {
appendExampleIcon(infos, theRow.displayExample, TRANS_HINT_ID);
}
+ if (theRow.forumStatus.hasPosts) {
+ appendForumStatus(infos, theRow.forumStatus, TRANS_HINT_ID);
+ }
cell.appendChild(infos);
}
cldrInfo.listen(null, tr, cell, null);
@@ -1114,6 +1117,19 @@ function appendTranslationHintIcon(parent, text, loc) {
return el;
}
+function appendForumStatus(parent, forumStatus, loc) {
+ const el = document.createElement("span");
+ el.textContent = "💬" + (forumStatus.hasOpenPosts ? "?" : ".");
+ el.title =
+ cldrText.get("forum_path_has_posts") +
+ (forumStatus.hasOpenPosts
+ ? cldrText.get("forum_path_has_open_posts")
+ : cldrText.get("forum_path_has_only_closed_posts"));
+ cldrSurvey.setLang(el, loc);
+ parent.appendChild(el);
+ return el;
+}
+
function appendExampleIcon(parent, text, loc) {
const el = appendExample(parent, text, loc);
const img = document.createElement("img");
diff --git a/tools/cldr-apps/js/src/esm/cldrText.mjs b/tools/cldr-apps/js/src/esm/cldrText.mjs
index e4de7986805..8117b66736c 100644
--- a/tools/cldr-apps/js/src/esm/cldrText.mjs
+++ b/tools/cldr-apps/js/src/esm/cldrText.mjs
@@ -360,6 +360,10 @@ const strings = {
forum_remember_vote:
"⚠️ Please remember to vote – submitting a forum post does NOT cause any actual vote to be made.",
+ forum_path_has_posts: "This item has forum posts, ",
+ forum_path_has_open_posts: "some of which are open",
+ forum_path_has_only_closed_posts: "all of which are closed",
+
generic_nolocale: "No locale chosen.",
defaultContent_msg:
"This locale, ${name} is the default content locale for ${dcParentName}, and thus editing or viewing is disabled.",
diff --git a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/SurveyForum.java b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/SurveyForum.java
index 31159c75dd9..4e8453982ba 100644
--- a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/SurveyForum.java
+++ b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/SurveyForum.java
@@ -90,7 +90,7 @@ private static String HTMLUnsafe(String s) {
private synchronized int getForumNumber(CLDRLocale locale) {
String forum = localeToForum(locale);
- if (forum.length() == 0 || LocaleNames.ROOT.equals(forum)) {
+ if (forum.isEmpty() || LocaleNames.ROOT.equals(forum)) {
return NO_FORUM; // all forums
}
// make sure it is a valid src!
@@ -198,7 +198,7 @@ private void gatherUsersInterestedInLocale(
UserRegistry.User u = sm.reg.getInfo(uid);
if (u != null
&& u.email != null
- && u.email.length() > 0
+ && !u.email.isEmpty()
&& !(UserRegistry.userIsLocked(u)
|| UserRegistry.userIsExactlyAnonymous(u))) {
if (UserRegistry.userIsVetter(u)) {
@@ -1559,7 +1559,7 @@ public void setSendEmail(boolean sendEmail) {
}
/** Status values associated with forum posts and threads */
- enum PostType {
+ public enum PostType {
CLOSE(0, "Close"),
DISCUSS(1, "Discuss"),
REQUEST(2, "Request"),
@@ -1628,4 +1628,52 @@ public static PostType fromName(String name, PostType defaultStatus) {
return defaultStatus;
}
}
+
+ public static class PathForumStatus {
+ public boolean hasPosts, hasOpenPosts;
+
+ public PathForumStatus(CLDRLocale locale, String xpath) {
+ Connection conn = null;
+ PreparedStatement ps = null;
+ final String tableName = DBUtils.Table.FORUM_POSTS.toString();
+ final String localeId = locale.getBaseName();
+ final int xpathId = CookieSession.sm.xpt.getByXpath(xpath);
+ try {
+ conn = DBUtils.getInstance().getAConnection();
+ if (conn == null) {
+ return;
+ }
+ // Expect most paths have NO posts, so check first for ANY posts (open or not).
+ // "LIMIT 1" may improve performance; no need to distinguish 1 from larger numbers
+ ps =
+ DBUtils.prepareForwardReadOnly(
+ conn,
+ "SELECT ID FROM " + tableName + " WHERE loc=? and xpath=? LIMIT 1");
+ ps.setString(1, localeId);
+ ps.setInt(2, xpathId);
+ if (DBUtils.sqlCount(ps) <= 0) { // sqlCount returns -1 (not 0) for none!
+ this.hasPosts = this.hasOpenPosts = false;
+ return;
+ }
+ this.hasPosts = true;
+ // Check for OPEN posts
+ ps =
+ DBUtils.prepareForwardReadOnly(
+ conn,
+ "SELECT ID FROM "
+ + tableName
+ + " WHERE loc=? and xpath=? AND is_open=1 LIMIT 1");
+ ps.setString(1, localeId);
+ ps.setInt(2, xpathId);
+ this.hasOpenPosts = DBUtils.sqlCount(ps) > 0;
+ } catch (SQLException e) {
+ SurveyLog.logException(
+ logger,
+ e,
+ "PathForumStatus for " + tableName + " " + locale + ":" + xpathId);
+ } finally {
+ DBUtils.close(ps, conn);
+ }
+ }
+ }
}
diff --git a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPI.java b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPI.java
index 544f482d433..74587e9de63 100644
--- a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPI.java
+++ b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPI.java
@@ -27,6 +27,7 @@
import org.unicode.cldr.web.Dashboard;
import org.unicode.cldr.web.DataPage;
import org.unicode.cldr.web.SubtypeToURLMap;
+import org.unicode.cldr.web.SurveyForum;
import org.unicode.cldr.web.api.VoteAPIHelper.VoteEntry;
@Path("/voting")
@@ -181,6 +182,7 @@ public static final class VotingResults {
public String rawEnglish;
public Map extraAttributes;
public boolean flagged;
+ public SurveyForum.PathForumStatus forumStatus;
public boolean hasVoted;
public String helpHtml;
public String inheritedLocale;
diff --git a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPIHelper.java b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPIHelper.java
index 1f5698a8553..9beee734bb2 100644
--- a/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPIHelper.java
+++ b/tools/cldr-apps/src/main/java/org/unicode/cldr/web/api/VoteAPIHelper.java
@@ -243,6 +243,7 @@ private static RowResponse.Row calculateRow(final DataRow r, boolean redacted) {
row.rawEnglish = r.getRawEnglish();
row.extraAttributes = r.getNonDistinguishingAttributes();
row.flagged = r.isFlagged();
+ row.forumStatus = new SurveyForum.PathForumStatus(r.getLocale(), xpath);
row.hasVoted = r.userHasVoted();
row.helpHtml = r.getHelpHTML();
row.inheritedLocale = r.getInheritedLocaleName();