* Naming services, addressbook, susidns:

- Fix search capability
    - Fix result count and view within results
    - Fix published address book
    - Fix ngettext
    - Cache size
    - Fix 0-9 filter
    - Addressbook updates via API, except for published
This commit is contained in:
zzz
2011-03-15 21:52:48 +00:00
parent 8b737b4adb
commit 12c5b9c21c
7 changed files with 338 additions and 76 deletions

View File

@@ -79,6 +79,7 @@ public class AddressbookBean
{
return addressbook != null && !addressbook.isEmpty();
}
public AddressbookBean()
{
properties = new Properties();
@@ -86,9 +87,11 @@ public class AddressbookBean
beginIndex = 0;
endIndex = DISPLAY_SIZE - 1;
}
private long configLastLoaded = 0;
private static final String PRIVATE_BOOK = "private_addressbook";
private static final String DEFAULT_PRIVATE_BOOK = "../privatehosts.txt";
protected void loadConfig()
{
long currentTime = System.currentTimeMillis();
@@ -113,6 +116,7 @@ public class AddressbookBean
try { fis.close(); } catch (IOException ioe) {}
}
}
public String getFileName()
{
loadConfig();
@@ -144,7 +148,7 @@ public class AddressbookBean
book.compareToIgnoreCase( "router" ) != 0 &&
book.compareToIgnoreCase( "private" ) != 0 &&
book.compareToIgnoreCase( "published" ) != 0 ))
book = "master";
book = "router";
return book;
}
@@ -215,40 +219,49 @@ public class AddressbookBean
* addressbook.jsp catches the case where the whole book is empty.
*/
protected String generateLoadMessage() {
String message = "";
String message;
String filterArg = "";
if( search != null && search.length() > 0 ) {
message = _("Search") + ' ';
}
int resultCount = resultSize();
if( filter != null && filter.length() > 0 ) {
if( search != null && search.length() > 0 )
message = _("Search within filtered list") + ' ';
message = ngettext("One result for search within filtered list.",
"{0} results for search within filtered list.",
resultCount);
else
message = _("Filtered list") + ' ';
message = ngettext("Filtered list contains 1 entry.",
"Fltered list contains {0} entries.",
resultCount);
filterArg = "&filter=" + filter;
}
if (entries.length == 0) {
message += "- " + _("no matches") + '.';
} else if (getBeginInt() == 0 && getEndInt() == entries.length - 1) {
if (message.length() == 0)
message = _("Addressbook") + ' ';
if (entries.length <= 0)
message += _("contains no entries");
} else if( search != null && search.length() > 0 ) {
message = ngettext("One result for search.",
"{0} results for search.",
resultCount);
} else {
if (resultCount <= 0)
// covered in jsp
//message = _("This addressbook is empty.");
message = "";
else
message += _(entries.length, "contains 1 entry", "contains {0} entries");
message += '.';
message = ngettext("Addressbook contains 1 entry.",
"Addressbook contains {0} entries.",
resultCount);
}
if (resultCount <= 0) {
// nothing to display
} else if (getBeginInt() == 0 && getEndInt() == resultCount - 1) {
// nothing to display
} else {
if (getBeginInt() > 0) {
int newBegin = Math.max(0, getBeginInt() - DISPLAY_SIZE);
int newEnd = Math.max(0, getBeginInt() - 1);
message += "<a href=\"addressbook.jsp?book=" + getBook() + filterArg +
message += " <a href=\"addressbook.jsp?book=" + getBook() + filterArg +
"&amp;begin=" + newBegin + "&amp;end=" + newEnd + "\">" + newBegin +
'-' + newEnd + "</a> | ";
}
message += _("Showing {0} of {1}", "" + getBegin() + '-' + getEnd(), entries.length);
if (getEndInt() < entries.length - 1) {
int newBegin = Math.min(entries.length - 1, getEndInt() + 1);
int newEnd = Math.min(entries.length, getEndInt() + DISPLAY_SIZE);
message += ' ' + _("Showing {0} of {1}", "" + getBegin() + '-' + getEnd(), Integer.valueOf(resultCount));
if (getEndInt() < resultCount - 1) {
int newBegin = Math.min(resultCount - 1, getEndInt() + 1);
int newEnd = Math.min(resultCount, getEndInt() + DISPLAY_SIZE);
message += " | <a href=\"addressbook.jsp?book=" + getBook() + filterArg +
"&amp;begin=" + newBegin + "&amp;end=" + newEnd + "\">" + newBegin +
'-' + newEnd + "</a>";
@@ -313,7 +326,8 @@ public class AddressbookBean
if (deleted == 1)
message = _("Destination {0} deleted.", name);
else
message = _("{0} destinations deleted.", deleted);
// parameter will always be >= 2
message = ngettext("1 destination deleted.", "{0} destinations deleted.", deleted);
}
}
if( changed ) {
@@ -394,29 +408,76 @@ public class AddressbookBean
public void setHostname(String hostname) {
this.hostname = DataHelper.stripHTML(hostname).trim(); // XSS
}
protected int getBeginInt() {
return Math.max(0, Math.min(entries.length - 1, beginIndex));
return Math.max(0, Math.min(resultSize() - 1, beginIndex));
}
public String getBegin() {
return "" + getBeginInt();
}
/**
* @return beginning index into results
* @since 0.8.6
*/
public String getResultBegin() {
return isPrefiltered() ? "0" : Integer.toString(getBeginInt());
}
public void setBegin(String s) {
try {
beginIndex = Integer.parseInt(s);
} catch (NumberFormatException nfe) {}
}
protected int getEndInt() {
return Math.max(0, Math.max(getBeginInt(), Math.min(entries.length - 1, endIndex)));
return Math.max(0, Math.max(getBeginInt(), Math.min(resultSize() - 1, endIndex)));
}
public String getEnd() {
return "" + getEndInt();
}
/**
* @return ending index into results
* @since 0.8.6
*/
public String getResultEnd() {
return Integer.toString(isPrefiltered() ? resultSize() - 1 : getEndInt());
}
public void setEnd(String s) {
try {
endIndex = Integer.parseInt(s);
} catch (NumberFormatException nfe) {}
}
/**
* Does the entries map contain only the lookup result,
* or must we index into it?
* @since 0.8.6
*/
protected boolean isPrefiltered() {
return false;
}
/**
* @return the size of the lookup result
* @since 0.8.6
*/
protected int resultSize() {
return entries.length;
}
/**
* @return the total size of the address book
* @since 0.8.6
*/
protected int totalSize() {
return entries.length;
}
/** translate */
protected static String _(String s) {
return Messages.getString(s);
@@ -433,7 +494,7 @@ public class AddressbookBean
}
/** translate (ngettext) @since 0.8.6 */
protected static String _(int n, String s, String p) {
protected static String ngettext(String s, String p, int n) {
return Messages.getString(n, s, p);
}
}

View File

@@ -37,24 +37,58 @@ import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
/**
* Talk to the NamingService API instead of modifying the hosts.txt files directly
* Talk to the NamingService API instead of modifying the hosts.txt files directly,
* except for the 'published' addressbook.
*
* @since 0.8.5
* @since 0.8.6
*/
public class NamingServiceBean extends AddressbookBean
{
private static final String DEFAULT_NS = "BlockfileNamingService";
private boolean isDirect() {
return getBook().equals("published");
}
@Override
protected boolean isPrefiltered() {
if (isDirect())
return super.isPrefiltered();
return (search == null || search.length() <= 0) &&
(filter == null || filter.length() <= 0);
// and right naming service...
}
@Override
protected int resultSize() {
if (isDirect())
return super.resultSize();
return isPrefiltered() ? totalSize() : entries.length;
}
@Override
protected int totalSize() {
if (isDirect())
return super.totalSize();
// only blockfile needs the list property
Properties props = new Properties();
props.setProperty("list", getFileName());
return getNamingService().size(props);
}
@Override
public boolean isNotEmpty()
{
return getNamingService().size() > 0;
if (isDirect())
return super.isNotEmpty();
return totalSize() > 0;
}
@Override
public String getFileName()
{
if (isDirect())
return super.getFileName();
loadConfig();
String filename = properties.getProperty( getBook() + "_addressbook" );
int slash = filename.lastIndexOf('/');
@@ -64,7 +98,7 @@ public class NamingServiceBean extends AddressbookBean
}
/** depth-first search */
private NamingService searchNamingService(NamingService ns, String srch)
private static NamingService searchNamingService(NamingService ns, String srch)
{
String name = ns.getName();
if (name == srch || name == DEFAULT_NS)
@@ -88,10 +122,16 @@ public class NamingServiceBean extends AddressbookBean
return rv != null ? rv : root;
}
/** Load addressbook and apply filter, returning messages about this. */
/**
* Load addressbook and apply filter, returning messages about this.
* To control memory, don't load the whole addressbook if we can help it...
* only load what is searched for.
*/
@Override
public String getLoadBookMessages()
{
if (isDirect())
return super.getLoadBookMessages();
NamingService service = getNamingService();
Debug.debug("Searching within " + service + " with filename=" + getFileName() + " and with filter=" + filter + " and with search=" + search);
String message = "";
@@ -100,16 +140,22 @@ public class NamingServiceBean extends AddressbookBean
Map<String, Destination> results;
Properties searchProps = new Properties();
// only blockfile needs this
searchProps.setProperty("list", getFileName());
searchProps.setProperty("list", getFileName());
if (filter != null) {
String startsAt = filter == "0-9" ? "0" : filter;
String startsAt = filter.equals("0-9") ? "[0-9]" : filter;
searchProps.setProperty("startsWith", startsAt);
}
if (beginIndex > 0)
searchProps.setProperty("skip", Integer.toString(beginIndex));
int limit = 1 + endIndex - beginIndex;
if (limit > 0)
searchProps.setProperty("limit", Integer.toString(limit));
if (isPrefiltered()) {
// Only limit if we not searching or filtering, so we will
// know the total number of results
if (beginIndex > 0)
searchProps.setProperty("skip", Integer.toString(beginIndex));
int limit = 1 + endIndex - beginIndex;
if (limit > 0)
searchProps.setProperty("limit", Integer.toString(limit));
}
if (search != null && search.length() > 0)
searchProps.setProperty("search", search.toLowerCase());
results = service.getEntries(searchProps);
Debug.debug("Result count: " + results.size());
@@ -151,6 +197,8 @@ public class NamingServiceBean extends AddressbookBean
@Override
public String getMessages()
{
if (isDirect())
return super.getMessages();
// Loading config and addressbook moved into getLoadBookMessages()
String message = "";
@@ -168,23 +216,22 @@ public class NamingServiceBean extends AddressbookBean
} else if (oldDest != null && !action.equals(_("Replace"))) {
message = _("Host name {0} is already in addressbook with a different destination. Click \"Replace\" to overwrite.", hostname);
} else {
boolean valid = true;
try {
Destination dest = new Destination(destination);
getNamingService().put(hostname, dest, nsOptions);
boolean success = getNamingService().put(hostname, dest, nsOptions);
if (success) {
changed = true;
if (oldDest == null)
message = _("Destination added for {0}.", hostname);
else
message = _("Destination changed for {0}.", hostname);
// clear form
hostname = null;
destination = null;
} else {
message = _("Failed to add Destination for {0} to naming service {1}", hostname, getNamingService()) + "<br>";
}
} catch (DataFormatException dfe) {
valid = false;
}
if (valid) {
changed = true;
if (oldDest == null)
message = _("Destination added for {0}.", hostname);
else
message = _("Destination changed for {0}.", hostname);
// clear form
hostname = null;
destination = null;
} else {
message = _("Invalid Base 64 destination.");
}
}
@@ -197,17 +244,20 @@ public class NamingServiceBean extends AddressbookBean
String name = null;
int deleted = 0;
for (String n : deletionMarks) {
getNamingService().remove(n, nsOptions);
if (deleted++ == 0) {
boolean success = getNamingService().remove(n, nsOptions);
if (!success) {
message += _("Failed to delete Destination for {0} from naming service {1}", name, getNamingService()) + "<br>";
} else if (deleted++ == 0) {
changed = true;
name = n;
}
}
if( changed ) {
if (deleted == 1)
message = _("Destination {0} deleted.", name);
message += _("Destination {0} deleted.", name);
else
message = _("{0} destinations deleted.", deleted);
// parameter will always be >= 2
message = ngettext("1 destination deleted.", "{0} destinations deleted.", deleted);
}
}
if( changed ) {

View File

@@ -144,7 +144,7 @@ ${book.loadBookMessages}
<th><%=intl._("Destination")%></th>
</tr>
<!-- limit iterator, or "Form too large" may result on submit, and is a huge web page if we don't -->
<c:forEach items="${book.entries}" var="addr" begin="${book.begin}" end="${book.end}">
<c:forEach items="${book.entries}" var="addr" begin="${book.resultBegin}" end="${book.resultEnd}">
<tr class="list${book.trClass}">
<c:if test="${book.master || book.router || book.published || book.private}">
<td class="checkbox"><input type="checkbox" name="checked" value="${addr.name}" title="<%=intl._("Mark for deletion")%>"></td>