/// <summary> /// Aktualisiert die für die View relevanten Properties der aktuell /// verwalteten Gruppen-Instanz mit den Werten der übergebenen neuen /// Gruppen-Instanz. /// </summary> /// <param name="currentGroup">Die aktuell im ViewModel verwaltete Gruppe.</param> /// <param name="newGroup">Die neue Gruppen-Instanz.</param> private void updateViewRelatedGroupProperties(Group currentGroup, Group newGroup) { currentGroup.Name = newGroup.Name; currentGroup.Term = newGroup.Term; currentGroup.HasNewEvent = newGroup.HasNewEvent; }
/// <summary> /// Führt Aktualisierung der Gruppe aus. Es wird ermittelt welche Properties eine Aktualisierung /// erhalten haben. Die Aktualisierungen werden an den Server übermittelt, der die Aktualisierung auf /// dem Serverdatensatz ausführt und die Teilnehmer über die Änderung informiert. Aktualisiert lokal jedoch /// nicht die Benachrichtigungseinstellungen für die Gruppe. Hierfür gibt es eine separate Funktion. /// </summary> /// <param name="oldGroup">Der Datensatz der Gruppe vor der Aktualisierung.</param> /// <param name="newGroup">Der Datensatz mit aktualisierten Daten.</param> /// <param name="ignorePassword">Gibt an, ob das Passwort Property bei der Aktualisierung ignoriert werden soll.</param> /// <returns>Liefert true, wenn die Aktualisierung erfolgreich war, ansonsten false.</returns> /// <exception cref="ClientException">Wirft ClientException, wenn Fehler während des Aktualisierungsvorgangs auftritt.</exception> public async Task<bool> UpdateGroupAsync(Group oldGroup, Group newGroup, bool ignorePassword) { if (oldGroup == null || newGroup == null) return false; User localUser = getLocalUser(); if (localUser == null) return false; // Gruppen-Admin sollte nicht 0 sein, falls das der Fall ist, setzte ihn auf den alten Wert. if (newGroup.GroupAdmin == 0) { newGroup.GroupAdmin = oldGroup.GroupAdmin; } // Führe Validierung aus. Wenn Validierung fehlschlägt, kann abgebrochen werden. clearValidationErrors(); newGroup.ClearValidationErrors(); newGroup.ValidateAll(); if (newGroup.HasValidationErrors() && !ignorePassword) { reportValidationErrors(newGroup.GetValidationErrors()); return false; } else if (ignorePassword) { // Prüfe alle Properties bis auf Passwort separat. if (newGroup.HasValidationError("Name") || newGroup.HasValidationError("Term") || newGroup.HasValidationError("Description")) { Dictionary<string, string> validationErrors = newGroup.GetValidationErrors(); if (validationErrors.ContainsKey("Password")) { validationErrors.Remove("Password"); } reportValidationErrors(validationErrors); return false; } } // Erstelle ein Objekt für die Aktualisierung, welches die Daten enthält, die aktualisiert werden müssen. Group updatableGroupObj = prepareUpdatableGroupInstance(oldGroup, newGroup); if (updatableGroupObj == null) return false; // Keine Aktualisierung notwendig. // Hash Passwort bei Änderung. if (updatableGroupObj.Password != null) { HashingHelper.HashingHelper hashHelper = new HashingHelper.HashingHelper(); string hashedPassword = hashHelper.GenerateSHA256Hash(updatableGroupObj.Password); updatableGroupObj.Password = hashedPassword; } // Erstelle Json-Dokument für die Aktualisierung. string jsonContent = base.jsonParser.ParseGroupToJson(updatableGroupObj); if (jsonContent == null) { Debug.WriteLine("UpdateGroupAsync: Group object could not be translated to a json document."); throw new ClientException(ErrorCodes.JsonParserError, "Could not generate json document."); } // Server Request. string serverResponse = null; try { serverResponse = await groupAPI.SendUpdateGroupRequest( getLocalUser().ServerAccessToken, oldGroup.Id, jsonContent); } catch (APIException ex) { //Keine explizite Behandlung von GroupNotFound, GroupParticipantNotFound, ... hier, //da nur Admin normal diese Funktionalität ausführen kann. Debug.WriteLine("UpdateGroupAsync: Request failed. Error code: {0}, status code: {1}.", ex.ErrorCode, ex.ResponseStatusCode); throw new ClientException(ex.ErrorCode, ex.Message); } // Führe lokale Aktualisierung des Datensatzes aus. Group updatedGroup = base.jsonParser.ParseGroupFromJson(serverResponse); if (updatedGroup == null) { throw new ClientException(ErrorCodes.JsonParserError, "Couldn't parse server response."); } // Speichere neuen Datensatz ab. UpdateGroup(updatedGroup, false); return true; }
/// <summary> /// Bereitet ein Objekt vom Typ Group vor, welches alle Properties enthält, die sich geändert haben. /// Die Methode bekommt eine alte Version eines Group Objekts und eine neue Version und ermittelt /// dann die Properties, die eine Aktualisierung erhalten haben und schreibt diese in eine neue Group /// Instanz. Die von der Methode zurückgelieferte Group Instanz kann dann direkt für die Aktualisierung auf /// dem Server verwendet werden. Achtung: Hat sich überhaupt keine Property geändert, so gibt die Methode null zurück. /// </summary> /// <param name="oldGroup">Das Group Objekt vor der Aktualisierung.</param> /// <param name="newGroup">Das Group Objekt mit den aktuellen Werten.</param> /// <returns>Ein Objekt der Klasse Group, bei dem die Properties, die sich geändert haben, mit den /// aktualisierten Werten gefüllt sind.</returns> private Group prepareUpdatableGroupInstance(Group oldGroup, Group newGroup) { bool hasChanged = false; Group updatedGroup = new Group(); // Vergleiche die Properties. if (oldGroup.Name != newGroup.Name) { hasChanged = true; updatedGroup.Name = newGroup.Name; } if (oldGroup.Description != newGroup.Description) { hasChanged = true; updatedGroup.Description = newGroup.Description; } // Aktualisiere Passwort, wenn es in der neuen Instanz gesetzt ist. if (newGroup.Password != null) { hasChanged = true; updatedGroup.Password = newGroup.Password; } if (oldGroup.Term != newGroup.Term) { hasChanged = true; updatedGroup.Term = newGroup.Term; } if (oldGroup.GroupAdmin != newGroup.GroupAdmin) { hasChanged = true; updatedGroup.GroupAdmin = newGroup.GroupAdmin; } // Prüfe, ob sich überhaupt ein Property geändert hat. if (!hasChanged) { Debug.WriteLine("prepareUpdatableGroupInstance: No property of group has been updated. Method will return null."); updatedGroup = null; } return updatedGroup; }
/// <summary> /// Aktualisiert den lokalen Datensatz der Gruppe. /// </summary> /// <param name="updatedGroup">Objekt der Klasse Group mit den aktualisierten Daten.</param> /// <param name="updateNotificationSettings">Gibt an, ob ebenfalls die Benachrichtigungseinstellungen der Gruppe /// aktualisiert werden sollen.</param> public void UpdateGroup(Group updatedGroup, bool updateNotificationSettings) { try { groupDBManager.UpdateGroup(updatedGroup, updateNotificationSettings); } catch (ClientException ex) { Debug.WriteLine("UpdateGroup: Failed to update the group. Msg is: '{0}'.", ex.Message); throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message); } }
/// <summary> /// Der lokale Nutzer verlässt die Gruppe mit der angegebenen Id. /// Schickt einen Request an den Server, um den Teilnehmer von der Gruppe zu entfernen. /// Behandelt auch den Fall, dass der lokale Nutzer Administrator dieser Gruppe ist. In diesem Fall /// wird zunächst ein neuer Administrator bestimmt und die Gruppe aktualisiert. /// </summary> /// <param name="groupId">Die Id der Gruppe, aus der der lokale Nutzer austritt.</param> /// <exception cref="ClientException">Wirft Exception, wenn der Austritt fehlschlägt, oder der /// entsprechende Request vom Server abgelehnt wurde.</exception> public async Task LeaveGroupAsync(int groupId) { User localUser = getLocalUser(); bool hasLeft = false; // Hole die betroffene Gruppe aus dem lokalen Speicher. Group affectedGroup = GetGroup(groupId); // Sonderfallbehandlung: Nutzer ist Administrator der Gruppe. if (affectedGroup.GroupAdmin == localUser.Id) { // Frage in diesem Fall zunächst alle Teilnehmer der Gruppe vom Server ab, um sicher den aktuellsten Datensatz zu haben. List<User> participants = await GetParticipantsOfGroupAsync(groupId, false); // Sortiere alle "inaktiven" Nutzer aus. for (int i=0; i < participants.Count; i++) { if (!participants[i].Active) { participants.RemoveAt(i); } } if (participants.Count <= 1) { // Sonderfall: Es gibt nur einen Teilnehmer der Gruppe, den lokalen Nutzer. // Nutzer kann Gruppe nicht verlassen, da diese sonst leer wäre. throw new ClientException(ErrorCodes.GroupAdminNotAllowedToExit, "Administrator cannot leave group."); } // Ansonsten: Wähle zufälligen Nutzer und übertrage die Administrationsrechte. Random rnd = new Random(); User newAdmin = localUser; while (newAdmin.Id == localUser.Id) { int rndIndex = rnd.Next(0, participants.Count); newAdmin = participants[rndIndex]; } Group newGroup = new Group() { Id = affectedGroup.Id, Name = affectedGroup.Name, Description = affectedGroup.Description, CreationDate = affectedGroup.CreationDate, ModificationDate = affectedGroup.ModificationDate, GroupNotificationSetting = affectedGroup.GroupNotificationSetting, Deleted = affectedGroup.Deleted, Participants = affectedGroup.Participants, Password = affectedGroup.Password, Term = affectedGroup.Term, Type = affectedGroup.Type }; newGroup.GroupAdmin = newAdmin.Id; // Führe Aktualisierung aus. bool successful = await UpdateGroupAsync(affectedGroup, newGroup, true); if (!successful) { // Konnte Rechte nicht übertragen. throw new ClientException(ErrorCodes.GroupAdminRightsTransferHasFailed, "Couldn't transfer admin rights."); } } // Aus Gruppe austreten. try { // Sende Request an den Server, um lokalen Nutzer von der Gruppe zu entfernen. await groupAPI.SendLeaveGroupRequest( localUser.ServerAccessToken, groupId, localUser.Id); hasLeft = true; Debug.WriteLine("LeaveGroupAsync: Successfully left group with id {0}.", groupId); } catch (APIException ex) { if (ex.ErrorCode == ErrorCodes.GroupNotFound) { Debug.WriteLine("LeaveGroupAsync: The group with id {0} seems to be deleted " + "from the server. The leaving process can then be considered successful."); hasLeft = true; } else if (ex.ErrorCode == ErrorCodes.GroupParticipantNotFound) { Debug.WriteLine("LeaveGroupAsync: The user seems to be already removed from the group. " + "The leaving process can then be considered successful."); hasLeft = true; } else { throw new ClientException(ex.ErrorCode, ex.Message); } } // Entferne die Gruppe aus den lokalen Datensätzen. Zugehörige Daten werden kaskadierend gelöscht. if (hasLeft) { DeleteGroupLocally(groupId); } }
/// <summary> /// Speichert die Daten der Gruppe in den lokalen Datensätzen ab. /// </summary> /// <param name="group">Die zu speichernde Gruppe.</param> /// <exception cref="ClientException">Wenn die Speicherung fehlschlägt.</exception> public void StoreGroupLocally(Group group) { try { if (!groupDBManager.IsGroupStored(group.Id)) { groupDBManager.StoreGroup(group); } else { Debug.WriteLine("StoreGroupLocally: Group is already locally stored."); } } catch (DatabaseException ex) { Debug.WriteLine("StoreGroupLocally: Failed to store the group."); throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message); } }
/// <summary> /// Anlegen einer neuen Gruppe im System. Die Daten der Gruppe /// werden in Form eines Objekts der Klasse Group übergeben. Ein Request /// zum Anlegen der Gruppe auf dem Server wird ausgeführt und die erzeugte /// Ressource lokal gespeichert. /// </summary> /// <param name="newGroup">Das Objekt mit den Daten der neuen Gruppe.</param> /// <returns>Liefert true, wenn das Anlegen erfolgreich war, ansonsten false.</returns> /// <exception cref="ClientException">Wirft ClientException, wenn Erstellungsvorgang fehlschlägt.</exception> public async Task<bool> CreateGroupAsync(Group newGroup) { if (newGroup == null) return false; // Hole das lokale Nutzerobjekt. User localUser = getLocalUser(); // Setze Nutzer als Gruppenadmin. newGroup.GroupAdmin = localUser.Id; // Führe Validierung durch. clearValidationErrors(); newGroup.ClearValidationErrors(); newGroup.ValidateAll(); if (newGroup.HasValidationErrors()) { // Melde Validierungsfehler. reportValidationErrors(newGroup.GetValidationErrors()); // Breche ab. return false; } // Hashe das Passwort, das der Nutzer eingegeben hat. HashingHelper.HashingHelper hashingHelper = new HashingHelper.HashingHelper(); newGroup.Password = hashingHelper.GenerateSHA256Hash(newGroup.Password); // Umwandeln des Objekts in Json. string jsonContent = jsonParser.ParseGroupToJson(newGroup); if (jsonContent == null) { Debug.WriteLine("Error during serialization from group object to json string. Could " + "not create a group. Execution is aborted."); throw new ClientException(ErrorCodes.JsonParserError, "Failed to generate json document."); } // Absetzen des Erstellungsrequests. string serverResponse = null; try { // Setzte Request zum Anlegen eines Kanals ab. serverResponse = await groupAPI.SendCreateGroupRequest( localUser.ServerAccessToken, jsonContent); } catch (APIException ex) { // Bilde ab auf ClientException. throw new ClientException(ex.ErrorCode, "Server rejected create group request."); } // Extrahiere Group Objekt aus Antwort des Servers. Group responseGroupObj = jsonParser.ParseGroupFromJson(serverResponse); if (responseGroupObj != null) { // Speichere Gruppe lokal ab. try { // Prüfe, ob Gruppenadmin (lokaler Nutzer in diesem Fall) als User lokal bereits gespeichert ist. if (!userController.IsUserLocallyStored(localUser.Id)) { // Speichere lokalen Nutzer in den lokalen Nutzerdatensätzen für Gruppen ab. // Wird benötigt, da sonst Foreign Key constraints verletzt werden beim Speichern der Gruppe. userController.StoreUserLocally(localUser); } groupDBManager.StoreGroup(responseGroupObj); // Füge Teilnehmer der Gruppe hinzu. AddParticipantToGroup(responseGroupObj.Id, getLocalUser()); } catch (DatabaseException ex) { Debug.WriteLine("Database Exception, message is: {0}.", ex.Message); // Bilde ab auf ClientException. throw new ClientException(ErrorCodes.LocalDatabaseException, "Storing process of " + "created group object in local DB has failed"); } } else { throw new ClientException(ErrorCodes.JsonParserError, "Parsing of server response has failed."); } return true; }
/// <summary> /// Aktualisiert die für die View relevanten Eigenschaften der Gruppeninstanzen. /// Führt somit eine Aktualisierung der Anzeige aus. /// Muss im UI-Thread ausgeführt werden. /// </summary> /// <param name="oldGroup">Das derzeit an die View gebundenen Gruppen Objekt.</param> /// <param name="newGroup">Das Gruppen Objekt mit den aktuellen Daten.</param> private void updateViewRelatedGroupProperties(Group oldGroup, Group newGroup) { oldGroup.Name = newGroup.Name; oldGroup.Description = newGroup.Description; oldGroup.Term = newGroup.Term; oldGroup.CreationDate = newGroup.CreationDate; oldGroup.ModificationDate = newGroup.ModificationDate; oldGroup.GroupAdmin = newGroup.GroupAdmin; oldGroup.Participants = newGroup.Participants; if (oldGroup.Deleted != newGroup.Deleted) { oldGroup.Deleted = newGroup.Deleted; checkCommandExecution(); } }
/// <summary> /// Erzeugt eine Instanz der Klasse Group aus den Daten /// der Eingabefelder. /// </summary> /// <returns>Eine Instanz von Group.</returns> private Group createGroupFromEnteredData() { string term = string.Empty; if (IsNoTermSelected) { // Semester ignorieren. term = null; } else if (IsWinterTermSelected) { term = "W" + TermYear; } else if (IsSummerTermSelected) { term = "S" + TermYear; } // Erzeuge neue Gruppeninstanz aus eingegebenen Daten. Group newGroup = new Group() { Name = GroupName, Description = GroupDescription, Term = term, Type = SelectedGroupType, Password = GroupPassword }; // Setze Passwort null, falls es nicht eingegeben wurde. if (newGroup.Password != null && newGroup.Password.Trim().Length == 0) newGroup.Password = null; return newGroup; }
/// <summary> /// Wandelt ein Objekt der Klasse Group in ein entsprechendes JSON Dokument um. /// </summary> /// <param name="group">Das umzuwandelnde Group Objekt.</param> /// <returns>Ein JSON Dokument in Form eines Strings. Oder null, wenn die Umwandlung fehlgeschlagen hat.</returns> public string ParseGroupToJson(Group group) { string groupJson = null; try { groupJson = JsonConvert.SerializeObject(group); } catch (JsonException ex) { Debug.WriteLine("ParseGroupToJson: Json parser error occurred. " + "Message is {0}.", ex.Message); } return groupJson; }