From 70ae122f5ccedc87447d86b588e52f4c951fa56d Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 10 May 2025 22:21:13 +0300 Subject: [PATCH] pass ui lang to qapps --- src/main/java/org/qortal/api/HTMLParser.java | 13 ++++++--- .../resource/DevProxyServerResource.java | 12 +++++++- .../restricted/resource/RenderResource.java | 29 ++++++++++--------- .../arbitrary/ArbitraryDataRenderer.java | 6 +++- src/main/resources/q-apps/q-apps.js | 8 +++-- 5 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/qortal/api/HTMLParser.java b/src/main/java/org/qortal/api/HTMLParser.java index 4887cf84..f5af7138 100644 --- a/src/main/java/org/qortal/api/HTMLParser.java +++ b/src/main/java/org/qortal/api/HTMLParser.java @@ -1,14 +1,13 @@ package org.qortal.api; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; import org.qortal.arbitrary.misc.Service; - import java.util.Objects; - public class HTMLParser { private static final Logger LOGGER = LogManager.getLogger(HTMLParser.class); @@ -22,10 +21,11 @@ public class HTMLParser { private String identifier; private String path; private String theme; + private String lang; private boolean usingCustomRouting; public HTMLParser(String resourceId, String inPath, String prefix, boolean includeResourceIdInPrefix, byte[] data, - String qdnContext, Service service, String identifier, String theme, boolean usingCustomRouting) { + String qdnContext, Service service, String identifier, String theme, boolean usingCustomRouting, String lang) { String inPathWithoutFilename = inPath.contains("/") ? inPath.substring(0, inPath.lastIndexOf('/')) : String.format("/%s",inPath); this.qdnBase = includeResourceIdInPrefix ? String.format("%s/%s", prefix, resourceId) : prefix; this.qdnBaseWithPath = includeResourceIdInPrefix ? String.format("%s/%s%s", prefix, resourceId, inPathWithoutFilename) : String.format("%s%s", prefix, inPathWithoutFilename); @@ -36,6 +36,7 @@ public class HTMLParser { this.identifier = identifier; this.path = inPath; this.theme = theme; + this.lang = lang; this.usingCustomRouting = usingCustomRouting; } @@ -61,9 +62,13 @@ public class HTMLParser { String identifier = this.identifier != null ? this.identifier.replace("\\", "").replace("\"","\\\"") : ""; String path = this.path != null ? this.path.replace("\\", "").replace("\"","\\\"") : ""; String theme = this.theme != null ? this.theme.replace("\\", "").replace("\"","\\\"") : ""; + String lang = this.lang != null ? this.lang.replace("\\", "").replace("\"", "\\\"") : ""; String qdnBase = this.qdnBase != null ? this.qdnBase.replace("\\", "").replace("\"","\\\"") : ""; String qdnBaseWithPath = this.qdnBaseWithPath != null ? this.qdnBaseWithPath.replace("\\", "").replace("\"","\\\"") : ""; - String qdnContextVar = String.format("", qdnContext, theme, service, name, identifier, path, qdnBase, qdnBaseWithPath); + String qdnContextVar = String.format( + "", + qdnContext, theme, lang, service, name, identifier, path, qdnBase, qdnBaseWithPath + ); head.get(0).prepend(qdnContextVar); // Add base href tag diff --git a/src/main/java/org/qortal/api/proxy/resource/DevProxyServerResource.java b/src/main/java/org/qortal/api/proxy/resource/DevProxyServerResource.java index 7972c551..bdaf1ced 100644 --- a/src/main/java/org/qortal/api/proxy/resource/DevProxyServerResource.java +++ b/src/main/java/org/qortal/api/proxy/resource/DevProxyServerResource.java @@ -142,10 +142,20 @@ public class DevProxyServerResource { } } + String lang = request.getParameter("lang"); + if (lang == null || lang.isBlank()) { + lang = "en"; // fallback + } + + String theme = request.getParameter("theme"); + if (theme == null || theme.isBlank()) { + theme = "light"; + } + // Parse and modify output if needed if (HTMLParser.isHtmlFile(filename)) { // HTML file - needs to be parsed - HTMLParser htmlParser = new HTMLParser("", inPath, "", false, data, "proxy", Service.APP, null, "light", true); + HTMLParser htmlParser = new HTMLParser("", inPath, "", false, data, "proxy", Service.APP, null, theme , true, lang); htmlParser.addAdditionalHeaderTags(); response.addHeader("Content-Security-Policy", "default-src 'self' 'unsafe-inline' 'unsafe-eval'; media-src 'self' data: blob:; img-src 'self' data: blob:; connect-src 'self' ws:; font-src 'self' data:;"); response.setContentType(con.getContentType()); diff --git a/src/main/java/org/qortal/api/restricted/resource/RenderResource.java b/src/main/java/org/qortal/api/restricted/resource/RenderResource.java index 5eb169a4..3bab7bf3 100644 --- a/src/main/java/org/qortal/api/restricted/resource/RenderResource.java +++ b/src/main/java/org/qortal/api/restricted/resource/RenderResource.java @@ -71,33 +71,33 @@ public class RenderResource { @Path("/signature/{signature}") @SecurityRequirement(name = "apiKey") public HttpServletResponse getIndexBySignature(@PathParam("signature") String signature, - @QueryParam("theme") String theme) { + @QueryParam("theme") String theme, @QueryParam("lang") String lang) { if (!Settings.getInstance().isQDNAuthBypassEnabled()) Security.requirePriorAuthorization(request, signature, Service.WEBSITE, null); - return this.get(signature, ResourceIdType.SIGNATURE, null, null, "/", null, "/render/signature", true, true, theme); + return this.get(signature, ResourceIdType.SIGNATURE, null, null, "/", null, "/render/signature", true, true, theme, lang); } @GET @Path("/signature/{signature}/{path:.*}") @SecurityRequirement(name = "apiKey") public HttpServletResponse getPathBySignature(@PathParam("signature") String signature, @PathParam("path") String inPath, - @QueryParam("theme") String theme) { + @QueryParam("theme") String theme, @QueryParam("lang") String lang) { if (!Settings.getInstance().isQDNAuthBypassEnabled()) Security.requirePriorAuthorization(request, signature, Service.WEBSITE, null); - return this.get(signature, ResourceIdType.SIGNATURE, null, null, inPath,null, "/render/signature", true, true, theme); + return this.get(signature, ResourceIdType.SIGNATURE, null, null, inPath,null, "/render/signature", true, true, theme, lang); } @GET @Path("/hash/{hash}") @SecurityRequirement(name = "apiKey") public HttpServletResponse getIndexByHash(@PathParam("hash") String hash58, @QueryParam("secret") String secret58, - @QueryParam("theme") String theme) { + @QueryParam("theme") String theme, @QueryParam("lang") String lang) { if (!Settings.getInstance().isQDNAuthBypassEnabled()) Security.requirePriorAuthorization(request, hash58, Service.WEBSITE, null); - return this.get(hash58, ResourceIdType.FILE_HASH, Service.ARBITRARY_DATA, null, "/", secret58, "/render/hash", true, false, theme); + return this.get(hash58, ResourceIdType.FILE_HASH, Service.ARBITRARY_DATA, null, "/", secret58, "/render/hash", true, false, theme, lang); } @GET @@ -105,11 +105,11 @@ public class RenderResource { @SecurityRequirement(name = "apiKey") public HttpServletResponse getPathByHash(@PathParam("hash") String hash58, @PathParam("path") String inPath, @QueryParam("secret") String secret58, - @QueryParam("theme") String theme) { + @QueryParam("theme") String theme, @QueryParam("lang") String lang) { if (!Settings.getInstance().isQDNAuthBypassEnabled()) Security.requirePriorAuthorization(request, hash58, Service.WEBSITE, null); - return this.get(hash58, ResourceIdType.FILE_HASH, Service.ARBITRARY_DATA, null, inPath, secret58, "/render/hash", true, false, theme); + return this.get(hash58, ResourceIdType.FILE_HASH, Service.ARBITRARY_DATA, null, inPath, secret58, "/render/hash", true, false, theme, lang); } @GET @@ -119,12 +119,12 @@ public class RenderResource { @PathParam("name") String name, @PathParam("path") String inPath, @QueryParam("identifier") String identifier, - @QueryParam("theme") String theme) { + @QueryParam("theme") String theme, @QueryParam("lang") String lang) { if (!Settings.getInstance().isQDNAuthBypassEnabled()) Security.requirePriorAuthorization(request, name, service, null); String prefix = String.format("/render/%s", service); - return this.get(name, ResourceIdType.NAME, service, identifier, inPath, null, prefix, true, true, theme); + return this.get(name, ResourceIdType.NAME, service, identifier, inPath, null, prefix, true, true, theme, lang); } @GET @@ -133,18 +133,18 @@ public class RenderResource { public HttpServletResponse getIndexByName(@PathParam("service") Service service, @PathParam("name") String name, @QueryParam("identifier") String identifier, - @QueryParam("theme") String theme) { + @QueryParam("theme") String theme, @QueryParam("lang") String lang) { if (!Settings.getInstance().isQDNAuthBypassEnabled()) Security.requirePriorAuthorization(request, name, service, null); String prefix = String.format("/render/%s", service); - return this.get(name, ResourceIdType.NAME, service, identifier, "/", null, prefix, true, true, theme); + return this.get(name, ResourceIdType.NAME, service, identifier, "/", null, prefix, true, true, theme, lang); } private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String identifier, - String inPath, String secret58, String prefix, boolean includeResourceIdInPrefix, boolean async, String theme) { + String inPath, String secret58, String prefix, boolean includeResourceIdInPrefix, boolean async, String theme, String lang) { ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, identifier, inPath, secret58, prefix, includeResourceIdInPrefix, async, "render", request, response, context); @@ -152,6 +152,9 @@ public class RenderResource { if (theme != null) { renderer.setTheme(theme); } + if (lang != null) { + renderer.setLang(lang); + } return renderer.render(); } diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java index eb51e8a4..72cd4097 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java @@ -37,6 +37,7 @@ public class ArbitraryDataRenderer { private final Service service; private final String identifier; private String theme = "light"; + private String lang = "en"; private String inPath; private final String secret58; private final String prefix; @@ -166,7 +167,7 @@ public class ArbitraryDataRenderer { if (HTMLParser.isHtmlFile(filename)) { // HTML file - needs to be parsed byte[] data = Files.readAllBytes(filePath); // TODO: limit file size that can be read into memory - HTMLParser htmlParser = new HTMLParser(resourceId, inPath, prefix, includeResourceIdInPrefix, data, qdnContext, service, identifier, theme, usingCustomRouting); + HTMLParser htmlParser = new HTMLParser(resourceId, inPath, prefix, includeResourceIdInPrefix, data, qdnContext, service, identifier, theme, usingCustomRouting, lang); htmlParser.addAdditionalHeaderTags(); response.addHeader("Content-Security-Policy", "default-src 'self' 'unsafe-inline' 'unsafe-eval'; font-src 'self' data:; media-src 'self' data: blob:; img-src 'self' data: blob:; connect-src 'self' wss:;"); response.setContentType(context.getMimeType(filename)); @@ -256,5 +257,8 @@ public class ArbitraryDataRenderer { public void setTheme(String theme) { this.theme = theme; } + public void setLang(String lang) { + this.lang = lang; + } } diff --git a/src/main/resources/q-apps/q-apps.js b/src/main/resources/q-apps/q-apps.js index d7222750..486c6543 100644 --- a/src/main/resources/q-apps/q-apps.js +++ b/src/main/resources/q-apps/q-apps.js @@ -45,6 +45,7 @@ function parseUrl(url) { // Remove theme, identifier, and time queries if they exist parsedUrl.searchParams.delete("theme"); + parsedUrl.searchParams.delete("lang"); parsedUrl.searchParams.delete("identifier"); parsedUrl.searchParams.delete("time"); parsedUrl.searchParams.delete("isManualNavigation"); @@ -213,8 +214,11 @@ function buildResourceUrl(service, name, identifier, path, isLink) { if (path != null) url = url.concat((path.startsWith("/") ? "" : "/") + path); } - if (isLink) url = url.concat((url.includes("?") ? "" : "?") + "&theme=" + _qdnTheme); - + if (isLink) { + const hasQuery = url.includes("?"); + const queryPrefix = hasQuery ? "&" : "?"; + url += queryPrefix + "theme=" + _qdnTheme + "&lang=" + _qdnLang; + } return url; }