forked from Qortal/qortal
Upgraded rendering to support identifiers, as well as single file resources.
This allows any QDN resource (e.g. an IMAGE) to be linked to from a website/app and then rendered on screen. It isn't yet supported in gateway or domain map mode, as these need some more thought.
This commit is contained in:
parent
e1e52b3165
commit
37b20aac66
@ -42,15 +42,15 @@ public class DomainMapResource {
|
|||||||
// Build synchronously, so that we don't need to make the summary API endpoints available over
|
// Build synchronously, so that we don't need to make the summary API endpoints available over
|
||||||
// the domain map server. This means that there will be no loading screen, but this is potentially
|
// the domain map server. This means that there will be no loading screen, but this is potentially
|
||||||
// preferred in this situation anyway (e.g. to avoid confusing search engine robots).
|
// preferred in this situation anyway (e.g. to avoid confusing search engine robots).
|
||||||
return this.get(domainMap.get(request.getServerName()), ResourceIdType.NAME, Service.WEBSITE, inPath, null, "", false, false);
|
return this.get(domainMap.get(request.getServerName()), ResourceIdType.NAME, Service.WEBSITE, null, inPath, null, "", false, false);
|
||||||
}
|
}
|
||||||
return ArbitraryDataRenderer.getResponse(response, 404, "Error 404: File Not Found");
|
return ArbitraryDataRenderer.getResponse(response, 404, "Error 404: File Not Found");
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String inPath,
|
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String identifier,
|
||||||
String secret58, String prefix, boolean usePrefix, boolean async) {
|
String inPath, String secret58, String prefix, boolean usePrefix, boolean async) {
|
||||||
|
|
||||||
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, inPath,
|
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, identifier, inPath,
|
||||||
secret58, prefix, usePrefix, async, "domainMap", request, response, context);
|
secret58, prefix, usePrefix, async, "domainMap", request, response, context);
|
||||||
return renderer.render();
|
return renderer.render();
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ public class GatewayResource {
|
|||||||
@PathParam("path") String inPath) {
|
@PathParam("path") String inPath) {
|
||||||
// Block requests from localhost, to prevent websites/apps from running javascript that fetches unvetted data
|
// Block requests from localhost, to prevent websites/apps from running javascript that fetches unvetted data
|
||||||
Security.disallowLoopbackRequests(request);
|
Security.disallowLoopbackRequests(request);
|
||||||
return this.get(name, ResourceIdType.NAME, Service.WEBSITE, inPath, null, "", true, true);
|
return this.get(name, ResourceIdType.NAME, Service.WEBSITE, null, inPath, null, "", true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@ -91,7 +91,7 @@ public class GatewayResource {
|
|||||||
public HttpServletResponse getIndexByName(@PathParam("name") String name) {
|
public HttpServletResponse getIndexByName(@PathParam("name") String name) {
|
||||||
// Block requests from localhost, to prevent websites/apps from running javascript that fetches unvetted data
|
// Block requests from localhost, to prevent websites/apps from running javascript that fetches unvetted data
|
||||||
Security.disallowLoopbackRequests(request);
|
Security.disallowLoopbackRequests(request);
|
||||||
return this.get(name, ResourceIdType.NAME, Service.WEBSITE, "/", null, "", true, true);
|
return this.get(name, ResourceIdType.NAME, Service.WEBSITE, null, "/", null, "", true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ public class GatewayResource {
|
|||||||
@PathParam("path") String inPath) {
|
@PathParam("path") String inPath) {
|
||||||
// Block requests from localhost, to prevent websites/apps from running javascript that fetches unvetted data
|
// Block requests from localhost, to prevent websites/apps from running javascript that fetches unvetted data
|
||||||
Security.disallowLoopbackRequests(request);
|
Security.disallowLoopbackRequests(request);
|
||||||
return this.get(name, ResourceIdType.NAME, Service.WEBSITE, inPath, null, "/site", true, true);
|
return this.get(name, ResourceIdType.NAME, Service.WEBSITE, null, inPath, null, "/site", true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@ -111,14 +111,14 @@ public class GatewayResource {
|
|||||||
public HttpServletResponse getSiteIndexByName(@PathParam("name") String name) {
|
public HttpServletResponse getSiteIndexByName(@PathParam("name") String name) {
|
||||||
// Block requests from localhost, to prevent websites/apps from running javascript that fetches unvetted data
|
// Block requests from localhost, to prevent websites/apps from running javascript that fetches unvetted data
|
||||||
Security.disallowLoopbackRequests(request);
|
Security.disallowLoopbackRequests(request);
|
||||||
return this.get(name, ResourceIdType.NAME, Service.WEBSITE, "/", null, "/site", true, true);
|
return this.get(name, ResourceIdType.NAME, Service.WEBSITE, null, "/", null, "/site", true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String inPath,
|
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String identifier,
|
||||||
String secret58, String prefix, boolean usePrefix, boolean async) {
|
String inPath, String secret58, String prefix, boolean usePrefix, boolean async) {
|
||||||
|
|
||||||
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, inPath,
|
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, identifier, inPath,
|
||||||
secret58, prefix, usePrefix, async, "gateway", request, response, context);
|
secret58, prefix, usePrefix, async, "gateway", request, response, context);
|
||||||
return renderer.render();
|
return renderer.render();
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ public class RenderResource {
|
|||||||
if (!Settings.getInstance().isQDNAuthBypassEnabled())
|
if (!Settings.getInstance().isQDNAuthBypassEnabled())
|
||||||
Security.requirePriorAuthorization(request, signature, Service.WEBSITE, null);
|
Security.requirePriorAuthorization(request, signature, Service.WEBSITE, null);
|
||||||
|
|
||||||
return this.get(signature, ResourceIdType.SIGNATURE, null, "/", null, "/render/signature", true, true, theme);
|
return this.get(signature, ResourceIdType.SIGNATURE, null, null, "/", null, "/render/signature", true, true, theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@ -157,7 +157,7 @@ public class RenderResource {
|
|||||||
if (!Settings.getInstance().isQDNAuthBypassEnabled())
|
if (!Settings.getInstance().isQDNAuthBypassEnabled())
|
||||||
Security.requirePriorAuthorization(request, signature, Service.WEBSITE, null);
|
Security.requirePriorAuthorization(request, signature, Service.WEBSITE, null);
|
||||||
|
|
||||||
return this.get(signature, ResourceIdType.SIGNATURE, null, inPath,null, "/render/signature", true, true, theme);
|
return this.get(signature, ResourceIdType.SIGNATURE, null, null, inPath,null, "/render/signature", true, true, theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@ -168,7 +168,7 @@ public class RenderResource {
|
|||||||
if (!Settings.getInstance().isQDNAuthBypassEnabled())
|
if (!Settings.getInstance().isQDNAuthBypassEnabled())
|
||||||
Security.requirePriorAuthorization(request, hash58, Service.WEBSITE, null);
|
Security.requirePriorAuthorization(request, hash58, Service.WEBSITE, null);
|
||||||
|
|
||||||
return this.get(hash58, ResourceIdType.FILE_HASH, Service.WEBSITE, "/", secret58, "/render/hash", true, false, theme);
|
return this.get(hash58, ResourceIdType.FILE_HASH, Service.WEBSITE, null, "/", secret58, "/render/hash", true, false, theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@ -180,7 +180,7 @@ public class RenderResource {
|
|||||||
if (!Settings.getInstance().isQDNAuthBypassEnabled())
|
if (!Settings.getInstance().isQDNAuthBypassEnabled())
|
||||||
Security.requirePriorAuthorization(request, hash58, Service.WEBSITE, null);
|
Security.requirePriorAuthorization(request, hash58, Service.WEBSITE, null);
|
||||||
|
|
||||||
return this.get(hash58, ResourceIdType.FILE_HASH, Service.WEBSITE, inPath, secret58, "/render/hash", true, false, theme);
|
return this.get(hash58, ResourceIdType.FILE_HASH, Service.WEBSITE, null, inPath, secret58, "/render/hash", true, false, theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@ -189,12 +189,13 @@ public class RenderResource {
|
|||||||
public HttpServletResponse getPathByName(@PathParam("service") Service service,
|
public HttpServletResponse getPathByName(@PathParam("service") Service service,
|
||||||
@PathParam("name") String name,
|
@PathParam("name") String name,
|
||||||
@PathParam("path") String inPath,
|
@PathParam("path") String inPath,
|
||||||
|
@QueryParam("identifier") String identifier,
|
||||||
@QueryParam("theme") String theme) {
|
@QueryParam("theme") String theme) {
|
||||||
if (!Settings.getInstance().isQDNAuthBypassEnabled())
|
if (!Settings.getInstance().isQDNAuthBypassEnabled())
|
||||||
Security.requirePriorAuthorization(request, name, service, null);
|
Security.requirePriorAuthorization(request, name, service, null);
|
||||||
|
|
||||||
String prefix = String.format("/render/%s", service);
|
String prefix = String.format("/render/%s", service);
|
||||||
return this.get(name, ResourceIdType.NAME, service, inPath, null, prefix, true, true, theme);
|
return this.get(name, ResourceIdType.NAME, service, identifier, inPath, null, prefix, true, true, theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@ -207,15 +208,15 @@ public class RenderResource {
|
|||||||
Security.requirePriorAuthorization(request, name, service, null);
|
Security.requirePriorAuthorization(request, name, service, null);
|
||||||
|
|
||||||
String prefix = String.format("/render/%s", service);
|
String prefix = String.format("/render/%s", service);
|
||||||
return this.get(name, ResourceIdType.NAME, service, "/", null, prefix, true, true, theme);
|
return this.get(name, ResourceIdType.NAME, service, null, "/", null, prefix, true, true, theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String inPath,
|
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String identifier,
|
||||||
String secret58, String prefix, boolean usePrefix, boolean async, String theme) {
|
String inPath, String secret58, String prefix, boolean usePrefix, boolean async, String theme) {
|
||||||
|
|
||||||
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, inPath,
|
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, identifier, inPath,
|
||||||
secret58, prefix, usePrefix, async, "render", request, response, context);
|
secret58, prefix, usePrefix, async, "render", request, response, context);
|
||||||
|
|
||||||
if (theme != null) {
|
if (theme != null) {
|
||||||
|
@ -2,6 +2,7 @@ package org.qortal.arbitrary;
|
|||||||
|
|
||||||
import com.google.common.io.Resources;
|
import com.google.common.io.Resources;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.qortal.api.HTMLParser;
|
import org.qortal.api.HTMLParser;
|
||||||
@ -34,6 +35,7 @@ public class ArbitraryDataRenderer {
|
|||||||
private final String resourceId;
|
private final String resourceId;
|
||||||
private final ResourceIdType resourceIdType;
|
private final ResourceIdType resourceIdType;
|
||||||
private final Service service;
|
private final Service service;
|
||||||
|
private final String identifier;
|
||||||
private String theme = "light";
|
private String theme = "light";
|
||||||
private String inPath;
|
private String inPath;
|
||||||
private final String secret58;
|
private final String secret58;
|
||||||
@ -45,13 +47,14 @@ public class ArbitraryDataRenderer {
|
|||||||
private final HttpServletResponse response;
|
private final HttpServletResponse response;
|
||||||
private final ServletContext context;
|
private final ServletContext context;
|
||||||
|
|
||||||
public ArbitraryDataRenderer(String resourceId, ResourceIdType resourceIdType, Service service, String inPath,
|
public ArbitraryDataRenderer(String resourceId, ResourceIdType resourceIdType, Service service, String identifier,
|
||||||
String secret58, String prefix, boolean usePrefix, boolean async, String qdnContext,
|
String inPath, String secret58, String prefix, boolean usePrefix, boolean async, String qdnContext,
|
||||||
HttpServletRequest request, HttpServletResponse response, ServletContext context) {
|
HttpServletRequest request, HttpServletResponse response, ServletContext context) {
|
||||||
|
|
||||||
this.resourceId = resourceId;
|
this.resourceId = resourceId;
|
||||||
this.resourceIdType = resourceIdType;
|
this.resourceIdType = resourceIdType;
|
||||||
this.service = service;
|
this.service = service;
|
||||||
|
this.identifier = identifier != null ? identifier : "default";
|
||||||
this.inPath = inPath;
|
this.inPath = inPath;
|
||||||
this.secret58 = secret58;
|
this.secret58 = secret58;
|
||||||
this.prefix = prefix;
|
this.prefix = prefix;
|
||||||
@ -73,14 +76,14 @@ public class ArbitraryDataRenderer {
|
|||||||
return ArbitraryDataRenderer.getResponse(response, 500, "QDN is disabled in settings");
|
return ArbitraryDataRenderer.getResponse(response, 500, "QDN is disabled in settings");
|
||||||
}
|
}
|
||||||
|
|
||||||
ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(resourceId, resourceIdType, service, null);
|
ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(resourceId, resourceIdType, service, identifier);
|
||||||
arbitraryDataReader.setSecret58(secret58); // Optional, used for loading encrypted file hashes only
|
arbitraryDataReader.setSecret58(secret58); // Optional, used for loading encrypted file hashes only
|
||||||
try {
|
try {
|
||||||
if (!arbitraryDataReader.isCachedDataAvailable()) {
|
if (!arbitraryDataReader.isCachedDataAvailable()) {
|
||||||
// If async is requested, show a loading screen whilst build is in progress
|
// If async is requested, show a loading screen whilst build is in progress
|
||||||
if (async) {
|
if (async) {
|
||||||
arbitraryDataReader.loadAsynchronously(false, 10);
|
arbitraryDataReader.loadAsynchronously(false, 10);
|
||||||
return this.getLoadingResponse(service, resourceId, theme);
|
return this.getLoadingResponse(service, resourceId, identifier, theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, loop until we have data
|
// Otherwise, loop until we have data
|
||||||
@ -113,6 +116,12 @@ public class ArbitraryDataRenderer {
|
|||||||
}
|
}
|
||||||
String unzippedPath = path.toString();
|
String unzippedPath = path.toString();
|
||||||
|
|
||||||
|
String[] files = ArrayUtils.removeElement(new File(unzippedPath).list(), ".qortal");
|
||||||
|
if (files.length == 1) {
|
||||||
|
// This is a single file resource
|
||||||
|
inPath = files[0];
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String filename = this.getFilename(unzippedPath, inPath);
|
String filename = this.getFilename(unzippedPath, inPath);
|
||||||
String filePath = Paths.get(unzippedPath, filename).toString();
|
String filePath = Paths.get(unzippedPath, filename).toString();
|
||||||
@ -174,7 +183,7 @@ public class ArbitraryDataRenderer {
|
|||||||
return userPath;
|
return userPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpServletResponse getLoadingResponse(Service service, String name, String theme) {
|
private HttpServletResponse getLoadingResponse(Service service, String name, String identifier, String theme) {
|
||||||
String responseString = "";
|
String responseString = "";
|
||||||
URL url = Resources.getResource("loading/index.html");
|
URL url = Resources.getResource("loading/index.html");
|
||||||
try {
|
try {
|
||||||
@ -183,6 +192,7 @@ public class ArbitraryDataRenderer {
|
|||||||
// Replace vars
|
// Replace vars
|
||||||
responseString = responseString.replace("%%SERVICE%%", service.toString());
|
responseString = responseString.replace("%%SERVICE%%", service.toString());
|
||||||
responseString = responseString.replace("%%NAME%%", name);
|
responseString = responseString.replace("%%NAME%%", name);
|
||||||
|
responseString = responseString.replace("%%IDENTIFIER%%", identifier);
|
||||||
responseString = responseString.replace("%%THEME%%", theme);
|
responseString = responseString.replace("%%THEME%%", theme);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -43,8 +43,9 @@
|
|||||||
var host = location.protocol + '//' + location.host;
|
var host = location.protocol + '//' + location.host;
|
||||||
var service = "%%SERVICE%%"
|
var service = "%%SERVICE%%"
|
||||||
var name = "%%NAME%%"
|
var name = "%%NAME%%"
|
||||||
|
var identifier = "%%IDENTIFIER%%"
|
||||||
|
|
||||||
var url = host + '/arbitrary/resource/status/' + service + '/' + name + '?build=true';
|
var url = host + '/arbitrary/resource/status/' + service + '/' + name + '/' + identifier + '?build=true';
|
||||||
var textStatus = "Loading...";
|
var textStatus = "Loading...";
|
||||||
var textProgress = "";
|
var textProgress = "";
|
||||||
var retryInterval = 2500;
|
var retryInterval = 2500;
|
||||||
|
@ -81,6 +81,8 @@ window.addEventListener("message", (event) => {
|
|||||||
url = "/" + data.name;
|
url = "/" + data.name;
|
||||||
}
|
}
|
||||||
if (data.path != null) url = url.concat((data.path.startsWith("/") ? "" : "/") + data.path);
|
if (data.path != null) url = url.concat((data.path.startsWith("/") ? "" : "/") + data.path);
|
||||||
|
if (data.identifier != null) url = url.concat("?identifier=" + data.identifier);
|
||||||
|
|
||||||
window.location = url;
|
window.location = url;
|
||||||
response = true;
|
response = true;
|
||||||
break;
|
break;
|
||||||
@ -228,11 +230,26 @@ function interceptClickEvent(e) {
|
|||||||
let parts = href.split("/");
|
let parts = href.split("/");
|
||||||
const service = parts[0].toUpperCase(); parts.shift();
|
const service = parts[0].toUpperCase(); parts.shift();
|
||||||
const name = parts[0]; parts.shift();
|
const name = parts[0]; parts.shift();
|
||||||
|
let identifier;
|
||||||
|
|
||||||
|
if (parts.length > 0) {
|
||||||
|
identifier = parts[0]; // Do not shift yet
|
||||||
|
// Check if a resource exists with this service, name and identifier combination
|
||||||
|
const url = "/arbitrary/resource/status/" + service + "/" + name + "/" + identifier;
|
||||||
|
const response = httpGet(url);
|
||||||
|
const responseObj = JSON.parse(response);
|
||||||
|
if (responseObj.totalChunkCount > 0) {
|
||||||
|
// Identifier exists, so don't include it in the path
|
||||||
|
parts.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const path = parts.join("/");
|
const path = parts.join("/");
|
||||||
qortalRequest({
|
qortalRequest({
|
||||||
action: "LINK_TO_QDN_RESOURCE",
|
action: "LINK_TO_QDN_RESOURCE",
|
||||||
service: service,
|
service: service,
|
||||||
name: name,
|
name: name,
|
||||||
|
identifier: identifier,
|
||||||
path: path
|
path: path
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user