/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */
package freenet.clients.http.bookmark;
import java.net.MalformedURLException;
import freenet.client.async.ClientGetter;
import freenet.keys.FreenetURI;
import freenet.keys.USK;
import freenet.l10n.NodeL10n;
import freenet.node.FSParseException;
import freenet.node.NodeClientCore;
import freenet.node.useralerts.AbstractUserAlert;
import freenet.node.useralerts.UserAlert;
import freenet.node.useralerts.UserAlertManager;
import freenet.support.HTMLNode;
import freenet.support.Logger;
import freenet.support.SimpleFieldSet;
public class BookmarkItem extends Bookmark {
public static final String NAME = "Bookmark";
private final BookmarkManager manager;
private FreenetURI key;
private boolean updated;
private boolean hasAnActivelink = false;
private final BookmarkUpdatedUserAlert alert;
private final UserAlertManager alerts;
protected String desc;
protected String shortDescription;
public BookmarkItem(FreenetURI k, String n, String d, String s, boolean hasAnActivelink,
BookmarkManager bm, UserAlertManager uam) throws MalformedURLException {
this.key = k;
this.name = n;
this.desc = d;
this.shortDescription = s;
this.hasAnActivelink = hasAnActivelink;
this.manager = bm;
this.alerts = uam;
alert = new BookmarkUpdatedUserAlert();
assert(name != null);
assert(key != null);
}
/** Please call {@link #registerUserAlert()} afterwards. */
public BookmarkItem(SimpleFieldSet sfs, BookmarkManager bm, UserAlertManager uam)
throws FSParseException, MalformedURLException {
this.name = sfs.get("Name");
if(name == null || name.isEmpty()) name = l10n("unnamedBookmark");
this.desc = sfs.get("Description");
if(desc == null) desc = "";
this.shortDescription = sfs.get("ShortDescription");
if(shortDescription == null) shortDescription = "";
this.hasAnActivelink = sfs.getBoolean("hasAnActivelink");
// "Updated" was added in 2016-08-19, so we must assume it doesn't exist in previously saved
// bookmark databases and provide a default to prevent getBoolean() from throwing.
// TODO: Code quality: Remove default after a year or so.
this.updated = sfs.getBoolean("Updated", false);
this.key = new FreenetURI(sfs.get("URI"));
this.manager = bm;
this.alerts = uam;
this.alert = new BookmarkUpdatedUserAlert();
}
private static volatile boolean logMINOR;
static {
Logger.registerClass(ClientGetter.class);
}
private class BookmarkUpdatedUserAlert extends AbstractUserAlert {
public BookmarkUpdatedUserAlert() {
super(true, null, null, null, null, UserAlert.MINOR, false, null, true, null);
}
@Override
public String getTitle() {
return l10n("bookmarkUpdatedTitle", "name", name);
}
@Override
public String getText() {
return l10n("bookmarkUpdated", new String[]{"name", "edition"},
new String[]{name, Long.toString(key.getSuggestedEdition())});
}
@Override
public HTMLNode getHTMLText() {
HTMLNode n = new HTMLNode("div");
NodeL10n.getBase().addL10nSubstitution(n, "BookmarkItem.bookmarkUpdatedWithLink", new String[]{"link", "name", "edition"},
new HTMLNode[] { HTMLNode.link("/"+key), HTMLNode.text(name), HTMLNode.text(key.getSuggestedEdition()) });
return n;
}
@Override
public boolean isValid() {
synchronized (BookmarkItem.this) {
return updated;
}
}
@Override
public void isValid(boolean validity) {
if (validity) {
return;
}
disableBookmark();
}
@Override
public String dismissButtonText() {
return l10n("deleteBookmarkUpdateNotification");
}
@Override
public void onDismiss() {
disableBookmark();
manager.storeBookmarks();
}
@Override
public String getShortText() {
return l10n("bookmarkUpdatedShort", "name", name);
}
@Override
public boolean isEventNotification() {
return true;
}
}
private synchronized void disableBookmark() {
updated = false;
alerts.unregister(alert);
}
private String l10n(String key) {
return NodeL10n.getBase().getString("BookmarkItem." + key);
}
private String l10n(String key, String pattern, String value) {
return NodeL10n.getBase().getString("BookmarkItem." + key, new String[]{pattern}, new String[]{value});
}
private String l10n(String key, String[] patterns, String[] values) {
return NodeL10n.getBase().getString("BookmarkItem." + key, patterns, values);
}
private synchronized void enableBookmark() {
if (updated) {
return;
}
assert(key.isUSK());
updated = true;
alerts.register(alert);
}
/**
* If this bookmark is marked as updated, registers a {@link UserAlert} which notifies the user.
* You usually only need to call this function after having loaded a bookmark from disk using
* {@link #BookmarkItem(SimpleFieldSet, BookmarkManager, UserAlertManager)}. */
synchronized void registerUserAlert() {
if (key.isUSK() && updated)
alerts.register(alert);
}
public UserAlert getUserAlert() {
return alert;
}
public String getKey() {
return key.toString();
}
public synchronized FreenetURI getURI() {
return key;
}
public synchronized void update(FreenetURI uri, boolean hasAnActivelink, String description, String shortDescription) {
this.key = uri;
this.desc = description;
this.shortDescription = shortDescription;
this.hasAnActivelink = hasAnActivelink;
if(!key.isUSK())
disableBookmark();
}
public synchronized String getKeyType() {
return key.getKeyType();
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return this.name + "###" + (this.desc != null ? this.desc : "") + "###" + this.hasAnActivelink + "###" + this.key.toString();
}
/** @return True if we updated the edition */
public synchronized boolean setEdition(long ed, NodeClientCore node) {
if (key.getSuggestedEdition() >= ed) {
if(logMINOR) Logger.minor(this, "Edition "+ed+" is too old, not updating "+key);
return false;
}
key = key.setSuggestedEdition(ed);
enableBookmark();
return true;
}
public USK getUSK() throws MalformedURLException {
return USK.create(key);
}
@Override
public int hashCode() {
int hash = super.hashCode();
hash = 31 * hash + this.key.setSuggestedEdition(0).hashCode();
hash = 31 * hash + (this.hasAnActivelink ? 1 : 0);
hash = 31 * hash + (this.desc != null ? this.desc.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof BookmarkItem) {
BookmarkItem b = (BookmarkItem) o;
if (!super.equals(o)) {
return false;
}
if (!b.key.equals(key)) {
if ("USK".equals(b.key.getKeyType())) {
if (!b.key.setSuggestedEdition(key.getSuggestedEdition()).equals(key)) {
return false;
}
} else {
return false;
}
}
if (b.alerts != alerts) {
return false;
} // Belongs to a different node???
if (b.hasAnActivelink != hasAnActivelink) {
return false;
}
if (b.desc.equals(desc))
return true;
if (b.desc == null || desc == null)
return false;
if(!b.desc.equals(desc)) {
return false;
}
return true;
} else {
return false;
}
}
public boolean hasUpdated() {
return updated;
}
public void clearUpdated() {
this.updated = false;
}
public boolean hasAnActivelink() {
return hasAnActivelink;
}
public String getDescription() {
if(desc == null) return "";
if(desc.toLowerCase().startsWith("l10n:"))
return NodeL10n.getBase().getString("Bookmarks.Defaults.Description."+desc.substring("l10n:".length()));
return desc;
}
public String getShortDescription() {
if(shortDescription == null) return "";
if(shortDescription.toLowerCase().startsWith("l10n:"))
return NodeL10n.getBase().getString("Bookmarks.Defaults.ShortDescription."+shortDescription.substring("l10n:".length()));
return shortDescription;
}
@Override
public SimpleFieldSet getSimpleFieldSet() {
SimpleFieldSet sfs = new SimpleFieldSet(true);
sfs.putSingle("Name", name);
sfs.putSingle("Description", desc);
sfs.putSingle("ShortDescription", shortDescription);
sfs.put("hasAnActivelink", hasAnActivelink);
sfs.put("Updated", updated);
sfs.putSingle("URI", key.toString());
return sfs;
}
}