+ * Marks the given reply as known, if it is currently not a known reply
+ * (according to {@link Reply#isKnown()}).
+ *
+ * @param reply
+ * The reply to mark as known
+ */
+ public void markReplyKnown(PostReply reply) {
+ reply.setKnown(true);
+ synchronized (knownReplies) {
+ eventBus.post(new MarkPostReplyKnownEvent(reply));
+ if (knownReplies.add(reply.getId())) {
+ touchConfiguration();
+ }
+ }
+ }
+
+ /**
+ * Creates a new top-level album for the given Sone.
+ *
+ * @param sone
+ * The Sone to create the album for
+ * @return The new album
+ */
+ public Album createAlbum(Sone sone) {
+ return createAlbum(sone, null);
+ }
+
+ /**
+ * Creates a new album for the given Sone.
+ *
+ * @param sone
+ * The Sone to create the album for
+ * @param parent
+ * The parent of the album (may be {@code null} to create a
+ * top-level album)
+ * @return The new album
+ */
+ public Album createAlbum(Sone sone, Album parent) {
+ Album album = new Album();
+ synchronized (albums) {
+ albums.put(album.getId(), album);
+ }
+ album.setSone(sone);
+ if (parent != null) {
+ parent.addAlbum(album);
+ } else {
+ sone.addAlbum(album);
+ }
+ return album;
+ }
+
+ /**
+ * Deletes the given album. The owner of the album has to be a local Sone,
+ * and the album has to be {@link Album#isEmpty() empty} to be deleted.
+ *
+ * @param album
+ * The album to remove
+ */
+ public void deleteAlbum(Album album) {
+ checkNotNull(album, "album must not be null");
+ checkArgument(album.getSone().isLocal(), "album’s Sone must be a local Sone");
+ if (!album.isEmpty()) {
+ return;
+ }
+ if (album.getParent() == null) {
+ album.getSone().removeAlbum(album);
+ } else {
+ album.getParent().removeAlbum(album);
+ }
+ synchronized (albums) {
+ albums.remove(album.getId());
+ }
+ touchConfiguration();
+ }
+
+ /**
+ * Creates a new image.
+ *
+ * @param sone
+ * The Sone creating the image
+ * @param album
+ * The album the image will be inserted into
+ * @param temporaryImage
+ * The temporary image to create the image from
+ * @return The newly created image
+ */
+ public Image createImage(Sone sone, Album album, TemporaryImage temporaryImage) {
+ checkNotNull(sone, "sone must not be null");
+ checkNotNull(album, "album must not be null");
+ checkNotNull(temporaryImage, "temporaryImage must not be null");
+ checkArgument(sone.isLocal(), "sone must be a local Sone");
+ checkArgument(sone.equals(album.getSone()), "album must belong to the given Sone");
+ Image image = new Image(temporaryImage.getId()).setSone(sone).setCreationTime(System.currentTimeMillis());
+ album.addImage(image);
+ synchronized (images) {
+ images.put(image.getId(), image);
+ }
+ imageInserter.insertImage(temporaryImage, image);
+ return image;
+ }
+
+ /**
+ * Deletes the given image. This method will also delete a matching
+ * temporary image.
+ *
+ * @see #deleteTemporaryImage(TemporaryImage)
+ * @param image
+ * The image to delete
+ */
+ public void deleteImage(Image image) {
+ checkNotNull(image, "image must not be null");
+ checkArgument(image.getSone().isLocal(), "image must belong to a local Sone");
+ deleteTemporaryImage(image.getId());
+ image.getAlbum().removeImage(image);
+ synchronized (images) {
+ images.remove(image.getId());
+ }
+ touchConfiguration();
+ }
+
+ /**
+ * Creates a new temporary image.
+ *
+ * @param mimeType
+ * The MIME type of the temporary image
+ * @param imageData
+ * The encoded data of the image
+ * @return The temporary image
+ */
+ public TemporaryImage createTemporaryImage(String mimeType, byte[] imageData) {
+ TemporaryImage temporaryImage = new TemporaryImage();
+ temporaryImage.setMimeType(mimeType).setImageData(imageData);
+ synchronized (temporaryImages) {
+ temporaryImages.put(temporaryImage.getId(), temporaryImage);
+ }
+ return temporaryImage;
+ }
+
+ /**
+ * Deletes the given temporary image.
+ *
+ * @param temporaryImage
+ * The temporary image to delete
+ */
+ public void deleteTemporaryImage(TemporaryImage temporaryImage) {
+ checkNotNull(temporaryImage, "temporaryImage must not be null");
+ deleteTemporaryImage(temporaryImage.getId());
+ }
+
+ /**
+ * Deletes the temporary image with the given ID.
+ *
+ * @param imageId
+ * The ID of the temporary image to delete
+ */
+ public void deleteTemporaryImage(String imageId) {
+ checkNotNull(imageId, "imageId must not be null");
+ synchronized (temporaryImages) {
+ temporaryImages.remove(imageId);
+ }
+ Image image = getImage(imageId, false);
+ if (image != null) {
+ imageInserter.cancelImageInsert(image);
+ }
+ }
+
+ /**
+ * Notifies the core that the configuration, either of the core or of a
+ * single local Sone, has changed, and that the configuration should be
+ * saved.
+ */
+ public void touchConfiguration() {
+ lastConfigurationUpdate = System.currentTimeMillis();
+ }
+
+ //
+ // SERVICE METHODS
+ //
+
+ /**