/// <summary> /// Creates a new message in the given thread /// Caller should validate input parameters. /// </summary> /// <param name="threadID">Thread wherein the new message will be placed</param> /// <param name="userID">User who posted this message</param> /// <param name="messageText">Message text</param> /// <param name="messageAsHTML">Message text as HTML</param> /// <param name="userIDIPAddress">IP address of user calling this method</param> /// <param name="messageAsXML">Message text as XML, which is the result of the parse action on MessageText.</param> /// <param name="subscribeToThread">if set to <c>true</c> [subscribe to thread].</param> /// <param name="threadUpdatedNotificationTemplate">The thread updated notification template.</param> /// <param name="emailData">The email data.</param> /// <param name="sendReplyNotifications">Flag to signal to send reply notifications. If set to false no notifications are mailed, /// otherwise a notification is mailed to all subscribers to the thread the new message is posted in</param> /// <returns>MessageID if succeeded, 0 if not.</returns> public static int CreateNewMessageInThread(int threadID, int userID, string messageText, string messageAsHTML, string userIDIPAddress, string messageAsXML, bool subscribeToThread, string threadUpdatedNotificationTemplate, Dictionary<string,string> emailData, bool sendReplyNotifications) { Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "InsertNewMessage"); int messageID = 0; try { DateTime postingDate = DateTime.Now; messageID = InsertNewMessage(threadID, userID, messageText, messageAsHTML, userIDIPAddress, messageAsXML, trans, postingDate); MessageManager.UpdateStatisticsAfterMessageInsert(threadID, userID, trans, postingDate, true, subscribeToThread); trans.Commit(); } catch(Exception) { trans.Rollback(); throw; } finally { trans.Dispose(); } if(sendReplyNotifications) { // send notification email to all subscribers. Do this outside the transaction so a failed email send action doesn't terminate the save process // of the message. ThreadManager.SendThreadReplyNotifications(threadID, userID, threadUpdatedNotificationTemplate, emailData); } return messageID; }
/// <summary> /// Adds the thread with the ID specified to the support queue with the ID specified /// </summary> /// <param name="threadID">The thread ID.</param> /// <param name="queueID">The queue ID.</param> /// <param name="userID">The user ID of the user causing this thread to be placed in the queue specified.</param> /// <param name="transactionToUse">The transaction to use. Is not null if there's a transaction in progress.</param> /// <remarks>first removes the thread from a queue if it's in a queue</remarks> public static void AddThreadToQueue(int threadID, int queueID, int userID, Transaction transactionToUse) { // first remove the thread from any queue it's in. RemoveThreadFromQueue(threadID, transactionToUse); // then add it to the queue specified. SupportQueueThreadEntity supportQueueThread = new SupportQueueThreadEntity(); supportQueueThread.ThreadID = threadID; supportQueueThread.QueueID = queueID; supportQueueThread.PlacedInQueueByUserID = userID; supportQueueThread.PlacedInQueueOn = DateTime.Now; if(transactionToUse != null) { // transaction in progress, add the entity to the transaction transactionToUse.Add(supportQueueThread); } supportQueueThread.Save(); }
/// <summary> /// Gets the thread subscription object for the thread - user combination passed in. If there's no subscription, null is returned. /// </summary> /// <param name="threadID">The thread ID.</param> /// <param name="userID">The user ID.</param> /// <param name="transactionToUse">The transaction to use. Pass in the transaction object if this fetch has to take place inside a transaction</param> /// <returns> /// requested Threadsubscription entity or null if not found /// </returns> public static ThreadSubscriptionEntity GetThreadSubscription(int threadID, int userID, Transaction transactionToUse) { ThreadSubscriptionEntity toReturn = new ThreadSubscriptionEntity(); if(transactionToUse != null) { // transaction in progress, add entity to it so it's not blocked transactionToUse.Add(toReturn); } // fetch the data toReturn.FetchUsingPK(userID, threadID); if(toReturn.IsNew) { // not found return null; } // done. Don't commit a passed in transaction here, it's controlled by the caller. return toReturn; }
/// <summary> /// Marks the thread as done. /// </summary> /// <param name="threadID">Thread ID.</param> /// <returns></returns> public static bool MarkThreadAsDone(int threadID) { // load the entity from the database ThreadEntity thread = ThreadGuiHelper.GetThread(threadID); if(thread == null) { // not found return false; } // get the support queue the thread is in (if any) SupportQueueEntity containingSupportQueue = SupportQueueGuiHelper.GetQueueOfThread(threadID); thread.MarkedAsDone = true; // if the thread is in a support queue, the thread has to be removed from that queue. This is a multi-entity action and therefore we've to start a // transaction if that's the case. If not, we can use the easy route and simply save the thread and be done with it. if(containingSupportQueue == null) { // not in a queue, simply save the thread. return thread.Save(); } // in a queue, so remove from the queue and save the entity. Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "MarkThreadDone"); trans.Add(thread); try { // save the thread bool result = thread.Save(); if(result) { // save succeeded, so remove from queue, pass the current transaction to the method so the action takes place inside this transaction. SupportQueueManager.RemoveThreadFromQueue(threadID, trans); } trans.Commit(); return true; } catch { // rollback transaction trans.Rollback(); throw; } finally { trans.Dispose(); } }
/// <summary> /// Creates a new message in the given thread and closes the thread right after the addition of the message, /// which makes the just added message the 'close' message of the thread. Close messages are handy when the /// closure of a thread is not obvious. /// Caller should validate input parameters. /// </summary> /// <param name="threadID">Thread wherein the new message will be placed</param> /// <param name="userID">User who posted this message</param> /// <param name="messageText">Message text</param> /// <param name="messageAsHTML">Message text as HTML</param> /// <param name="userIDIPAddress">IP address of user calling this method</param> /// <param name="messageAsXML">Message text as XML, which is the result of the parse action on MessageText.</param> /// <param name="threadUpdatedNotificationTemplate">The thread updated notification template.</param> /// <param name="emailData">The email data.</param> /// <param name="sendReplyNotifications">Flag to signal to send reply notifications. If set to false no notifications are mailed, /// otherwise a notification is mailed to all subscribers to the thread the new message is posted in</param> /// <returns>MessageID if succeeded, 0 if not.</returns> public static int CreateNewMessageInThreadAndCloseThread(int threadID, int userID, string messageText, string messageAsHTML, string userIDIPAddress, string messageAsXML, string threadUpdatedNotificationTemplate, Dictionary<string, string> emailData, bool sendReplyNotifications) { Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "InsertNewMessage"); int messageID = 0; try { DateTime postingDate = DateTime.Now; messageID = InsertNewMessage(threadID, userID, messageText, messageAsHTML, userIDIPAddress, messageAsXML, trans, postingDate); MessageManager.UpdateStatisticsAfterMessageInsert(threadID, userID, trans, postingDate, false, false); ThreadEntity thread = new ThreadEntity(); trans.Add(thread); thread.FetchUsingPK(threadID); thread.IsClosed=true; thread.IsSticky=false; thread.MarkedAsDone = true; bool result = thread.Save(); if(result) { // save succeeded, so remove from queue, pass the current transaction to the method so the action takes place inside this transaction. SupportQueueManager.RemoveThreadFromQueue(threadID, trans); } trans.Commit(); } catch(Exception) { trans.Rollback(); throw; } finally { trans.Dispose(); } if(sendReplyNotifications) { // send notification email to all subscribers. Do this outside the transaction so a failed email send action doesn't terminate the save process // of the message. ThreadManager.SendThreadReplyNotifications(threadID, userID, threadUpdatedNotificationTemplate, emailData); } return messageID; }
/// <summary> /// Deletes the given Thread from the system, including <b>all</b> messages and related data in this Thread. /// </summary> /// <param name="ID">Thread to delete.</param> /// <returns>True if succeeded, false otherwise</returns> public static bool DeleteThread(int threadID) { // trying to delete the entity directly from the database without first loading it. // for that we use an entity collection and use the DeleteMulti method with a filter on the PK. PredicateExpression threadFilter = new PredicateExpression(ThreadFields.ThreadID == threadID); Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "DeleteThread"); try { DeleteThreads(threadFilter, trans); trans.Commit(); return true; } catch { // error occured trans.Rollback(); throw; } finally { trans.Dispose(); } }
/// <summary> /// Modifies the given role: it resets the system action rights for the given role to the given set of action rights and it modifies /// the role description for the given role. If the user specified a role description that is already available, false will be returned to signal /// that the save failed. /// </summary> /// <param name="actionRightIDs">The action rights.</param> /// <param name="roleID">The role ID.</param> /// <param name="roleDescription">The role description.</param> /// <returns>true if succeeded, false otherwise</returns> public static bool ModifyRole(List<int> actionRightIDs, int roleID, string roleDescription) { // read the existing role entity from the database. RoleEntity roleToModify = new RoleEntity(roleID); if(roleToModify.IsNew) { // not found return false; } // check if the description is different. If so, we've to check if the new roledescription is already present. If so, we'll // abort the save if(roleToModify.RoleDescription != roleDescription) { if(CheckIfRoleDescriptionIsPresent(roleDescription)) { // new description, is already present, fail return false; } } // all set. We're going to delete all Role - SystemAction Rights combinations first, as we're going to re-insert them later on. // We'll use a transaction to be able to roll back all our changes if something fails. Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "ModifyRole"); try { RoleSystemActionRightCollection roleActionRights = new RoleSystemActionRightCollection(); // add this collection to the transaction so all actions executed through this collection will be inside the transaction trans.Add(roleActionRights); // delete all role-systemactionright combinations directly from the database, by issuing a direct delete on the database, using a filter // on roleid roleActionRights.DeleteMulti(RoleSystemActionRightFields.RoleID == roleID); // add new role-systemactionright entities which we'll save to the database after that foreach(int actionRightID in actionRightIDs) { RoleSystemActionRightEntity toAdd = new RoleSystemActionRightEntity(); toAdd.ActionRightID = actionRightID; toAdd.RoleID = roleID; roleActionRights.Add(toAdd); } // save the new entities to the database roleActionRights.SaveMulti(); // we'll now save the role and the role description, if it's changed. Otherwise the save action will be a no-op. // add it to the transaction trans.Add(roleToModify); roleToModify.RoleDescription = roleDescription; roleToModify.Save(); // all done, commit the transaction trans.Commit(); return true; } catch { // failed, roll back transaction. trans.Rollback(); throw; } finally { trans.Dispose(); } }
/// <summary> /// Deletes the user with the ID passed in. Will reset all posts made by the user to the userid 0. /// </summary> /// <param name="userID">The user ID.</param> /// <remarks>Can't delete user 0</remarks> /// <returns>true if succeeded, false otherwise</returns> public static bool DeleteUser(int userID) { if(userID == 0) { // can't delete the Anonymous coward user. return false; } UserEntity toDelete = UserGuiHelper.GetUser(userID); if(toDelete==null) { // user doesn't exist return false; } // all actions have to take place in a transaction. Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "DeleteUser"); try { // we'll first update all PostedByUserId fields of all messages which are posted by the user to delete. MessageEntity messageUpdater = new MessageEntity(); messageUpdater.PostedByUserID = 0; // reset to AC. MessageCollection messages = new MessageCollection(); trans.Add(messages); // add to the transaction // update all entities directly in the DB, which match the following filter and update them with the new values set in messageUpdater. messages.UpdateMulti(messageUpdater, (MessageFields.PostedByUserID == userID)); // set the startuser of threads started by this user to 0 ThreadEntity threadUpdater = new ThreadEntity(); threadUpdater.StartedByUserID = 0; ThreadCollection threads = new ThreadCollection(); trans.Add(threads); threads.UpdateMulti(threadUpdater, (ThreadFields.StartedByUserID == userID)); // remove the user from the UserRoles set, as the user shouldn't be in any roles. RoleUserCollection roleUsersDeleter = new RoleUserCollection(); trans.Add(roleUsersDeleter); // delete all entities directly from the DB which match the following filter. roleUsersDeleter.DeleteMulti(RoleUserFields.UserID == userID); // delete all bookmarks of user BookmarkCollection bookmarkDeleter = new BookmarkCollection(); trans.Add(bookmarkDeleter); // delete all bookmarks for this user directly from the DB using the following filter. bookmarkDeleter.DeleteMulti(BookmarkFields.UserID == userID); // delete all audit data AuditDataCoreCollection auditDataDeleter = new AuditDataCoreCollection(); // first fetch it, then delete all entities from the collection, as the audit data is in an inheritance hierarchy of TargetPerEntity which can't // be deleted directly from the db. trans.Add(auditDataDeleter); auditDataDeleter.GetMulti(AuditDataCoreFields.UserID == userID); auditDataDeleter.DeleteMulti(); // set IP bans set by this user to userid 0 IPBanEntity ipbanUpdater = new IPBanEntity(); ipbanUpdater.IPBanSetByUserID = 0; IPBanCollection ipBans = new IPBanCollection(); trans.Add(ipBans); ipBans.UpdateMulti(ipbanUpdater, (IPBanFields.IPBanSetByUserID == userID)); // delete threadsubscriptions ThreadSubscriptionCollection threadSubscriptionsDeleter = new ThreadSubscriptionCollection(); trans.Add(threadSubscriptionsDeleter); threadSubscriptionsDeleter.DeleteMulti(ThreadSubscriptionFields.UserID == userID); // remove supportqueuethread claims SupportQueueThreadCollection supportQueueThreads = new SupportQueueThreadCollection(); trans.Add(supportQueueThreads); supportQueueThreads.DeleteMulti(SupportQueueThreadFields.ClaimedByUserID == userID); // set all placed in queue references to userid 0, so the threads stay in the queues. SupportQueueThreadEntity supportQueueThreadUpdater = new SupportQueueThreadEntity(); supportQueueThreadUpdater.PlacedInQueueByUserID=0; supportQueueThreads.UpdateMulti(supportQueueThreadUpdater, (SupportQueueThreadFields.PlacedInQueueByUserID == userID)); // now delete the actual user entity trans.Add(toDelete); toDelete.Delete(); // all done trans.Commit(); return true; } catch { trans.Rollback(); throw; } finally { trans.Dispose(); } }
/// <summary> /// Saves the audit actions for role specified. First removes all present rows for the roleid /// </summary> /// <param name="auditActionIDs">Audit action IDs.</param> /// <param name="roleID">Role ID.</param> /// <returns>true if the save action succeeded, false otherwise</returns> public static bool SaveAuditActionsForRole(List<int> auditActionIDs, int roleID) { RoleAuditActionCollection roleAuditActions = new RoleAuditActionCollection(); Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "SaveAuditActionsForRole"); // add this collection to the transaction so all actions executed through this collection will be inside the transaction trans.Add(roleAuditActions); try { // first remove the existing rows for the role roleAuditActions.DeleteMulti((RoleAuditActionFields.RoleID == roleID)); // THEN add new ones to the same collection. foreach(int auditActionID in auditActionIDs) { RoleAuditActionEntity newRoleAuditAction = new RoleAuditActionEntity(); newRoleAuditAction.AuditActionID = auditActionID; newRoleAuditAction.RoleID = roleID; roleAuditActions.Add(newRoleAuditAction); } // save all new entities roleAuditActions.SaveMulti(); // succeeded, commit transaction trans.Commit(); return true; } catch { // failed, rollback transaction trans.Rollback(); throw; } finally { trans.Dispose(); } }
/// <summary> /// Removes the thread with the threadid specified from the queue it's in. A thread can be in a single queue, so we don't need the queueID. /// </summary> /// <param name="threadID">The thread ID.</param> /// <param name="transactionToUse">The transaction currently in progress. Can be null if no transaction is in progress.</param> public static void RemoveThreadFromQueue(int threadID, Transaction transactionToUse) { // delete the SupportQueueThread entity for this thread directly from the db, inside the transaction specified (if applicable) SupportQueueThreadCollection supportQueueThreads = new SupportQueueThreadCollection(); if(transactionToUse != null) { // there's a transaction in progress, simply add it to the transaction transactionToUse.Add(supportQueueThreads); } // delete directly, using a filter on threadid supportQueueThreads.DeleteMulti((SupportQueueThreadFields.ThreadID == threadID)); // don't commit the current transaction if specified, simply return to caller. }
/// <summary> /// Marks the thread as un-done, and add it to the default queue of the forum. /// </summary> /// <param name="threadID">Thread ID</param> /// <returns></returns> public static bool UnMarkThreadAsDone(int threadID, int userID) { // load the entity from the database ThreadEntity thread = ThreadGuiHelper.GetThread(threadID); if (thread == null) { // not found return false; } thread.MarkedAsDone = false; ForumEntity forum = new ForumEntity(thread.ForumID); // Save the thread and add it to the default queue of the forum. Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "MarkThreadUnDone"); trans.Add(thread); try { thread.Save(); if ((forum.Fields.State == EntityState.Fetched) && (forum.DefaultSupportQueueID.HasValue)) { // not in a queue, and the forum has a default queue. Add the thread to the queue of the forum SupportQueueManager.AddThreadToQueue(threadID, forum.DefaultSupportQueueID.Value, userID, trans); } trans.Commit(); return true; } catch { // rollback transaction trans.Rollback(); throw; } finally { trans.Dispose(); } }
/// <summary> /// Deletes the threads matching the passed in filter, inside the transaction specified. /// </summary> /// <param name="threadFilter">The thread filter.</param> /// <param name="trans">The transaction to use.</param> private static void DeleteThreads(PredicateExpression threadFilter, Transaction trans) { // we've to perform a set of actions in a given order to make sure we're not violating FK constraints in the DB. // delete messages in thread MessageManager.DeleteAllMessagesInThreads(threadFilter, trans); // delete bookmarks (if exists) of the threads to be deleted BookmarkCollection bookmarks = new BookmarkCollection(); trans.Add(bookmarks); // use again a fieldcompareset predicate bookmarks.DeleteMulti(new FieldCompareSetPredicate(BookmarkFields.ThreadID, ThreadFields.ThreadID, SetOperator.In, threadFilter)); // delete audit info related to this thread. Can't be done directly on the db due to the fact the entities are in a TargetPerEntity hierarchy, which // can't be deleted directly on the db, so we've to fetch the entities first. AuditDataThreadRelatedCollection threadAuditData = new AuditDataThreadRelatedCollection(); trans.Add(threadAuditData); // use a fieldcompareset predicate filter, based on the threadFilter. threadAuditData.GetMulti(new FieldCompareSetPredicate(AuditDataThreadRelatedFields.ThreadID, ThreadFields.ThreadID, SetOperator.In, threadFilter)); threadAuditData.DeleteMulti(); // delete support queue thread entity for this thread (if any) SupportQueueThreadCollection supportQueueThreads = new SupportQueueThreadCollection(); trans.Add(supportQueueThreads); // use again a fieldcompareset predicate supportQueueThreads.DeleteMulti(new FieldCompareSetPredicate(SupportQueueThreadFields.ThreadID, ThreadFields.ThreadID, SetOperator.In, threadFilter)); // delete threadsubscription entities ThreadSubscriptionCollection threadSubscriptions = new ThreadSubscriptionCollection(); trans.Add(threadSubscriptions); // use again a fieldcompareset predicate threadSubscriptions.DeleteMulti(new FieldCompareSetPredicate(ThreadSubscriptionFields.ThreadID, ThreadFields.ThreadID, SetOperator.In, threadFilter)); // delete the threads ThreadCollection threads = new ThreadCollection(); trans.Add(threads); // we already have the filter to use, namely the filter passed in. threads.DeleteMulti(threadFilter); // don't commit the transaction, that's up to the caller. }
/// <summary> /// Deletes the given message from the database and the complete logged history. /// </summary> /// <param name="messageID">The ID of the message to delete</param> /// <returns>True if succeeded, false otherwise</returns> public static bool DeleteMessage(int messageID) { Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "DeleteMessage"); try { // formulate a filter so we can re-use the delete routine for all messages in threads matching a filter. PredicateExpression messageFilter = new PredicateExpression(MessageFields.MessageID == messageID); // call the routine which is used to delete 1 or more messages and related data from the db. DeleteMessages(messageFilter, trans); trans.Commit(); return true; } catch(Exception) { trans.Rollback(); throw; } finally { trans.Dispose(); } }
/// <summary> /// Deletes the messages matching the messagefilter passed in /// </summary> /// <param name="messageFilter">The message filter.</param> /// <param name="trans">The transaction to use.</param> private static void DeleteMessages(IPredicate messageFilter, Transaction trans) { // first delete all audit info for these message. This isn't done by a batch call directly on the db, as this is a targetperentity hierarchy // which can't be deleted directly into the database in all cases, so we first fetch the entities to delete. AuditDataMessageRelatedCollection messageAudits = new AuditDataMessageRelatedCollection(); trans.Add(messageAudits); // use a fieldcompareset predicate to get the auditdata related to this message and then delete all of them using the collection. messageAudits.GetMulti(new FieldCompareSetPredicate(AuditDataMessageRelatedFields.MessageID, MessageFields.MessageID, SetOperator.In, messageFilter)); messageAudits.DeleteMulti(); // delete all attachments for this message. This can be done directly onto the db. AttachmentCollection attachments = new AttachmentCollection(); trans.Add(attachments); // delete these directly from the db, using a fieldcompareset predicate attachments.DeleteMulti(new FieldCompareSetPredicate(AttachmentFields.MessageID, MessageFields.MessageID, SetOperator.In, messageFilter)); // delete the message/messages MessageCollection messages = new MessageCollection(); trans.Add(messages); // use the passed in filter to remove the messages messages.DeleteMulti(messageFilter); // don't commit the transaction, leave that to the caller. }
/// <summary> /// Updates the user/forum/thread statistics after a message insert. Also makes sure if the thread isn't in a queue and the forum has a default support /// queue that the thread is added to that queue /// </summary> /// <param name="threadID">The thread ID.</param> /// <param name="userID">The user ID.</param> /// <param name="transactionToUse">The transaction to use.</param> /// <param name="postingDate">The posting date.</param> /// <param name="addToQueueIfRequired">if set to true, the thread will be added to the default queue of the forum the thread is in, if the forum /// has a default support queue and the thread isn't already in a queue.</param> /// <remarks>Leaves the passed in transaction open, so it doesn't commit/rollback, it just performs a set of actions inside the /// passed in transaction.</remarks> internal static void UpdateStatisticsAfterMessageInsert(int threadID, int userID, Transaction transactionToUse, DateTime postingDate, bool addToQueueIfRequired, bool subscribeToThread) { // user statistics UserEntity userUpdater = new UserEntity(); // set the amountofpostings field to an expression so it will be increased with 1. userUpdater.Fields[(int)UserFieldIndex.AmountOfPostings].ExpressionToApply = (UserFields.AmountOfPostings + 1); UserCollection users = new UserCollection(); transactionToUse.Add(users); users.UpdateMulti(userUpdater, (UserFields.UserID == userID)); // update directly on the DB. // thread statistics ThreadEntity threadUpdater = new ThreadEntity(); threadUpdater.ThreadLastPostingDate = postingDate; threadUpdater.MarkedAsDone = false; ThreadCollection threads = new ThreadCollection(); transactionToUse.Add(threads); threads.UpdateMulti(threadUpdater, (ThreadFields.ThreadID == threadID)); // forum statistics. Load the forum from the DB, as we need it later on. Use a fieldcompareset predicate to fetch the forum as we don't know the // forumID as we haven't fetched the thread ForumCollection forums = new ForumCollection(); transactionToUse.Add(forums); // use a fieldcompare set predicate to select the forumid based on the thread. This filter is equal to // WHERE ForumID == (SELECT ForumID FROM Thread WHERE ThreadID=@ThreadID) var forumFilter = new FieldCompareSetPredicate( ForumFields.ForumID, ThreadFields.ForumID, SetOperator.Equal, (ThreadFields.ThreadID == threadID)); forums.GetMulti(forumFilter); ForumEntity containingForum = null; if(forums.Count>0) { // forum found. There's just one. containingForum = forums[0]; containingForum.ForumLastPostingDate = postingDate; // save the forum. Just save the collection forums.SaveMulti(); } if(addToQueueIfRequired) { // If the thread involved isn't in a queue, place it in the default queue of the forum (if applicable) SupportQueueEntity containingQueue = SupportQueueGuiHelper.GetQueueOfThread(threadID, transactionToUse); if((containingQueue == null) && (containingForum != null) && (containingForum.DefaultSupportQueueID.HasValue)) { // not in a queue, and the forum has a default queue. Add the thread to the queue of the forum SupportQueueManager.AddThreadToQueue(threadID, containingForum.DefaultSupportQueueID.Value, userID, transactionToUse); } } //subscribe to thread if indicated if(subscribeToThread) { UserManager.AddThreadToSubscriptions(threadID, userID, transactionToUse); } }
/// <summary> /// Deletes all messages in threads which match the passed in filter. /// </summary> /// <param name="threadFilter">The thread filter.</param> /// <param name="trans">The transaction to use.</param> internal static void DeleteAllMessagesInThreads(PredicateExpression threadFilter, Transaction trans) { // fabricate the messagefilter, based on the passed in filter on threads. We do this by creating a FieldCompareSetPredicate: // WHERE Message.ThreadID IN (SELECT ThreadID FROM Thread WHERE threadFilter) var messageFilter = MessageFields.ThreadID.In(new QueryFactory().Create() .Select(ThreadFields.ThreadID) .Where(threadFilter)); DeleteMessages(messageFilter, trans); }
/// <summary> /// Saves the given set of actionrights as the set of forum action rights for the given forum / role combination. /// It first removes all current action rights for that combination. /// </summary> /// <param name="actionRightIDs">List of actionrights to set of this role</param> /// <param name="roleID">Role to use</param> /// <param name="forumID">Forum to use</param> /// <returns>true if succeeded, false otherwise</returns> public static bool SaveForumActionRightsForForumRole(List<int> actionRightIDs, int roleID, int forumID) { ForumRoleForumActionRightCollection forumRightsPerRole = new ForumRoleForumActionRightCollection(); Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "SaveForumActionRights"); // add this collection to the transaction so all actions executed through this collection will be inside the transaction trans.Add(forumRightsPerRole); try { // first remove the existing rows for the role forumRightsPerRole.DeleteMulti((ForumRoleForumActionRightFields.RoleID == roleID).And(ForumRoleForumActionRightFields.ForumID == forumID)); // THEN add new ones foreach(int actionRightID in actionRightIDs) { ForumRoleForumActionRightEntity newForumRightPerRole = new ForumRoleForumActionRightEntity(); newForumRightPerRole.ActionRightID = actionRightID; newForumRightPerRole.ForumID = forumID; newForumRightPerRole.RoleID = roleID; forumRightsPerRole.Add(newForumRightPerRole); } // save the new entities forumRightsPerRole.SaveMulti(); // all done, commit transaction trans.Commit(); return true; } catch { // failed, rollback transaction trans.Rollback(); throw; } finally { trans.Dispose(); } }
/// <summary> /// Deletes all threads in the specified forum. /// </summary> /// <param name="forumFilter">The forum filter.</param> /// <param name="trans">The transaction to use.</param> internal static void DeleteAllThreadsInForum(int forumID, Transaction trans) { // fabricate the threadfilter, based on the passed in forumId. We do this by creating a FieldCompareValuePredicate: // WHERE Thread.ForumID = forumID PredicateExpression threadFilter = new PredicateExpression(); threadFilter.Add(ThreadFields.ForumID == forumID); DeleteThreads(threadFilter, trans); }
/// <summary> /// Gets the support queue of the thread with the threadID specified. /// </summary> /// <param name="threadID">The thread ID.</param> /// <param name="trans">The transaction currently in progress. Can be null if no transaction is in progress.</param> /// <returns> /// The requested supportqueue entity, or null if the thread isn't in a support queue. /// </returns> public static SupportQueueEntity GetQueueOfThread(int threadID, Transaction trans) { // the relation supportqueue - thread is stored in a SupportQueueThread entity. Use that entity as a filter for the support queue. If // that entity doesn't exist, the thread isn't in a supportqueue. var qf = new QueryFactory(); var q = qf.SupportQueueThread .From(QueryTarget.InnerJoin(qf.SupportQueue).On(SupportQueueThreadFields.QueueID == SupportQueueFields.QueueID)) .Where(SupportQueueThreadFields.ThreadID == threadID); // use a supportqueue collection to fetch the support queue, which will contain 0 or 1 entities after the fetch SupportQueueCollection supportQueues = new SupportQueueCollection(); // if a transaction has been specified, we've to add the collection to the transaction so the fetch takes place inside the same transaction so no // deadlocks occur on sqlserver if(trans != null) { trans.Add(supportQueues); } supportQueues.GetMulti(q); if(supportQueues.Count > 0) { // in a queue, return the instance return supportQueues[0]; } else { // not in a queue, return null return null; } }
/// <summary> /// Inserts a new message in thread given. All exceptions are passed upwards, caller has full control over transaction. /// </summary> /// <param name="threadID">Thread wherein the new message will be placed</param> /// <param name="userID">User who posted this message</param> /// <param name="messageText">Message text</param> /// <param name="messageAsHTML">Message text as HTML</param> /// <param name="userIDIPAddress">IP address of user calling this method</param> /// <param name="messageAsXML">Message text as XML, which is the result of the parse action on MessageText.</param> /// <param name="transactionToUse">the open transaction to use for saving this message.</param> /// <param name="postingDate">The posting date.</param> /// <returns>new messageid</returns> private static int InsertNewMessage(int threadID, int userID, string messageText, string messageAsHTML, string userIDIPAddress, string messageAsXML, Transaction transactionToUse, DateTime postingDate) { MessageEntity message = new MessageEntity(); message.MessageText = messageText; message.MessageTextAsHTML = messageAsHTML; message.PostedByUserID = userID; message.PostingDate = postingDate; message.ThreadID = threadID; message.PostedFromIP = userIDIPAddress; message.MessageTextAsXml = messageAsXML; transactionToUse.Add(message); bool result = message.Save(); if(result) { return message.MessageID; } else { return 0; } }
/// <summary> /// Creates a new thread in the given forum and places the passed in message as the first message in the thread. /// Caller should validate input parameters. /// </summary> /// <param name="forumID">Forum wherein the new thread will be placed</param> /// <param name="userID">User who started this thread</param> /// <param name="subject">Subject of the thread</param> /// <param name="messageText">First message of the thread</param> /// <param name="messageAsHTML">Message text as HTML</param> /// <param name="isSticky">Flag if the thread is sticky / pinned (true) or not (false)</param> /// <param name="userIDIPAddress">IP address of user calling this method</param> /// <param name="defaultSupportQueueID">The ID of the default support queue for this forum. If not null, the thread created will be /// added to the support queue with the ID specified.</param> /// <param name="messageID">The message ID of the new message, which is the start message in the thread.</param> /// <returns>ThreadID if succeeded, 0 if not.</returns> public static int CreateNewThreadInForum(int forumID, int userID, string subject, string messageText, string messageAsHTML, bool isSticky, string userIDIPAddress, int? defaultSupportQueueID, bool subscribeToThread, out int messageID) { Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "NewThread"); try { DateTime postDate = DateTime.Now; ThreadEntity newThread = new ThreadEntity(); newThread.ForumID = forumID; newThread.IsClosed = false; newThread.IsSticky = isSticky; newThread.StartedByUserID = userID; newThread.Subject = subject; newThread.ThreadLastPostingDate = postDate; if(defaultSupportQueueID.HasValue) { // a support queue has been specified as the default support queue for this forum. Add the new thread to this support queue. SupportQueueThreadEntity supportQueueThread = new SupportQueueThreadEntity(); supportQueueThread.QueueID = defaultSupportQueueID.Value; supportQueueThread.PlacedInQueueByUserID = userID; supportQueueThread.PlacedInQueueOn = DateTime.Now; // assign the Thread property to the newly created thread entity, so this supportqueuethreadentity will be part of the graph // of objects which will be saved. supportQueueThread.Thread = newThread; } DateTime postingDate = DateTime.Now; MessageEntity newMessage = new MessageEntity(); newMessage.MessageText = messageText; newMessage.MessageTextAsHTML = messageAsHTML; newMessage.PostedByUserID = userID; newMessage.PostingDate = postingDate; newMessage.PostedFromIP = userIDIPAddress; newMessage.Thread = newThread; // add the newMessage entity to the transaction object. All entities saved recursively will be added automatically trans.Add(newMessage); // save the complete graph newMessage.Save(true); messageID = newMessage.MessageID; int threadID = newMessage.ThreadID; // update thread statistics, this is the task for the message manager, and we pass the transaction object so the actions will run in // the same transaction. MessageManager.UpdateStatisticsAfterMessageInsert(threadID, userID, trans, postingDate, false, subscribeToThread); trans.Commit(); return newThread.ThreadID; } catch(Exception) { trans.Rollback(); throw; } finally { trans.Dispose(); } }
/// <summary> /// Deletes the support queue with the ID specified. /// </summary> /// <param name="queueID">The queue ID of the queue to delete.</param> /// <returns>true if succeeded, false otherwise</returns> /// <remarks>All threads in the queue are automatically de-queued and not in a queue anymore. The Default support queue /// for forums which have this queue as the default support queue is reset to null.</remarks> public static bool DeleteSupportQueue(int queueID) { // we'll do several actions in one atomic transaction, so start a transaction first. Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "DeleteSupportQ"); try { // first reset all the FKs in Forum to NULL if they point to this queue. ForumEntity forumUpdater = new ForumEntity(); // set the field to NULL. This is a nullable field, so we can just set the field to 'null', thanks to nullable types. forumUpdater.DefaultSupportQueueID = null; // update the entities directly in the db, use a forum collection for that ForumCollection forums = new ForumCollection(); trans.Add(forums); // specify a filter that only the forums which have this queue as the default queue are updated and have their FK field set to NULL. forums.UpdateMulti(forumUpdater, (ForumFields.DefaultSupportQueueID == queueID)); // delete all SupportQueueThread entities which refer to this queue. This will make all threads which are in this queue become queue-less. SupportQueueThreadCollection supportQueueThreads = new SupportQueueThreadCollection(); trans.Add(supportQueueThreads); // delete them directly from the db. supportQueueThreads.DeleteMulti((SupportQueueThreadFields.QueueID == queueID)); // it's now time to delete the actual supportqueue entity. SupportQueueCollection supportQueues = new SupportQueueCollection(); trans.Add(supportQueues); // delete it directly from the db. int numberOfQueuesDeleted = supportQueues.DeleteMulti((SupportQueueFields.QueueID == queueID)); // done so commit the transaction. trans.Commit(); return (numberOfQueuesDeleted > 0); } catch { // first roll back the transaction trans.Rollback(); // then bubble up the exception throw; } finally { trans.Dispose(); } }
/// <summary> /// Deletes the given forum from the system, including <b>all</b> threads in this forum and messages in those threads. /// </summary> /// <param name="forumID">Forum ID.</param> /// <returns>True if succeeded, false otherwise</returns> public static bool DeleteForum(int forumID) { // first all threads in this forum have to be removed, then this forum should be removed. Do this in one transaction. Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "DeleteForum"); try { PredicateExpression forumFilter = new PredicateExpression(); forumFilter.Add((ForumFields.ForumID == forumID)); // remove all threads in this forum ThreadManager.DeleteAllThreadsInForum(forumID, trans); // remove all ForumRoleForumActionRight entities for this forum ForumRoleForumActionRightCollection forumRoleActionRights = new ForumRoleForumActionRightCollection(); trans.Add(forumRoleActionRights); forumRoleActionRights.DeleteMulti(ForumRoleForumActionRightFields.ForumID == forumID); // remove the forum entity. do this by executing a direct delete statement on the database ForumCollection forums = new ForumCollection(); trans.Add(forums); forums.DeleteMulti(forumFilter); trans.Commit(); return true; } catch { // exception occured, rollback trans.Rollback(); throw; } finally { trans.Dispose(); } }
/// <summary> /// Subscribes the user specified to the thread specified for notifications. A transaction can be specified to save the information inside the /// transaction specified. If the user is already subscribed to this thread, nothing is done /// </summary> /// <param name="threadID">The thread ID.</param> /// <param name="userID">The user ID.</param> /// <param name="transactionToUse">The transaction to use. If no transaction is specified, no transaction is created</param> /// <returns></returns> public static bool AddThreadToSubscriptions(int threadID, int userID, Transaction transactionToUse) { // check if this user is already subscribed to this thread. If not, add a new subscription. ThreadSubscriptionEntity subscription = ThreadGuiHelper.GetThreadSubscription(threadID, userID, transactionToUse); if(subscription == null) { // user isn't yet subscribed, add the subscription subscription = new ThreadSubscriptionEntity(); subscription.UserID = userID; subscription.ThreadID = threadID; if(transactionToUse != null) { transactionToUse.Add(subscription); } return subscription.Save(); } // already subscribed, no-op. return true; }
/// <summary> /// Deletes the given role from the system. /// </summary> /// <param name="roleID">ID of role to delete</param> /// <returns>true if succeeded, false otherwise</returns> public static bool DeleteRole(int roleID) { RoleEntity toDelete = SecurityGuiHelper.GetRole(roleID); if(toDelete == null) { // not found return false; } Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "DeleteRole"); try { // remove the role - forum - action right entities ForumRoleForumActionRightCollection forumRoleActionRights = new ForumRoleForumActionRightCollection(); trans.Add(forumRoleActionRights); forumRoleActionRights.DeleteMulti(ForumRoleForumActionRightFields.RoleID == roleID); // Remove role-audit action entities RoleAuditActionCollection roleAuditActions = new RoleAuditActionCollection(); trans.Add(roleAuditActions); roleAuditActions.DeleteMulti(RoleAuditActionFields.RoleID == roleID); // remove Role - systemright entities RoleSystemActionRightCollection roleSystemRights = new RoleSystemActionRightCollection(); trans.Add(roleSystemRights); roleSystemRights.DeleteMulti(RoleSystemActionRightFields.RoleID == roleID); // remove Role - user entities RoleUserCollection roleUsers = new RoleUserCollection(); trans.Add(roleUsers); roleUsers.DeleteMulti(RoleUserFields.RoleID == roleID); // delete the actual role trans.Add(toDelete); toDelete.Delete(); trans.Commit(); return true; } catch { // error occured, rollback trans.Rollback(); throw; } finally { trans.Dispose(); } }