WIP on file details page
This commit is contained in:
191
webui/src/main/java/com/muwire/webui/FileInfoServlet.java
Normal file
191
webui/src/main/java/com/muwire/webui/FileInfoServlet.java
Normal file
@@ -0,0 +1,191 @@
|
||||
package com.muwire.webui;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.Collator;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.muwire.core.Core;
|
||||
import com.muwire.core.InfoHash;
|
||||
import com.muwire.core.Persona;
|
||||
import com.muwire.core.SharedFile;
|
||||
import com.muwire.core.filecert.Certificate;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
public class FileInfoServlet extends HttpServlet {
|
||||
|
||||
private Core core;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
String path = req.getParameter("path");
|
||||
if (path == null) {
|
||||
resp.sendError(403, "bad param");
|
||||
return;
|
||||
}
|
||||
File file = Util.getFromPathElements(path);
|
||||
SharedFile sf = core.getFileManager().getFileToSharedFile().get(file);
|
||||
if (sf == null) {
|
||||
resp.sendError(403, file + " is not shared");
|
||||
return;
|
||||
}
|
||||
|
||||
String section = req.getParameter("section");
|
||||
if (section == null) {
|
||||
resp.sendError(403, "Bad param");
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<?xml version='1.0' encoding='UTF-8'?>");
|
||||
|
||||
if (section.equals("searchers")) {
|
||||
List<SearchEntry> searchEntries = sf.getSearches().stream().
|
||||
map(e -> new SearchEntry(e)).
|
||||
collect(Collectors.toList());
|
||||
SEARCH_ENTRY_COMPARATORS.sort(searchEntries, req);
|
||||
sb.append("<SearchEntries>");
|
||||
searchEntries.forEach(e -> e.toXML(sb));
|
||||
sb.append("</SearchEntries>");
|
||||
} else if (section.equals("downloaders")) {
|
||||
List<DownloadEntry> downloadEntries = sf.getDownloaders().stream().
|
||||
map(d -> new DownloadEntry(d)).
|
||||
collect(Collectors.toList());
|
||||
DOWNLOADER_COMPARATORS.sort(downloadEntries, req);
|
||||
sb.append("<Downloaders>");
|
||||
downloadEntries.forEach(e -> e.toXML(sb));
|
||||
sb.append("</Downloaders>");
|
||||
} else if (section.equals("certificates")) {
|
||||
List<CertificateEntry> certificateEntries = core.getCertificateManager().getByInfoHash(new InfoHash(sf.getRoot())).stream().
|
||||
map(c -> new CertificateEntry(c)).
|
||||
collect(Collectors.toList());
|
||||
CERT_COMPARATORS.sort(certificateEntries, req);
|
||||
sb.append("<Certificates>");
|
||||
certificateEntries.forEach(e -> e.toXML(sb));
|
||||
sb.append("</Certificates>");
|
||||
} else {
|
||||
resp.sendError(403, "Bad param");
|
||||
return;
|
||||
}
|
||||
|
||||
resp.setContentType("text/xml");
|
||||
resp.setCharacterEncoding("UTF-8");
|
||||
resp.setDateHeader("Expires", 0);
|
||||
resp.setHeader("Pragma", "no-cache");
|
||||
resp.setHeader("Cache-Control", "no-store, max-age=0, no-cache, must-revalidate");
|
||||
byte[] out = sb.toString().getBytes("UTF-8");
|
||||
resp.setContentLength(out.length);
|
||||
resp.getOutputStream().write(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig config) throws ServletException {
|
||||
core = (Core) config.getServletContext().getAttribute("core");
|
||||
}
|
||||
|
||||
private static class SearchEntry {
|
||||
private final Persona persona;
|
||||
private final long timestamp;
|
||||
private final String query;
|
||||
|
||||
SearchEntry(SharedFile.SearchEntry e) {
|
||||
this.persona = e.getSearcher();
|
||||
this.timestamp = e.getTimestamp();
|
||||
this.query = e.getQuery();
|
||||
}
|
||||
|
||||
void toXML(StringBuilder sb) {
|
||||
sb.append("<SearchEntry>");
|
||||
sb.append("<Persona>").append(Util.escapeHTMLinXML(persona.getHumanReadableName())).append("</Persona>");
|
||||
sb.append("<Timestamp>").append(DataHelper.formatTime(timestamp)).append("</Timestamp>");
|
||||
sb.append("<Query>").append(Util.escapeHTMLinXML(query)).append("</Query>");
|
||||
sb.append("</SearchEntry>");
|
||||
}
|
||||
}
|
||||
|
||||
private static final Comparator<SearchEntry> BY_SEARCHER = (l, r) -> {
|
||||
return Collator.getInstance().compare(l.persona.getHumanReadableName(), r.persona.getHumanReadableName());
|
||||
};
|
||||
|
||||
private static final Comparator<SearchEntry> BY_TIMESTAMP = (l, r) -> {
|
||||
return Long.compare(l.timestamp, r.timestamp);
|
||||
};
|
||||
|
||||
private static final Comparator<SearchEntry> BY_QUERY = (l, r) -> {
|
||||
return Collator.getInstance().compare(l.query, r.query);
|
||||
};
|
||||
|
||||
private static final ColumnComparators<SearchEntry> SEARCH_ENTRY_COMPARATORS = new ColumnComparators<>();
|
||||
static {
|
||||
SEARCH_ENTRY_COMPARATORS.add("Searcher", BY_SEARCHER);
|
||||
SEARCH_ENTRY_COMPARATORS.add("Timestamp", BY_TIMESTAMP);
|
||||
SEARCH_ENTRY_COMPARATORS.add("Query", BY_QUERY);
|
||||
}
|
||||
|
||||
private static class DownloadEntry {
|
||||
private final String downloader;
|
||||
DownloadEntry(String downloader) {
|
||||
this.downloader = downloader;
|
||||
}
|
||||
|
||||
void toXML(StringBuilder sb) {
|
||||
sb.append("<Downloader>");
|
||||
sb.append("<Persona>").append(Util.escapeHTMLinXML(downloader)).append("</Persona>");
|
||||
sb.append("</Downloader>");
|
||||
}
|
||||
}
|
||||
|
||||
private static final Comparator<DownloadEntry> BY_DOWNLOADER = (l, r) -> {
|
||||
return Collator.getInstance().compare(l.downloader, r.downloader);
|
||||
};
|
||||
|
||||
private static final ColumnComparators<DownloadEntry> DOWNLOADER_COMPARATORS = new ColumnComparators<>();
|
||||
static {
|
||||
DOWNLOADER_COMPARATORS.add("Downloader", BY_DOWNLOADER);
|
||||
}
|
||||
|
||||
private static class CertificateEntry {
|
||||
private final Certificate certificate;
|
||||
CertificateEntry(Certificate certificate) {
|
||||
this.certificate = certificate;
|
||||
}
|
||||
|
||||
void toXML(StringBuilder sb) {
|
||||
sb.append("<Certificate>");
|
||||
sb.append("<Name>").append(Util.escapeHTMLinXML(certificate.getName().getName())).append("</Name>");
|
||||
if (certificate.getComment() != null)
|
||||
sb.append("<Comment>").append(Util.escapeHTMLinXML(certificate.getComment().getName())).append("</Comment>");
|
||||
sb.append("<Timestamp>").append(DataHelper.formatTime(certificate.getTimestamp())).append("</Timestamp>");
|
||||
sb.append("<Issuer>").append(Util.escapeHTMLinXML(certificate.getIssuer().getHumanReadableName())).append("</Issuer>");
|
||||
sb.append("</Certificate>");
|
||||
}
|
||||
}
|
||||
|
||||
private static final Comparator<CertificateEntry> CERT_BY_NAME = (l, r) -> {
|
||||
return Collator.getInstance().compare(l.certificate.getName().getName(), r.certificate.getName().getName());
|
||||
};
|
||||
|
||||
private static final Comparator<CertificateEntry> CERT_BY_TIMESTAMP = (l, r) -> {
|
||||
return Long.compare(l.certificate.getTimestamp(), r.certificate.getTimestamp());
|
||||
};
|
||||
|
||||
private static final Comparator<CertificateEntry> CERT_BY_ISSUER = (l, r) -> {
|
||||
return Collator.getInstance().compare(l.certificate.getIssuer().getHumanReadableName(), r.certificate.getIssuer().getHumanReadableName());
|
||||
};
|
||||
|
||||
private static final ColumnComparators<CertificateEntry> CERT_COMPARATORS = new ColumnComparators<>();
|
||||
static {
|
||||
CERT_COMPARATORS.add("Name", CERT_BY_NAME);
|
||||
CERT_COMPARATORS.add("Timestamp", CERT_BY_TIMESTAMP);
|
||||
CERT_COMPARATORS.add("Issuer", CERT_BY_ISSUER);
|
||||
}
|
||||
}
|
||||
@@ -108,11 +108,13 @@ public class Util {
|
||||
_x("Retry"),
|
||||
_x("Save"),
|
||||
_x("Search"),
|
||||
_x("Searcher"),
|
||||
_x("Sender"),
|
||||
_x("Senders"),
|
||||
_x("Shared Files"),
|
||||
_x("Sharing"),
|
||||
_x("Show Comment"),
|
||||
_x("Show Details"),
|
||||
_x("Size"),
|
||||
_x("Sources"),
|
||||
_x("Speed"),
|
||||
@@ -122,6 +124,7 @@ public class Util {
|
||||
_x("Subscribe"),
|
||||
_x("Subscribed"),
|
||||
_x("Times Browsed"),
|
||||
_x("Timestamp"),
|
||||
_x("Total Pieces"),
|
||||
_x("Trust"),
|
||||
_x("Trusted"),
|
||||
|
||||
119
webui/src/main/js/fileDetails.js
Normal file
119
webui/src/main/js/fileDetails.js
Normal file
@@ -0,0 +1,119 @@
|
||||
class SearchEntry {
|
||||
constructor(xmlNode) {
|
||||
this.persona = xmlNode.getElementsByTagName("Persona")[0].childNodes[0].nodeValue
|
||||
this.timestamp = xmlNode.getElementsByTagName("Timestamp")[0].childNodes[0].nodeValue
|
||||
this.query = xmlNode.getElementsByTagName("Query")[0].childNodes[0].nodeValue
|
||||
}
|
||||
|
||||
getMapping() {
|
||||
var mapping = new Map()
|
||||
mapping.set("Searcher", this.persona)
|
||||
mapping.set("Timestamp", this.timestamp)
|
||||
mapping.set("Query", this.query)
|
||||
return mapping
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadEntry {
|
||||
constructor(xmlNode) {
|
||||
this.persona = xmlNode.getElementsByTagName("Persona")[0].childNodes[0].nodeValue
|
||||
}
|
||||
|
||||
getMapping() {
|
||||
var mapping = new Map()
|
||||
mapping.set("Downloader", this.persona)
|
||||
return mapping
|
||||
}
|
||||
}
|
||||
|
||||
class CertificateEntry {
|
||||
constructor(xmlNode) {
|
||||
this.name = xmlNode.getElementsByTagName("Name")[0].childNodes[0].nodeValue
|
||||
this.timestamp = xmlNode.getElementsByTagName("Timestamp")[0].childNodes[0].nodeValue
|
||||
this.issuer = xmlNode.getElementsByTagName("Issuer")[0].childNodes[0].nodeValue
|
||||
try {
|
||||
this.comment = xmlNode.getElementsByTagName("Comment")[0].childNodes[0].nodeValue
|
||||
} catch (ignored) {
|
||||
this.comment = null
|
||||
}
|
||||
}
|
||||
|
||||
getMapping() {
|
||||
var mapping = new Map()
|
||||
// TODO: comments
|
||||
mapping.set("Name", this.name)
|
||||
mapping.set("Timestamp", this.timestamp)
|
||||
mapping.set("Issuer", this.issuer)
|
||||
return mapping
|
||||
}
|
||||
}
|
||||
|
||||
function initFileDetails() {
|
||||
setTimeout(refreshAll, 1)
|
||||
setInterval(refreshAll, 3000)
|
||||
}
|
||||
|
||||
function refreshAll() {
|
||||
refreshSearchers()
|
||||
refreshDownloaders()
|
||||
refreshCertificates()
|
||||
}
|
||||
|
||||
function refreshSearchers() {
|
||||
var xmlhttp = new XMLHttpRequest()
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
var searchers = []
|
||||
var searchNodes = this.responseXML.getElementsByTagName("SearchEntry")
|
||||
var i
|
||||
for (i = 0; i < searchNodes.length; i++) {
|
||||
searchers.push(new SearchEntry(searchNodes[i]))
|
||||
}
|
||||
|
||||
var newOrder
|
||||
if (searchersSortOrder == "descending")
|
||||
newOrder = "ascending"
|
||||
else if (searchersSortOrder == "ascending")
|
||||
newOrder = "descending"
|
||||
var table = new Table(["Searcher", "Timestamp", "Query"], "sortSearchers", searchersSortKey, newOrder, null)
|
||||
|
||||
for (i = 0; i < searchers.length; i++) {
|
||||
table.addRow(searchers[i].getMapping())
|
||||
}
|
||||
|
||||
var hitsDiv = document.getElementById("hitsTable")
|
||||
if (searchNodes.length > 0)
|
||||
hitsDiv.innerHTML = table.render()
|
||||
else
|
||||
hitsDiv.innerHTML = ""
|
||||
}
|
||||
}
|
||||
var sortParam = "&key=" + searchersSortKey + "&order=" + searchersSortOrder
|
||||
xmlhttp.open("GET", encodeURI("/MuWire/FileInfo?path=" + path + "§ion=searchers" + sortParam))
|
||||
xmlhttp.send()
|
||||
}
|
||||
|
||||
function refreshDownloaders() {
|
||||
|
||||
}
|
||||
|
||||
function refreshCertificates() {
|
||||
|
||||
}
|
||||
|
||||
function sortSearchers(key, order) {
|
||||
searchersSortKey = key
|
||||
searchersSortOrder = order
|
||||
refreshSearchers()
|
||||
}
|
||||
|
||||
var path = null
|
||||
|
||||
var expandedComments = new Map()
|
||||
|
||||
var searchersSortKey = "searcher"
|
||||
var searchersSortOrder = "descending"
|
||||
var downloadersSortKey = "downloader"
|
||||
var downloadersSortOrder = "descending"
|
||||
var certificatesSortKey = "name"
|
||||
var certificatesSortOrder = "descending"
|
||||
@@ -49,10 +49,12 @@ class Node {
|
||||
var infoHashTextArea = "<textarea class='copypaste' readOnly='true' id='" + this.infoHash + "'>" + this.infoHash + "</textarea>"
|
||||
var copyInfoHashLink = new Link(_t("Copy hash to clipboard"), "copyAndAlert", [this.infoHash, _t("Hash copied to clipboard")])
|
||||
|
||||
var detailsLink = "<a href='/MuWire/FileDetails?path=" + encodeURI(encodedPathToRoot(this)) + "'>" + _t("Show Details") + "</a>"
|
||||
|
||||
var nameLink = "<a href='/MuWire/DownloadedContent/" + this.infoHash + "'>" + this.path + "</a>"
|
||||
var html = "<li class='fileTree'>" + nameLink + infoHashTextArea
|
||||
html += "<div class='right'>" + certified + " " + published + " <div class='dropdown'>" + actionsLink + "<div class='dropdown-content'>"
|
||||
html += copyInfoHashLink.render() + unshareLink + commentLink + certifyLink + publish.render()
|
||||
html += copyInfoHashLink.render() + unshareLink + commentLink + certifyLink + publish.render() + detailsLink
|
||||
html += "</div></div></div>"
|
||||
html += "<div class='centercomment' id='comment-" + this.nodeId + "'></div>"
|
||||
html += "</li>"
|
||||
|
||||
58
webui/src/main/webapp/FileDetails.jsp
Normal file
58
webui/src/main/webapp/FileDetails.jsp
Normal file
@@ -0,0 +1,58 @@
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<%@ page import="com.muwire.webui.*" %>
|
||||
<%@ page import="java.io.*" %>
|
||||
<%@include file="initcode.jsi"%>
|
||||
|
||||
<%
|
||||
|
||||
String pagetitle=Util._t("File Details");
|
||||
|
||||
String path = request.getParameter("path");
|
||||
File file = Util.getFromPathElements(path);
|
||||
|
||||
%>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<%@ include file="css.jsi"%>
|
||||
<script src="js/util.js?<%=version%>" type="text/javascript"></script>
|
||||
<script src="js/tables.js?<%=version%> type="text/javascript"></script>
|
||||
<script src="js/fileDetails.js?<%=version%>" type="text/javascript"></script>
|
||||
|
||||
<script>
|
||||
path="<%=path%>"
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body onload="initTranslate(jsTranslations); initConnectionsCount(); initFileDetails();">
|
||||
<%@ include file="header.jsi"%>
|
||||
<aside>
|
||||
<div class="menubox-divider"></div>
|
||||
<%@include file="sidebar.jsi"%>
|
||||
</aside>
|
||||
<section class="main foldermain">
|
||||
<h2><%=Util._t("Details for {0}", file.getAbsolutePath())%></h2>
|
||||
<h3><%=Util._t("Search Hits")%></h3>
|
||||
<div id="table-wrapper">
|
||||
<div id="table-scroll">
|
||||
<div id="hitsTable"></div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<h3><%=Util._t("Downloaders")%></h3>
|
||||
<div id="table-wrapper">
|
||||
<div id="table-scroll">
|
||||
<div id="downloadersTable"></div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<h3><%=Util._t("Certificates")%></h3>
|
||||
<div id="table-wrapper">
|
||||
<div id="table-scroll">
|
||||
<div id="certificatesTable"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
@@ -76,6 +76,11 @@
|
||||
<servlet-class>com.muwire.webui.StatusServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>com.muwire.webui.FileInfoServlet</servlet-name>
|
||||
<servlet-class>com.muwire.webui.FileInfoServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>com.muwire.webui.MuWireServlet</servlet-name>
|
||||
<url-pattern>/index.jsp</url-pattern>
|
||||
@@ -141,6 +146,11 @@
|
||||
<url-pattern>/Status</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>com.muwire.webui.FileInfoServlet</servlet-name>
|
||||
<url-pattern>/FileInfo</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
__JASPER__
|
||||
|
||||
<!--
|
||||
@@ -218,4 +228,9 @@ Mappings without the .jsp suffix
|
||||
<url-pattern>/MuStatus</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>com.muwire.webui.FileDetails_jsp</servlet-name>
|
||||
<url-pattern>/FileDetails</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
||||
|
||||
Reference in New Issue
Block a user