Add Local Account Deletion #108
@@ -22,20 +22,21 @@ import envoy.client.net.WriteProxy;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
public class Chat implements Serializable {
 | 
					public class Chat implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected boolean disabled;
 | 
						protected transient ObservableList<Message> messages = FXCollections.observableArrayList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected final Contact recipient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected boolean	disabled;
 | 
				
			||||||
 | 
						protected boolean	underlyingContactDeleted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Stores the last time an {@link envoy.event.IsTyping} event has been sent.
 | 
						 * Stores the last time an {@link envoy.event.IsTyping} event has been sent.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	protected transient long lastWritingEvent;
 | 
						protected transient long lastWritingEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected transient ObservableList<Message> messages = FXCollections.observableArrayList();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	protected int						unreadAmount;
 | 
						protected int						unreadAmount;
 | 
				
			||||||
	protected static IntegerProperty	totalUnreadAmount	= new SimpleIntegerProperty();
 | 
						protected static IntegerProperty	totalUnreadAmount	= new SimpleIntegerProperty();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected final Contact recipient;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private static final long serialVersionUID = 2L;
 | 
						private static final long serialVersionUID = 2L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -248,6 +248,10 @@ public final class LocalDB implements EventListener {
 | 
				
			|||||||
	 */
 | 
						 */
 | 
				
			||||||
	@Event(eventType = EnvoyCloseEvent.class, priority = 500)
 | 
						@Event(eventType = EnvoyCloseEvent.class, priority = 500)
 | 
				
			||||||
	private synchronized void save() {
 | 
						private synchronized void save() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Stop saving if this account has been deleted
 | 
				
			||||||
 | 
							if (userFile == null)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
		EnvoyLog.getLogger(LocalDB.class).log(Level.FINER, "Saving local database...");
 | 
							EnvoyLog.getLogger(LocalDB.class).log(Level.FINER, "Saving local database...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Save users
 | 
							// Save users
 | 
				
			||||||
@@ -273,6 +277,20 @@ public final class LocalDB implements EventListener {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Deletes any local remnant of this user.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @since Envoy Client v0.3-beta
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public void delete() {
 | 
				
			||||||
 | 
							if (lastLoginFile != null)
 | 
				
			||||||
 | 
								lastLoginFile.delete();
 | 
				
			||||||
 | 
							userFile.delete();
 | 
				
			||||||
 | 
							users.remove(user.getName());
 | 
				
			||||||
 | 
							userFile = null;
 | 
				
			||||||
 | 
							onLogout();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Event(priority = 500)
 | 
						@Event(priority = 500)
 | 
				
			||||||
	private void onMessage(Message msg) {
 | 
						private void onMessage(Message msg) {
 | 
				
			||||||
		if (msg.getStatus() == MessageStatus.SENT)
 | 
							if (msg.getStatus() == MessageStatus.SENT)
 | 
				
			||||||
@@ -404,6 +422,17 @@ public final class LocalDB implements EventListener {
 | 
				
			|||||||
		getChat(event.get().getID()).ifPresent(chat -> chat.setDisabled(true));
 | 
							getChat(event.get().getID()).ifPresent(chat -> chat.setDisabled(true));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Event(priority = 500)
 | 
				
			||||||
 | 
						private void onAccountDeletion(AccountDeletion deletion) {
 | 
				
			||||||
 | 
							if (user.getID() == deletion.get())
 | 
				
			||||||
 | 
								logger.log(Level.WARNING,
 | 
				
			||||||
 | 
									"I have been informed by the server that I have been deleted without even knowing it...");
 | 
				
			||||||
 | 
							getChat(deletion.get()).ifPresent(chat -> {
 | 
				
			||||||
 | 
								chat.setDisabled(true);
 | 
				
			||||||
 | 
								chat.setUnderlyingContactDeleted(true);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * @return a {@code Map<String, User>} of all users stored locally with their user names as keys
 | 
						 * @return a {@code Map<String, User>} of all users stored locally with their user names as keys
 | 
				
			||||||
	 * @since Envoy Client v0.2-alpha
 | 
						 * @since Envoy Client v0.2-alpha
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ import java.io.*;
 | 
				
			|||||||
import java.nio.file.Files;
 | 
					import java.nio.file.Files;
 | 
				
			||||||
import java.time.LocalDateTime;
 | 
					import java.time.LocalDateTime;
 | 
				
			||||||
import java.time.format.DateTimeFormatter;
 | 
					import java.time.format.DateTimeFormatter;
 | 
				
			||||||
import java.util.*;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.logging.*;
 | 
					import java.util.logging.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javafx.animation.RotateTransition;
 | 
					import javafx.animation.RotateTransition;
 | 
				
			||||||
@@ -25,9 +25,8 @@ import javafx.scene.shape.Rectangle;
 | 
				
			|||||||
import javafx.stage.FileChooser;
 | 
					import javafx.stage.FileChooser;
 | 
				
			||||||
import javafx.util.Duration;
 | 
					import javafx.util.Duration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dev.kske.eventbus.*;
 | 
				
			||||||
import dev.kske.eventbus.Event;
 | 
					import dev.kske.eventbus.Event;
 | 
				
			||||||
import dev.kske.eventbus.EventBus;
 | 
					 | 
				
			||||||
import dev.kske.eventbus.EventListener;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import envoy.data.*;
 | 
					import envoy.data.*;
 | 
				
			||||||
import envoy.data.Attachment.AttachmentType;
 | 
					import envoy.data.Attachment.AttachmentType;
 | 
				
			||||||
@@ -882,24 +881,22 @@ public final class ChatScene implements EventListener, Restorable, KeyboardMappi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Map<KeyCombination, Runnable> getKeyboardShortcuts() {
 | 
						public Map<KeyCombination, Runnable> getKeyboardShortcuts() {
 | 
				
			||||||
		final var map = new HashMap<KeyCombination, Runnable>();
 | 
							return Map.<KeyCombination, Runnable>of(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Delete text before the caret with "Control" + U
 | 
								// Delete text before the caret with "Control" + U
 | 
				
			||||||
		map.put(new KeyCodeCombination(KeyCode.U, KeyCombination.CONTROL_DOWN), () -> {
 | 
								new KeyCodeCombination(KeyCode.U, KeyCombination.CONTROL_DOWN), () -> {
 | 
				
			||||||
			messageTextArea
 | 
									messageTextArea
 | 
				
			||||||
				.setText(messageTextArea.getText().substring(messageTextArea.getCaretPosition()));
 | 
										.setText(
 | 
				
			||||||
			checkPostConditions(false);
 | 
											messageTextArea.getText().substring(messageTextArea.getCaretPosition()));
 | 
				
			||||||
		});
 | 
									checkPostConditions(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Delete text after the caret with "Control" + K
 | 
									// Delete text after the caret with "Control" + K
 | 
				
			||||||
		map.put(new KeyCodeCombination(KeyCode.K, KeyCombination.CONTROL_DOWN), () -> {
 | 
								}, new KeyCodeCombination(KeyCode.K, KeyCombination.CONTROL_DOWN), () -> {
 | 
				
			||||||
			messageTextArea
 | 
									messageTextArea
 | 
				
			||||||
				.setText(
 | 
										.setText(
 | 
				
			||||||
					messageTextArea.getText().substring(0, messageTextArea.getCaretPosition()));
 | 
											messageTextArea.getText().substring(0, messageTextArea.getCaretPosition()));
 | 
				
			||||||
			checkPostConditions(false);
 | 
									checkPostConditions(false);
 | 
				
			||||||
			messageTextArea.positionCaret(messageTextArea.getText().length());
 | 
									messageTextArea.positionCaret(messageTextArea.getText().length());
 | 
				
			||||||
		});
 | 
								});
 | 
				
			||||||
 | 
					 | 
				
			||||||
		return map;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ import dev.kske.eventbus.*;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import envoy.data.*;
 | 
					import envoy.data.*;
 | 
				
			||||||
import envoy.event.GroupCreation;
 | 
					import envoy.event.GroupCreation;
 | 
				
			||||||
import envoy.event.contact.UserOperation;
 | 
					import envoy.event.contact.*;
 | 
				
			||||||
import envoy.util.Bounds;
 | 
					import envoy.util.Bounds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import envoy.client.data.*;
 | 
					import envoy.client.data.*;
 | 
				
			||||||
@@ -252,4 +252,10 @@ public class GroupCreationTab implements EventListener {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Event
 | 
				
			||||||
 | 
						private void onAccountDeletion(AccountDeletion deletion) {
 | 
				
			||||||
 | 
							final var deletedID = deletion.get();
 | 
				
			||||||
 | 
							Platform.runLater(() -> userList.getItems().removeIf(user -> (user.getID() == deletedID)));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,7 @@ import envoy.event.*;
 | 
				
			|||||||
import envoy.util.*;
 | 
					import envoy.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import envoy.client.ui.control.ProfilePicImageView;
 | 
					import envoy.client.ui.control.ProfilePicImageView;
 | 
				
			||||||
import envoy.client.util.IconUtil;
 | 
					import envoy.client.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author Leon Hofmeister
 | 
					 * @author Leon Hofmeister
 | 
				
			||||||
@@ -38,6 +38,7 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
 | 
				
			|||||||
	private final PasswordField	newPasswordField		= new PasswordField();
 | 
						private final PasswordField	newPasswordField		= new PasswordField();
 | 
				
			||||||
	private final PasswordField	repeatNewPasswordField	= new PasswordField();
 | 
						private final PasswordField	repeatNewPasswordField	= new PasswordField();
 | 
				
			||||||
	private final Button		saveButton				= new Button("Save");
 | 
						private final Button		saveButton				= new Button("Save");
 | 
				
			||||||
 | 
						private final Button		deleteAccountButton		= new Button("Delete Account");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static final EventBus	eventBus	= EventBus.getInstance();
 | 
						private static final EventBus	eventBus	= EventBus.getInstance();
 | 
				
			||||||
	private static final Logger		logger		= EnvoyLog.getLogger(UserSettingsPane.class);
 | 
						private static final Logger		logger		= EnvoyLog.getLogger(UserSettingsPane.class);
 | 
				
			||||||
@@ -112,16 +113,19 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		final PasswordField[]					passwordFields	=
 | 
							final PasswordField[]					passwordFields	=
 | 
				
			||||||
			{ currentPasswordField, newPasswordField, repeatNewPasswordField };
 | 
								{ currentPasswordField, newPasswordField, repeatNewPasswordField };
 | 
				
			||||||
		final EventHandler<? super InputEvent>	passwordEntered	= e -> {
 | 
							final EventHandler<? super InputEvent>	passwordEntered	=
 | 
				
			||||||
																	newPassword		=
 | 
								e -> {
 | 
				
			||||||
																		newPasswordField.getText();
 | 
																							newPassword	=
 | 
				
			||||||
																	validPassword	= newPassword
 | 
																								newPasswordField
 | 
				
			||||||
																		.equals(
 | 
																									.getText();
 | 
				
			||||||
																			repeatNewPasswordField
 | 
																							validPassword =
 | 
				
			||||||
																				.getText())
 | 
																								newPassword.equals(
 | 
				
			||||||
																		&& !newPasswordField
 | 
																									repeatNewPasswordField
 | 
				
			||||||
																			.getText().isBlank();
 | 
																										.getText())
 | 
				
			||||||
																};
 | 
																									&& !newPasswordField
 | 
				
			||||||
 | 
																										.getText()
 | 
				
			||||||
 | 
																										.isBlank();
 | 
				
			||||||
 | 
																						};
 | 
				
			||||||
		newPasswordField.setOnInputMethodTextChanged(passwordEntered);
 | 
							newPasswordField.setOnInputMethodTextChanged(passwordEntered);
 | 
				
			||||||
		newPasswordField.setOnKeyTyped(passwordEntered);
 | 
							newPasswordField.setOnKeyTyped(passwordEntered);
 | 
				
			||||||
		repeatNewPasswordField.setOnInputMethodTextChanged(passwordEntered);
 | 
							repeatNewPasswordField.setOnInputMethodTextChanged(passwordEntered);
 | 
				
			||||||
@@ -140,6 +144,11 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
 | 
				
			|||||||
			.setOnAction(e -> save(currentPasswordField.getText()));
 | 
								.setOnAction(e -> save(currentPasswordField.getText()));
 | 
				
			||||||
		saveButton.setAlignment(Pos.BOTTOM_RIGHT);
 | 
							saveButton.setAlignment(Pos.BOTTOM_RIGHT);
 | 
				
			||||||
		getChildren().add(saveButton);
 | 
							getChildren().add(saveButton);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Displaying the delete account button
 | 
				
			||||||
 | 
							deleteAccountButton.setAlignment(Pos.BASELINE_CENTER);
 | 
				
			||||||
 | 
							deleteAccountButton.setOnAction(e -> UserUtil.deleteAccount());
 | 
				
			||||||
 | 
							getChildren().add(deleteAccountButton);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ package envoy.client.util;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.util.logging.*;
 | 
					import java.util.logging.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javafx.scene.control.Alert;
 | 
					import javafx.scene.control.*;
 | 
				
			||||||
import javafx.scene.control.Alert.AlertType;
 | 
					import javafx.scene.control.Alert.AlertType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dev.kske.eventbus.EventBus;
 | 
					import dev.kske.eventbus.EventBus;
 | 
				
			||||||
@@ -10,7 +10,7 @@ import dev.kske.eventbus.EventBus;
 | 
				
			|||||||
import envoy.data.*;
 | 
					import envoy.data.*;
 | 
				
			||||||
import envoy.data.User.UserStatus;
 | 
					import envoy.data.User.UserStatus;
 | 
				
			||||||
import envoy.event.*;
 | 
					import envoy.event.*;
 | 
				
			||||||
import envoy.event.contact.UserOperation;
 | 
					import envoy.event.contact.*;
 | 
				
			||||||
import envoy.util.EnvoyLog;
 | 
					import envoy.util.EnvoyLog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import envoy.client.data.Context;
 | 
					import envoy.client.data.Context;
 | 
				
			||||||
@@ -121,4 +121,40 @@ public final class UserUtil {
 | 
				
			|||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Deletes anything pointing to this user, independent of client or server. Will do nothing if
 | 
				
			||||||
 | 
						 * the client is currently offline.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @since Envoy Client v0.3-beta
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static void deleteAccount() {
 | 
				
			||||||
 | 
							if (!context.getClient().isOnline())
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Show the first wall of defense, if not disabled by the user
 | 
				
			||||||
 | 
								final var outerAlert = new Alert(AlertType.CONFIRMATION);
 | 
				
			||||||
 | 
								outerAlert.setContentText(
 | 
				
			||||||
 | 
									"Are you sure you want to delete your account entirely? This action can seriously not be undone.");
 | 
				
			||||||
 | 
								outerAlert.setTitle("Delete Account?");
 | 
				
			||||||
 | 
								AlertHelper.confirmAction(outerAlert, () -> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Show the final wall of defense in every case
 | 
				
			||||||
 | 
									final var lastAlert = new Alert(AlertType.WARNING,
 | 
				
			||||||
 | 
										"Do you REALLY want to delete your account? Last Warning. Proceed?",
 | 
				
			||||||
 | 
										ButtonType.CANCEL, ButtonType.OK);
 | 
				
			||||||
 | 
									lastAlert.setTitle("Delete Account?");
 | 
				
			||||||
 | 
									lastAlert.showAndWait().filter(ButtonType.OK::equals).ifPresent(b2 -> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// Delete the account
 | 
				
			||||||
 | 
										context.getClient()
 | 
				
			||||||
 | 
											.send(new AccountDeletion(context.getLocalDB().getUser().getID()));
 | 
				
			||||||
 | 
										context.getLocalDB().delete();
 | 
				
			||||||
 | 
										logger.log(Level.INFO, "The user just deleted his account. Goodbye.");
 | 
				
			||||||
 | 
										ShutdownHelper.exit(true);
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					package envoy.event.contact;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import envoy.event.Event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Signifies the deletion of an account.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * @author Leon Hofmeister
 | 
				
			||||||
 | 
					 * @since Envoy Common v0.3-beta
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class AccountDeletion extends Event<Long> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @param value the ID of the contact that was deleted
 | 
				
			||||||
 | 
						 * @since Envoy Common v0.3-beta
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public AccountDeletion(Long value) {
 | 
				
			||||||
 | 
							super(value);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -59,7 +59,8 @@ public final class Startup {
 | 
				
			|||||||
				new NameChangeProcessor(),
 | 
									new NameChangeProcessor(),
 | 
				
			||||||
				new ProfilePicChangeProcessor(),
 | 
									new ProfilePicChangeProcessor(),
 | 
				
			||||||
				new PasswordChangeRequestProcessor(),
 | 
									new PasswordChangeRequestProcessor(),
 | 
				
			||||||
				new IssueProposalProcessor())));
 | 
									new IssueProposalProcessor(),
 | 
				
			||||||
 | 
									new AccountDeletionProcessor())));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Initialize the current message ID
 | 
							// Initialize the current message ID
 | 
				
			||||||
		final var persistenceManager = PersistenceManager.getInstance();
 | 
							final var persistenceManager = PersistenceManager.getInstance();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,14 +27,18 @@ import envoy.data.Message.MessageStatus;
 | 
				
			|||||||
@Entity
 | 
					@Entity
 | 
				
			||||||
@Table(name = "messages")
 | 
					@Table(name = "messages")
 | 
				
			||||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
 | 
					@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
 | 
				
			||||||
@NamedQuery(name = Message.getPending, query = "SELECT m FROM Message m WHERE "
 | 
					@NamedQueries({
 | 
				
			||||||
	// Send to or by the user before last seen
 | 
									@NamedQuery(name = Message.getPending, query = "SELECT m FROM Message m WHERE "
 | 
				
			||||||
	+ "(m.sender = :user OR m.recipient = :user) AND m.creationDate > :lastSeen "
 | 
										// Send to or by the user before last seen
 | 
				
			||||||
	// SENT to the user
 | 
										+ "(m.sender = :user OR m.recipient = :user) AND m.creationDate > :lastSeen "
 | 
				
			||||||
	+ "OR m.recipient = :user AND m.status = envoy.data.Message$MessageStatus.SENT "
 | 
										// SENT to the user
 | 
				
			||||||
	// Sent by the user and RECEIVED / READ after last seen
 | 
										+ "OR m.recipient = :user AND m.status = envoy.data.Message$MessageStatus.SENT "
 | 
				
			||||||
	+ "OR m.sender = :user AND (m.status = envoy.data.Message$MessageStatus.RECEIVED AND m.receivedDate > :lastSeen "
 | 
										// Sent by the user and RECEIVED / READ after last seen
 | 
				
			||||||
	+ "OR m.status = envoy.data.Message$MessageStatus.READ AND m.readDate > :lastSeen)")
 | 
										+ "OR m.sender = :user AND (m.status = envoy.data.Message$MessageStatus.RECEIVED AND m.receivedDate > :lastSeen "
 | 
				
			||||||
 | 
										+ "OR m.status = envoy.data.Message$MessageStatus.READ AND m.readDate > :lastSeen)"),
 | 
				
			||||||
 | 
									@NamedQuery(name = Message.deleteByRecipient, query = "DELETE FROM Message m WHERE m.recipient = :deleted OR m.sender = :deleted")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
public class Message {
 | 
					public class Message {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
@@ -45,6 +49,13 @@ public class Message {
 | 
				
			|||||||
	 */
 | 
						 */
 | 
				
			||||||
	public static final String getPending = "Message.getPending";
 | 
						public static final String getPending = "Message.getPending";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Named query deleting all messages of a user (parameter {@code :deleted}).
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @since Envoy Server v0.3-beta
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public static final String deleteByRecipient = "Message.deleteByRecipient";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Id
 | 
						@Id
 | 
				
			||||||
	protected long id;
 | 
						protected long id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -123,6 +123,10 @@ public final class PersistenceManager {
 | 
				
			|||||||
			// Remove this contact from the contact list of his contacts
 | 
								// Remove this contact from the contact list of his contacts
 | 
				
			||||||
			for (final var remainingContact : contact.getContacts())
 | 
								for (final var remainingContact : contact.getContacts())
 | 
				
			||||||
				remainingContact.getContacts().remove(contact);
 | 
									remainingContact.getContacts().remove(contact);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								entityManager
 | 
				
			||||||
 | 
									.createNamedQuery(Message.deleteByRecipient).setParameter("deleted", contact)
 | 
				
			||||||
 | 
									.executeUpdate();
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
		remove(contact);
 | 
							remove(contact);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,8 +49,10 @@ public final class ConnectionManager implements ISocketIdListener {
 | 
				
			|||||||
			// Notify contacts of this users offline-going
 | 
								// Notify contacts of this users offline-going
 | 
				
			||||||
			final envoy.server.data.User user =
 | 
								final envoy.server.data.User user =
 | 
				
			||||||
				PersistenceManager.getInstance().getUserByID(getUserIDBySocketID(socketID));
 | 
									PersistenceManager.getInstance().getUserByID(getUserIDBySocketID(socketID));
 | 
				
			||||||
			user.setLastSeen(Instant.now());
 | 
								if (user != null) {
 | 
				
			||||||
			UserStatusChangeProcessor.updateUserStatus(user, UserStatus.OFFLINE);
 | 
									user.setLastSeen(Instant.now());
 | 
				
			||||||
 | 
									UserStatusChangeProcessor.updateUserStatus(user, UserStatus.OFFLINE);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Remove the socket
 | 
								// Remove the socket
 | 
				
			||||||
			sockets.entrySet().removeIf(e -> e.getValue() == socketID);
 | 
								sockets.entrySet().removeIf(e -> e.getValue() == socketID);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					package envoy.server.processors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.time.Instant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import envoy.event.contact.AccountDeletion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import envoy.server.data.*;
 | 
				
			||||||
 | 
					import envoy.server.net.ObjectWriteProxy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author Leon Hofmeister
 | 
				
			||||||
 | 
					 * @since Envoy Server v0.3-beta
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class AccountDeletionProcessor implements ObjectProcessor<AccountDeletion> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final PersistenceManager persistenceManager = PersistenceManager.getInstance();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public void process(AccountDeletion input, long socketID, ObjectWriteProxy writeProxy)
 | 
				
			||||||
 | 
							throws IOException {
 | 
				
			||||||
 | 
							final var contact = persistenceManager.getContactByID(input.get());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							contact.getContacts().forEach(c -> {
 | 
				
			||||||
 | 
								persistenceManager.removeContactBidirectional(contact, c);
 | 
				
			||||||
 | 
								if (c instanceof User)
 | 
				
			||||||
 | 
									((User) c).setLatestContactDeletion(Instant.now());
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							writeProxy.writeToOnlineContacts(contact.getContacts(), input);
 | 
				
			||||||
 | 
							persistenceManager.deleteContact(contact);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,6 +4,7 @@ import java.time.Instant;
 | 
				
			|||||||
import java.util.logging.Level;
 | 
					import java.util.logging.Level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import envoy.event.GroupResize;
 | 
					import envoy.event.GroupResize;
 | 
				
			||||||
 | 
					import envoy.event.contact.AccountDeletion;
 | 
				
			||||||
import envoy.util.EnvoyLog;
 | 
					import envoy.util.EnvoyLog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import envoy.server.data.*;
 | 
					import envoy.server.data.*;
 | 
				
			||||||
@@ -24,6 +25,12 @@ public final class GroupResizeProcessor implements ObjectProcessor<GroupResize>
 | 
				
			|||||||
		final var	group	= persistenceManager.getGroupByID(groupResize.getGroupID());
 | 
							final var	group	= persistenceManager.getGroupByID(groupResize.getGroupID());
 | 
				
			||||||
		final var	sender	= persistenceManager.getUserByID(groupResize.get().getID());
 | 
							final var	sender	= persistenceManager.getUserByID(groupResize.get().getID());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Inform the sender that this group has already been deleted
 | 
				
			||||||
 | 
							if (group == null) {
 | 
				
			||||||
 | 
								writeProxy.write(socketID, new AccountDeletion(groupResize.getGroupID()));
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Perform the desired operation
 | 
							// Perform the desired operation
 | 
				
			||||||
		switch (groupResize.getOperation()) {
 | 
							switch (groupResize.getOperation()) {
 | 
				
			||||||
			case ADD:
 | 
								case ADD:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ import java.time.Instant;
 | 
				
			|||||||
import java.util.logging.*;
 | 
					import java.util.logging.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import envoy.event.ElementOperation;
 | 
					import envoy.event.ElementOperation;
 | 
				
			||||||
import envoy.event.contact.UserOperation;
 | 
					import envoy.event.contact.*;
 | 
				
			||||||
import envoy.util.EnvoyLog;
 | 
					import envoy.util.EnvoyLog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import envoy.server.data.PersistenceManager;
 | 
					import envoy.server.data.PersistenceManager;
 | 
				
			||||||
@@ -22,10 +22,18 @@ public final class UserOperationProcessor implements ObjectProcessor<UserOperati
 | 
				
			|||||||
	private static final PersistenceManager	persistenceManager	= PersistenceManager.getInstance();
 | 
						private static final PersistenceManager	persistenceManager	= PersistenceManager.getInstance();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void process(UserOperation evt, long socketId, ObjectWriteProxy writeProxy) {
 | 
						public void process(UserOperation evt, long socketID, ObjectWriteProxy writeProxy) {
 | 
				
			||||||
		final long	userID		= ConnectionManager.getInstance().getUserIDBySocketID(socketId);
 | 
							final long	userID		= ConnectionManager.getInstance().getUserIDBySocketID(socketID);
 | 
				
			||||||
		final long	contactID	= evt.get().getID();
 | 
							final long	contactID	= evt.get().getID();
 | 
				
			||||||
		final var	sender		= persistenceManager.getUserByID(userID);
 | 
							final var	recipient	= persistenceManager.getUserByID(contactID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Inform the sender if the requested contact has already been deleted
 | 
				
			||||||
 | 
							if (recipient == null) {
 | 
				
			||||||
 | 
								writeProxy.write(socketID, new AccountDeletion(contactID));
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							final var sender = persistenceManager.getUserByID(userID);
 | 
				
			||||||
		switch (evt.getOperationType()) {
 | 
							switch (evt.getOperationType()) {
 | 
				
			||||||
			case ADD:
 | 
								case ADD:
 | 
				
			||||||
				logger.log(Level.FINE,
 | 
									logger.log(Level.FINE,
 | 
				
			||||||
@@ -45,7 +53,7 @@ public final class UserOperationProcessor implements ObjectProcessor<UserOperati
 | 
				
			|||||||
				sender.setLatestContactDeletion(Instant.now());
 | 
									sender.setLatestContactDeletion(Instant.now());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Notify the removed contact on next startup(s) of this deletion
 | 
									// Notify the removed contact on next startup(s) of this deletion
 | 
				
			||||||
				persistenceManager.getUserByID(contactID).setLatestContactDeletion(Instant.now());
 | 
									recipient.setLatestContactDeletion(Instant.now());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Notify the removed contact if online
 | 
									// Notify the removed contact if online
 | 
				
			||||||
				if (connectionManager.isOnline(contactID))
 | 
									if (connectionManager.isOnline(contactID))
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user