WIP on file details page

This commit is contained in:
Zlatin Balevsky
2020-03-22 02:13:23 +00:00
parent c77b848d44
commit e3c5fe291d
6 changed files with 389 additions and 1 deletions

View 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);
}
}

View File

@@ -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"),

View 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 + "&section=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"

View File

@@ -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>"

View 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>

View File

@@ -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>