forked from I2P_Developers/i2p.i2p
I2PSnark UI bugfixes
- Fix broken collapsible panels issue for browsers that don't support the feature by conditionally loading override CSS to expand panels by default and disable hover/active states for panel headings (tickets #2002, #2026) - Add UI option to configuration section to enable collapsible panels, and disable the option if a non-compliant browser is detected - Fix multiple instances of snark refreshing to the homepage (ticket #2028) (patch supplied by mindless) - Tentative fix for caching of images so ajax refresh doesn't reload all image resources - Standardize 'Save Configuration' action to return to top of the page (so we can see message log entry)
This commit is contained in:
@ -65,6 +65,7 @@ public class I2PSnarkUtil {
|
||||
private int _maxConnections;
|
||||
private final File _tmpDir;
|
||||
private int _startupDelay;
|
||||
private boolean _collapsePanels;
|
||||
private boolean _shouldUseOT;
|
||||
private boolean _shouldUseDHT;
|
||||
private boolean _enableRatings, _enableComments;
|
||||
@ -77,6 +78,7 @@ public class I2PSnarkUtil {
|
||||
private static final int EEPGET_CONNECT_TIMEOUT = 45*1000;
|
||||
private static final int EEPGET_CONNECT_TIMEOUT_SHORT = 5*1000;
|
||||
public static final int DEFAULT_STARTUP_DELAY = 3;
|
||||
public static final boolean DEFAULT_COLLAPSE_PANELS = true;
|
||||
public static final boolean DEFAULT_USE_OPENTRACKERS = true;
|
||||
public static final int MAX_CONNECTIONS = 24; // per torrent
|
||||
public static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
|
||||
@ -106,6 +108,7 @@ public class I2PSnarkUtil {
|
||||
_shouldUseOT = DEFAULT_USE_OPENTRACKERS;
|
||||
_openTrackers = Collections.emptyList();
|
||||
_shouldUseDHT = DEFAULT_USE_DHT;
|
||||
_collapsePanels = DEFAULT_COLLAPSE_PANELS;
|
||||
_enableRatings = _enableComments = true;
|
||||
_commentsName = "";
|
||||
// This is used for both announce replies and .torrent file downloads,
|
||||
@ -687,6 +690,16 @@ public class I2PSnarkUtil {
|
||||
return _enableRatings || _enableComments;
|
||||
}
|
||||
|
||||
/** @since 0.9.32 */
|
||||
public boolean collapsePanels() {
|
||||
return _collapsePanels;
|
||||
}
|
||||
|
||||
/** @since 0.9.32 */
|
||||
public void setCollapsePanels(boolean yes) {
|
||||
_collapsePanels = yes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like DataHelper.toHexString but ensures no loss of leading zero bytes
|
||||
* @since 0.8.4
|
||||
|
@ -131,6 +131,8 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
public static final String RC_PROP_UNIVERSAL_THEMING = "routerconsole.universal.theme";
|
||||
public static final String PROP_THEME = "i2psnark.theme";
|
||||
public static final String DEFAULT_THEME = "ubergine";
|
||||
/** @since 0.9.32 */
|
||||
public static final String PROP_COLLAPSE_PANELS = "i2psnark.collapsePanels";
|
||||
private static final String PROP_USE_OPENTRACKERS = "i2psnark.useOpentrackers";
|
||||
public static final String PROP_OPENTRACKERS = "i2psnark.opentrackers";
|
||||
public static final String PROP_PRIVATETRACKERS = "i2psnark.privatetrackers";
|
||||
@ -454,6 +456,17 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
return Boolean.parseBoolean(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return default true
|
||||
* @since 0.9.32
|
||||
*/
|
||||
public boolean isCollapsePanelsEnabled() {
|
||||
String val = _config.getProperty(PROP_COLLAPSE_PANELS);
|
||||
if (val == null)
|
||||
return I2PSnarkUtil.DEFAULT_COLLAPSE_PANELS;
|
||||
return Boolean.parseBoolean(val);
|
||||
}
|
||||
|
||||
/****
|
||||
public String linkPrefix() {
|
||||
return _config.getProperty(PROP_LINK_PREFIX, DEFAULT_LINK_PREFIX + getDataDir().getAbsolutePath() + File.separatorChar);
|
||||
@ -798,6 +811,9 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
_config.setProperty(PROP_COMMENTS, "true");
|
||||
if (!_config.containsKey(PROP_COMMENTS_NAME))
|
||||
_config.setProperty(PROP_COMMENTS_NAME, "");
|
||||
if (!_config.containsKey(PROP_COLLAPSE_PANELS))
|
||||
_config.setProperty(PROP_COLLAPSE_PANELS,
|
||||
Boolean.toString(I2PSnarkUtil.DEFAULT_COLLAPSE_PANELS));
|
||||
updateConfig();
|
||||
}
|
||||
|
||||
@ -871,7 +887,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
_util.setMaxUpBW(limits[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updateConfig() {
|
||||
String i2cpHost = _config.getProperty(PROP_I2CP_HOST);
|
||||
int i2cpPort = getInt(PROP_I2CP_PORT, 7654);
|
||||
@ -908,6 +924,8 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
_util.setRatingsEnabled(Boolean.parseBoolean(_config.getProperty(PROP_RATINGS, "true")));
|
||||
_util.setCommentsEnabled(Boolean.parseBoolean(_config.getProperty(PROP_COMMENTS, "true")));
|
||||
_util.setCommentsName(_config.getProperty(PROP_COMMENTS_NAME, ""));
|
||||
_util.setCollapsePanels(Boolean.parseBoolean(_config.getProperty(PROP_COLLAPSE_PANELS,
|
||||
Boolean.toString(I2PSnarkUtil.DEFAULT_COLLAPSE_PANELS))));
|
||||
File dd = getDataDir();
|
||||
if (dd.isDirectory()) {
|
||||
if (!dd.canWrite())
|
||||
@ -918,7 +936,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
}
|
||||
initTrackerMap();
|
||||
}
|
||||
|
||||
|
||||
private int getInt(String prop, int defaultVal) {
|
||||
String p = _config.getProperty(prop);
|
||||
try {
|
||||
@ -929,29 +947,29 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
}
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* all params may be null or need trimming
|
||||
*/
|
||||
public void updateConfig(String dataDir, boolean filesPublic, boolean autoStart, boolean smartSort, String refreshDelay,
|
||||
String startDelay, String pageSize, String seedPct, String eepHost,
|
||||
String startDelay, String pageSize, String seedPct, String eepHost,
|
||||
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
|
||||
String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme,
|
||||
String lang, boolean enableRatings, boolean enableComments, String commentName) {
|
||||
String lang, boolean enableRatings, boolean enableComments, String commentName, boolean collapsePanels) {
|
||||
synchronized(_configLock) {
|
||||
locked_updateConfig(dataDir, filesPublic, autoStart, smartSort,refreshDelay,
|
||||
startDelay, pageSize, seedPct, eepHost,
|
||||
eepPort, i2cpHost, i2cpPort, i2cpOpts,
|
||||
upLimit, upBW, useOpenTrackers, useDHT, theme,
|
||||
lang, enableRatings, enableComments, commentName);
|
||||
locked_updateConfig(dataDir, filesPublic, autoStart, smartSort, refreshDelay,
|
||||
startDelay, pageSize, seedPct, eepHost,
|
||||
eepPort, i2cpHost, i2cpPort, i2cpOpts,
|
||||
upLimit, upBW, useOpenTrackers, useDHT, theme,
|
||||
lang, enableRatings, enableComments, commentName, collapsePanels);
|
||||
}
|
||||
}
|
||||
|
||||
private void locked_updateConfig(String dataDir, boolean filesPublic, boolean autoStart, boolean smartSort, String refreshDelay,
|
||||
String startDelay, String pageSize, String seedPct, String eepHost,
|
||||
String startDelay, String pageSize, String seedPct, String eepHost,
|
||||
String eepPort, String i2cpHost, String i2cpPort, String i2cpOpts,
|
||||
String upLimit, String upBW, boolean useOpenTrackers, boolean useDHT, String theme,
|
||||
String lang, boolean enableRatings, boolean enableComments, String commentName) {
|
||||
String lang, boolean enableRatings, boolean enableComments, String commentName, boolean collapsePanels) {
|
||||
boolean changed = false;
|
||||
boolean interruptMonitor = false;
|
||||
//if (eepHost != null) {
|
||||
@ -996,7 +1014,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (startDelay != null && _context.isRouterContext()) {
|
||||
int minutes = _util.getStartupDelay();
|
||||
try { minutes = Integer.parseInt(startDelay.trim()); } catch (NumberFormatException nfe) {}
|
||||
@ -1115,7 +1133,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
if (split > 0)
|
||||
oldOpts.put(pair.substring(0, split), pair.substring(split+1));
|
||||
}
|
||||
|
||||
|
||||
boolean reconnect = i2cpHost != null && i2cpHost.trim().length() > 0 && port > 0 &&
|
||||
(port != _util.getI2CPPort() || !oldI2CPHost.equals(i2cpHost));
|
||||
if (reconnect || !oldOpts.equals(opts)) {
|
||||
@ -1262,6 +1280,15 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (_util.collapsePanels() != collapsePanels) {
|
||||
_config.setProperty(PROP_COLLAPSE_PANELS, Boolean.toString(collapsePanels));
|
||||
if (collapsePanels)
|
||||
addMessage(_t("Collapsible panels enabled."));
|
||||
else
|
||||
addMessage(_t("Collapsible panels disabled."));
|
||||
_util.setCollapsePanels(collapsePanels);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
saveConfig();
|
||||
if (interruptMonitor)
|
||||
@ -1485,7 +1512,7 @@ public class SnarkManager implements CompleteListener, ClientApp {
|
||||
fis.close();
|
||||
fis = null;
|
||||
} catch (IOException e) {}
|
||||
|
||||
|
||||
// This test may be a duplicate, but not if we were called
|
||||
// from the DirMonitor, which only checks for dup torrent file names.
|
||||
Snark snark = getTorrentByInfoHash(info.getInfoHash());
|
||||
|
@ -268,6 +268,9 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean noCollapse = noCollapsePanels(req);
|
||||
boolean collapsePanels = _manager.util().collapsePanels();
|
||||
|
||||
setHTMLHeaders(resp);
|
||||
PrintWriter out = resp.getWriter();
|
||||
out.write(DOCTYPE + "<html>\n" +
|
||||
@ -295,7 +298,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
String jsPfx = _context.isRouterContext() ? "" : ".resources";
|
||||
String downMsg = _context.isRouterContext() ? _t("Router is down") : _t("I2PSnark has stopped");
|
||||
// fallback to metarefresh when javascript is disabled
|
||||
out.write("<noscript><meta http-equiv=\"refresh\" content=\"" + delay + ";/i2psnark/" + peerString + "\"></noscript>\n");
|
||||
out.write("<noscript><meta http-equiv=\"refresh\" content=\"" + delay + ";" + _contextPath + "/" + peerString + "\"></noscript>\n");
|
||||
out.write("<script src=\"" + jsPfx + "/js/ajax.js\" type=\"text/javascript\"></script>\n" +
|
||||
"<script type=\"text/javascript\">\n" +
|
||||
"var failMessage = \"<div class=\\\"routerdown\\\"><b>" + downMsg + "<\\/b><\\/div>\";\n" +
|
||||
@ -306,7 +309,14 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
"</script>\n");
|
||||
}
|
||||
}
|
||||
out.write(HEADER_A + _themePath + HEADER_B + "</head>\n");
|
||||
out.write(HEADER_A + _themePath + HEADER_B);
|
||||
|
||||
// ...and inject CSS to display panels uncollapsed
|
||||
if (noCollapse || !collapsePanels) {
|
||||
out.write(HEADER_A + _themePath + HEADER_C);
|
||||
}
|
||||
out.write("</head>\n");
|
||||
|
||||
if (isConfigure || delay <= 0)
|
||||
out.write("<body>");
|
||||
else
|
||||
@ -381,9 +391,10 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
private static void setHTMLHeaders(HttpServletResponse resp) {
|
||||
resp.setCharacterEncoding("UTF-8");
|
||||
resp.setContentType("text/html; charset=UTF-8");
|
||||
resp.setHeader("Cache-Control", "no-store, max-age=0, no-cache, must-revalidate");
|
||||
// "no-store, max-age=0" forces all our images to be reloaded on ajax refresh
|
||||
resp.setHeader("Cache-Control", "max-age=86400, no-cache, must-revalidate");
|
||||
resp.setHeader("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'");
|
||||
resp.setDateHeader("Expires", 0);
|
||||
resp.setDateHeader("Expires", 86400);
|
||||
resp.setHeader("Pragma", "no-cache");
|
||||
resp.setHeader("X-Frame-Options", "SAMEORIGIN");
|
||||
resp.setHeader("X-XSS-Protection", "1; mode=block");
|
||||
@ -1190,10 +1201,11 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
boolean comments = req.getParameter("comments") != null;
|
||||
// commentsName is filtered in SnarkManager.updateConfig()
|
||||
String commentsName = req.getParameter("nofilter_commentsName");
|
||||
boolean collapsePanels = req.getParameter("collapsePanels") != null;
|
||||
_manager.updateConfig(dataDir, filesPublic, autoStart, smartSort, refreshDel, startupDel, pageSize,
|
||||
seedPct, eepHost, eepPort, i2cpHost, i2cpPort, i2cpOpts,
|
||||
upLimit, upBW, useOpenTrackers, useDHT, theme,
|
||||
lang, ratings, comments, commentsName);
|
||||
lang, ratings, comments, commentsName, collapsePanels);
|
||||
// update servlet
|
||||
try {
|
||||
setResourceBase(_manager.getDataDir());
|
||||
@ -2271,9 +2283,12 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
boolean useDHT = _manager.util().shouldUseDHT();
|
||||
boolean useRatings = _manager.util().ratingsEnabled();
|
||||
boolean useComments = _manager.util().commentsEnabled();
|
||||
boolean collapsePanels = _manager.util().collapsePanels();
|
||||
//int seedPct = 0;
|
||||
|
||||
out.write("<form action=\"" + _contextPath + "/configure\" method=\"POST\">\n" +
|
||||
boolean noCollapse = noCollapsePanels(req);
|
||||
|
||||
out.write("<form action=\"" + _contextPath + "/configure\" method=\"POST\" target=\"_top\">\n" +
|
||||
"<div class=\"configsectionpanel\"><div class=\"snarkConfig\">\n");
|
||||
writeHiddenInputs(out, req, "Save");
|
||||
out.write("<span class=\"snarkConfigTitle\">");
|
||||
@ -2309,10 +2324,23 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
"<tr><td><label for=\"smartSort\">");
|
||||
out.write(_t("Smart torrent sorting"));
|
||||
out.write(":</label><td colspan=\"2\"><input type=\"checkbox\" class=\"optbox\" name=\"smartSort\" id=\"smartSort\" value=\"true\" "
|
||||
+ (smartSort ? "checked " : "")
|
||||
+ (smartSort ? "checked " : "")
|
||||
+ "title=\"");
|
||||
out.write(_t("Ignore words such as 'a' and 'the' when sorting"));
|
||||
out.write("\" >");
|
||||
out.write("\" >" +
|
||||
|
||||
"<tr><td><label for=\"collapsePanels\">");
|
||||
out.write(_t("Collapsible panels"));
|
||||
out.write(":</label><td colspan=\"2\"><input type=\"checkbox\" class=\"optbox\" name=\"collapsePanels\" id=\"collapsePanels\" value=\"true\" "
|
||||
+ (collapsePanels ? "checked " : "")
|
||||
+ "title=\"");
|
||||
if (noCollapse) {
|
||||
out.write(_t("Your browser does not support this feature."));
|
||||
out.write("\" disabled=\"disabled");
|
||||
} else {
|
||||
out.write(_t("Allow the 'Add Torrent' and 'Create Torrent' panels to be collapsed, and collapse by default in non-embedded mode"));
|
||||
}
|
||||
out.write("\">");
|
||||
|
||||
if (!_context.isRouterContext()) {
|
||||
try {
|
||||
@ -2744,7 +2772,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
buf.append("<a href=\"").append(link).append("\">").append(display).append("</a>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is for a full URL. For a path only, use encodePath().
|
||||
* @since 0.8.13
|
||||
@ -2758,7 +2786,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
private static final String DOCTYPE = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
|
||||
private static final String HEADER_A = "<link href=\"";
|
||||
private static final String HEADER_B = "snark.css?" + CoreVersion.VERSION + "\" rel=\"stylesheet\" type=\"text/css\" >";
|
||||
|
||||
private static final String HEADER_C = "nocollapse.css?" + CoreVersion.VERSION + "\" rel=\"stylesheet\" type=\"text/css\" >";
|
||||
|
||||
private static final String TABLE_HEADER = "<table border=\"0\" class=\"snarkTorrents\" width=\"100%\" >\n" +
|
||||
"<thead>\n";
|
||||
@ -3851,6 +3879,14 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
_manager.setSavedCommentsEnabled(snark, yes);
|
||||
}
|
||||
|
||||
private static boolean noCollapsePanels(HttpServletRequest req) {
|
||||
// check for user agents that can't toggle the collapsible panels...
|
||||
String ua = req.getHeader("user-agent");
|
||||
return ua != null && (ua.contains("Konq") || ua.contains("konq") ||
|
||||
ua.contains("Qupzilla") || ua.contains("Dillo") ||
|
||||
ua.contains("Netsurf") || ua.contains("Midori"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is "a" equal to "b",
|
||||
* or is "a" a directory and a parent of file or directory "b",
|
||||
|
Reference in New Issue
Block a user