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();