<modelVersion>4.0.0</modelVersion>
<groupId>net.pterodactylus</groupId>
<artifactId>sone</artifactId>
- <version>0.3.4</version>
+ <version>0.3.5</version>
<dependencies>
<dependency>
<groupId>net.pterodactylus</groupId>
<artifactId>utils</artifactId>
- <version>0.7.2</version>
+ <version>0.7.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
*/
public void lockSone(Sone sone) {
synchronized (lockedSones) {
- lockedSones.add(sone);
+ if (lockedSones.add(sone)) {
+ coreListenerManager.fireSoneLocked(sone);
+ }
}
}
*/
public void unlockSone(Sone sone) {
synchronized (lockedSones) {
- lockedSones.remove(sone);
+ if (lockedSones.remove(sone)) {
+ coreListenerManager.fireSoneUnlocked(sone);
+ }
}
}
* @param sone
* The Sone to save
*/
- public void saveSone(Sone sone) {
+ public synchronized void saveSone(Sone sone) {
if (!isLocalSone(sone)) {
logger.log(Level.FINE, "Tried to save non-local Sone: %s", sone);
return;
}
configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null);
+ configuration.save();
logger.log(Level.INFO, "Sone %s saved.", sone);
} catch (ConfigurationException ce1) {
logger.log(Level.WARNING, "Could not save Sone: " + sone, ce1);
/**
* Saves the current options.
*/
- public void saveConfiguration() {
+ public synchronized void saveConfiguration() {
/* store the options first. */
try {
configuration.getIntValue("Option/InsertionDelay").setValue(options.getIntegerOption("InsertionDelay").getReal());
*/
public void replyRemoved(Reply reply);
+ /**
+ * Notifies a listener when a Sone was locked.
+ *
+ * @param sone
+ * The Sone that was locked
+ */
+ public void soneLocked(Sone sone);
+
+ /**
+ * Notifies a listener that a Sone was unlocked.
+ *
+ * @param sone
+ * The Sone that was unlocked
+ */
+ public void soneUnlocked(Sone sone);
+
}
}
}
+ /**
+ * Notifies all listeners that the given Sone was locked.
+ *
+ * @see CoreListener#soneLocked(Sone)
+ * @param sone
+ * The Sone that was locked
+ */
+ void fireSoneLocked(Sone sone) {
+ for (CoreListener coreListener : getListeners()) {
+ coreListener.soneLocked(sone);
+ }
+ }
+
+ /**
+ * Notifies all listeners that the given Sone was unlocked.
+ *
+ * @see CoreListener#soneUnlocked(Sone)
+ * @param sone
+ * The Sone that was unlocked
+ */
+ void fireSoneUnlocked(Sone sone) {
+ for (CoreListener coreListener : getListeners()) {
+ coreListener.soneUnlocked(sone);
+ }
+ }
+
}
soneProperties.put("posts", new ArrayList<Post>(sone.getPosts()));
soneProperties.put("replies", new HashSet<Reply>(sone.getReplies()));
soneProperties.put("likedPostIds", new HashSet<String>(sone.getLikedPostIds()));
- soneProperties.put("likeReplyIds", new HashSet<String>(sone.getLikedReplyIds()));
+ soneProperties.put("likedReplyIds", new HashSet<String>(sone.getLikedReplyIds()));
}
//
import freenet.pluginmanager.FredPluginThreadless;
import freenet.pluginmanager.FredPluginVersioned;
import freenet.pluginmanager.PluginRespirator;
-import freenet.pluginmanager.PluginStore;
/**
* This class interfaces with Freenet. It is the class that is loaded by the
/** The l10n helper. */
private PluginL10n l10n;
- /** The plugin store. */
- private PluginStore pluginStore;
-
/** The identity manager. */
private IdentityManager identityManager;
/* stop the identity manager. */
identityManager.stop();
-
- /* TODO wait for core to stop? */
- try {
- pluginRespirator.putStore(pluginStore);
- } catch (DatabaseDisabledException dde1) {
- logger.log(Level.WARNING, "Could not store plugin store, database is disabled.", dde1);
- }
-
+ } catch (Throwable t1) {
+ logger.log(Level.SEVERE, "Error while shutting down!", t1);
} finally {
/* shutdown logger. */
Logging.shutdown();
}
/**
+ * Returns the currently logged in Sone.
+ *
+ * @param toadletContext
+ * The toadlet context
+ * @param create
+ * {@code true} to create a new session if no session exists,
+ * {@code false} to not create a new session
+ * @return The currently logged in Sone, or {@code null} if no Sone is
+ * currently logged in
+ */
+ protected Sone getCurrentSone(ToadletContext toadletContext, boolean create) {
+ return webInterface.getCurrentSone(toadletContext, create);
+ }
+
+ /**
* Sets the currently logged in Sone.
*
* @param toadletContext
@Override
public boolean isEnabled(ToadletContext toadletContext) {
if (requiresLogin()) {
- return getCurrentSone(toadletContext) != null;
+ return getCurrentSone(toadletContext, false) != null;
}
return true;
}
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
/** The “Sone rescued” notification. */
private final ListNotification<Sone> sonesRescuedNotification;
+ /** Sone locked notification ticker objects. */
+ private final Map<Sone, Object> lockedSonesTickerObjects = Collections.synchronizedMap(new HashMap<Sone, Object>());
+
+ /** The “Sone locked” notification. */
+ private final ListNotification<Sone> lockedSonesNotification;
+
/**
* Creates a new web interface.
*
Template sonesRescuedTemplate = templateFactory.createTemplate(createReader("/templates/notify/sonesRescuedNotification.html"));
sonesRescuedNotification = new ListNotification<Sone>("sones-rescued-notification", "sones", sonesRescuedTemplate);
+
+ Template lockedSonesTemplate = templateFactory.createTemplate(createReader("/templates/notify/lockedSonesNotification.html"));
+ lockedSonesNotification = new ListNotification<Sone>("sones-locked-notification", "sones", lockedSonesTemplate);
}
//
* currently logged in
*/
public Sone getCurrentSone(ToadletContext toadletContext) {
- return getCurrentSone(getCurrentSession(toadletContext));
+ return getCurrentSone(toadletContext, true);
+ }
+
+ /**
+ * Returns the currently logged in Sone.
+ *
+ * @param toadletContext
+ * The toadlet context
+ * @param create
+ * {@code true} to create a new session if no session exists,
+ * {@code false} to not create a new session
+ * @return The currently logged in Sone, or {@code null} if no Sone is
+ * currently logged in
+ */
+ public Sone getCurrentSone(ToadletContext toadletContext, boolean create) {
+ return getCurrentSone(getCurrentSession(toadletContext, create));
}
/**
newPostNotification.add(post);
if (!hasFirstStartNotification()) {
notificationManager.addNotification(newPostNotification);
+ } else {
+ getCore().markPostKnown(post);
}
}
newReplyNotification.add(reply);
if (!hasFirstStartNotification()) {
notificationManager.addNotification(newReplyNotification);
+ } else {
+ getCore().markReplyKnown(reply);
}
}
*/
@Override
public void postRemoved(Post post) {
- /* TODO */
+ newPostNotification.remove(post);
}
/**
*/
@Override
public void replyRemoved(Reply reply) {
- /* TODO */
+ newReplyNotification.remove(reply);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void soneLocked(final Sone sone) {
+ Object tickerObject = Ticker.getInstance().registerEvent(System.currentTimeMillis() + (5 * 60) * 1000, new Runnable() {
+
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public void run() {
+ lockedSonesNotification.add(sone);
+ lockedSonesTickerObjects.remove(sone);
+ notificationManager.addNotification(lockedSonesNotification);
+ }
+ }, "Sone Locked Notification");
+ lockedSonesTickerObjects.put(sone, tickerObject);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void soneUnlocked(Sone sone) {
+ lockedSonesNotification.remove(sone);
+ Ticker.getInstance().deregisterEvent(lockedSonesTickerObjects.remove(sone));
}
/**
import java.io.StringWriter;
import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.util.io.Closer;
import net.pterodactylus.util.json.JsonObject;
+import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateException;
if (post == null) {
return createErrorJsonObject("invalid-post-id");
}
- postTemplate.set("currentSone", getCurrentSone(request.getToadletContext()));
- return createSuccessJsonObject().put("post", createJsonPost(post));
+ return createSuccessJsonObject().put("post", createJsonPost(post, getCurrentSone(request.getToadletContext())));
}
/**
*
* @param post
* The post to create a JSON object from
+ * @param currentSone
+ * The currently logged in Sone (to store in the template)
* @return The JSON representation of the post
*/
- private JsonObject createJsonPost(Post post) {
+ private JsonObject createJsonPost(Post post, Sone currentSone) {
JsonObject jsonPost = new JsonObject();
jsonPost.put("id", post.getId());
jsonPost.put("sone", post.getSone().getId());
jsonPost.put("recipient", (post.getRecipient() == null) ? null : post.getRecipient().getId());
jsonPost.put("time", post.getTime());
StringWriter stringWriter = new StringWriter();
- synchronized (postTemplate) {
- postTemplate.set("post", post);
- try {
- postTemplate.render(stringWriter);
- } catch (TemplateException te1) {
- /* TODO - shouldn’t happen. */
- } finally {
- Closer.close(stringWriter);
- }
+ DataProvider dataProvider = postTemplate.createDataProvider();
+ dataProvider.setData("post", post);
+ dataProvider.setData("currentSone", currentSone);
+ try {
+ postTemplate.render(dataProvider, stringWriter);
+ } catch (TemplateException te1) {
+ /* TODO - shouldn’t happen. */
+ } finally {
+ Closer.close(stringWriter);
}
jsonPost.put("html", stringWriter.toString());
return jsonPost;
import java.io.StringWriter;
import net.pterodactylus.sone.data.Reply;
+import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.util.io.Closer;
import net.pterodactylus.util.json.JsonObject;
+import net.pterodactylus.util.template.DataProvider;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateException;
if ((reply == null) || (reply.getSone() == null)) {
return createErrorJsonObject("invalid-reply-id");
}
- replyTemplate.set("currentSone", getCurrentSone(request.getToadletContext()));
- return createSuccessJsonObject().put("reply", createJsonReply(reply));
+ return createSuccessJsonObject().put("reply", createJsonReply(reply, getCurrentSone(request.getToadletContext())));
}
/**
*
* @param reply
* The reply to convert
+ * @param currentSone
+ * The currently logged in Sone (to store in the template)
* @return The JSON representation of the reply
*/
- private JsonObject createJsonReply(Reply reply) {
+ private JsonObject createJsonReply(Reply reply, Sone currentSone) {
JsonObject jsonReply = new JsonObject();
jsonReply.put("id", reply.getId());
jsonReply.put("postId", reply.getPost().getId());
jsonReply.put("soneId", reply.getSone().getId());
jsonReply.put("time", reply.getTime());
StringWriter stringWriter = new StringWriter();
- synchronized (replyTemplate) {
- replyTemplate.set("reply", reply);
- try {
- replyTemplate.render(stringWriter);
- } catch (TemplateException te1) {
- /* TODO - shouldn’t happen. */
- } finally {
- Closer.close(stringWriter);
- }
+ DataProvider dataProvider = replyTemplate.createDataProvider();
+ dataProvider.setData("reply", reply);
+ dataProvider.setData("currentSone", currentSone);
+ try {
+ replyTemplate.render(dataProvider, stringWriter);
+ } catch (TemplateException te1) {
+ /* TODO - shouldn’t happen. */
+ } finally {
+ Closer.close(stringWriter);
}
return jsonReply.put("html", stringWriter.toString());
}
Notification.SoneIsBeingRescued.Text=The following Sones are currently being rescued:
Notification.SoneRescued.Text=The following Sones have been rescued:
Notification.SoneRescued.Text.RememberToUnlock=Please remember to control the posts and replies you have given and don’t forget to unlock your Sones!
-
+Notification.LockedSones.Text=The following Sones have been locked for more than 5 minutes. Please check if you really want to keep these Sones locked:
</div>
<div class="post-status-line status-line">
<div class="time"><a href="viewPost.html?post=<% post.id|html>"><% post.time|date format="MMM d, yyyy, HH:mm:ss"></a></div>
- <div class="likes<%if post.likes.size|match value=0> hidden<%/if>"><span title="<% post.likes.soneNames|html>">â¬\86<span class="like-count"><% post.likes.size></span></span></div>
+ <div class="likes<%if post.likes.size|match value=0> hidden<%/if>"><span title="<% post.likes.soneNames|html>">â\86\91<span class="like-count"><% post.likes.size></span></span></div>
<%ifnull ! currentSone>
<form class="like like-post<%if post.liked> hidden<%/if>" action="like.html" method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
</div>
<div class="reply-status-line status-line">
<div class="time"><% reply.time|date format="MMM d, yyyy, HH:mm:ss"></div>
- <div class="likes<%if reply.likes.size|match value=0> hidden<%/if>"><span title="<% reply.likes.soneNames|html>">â¬\86<span class="like-count"><% reply.likes.size></span></span></div>
+ <div class="likes<%if reply.likes.size|match value=0> hidden<%/if>"><span title="<% reply.likes.soneNames|html>">â\86\91<span class="like-count"><% reply.likes.size></span></span></div>
<%ifnull ! currentSone>
<form class="like like-reply<%if reply.liked> hidden<%/if>" action="like.html" method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
--- /dev/null
+<div class="text">
+ <%= Notification.LockedSones.Text|l10n|html>
+ <%foreach sones sone>
+ <a href="viewSone.html?sone=<% sone.id|html>" title="<% sone.requestUri|html>"><% sone.niceName|html></a><%notlast>,<%/notlast><%last>.<%/last>
+ <%/foreach>
+</div>