package de.dhbw.humbuch.viewmodel; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import java.util.Collection; import org.hibernate.criterion.Restrictions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.EventBus; import com.google.inject.Inject; import de.davherrmann.mvvm.ActionHandler; import de.davherrmann.mvvm.BasicState; import de.davherrmann.mvvm.State; import de.davherrmann.mvvm.StateChangeListener; import de.davherrmann.mvvm.annotations.AfterVMBinding; import de.davherrmann.mvvm.annotations.HandlesAction; import de.davherrmann.mvvm.annotations.ProvidesState; import de.dhbw.humbuch.event.MessageEvent; import de.dhbw.humbuch.event.MessageEvent.Type; import de.dhbw.humbuch.model.DAO; import de.dhbw.humbuch.model.entity.Category; import de.dhbw.humbuch.model.entity.SchoolYear; import de.dhbw.humbuch.model.entity.SettingsEntry; import de.dhbw.humbuch.model.entity.User; import de.dhbw.humbuch.util.PasswordHash; /** * Provides {@link State}s and methods for inserting and updateing {@link SchoolYear}s and {@link Category}s. * * @author David Vitt * */ public class SettingsViewModel { private final static Logger LOG = LoggerFactory.getLogger(SettingsViewModel.class); public interface DoUpdateUser extends ActionHandler {} public interface DoPasswordChange extends ActionHandler {} public interface SchoolYears extends State<Collection<SchoolYear>> {} public interface Categories extends State<Collection<Category>> {} public interface SettingsEntries extends State<Collection<SettingsEntry>> {} public interface PasswordChangeStatus extends State<ChangeStatus> {} public interface UserName extends State<String> {} public interface UserEmail extends State<String> {} @ProvidesState(SchoolYears.class) public final State<Collection<SchoolYear>> schoolYears = new BasicState<>(Collection.class); @ProvidesState(Categories.class) public final State<Collection<Category>> categories = new BasicState<>(Collection.class); @ProvidesState(SettingsEntries.class) public final State<Collection<SettingsEntry>> settingsEntries = new BasicState<>(Collection.class); @ProvidesState(PasswordChangeStatus.class) public final State<ChangeStatus> passwordChangeStatus = new BasicState<>(ChangeStatus.class); @ProvidesState(UserName.class) public final State<String> userName = new BasicState<>(String.class); @ProvidesState(UserEmail.class) public final State<String> userEmail = new BasicState<>(String.class); private EventBus eventBus; private State<User> currentUser; private DAO<SchoolYear> daoSchoolYear; private DAO<User> daoUser; private DAO<Category> daoCategory; private DAO<SettingsEntry> daoSettingsEntry; /** * Constructor * * @param daoSchoolYear * @param daoUser * @param daoCategory * @param daoSettingsEntry * @param properties * @param eventBus */ @Inject public SettingsViewModel(DAO<SchoolYear> daoSchoolYear, DAO<User> daoUser, DAO<Category> daoCategory, DAO<SettingsEntry> daoSettingsEntry, Properties properties, EventBus eventBus) { this.eventBus = eventBus; this.daoSchoolYear = daoSchoolYear; this.daoUser = daoUser; this.daoCategory = daoCategory; this.daoSettingsEntry = daoSettingsEntry; this.currentUser = properties.currentUser; this.currentUser.addStateChangeListener(new StateChangeListener() { @Override public void stateChange(Object value) { if (value != null) updateUser(); } }); } @AfterVMBinding public void initialiseStates() { schoolYears.set(new ArrayList<SchoolYear>()); categories.set(new ArrayList<Category>()); settingsEntries.set(new ArrayList<SettingsEntry>()); passwordChangeStatus.set(null); userName.set(null); userEmail.set(null); } public void refresh() { updateSchoolYears(); updateCategories(); updateSettingsEntries(); updateUser(); } private void updateSchoolYears() { schoolYears.set(daoSchoolYear.findAll()); } private void updateCategories() { categories.set(daoCategory.findAll()); } private void updateSettingsEntries() { settingsEntries.set(daoSettingsEntry.findAll()); } private void updateUser() { userName.set(currentUser.get().getUsername()); userEmail.set(currentUser.get().getEmail()); } public void doUpdateSchoolYear(SchoolYear schoolYear) { daoSchoolYear.update(schoolYear); updateSchoolYears(); } /** * Delets {@link SchoolYear} if it isn't the current one. * * @param schoolYear */ public void doDeleteSchoolYear(SchoolYear schoolYear) { if (!schoolYear.isActive()) { daoSchoolYear.delete(schoolYear); } else { eventBus.post(new MessageEvent("Löschen nicht möglich!", "Das aktuelle Schuljahr kann nicht gelöscht werden.", Type.WARNING)); } updateSchoolYears(); } public void doUpdateCategory(Category category) { if (category.getName().isEmpty()) { eventBus.post(new MessageEvent("Speichern nicht möglich!", "Das Feld 'Kategorie' darf nicht leer sein.", Type.WARNING)); } else { daoCategory.update(category); } updateCategories(); } public void doUpdateSettingsEntry(SettingsEntry settingsEntry) { daoSettingsEntry.update(settingsEntry); updateSettingsEntries(); } public void doDeleteCategory(Category category) { if (category.getTeachingMaterials().isEmpty()) { daoCategory.delete(category); } else { eventBus.post(new MessageEvent("Löschen nicht möglich!", "Kategorie wird noch verwendet.", Type.WARNING)); } updateCategories(); } /** * Updates {@code userName} and {@code userEmail} of the current {@link User}.<br> * Checks if username or email are already in use by another {@link User}. * * @param userName * @param userEmail */ @HandlesAction(DoUpdateUser.class) public void doUpdateUser(String userName, String userEmail) { if (userEmail.isEmpty()) userEmail = null; Collection<User> userWithSameNameOrPassword = daoUser.findAllWithCriteria( Restrictions.or( Restrictions.eq("username", userName), Restrictions.eq("email", userEmail))); if ((userWithSameNameOrPassword.size() == 1 && userWithSameNameOrPassword.contains(currentUser.get())) || userWithSameNameOrPassword.isEmpty()) { User user = currentUser.get(); user.setUsername(userName); user.setEmail(userEmail); daoUser.update(user); this.userName.set(userName); this.userEmail.set(userEmail); currentUser.notifyAllListeners(); eventBus.post(new MessageEvent("Daten wurden geändert")); } else { eventBus.post(new MessageEvent("Speichern fehlgeschlagen!", "Es existiert bereits ein Nutzer mit dem Nutzername oder der E-Mail-Adresse.", Type.WARNING)); } } /** * Tries to change the password of the currently logged in user. If one of * the parameters is empty, a {@link MessageEvent} is posted to the {@link EventBus}.<br> * If the new password and the new verified password do not match, a * {@link MessageEvent} is posted to the {@link EventBus}. * * After successfully checking the current password, the password is changed. * * @param currentPassword * String with the current password * @param newPassword * String with the new password * @param newPasswordVerified * String with the verified new password */ @HandlesAction(DoPasswordChange.class) public void doPasswordChange(String currentPassword, String newPassword, String newPasswordVerified) { User user = currentUser.get(); try { // Check if one of the fields is empty or the two new passwords do // not match if (currentPassword.isEmpty() || newPassword.isEmpty() || newPasswordVerified.isEmpty()) { eventBus.post(new MessageEvent("Leere Felder!", "Bitte alle Felder ausfüllen.", Type.WARNING)); } else if (!newPassword.equals(newPasswordVerified)) { eventBus.post(new MessageEvent( "Passwörter stimmen nicht überein!", "Die beiden neuen Passwörter stimmen nicht überein.", Type.WARNING)); } else if (!PasswordHash.validatePassword(currentPassword, user.getPassword())) { eventBus.post(new MessageEvent("Falsches Passwort!", "Das aktuelle Passwort ist nicht korrekt.", Type.WARNING)); } else { // Change the password in the database and update the user object user.setPassword(PasswordHash.createHash(newPassword)); daoUser.update(user); currentUser.notifyAllListeners(); // TODO: passwordChangeStatus.notifyAllListeners(); does not work passwordChangeStatus.set(null); passwordChangeStatus.set(ChangeStatus.SUCCESSFULL); eventBus.post(new MessageEvent("Passwort geändert")); } } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { LOG.warn(e.getMessage()); eventBus.post(new MessageEvent("Fehler bei der Passwortänderung!", "Bitte kontaktieren Sie einen Entwickler.", Type.WARNING)); } } public enum ChangeStatus { EMPTY_FIELDS, CURRENT_PASSWORD_WRONG, NEW_PASSWORD_NOT_EQUALS, NAME_OR_MAIL_ALREADY_EXISTS, FAILED, SUCCESSFULL; } }