/// <summary> /// Marks the thread as done. /// </summary> /// <param name="threadId">Thread ID.</param> /// <returns></returns> public static async Task <bool> MarkThreadAsDoneAsync(int threadId) { var thread = await ThreadGuiHelper.GetThreadAsync(threadId); if (thread == null) { // not found return(false); } var containingSupportQueue = await SupportQueueGuiHelper.GetQueueOfThreadAsync(threadId); thread.MarkedAsDone = true; using (var adapter = new DataAccessAdapter()) { // 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(await adapter.SaveEntityAsync(thread).ConfigureAwait(false)); } // in a queue, so remove from the queue and save the entity. await adapter.StartTransactionAsync(IsolationLevel.ReadCommitted, "MarkThreadDone").ConfigureAwait(false); try { // save the thread var result = await adapter.SaveEntityAsync(thread).ConfigureAwait(false); if (result) { // save succeeded, so remove from queue, pass the current adapter to the method so the action takes place inside this transaction. await SupportQueueManager.RemoveThreadFromQueueAsync(threadId, adapter); } adapter.Commit(); return(true); } catch { // rollback transaction adapter.Rollback(); throw; } } }
/// <summary> /// Moves the given thread to the given forum. /// </summary> /// <param name="threadId">ID of thread to move</param> /// <param name="newForumId">ID of forum to move the thread to</param> /// <returns></returns> public static async Task <bool> MoveThreadAsync(int threadId, int newForumId) { var thread = await ThreadGuiHelper.GetThreadAsync(threadId); if (thread == null) { // not found return(false); } thread.ForumID = newForumId; using (var adapter = new DataAccessAdapter()) { return(await adapter.SaveEntityAsync(thread).ConfigureAwait(false)); } }
/// <summary> /// Updates the memo field for the given thread /// </summary> /// <param name="threadId">Thread ID.</param> /// <param name="memo">Memo.</param> /// <returns></returns> public static async Task <bool> UpdateMemoAsync(int threadId, string memo) { // load the entity from the database var thread = await ThreadGuiHelper.GetThreadAsync(threadId); if (thread == null) { // not found return(false); } using (var adapter = new DataAccessAdapter()) { thread.Memo = memo; return(await adapter.SaveEntityAsync(thread).ConfigureAwait(false)); } }
/// <summary> /// Marks the thread as un-done, and add it to the default queue of the forum. /// </summary> /// <param name="threadId">Thread ID</param> /// <param name="userId">the user adding the thread to a queue by unmarking it as done </param> /// <returns></returns> public static async Task <bool> UnMarkThreadAsDoneAsync(int threadId, int userId) { var thread = await ThreadGuiHelper.GetThreadAsync(threadId); if (thread == null) { // not found return(false); } thread.MarkedAsDone = false; using (var adapter = new DataAccessAdapter()) { // Save the thread and add it to the default queue of the forum. await adapter.StartTransactionAsync(IsolationLevel.ReadCommitted, "MarkThreadUnDone").ConfigureAwait(false); try { await adapter.SaveEntityAsync(thread, true).ConfigureAwait(false); var q = new QueryFactory().Forum.Where(ForumFields.ForumID.Equal(thread.ForumID)); var forum = await adapter.FetchFirstAsync(q).ConfigureAwait(false); if (forum?.DefaultSupportQueueID != null) { // not in a queue, and the forum has a default queue. Add the thread to the queue of the forum await SupportQueueManager.AddThreadToQueueAsync(threadId, forum.DefaultSupportQueueID.Value, userId, adapter); } adapter.Commit(); return(true); } catch { // rollback transaction adapter.Rollback(); throw; } } }
/// <summary> /// Modifies the given properties of the given thread. /// </summary> /// <param name="threadId">The threadID of the thread which properties should be changed</param> /// <param name="subject">The new subject for this thread</param> /// <param name="isSticky">The new value for IsSticky</param> /// <param name="isClosed">The new value for IsClosed</param> public static async Task ModifyThreadPropertiesAsync(int threadId, string subject, bool isSticky, bool isClosed) { var thread = await ThreadGuiHelper.GetThreadAsync(threadId); if (thread == null) { // not found return; } // update the fields with new values thread.Subject = subject; thread.IsSticky = isSticky; thread.IsClosed = isClosed; using (var adapter = new DataAccessAdapter()) { await adapter.StartTransactionAsync(IsolationLevel.ReadCommitted, "ModifyThreadProperties"); try { var result = await adapter.SaveEntityAsync(thread).ConfigureAwait(false); if (result) { // save succeeded, so remove from queue, pass the current transaction to the method so the action takes place inside this transaction. await SupportQueueManager.RemoveThreadFromQueueAsync(threadId, adapter); } adapter.Commit(); } catch { adapter.Rollback(); throw; } } }
/// <summary> /// Sends email to all users subscribed to a specified thread, except the user who initiated the thread update. /// </summary> /// <param name="threadId">The thread that was updated.</param> /// <param name="initiatedByUserId">The user who initiated the update (who will not receive notification).</param> /// <param name="emailData">The email data.</param> private static async Task SendThreadReplyNotifications(int threadId, int initiatedByUserId, Dictionary <string, string> emailData) { // get list of subscribers to thread, minus the initiator. Do this by fetching the subscriptions plus the related user entity entity instances. // The related user entities are loaded using a prefetch path. var q = new QueryFactory().ThreadSubscription .Where((ThreadSubscriptionFields.ThreadID.Equal(threadId)).And(ThreadSubscriptionFields.UserID.NotEqual(initiatedByUserId))) .WithPath(ThreadSubscriptionEntity.PrefetchPathUser); var subscriptions = new EntityCollection <ThreadSubscriptionEntity>(); using (var adapter = new DataAccessAdapter()) { await adapter.FetchQueryAsync(q, subscriptions).ConfigureAwait(false); if (subscriptions.Count <= 0) { // no subscriptions, nothing to do return; } } // now collect all email addresses into an array so we can pass that to the email routine. var toAddresses = new string[subscriptions.Count]; for (var i = 0; i < subscriptions.Count; i++) { toAddresses[i] = subscriptions[i].User.EmailAddress; } // use template to construct message to send. var emailTemplate = emailData.GetValue("emailTemplate") ?? string.Empty; var mailBody = SD.HnD.Utility.StringBuilderCache.Acquire(emailTemplate.Length + 256); mailBody.Append(emailTemplate); var applicationURL = emailData.GetValue("applicationURL") ?? string.Empty; if (!string.IsNullOrEmpty(emailTemplate)) { // Use the existing template to format the body var siteName = emailData.GetValue("siteName") ?? string.Empty; var thread = await ThreadGuiHelper.GetThreadAsync(threadId); if (thread == null) { // thread doesn't exist, exit return; } var threadSubject = thread.Subject; var threadUrl = string.Format("{0}Thread/{1}", applicationURL, thread.ThreadID); mailBody.Replace("[SiteURL]", applicationURL); mailBody.Replace("[SiteName]", siteName); mailBody.Replace("[ThreadSubject]", threadSubject); mailBody.Replace("[ThreadURL]", threadUrl); } // format the subject var subject = (emailData.GetValue("emailThreadNotificationSubject") ?? string.Empty); var fromAddress = emailData.GetValue("defaultFromEmailAddress") ?? string.Empty; try { //send message await HnDGeneralUtils.SendEmail(subject, SD.HnD.Utility.StringBuilderCache.GetStringAndRelease(mailBody), fromAddress, toAddresses, emailData) .ConfigureAwait(false); } catch (SmtpFailedRecipientsException) { // swallow as it shouldn't have any effect on further operations } catch (SmtpException) { // swallow, as there's nothing we can do } catch (SmtpCommandException) { // recipient didn't exist, forget it. } // rest: problematic, so bubble upwards. }