/// <summary>Creates a new, empty SupportQueueThreadEntity object.</summary> /// <returns>A new, empty SupportQueueThreadEntity object.</returns> public override IEntity Create() { IEntity toReturn = new SupportQueueThreadEntity(); // __LLBLGENPRO_USER_CODE_REGION_START CreateNewSupportQueueThread // __LLBLGENPRO_USER_CODE_REGION_END return(toReturn); }
/// <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="adapter">The live adapter with an active transaction. Can be null, in which case a local transaction is used.</param> /// <remarks>first removes the thread from a queue if it's in a queue</remarks> public static async Task AddThreadToQueueAsync(int threadId, int queueId, int userId, IDataAccessAdapter adapter) { var localAdapter = adapter == null; var adapterToUse = adapter ?? new DataAccessAdapter(); try { if (localAdapter) { await adapterToUse.StartTransactionAsync(IsolationLevel.ReadCommitted, "AddThreadToQueue").ConfigureAwait(false); } // first remove the thread from any queue it's in. await RemoveThreadFromQueueAsync(threadId, adapterToUse); // then add it to the queue specified. var supportQueueThread = new SupportQueueThreadEntity { ThreadID = threadId, QueueID = queueId, PlacedInQueueByUserID = userId, PlacedInQueueOn = DateTime.Now }; await adapterToUse.SaveEntityAsync(supportQueueThread).ConfigureAwait(false); if (localAdapter) { adapterToUse.Commit(); } } catch { if (localAdapter) { adapterToUse.Rollback(); } throw; } finally { if (localAdapter) { adapterToUse.Dispose(); } } }
/// <summary> /// Releases the claim on the thread specified. As the thread can be in one queue at a time, it simply has to update the SupportQueueThread entity. /// </summary> /// <param name="threadID">The thread ID.</param> public static void ReleaseClaimOnThread(int threadID) { SupportQueueThreadEntity supportQueueThread = new SupportQueueThreadEntity(); supportQueueThread.FetchUsingUCThreadID(threadID); if (supportQueueThread.IsNew) { // not found, return return; } // simply reset an existing claim supportQueueThread.ClaimedByUserID = null; // nullable type, so set to null. supportQueueThread.ClaimedOn = null; // nullable type, so set to null. // done, save it supportQueueThread.Save(); }
/// <summary> /// Claims the thread specified for the user specified. As the thread can be in one queue at a time, it simply has to update the SupportQueueThread entity. /// </summary> /// <param name="userID">The user ID.</param> /// <param name="threadID">The thread ID.</param> public static void ClaimThread(int userID, int threadID) { SupportQueueThreadEntity supportQueueThread = new SupportQueueThreadEntity(); supportQueueThread.FetchUsingUCThreadID(threadID); if (supportQueueThread.IsNew) { // not found, return return; } // simply overwrite an existing claim if any. supportQueueThread.ClaimedByUserID = userID; supportQueueThread.ClaimedOn = DateTime.Now; // done, save it supportQueueThread.Save(); }
/// <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 support queue thread info entity and if specified, prefetches the user entity which claimed the related thread. /// </summary> /// <param name="threadID">The thread ID.</param> /// <param name="prefetchClaimUser">if set to true it will </param> /// <returns>fetched entity if found, otherwise null</returns> public static SupportQueueThreadEntity GetSupportQueueThreadInfo(int threadID, bool prefetchClaimUser) { SupportQueueThreadEntity toReturn = new SupportQueueThreadEntity(); PrefetchPath path = null; if (prefetchClaimUser) { // prefetch the user who claimed this thread (if any) path = new PrefetchPath((int)EntityType.SupportQueueThreadEntity); path.Add(SupportQueueThreadEntity.PrefetchPathClaimedByUser); } // now fetch the entity using the unique constraint on Thread by specifying the threadID passed in. Also specify the prefetch path (if any) toReturn.FetchUsingUCThreadID(threadID, path); if (toReturn.IsNew) { // not found return(null); } return(toReturn); }
/// <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 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 async Task <bool> DeleteUserAsync(int userId) { if (userId <= 1) { // can't delete the Anonymous user nor the admin user return(false); } var toDelete = await UserGuiHelper.GetUserAsync(userId); if (toDelete == null) { // user doesn't exist return(false); } if (toDelete.NickName == "Admin") { // can't delete admin return(false); } using (var adapter = new DataAccessAdapter()) { await adapter.StartTransactionAsync(IsolationLevel.ReadCommitted, "DeleteUser").ConfigureAwait(false); try { // we'll first update all PostedByUserId fields of all messages which are posted by the user to delete. var messageUpdater = new MessageEntity { PostedByUserID = 0 }; // reset to AC. await adapter.UpdateEntitiesDirectlyAsync(messageUpdater, new RelationPredicateBucket(MessageFields.PostedByUserID.Equal(userId))) .ConfigureAwait(false); // set the startuser of threads started by this user to 0 var threadUpdater = new ThreadEntity { StartedByUserID = 0 }; await adapter.UpdateEntitiesDirectlyAsync(threadUpdater, new RelationPredicateBucket(ThreadFields.StartedByUserID.Equal(userId))) .ConfigureAwait(false); // remove the user from the UserRoles set, as the user shouldn't be in any roles. await adapter.DeleteEntitiesDirectlyAsync(typeof(RoleUserEntity), new RelationPredicateBucket(RoleUserFields.UserID.Equal(userId))) .ConfigureAwait(false); // delete all bookmarks of user await adapter.DeleteEntitiesDirectlyAsync(typeof(BookmarkEntity), new RelationPredicateBucket(BookmarkFields.UserID.Equal(userId))) .ConfigureAwait(false); // delete all audit data // 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. var auditData = await adapter.FetchQueryAsync(new QueryFactory().User.Where(UserFields.UserID.Equal(userId))).ConfigureAwait(false); await adapter.DeleteEntityCollectionAsync(auditData).ConfigureAwait(false); // set IP bans set by this user to userid 0 var ipbanUpdater = new IPBanEntity { IPBanSetByUserID = 0 }; await adapter.UpdateEntitiesDirectlyAsync(ipbanUpdater, new RelationPredicateBucket(IPBanFields.IPBanSetByUserID.Equal(userId))) .ConfigureAwait(false); // delete threadsubscriptions await adapter.DeleteEntitiesDirectlyAsync(typeof(ThreadSubscriptionEntity), new RelationPredicateBucket(ThreadSubscriptionFields.UserID.Equal(userId))) .ConfigureAwait(false); // remove supportqueuethread claims await adapter.DeleteEntitiesDirectlyAsync(typeof(SupportQueueThreadEntity), new RelationPredicateBucket(SupportQueueThreadFields.ClaimedByUserID.Equal(userId))) .ConfigureAwait(false); // set all placed in queue references to userid 0, so the threads stay in the queues. var supportQueueThreadUpdater = new SupportQueueThreadEntity { PlacedInQueueByUserID = 0 }; await adapter.UpdateEntitiesDirectlyAsync(supportQueueThreadUpdater, new RelationPredicateBucket(SupportQueueThreadFields.PlacedInQueueByUserID.Equal(userId))) .ConfigureAwait(false); // now delete the actual user entity await adapter.DeleteEntityAsync(toDelete).ConfigureAwait(false); adapter.Commit(); return(true); } catch { adapter.Rollback(); throw; } } }
/// <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> /// Handles the Load event of the Page control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> private void Page_Load(object sender, System.EventArgs e) { int threadID = HnDGeneralUtils.TryConvertToInt(Request.QueryString["ThreadID"]); _thread = ThreadGuiHelper.GetThread(threadID); if (_thread == null) { // not found, return to start page Response.Redirect("default.aspx"); } // Check credentials bool userHasAccess = SessionAdapter.CanPerformForumActionRight(_thread.ForumID, ActionRights.AccessForum); if (!userHasAccess) { // doesn't have access to this forum. redirect Response.Redirect("default.aspx"); } _startMessageNo = HnDGeneralUtils.TryConvertToInt(Request.QueryString["StartAtMessage"]); bool highLightSearchResults = (HnDGeneralUtils.TryConvertToInt(Request.QueryString["HighLight"]) == 1); if (!_thread.IsClosed) { if (_thread.IsSticky) { _userMayAddNewMessages = SessionAdapter.CanPerformForumActionRight(_thread.ForumID, ActionRights.AddAndEditMessageInSticky); } else { _userMayAddNewMessages = SessionAdapter.CanPerformForumActionRight(_thread.ForumID, ActionRights.AddAndEditMessage); } // set show*link class members. These have to be set despite the postback status, as they're used in the repeater. Only set // them to true if the thread isn't closed. They've been initialized to false already. _showEditMessageLink = SessionAdapter.CanPerformForumActionRight(_thread.ForumID, ActionRights.EditDeleteOtherUsersMessages); _showDeleteMessageLink = _showEditMessageLink; _showQuoteMessageLink = _userMayAddNewMessages; } // show user IP addresses if the user has system admin rights, security admin rights or user admin rights. _showIPAddresses = (SessionAdapter.HasSystemActionRight(ActionRights.SystemManagement) || SessionAdapter.HasSystemActionRight(ActionRights.SecurityManagement) || SessionAdapter.HasSystemActionRight(ActionRights.UserManagement)); // Get the forum entity related to the thread. Use BL class. We could have used Lazy loading, though for the sake of separation, we'll // call into the BL class. ForumEntity forum = CacheManager.GetForum(_thread.ForumID); if (forum == null) { // not found, orphaned thread, return to default page. Response.Redirect("default.aspx"); } _forumAllowsAttachments = (forum.MaxNoOfAttachmentsPerMessage > 0); // check if the user can view this thread. If not, don't continue. if ((_thread.StartedByUserID != SessionAdapter.GetUserID()) && !SessionAdapter.CanPerformForumActionRight(_thread.ForumID, ActionRights.ViewNormalThreadsStartedByOthers) && !_thread.IsSticky) { // can't view this thread, it isn't visible to the user Response.Redirect("default.aspx", true); } _threadStartedByCurrentUser = (_thread.StartedByUserID == SessionAdapter.GetUserID()); _userMayAddAttachments = SessionAdapter.CanPerformForumActionRight(_thread.ForumID, ActionRights.AddAttachment); _userCanCreateThreads = SessionAdapter.CanPerformForumActionRight(_thread.ForumID, ActionRights.AddNormalThread) || SessionAdapter.CanPerformForumActionRight(_thread.ForumID, ActionRights.AddStickyThread); _userMayDoForumSpecificThreadManagement = SessionAdapter.CanPerformForumActionRight(_thread.ForumID, ActionRights.ForumSpecificThreadManagement); _userMayDoSystemWideThreadManagement = SessionAdapter.HasSystemActionRight(ActionRights.SystemWideThreadManagement); _userMayEditMemo = SessionAdapter.CanPerformForumActionRight(_thread.ForumID, ActionRights.EditThreadMemo); _userMayMarkThreadAsDone = (SessionAdapter.CanPerformForumActionRight(_thread.ForumID, ActionRights.FlagThreadAsDone) || _threadStartedByCurrentUser); _userMayManageSupportQueueContents = SessionAdapter.HasSystemActionRight(ActionRights.QueueContentManagement); _userMayDoBasicThreadOperations = (SessionAdapter.GetUserID() > 0); if (!Page.IsPostBack) { plPageListBottom.HighLight = highLightSearchResults; plPageListTop.HighLight = highLightSearchResults; litHighLightLogic.Visible = highLightSearchResults; if (highLightSearchResults) { // make highlighting of search results possible string searchTerms = SessionAdapter.GetSearchTerms(); if (searchTerms == null) { searchTerms = string.Empty; } this.ClientScript.RegisterHiddenField("searchTerms", searchTerms.Replace("AND", "").Replace("OR", "").Replace("and", "").Replace("or", "").Replace("\"", "")); } else { // replace hightlighting scriptblock. this.ClientScript.RegisterClientScriptBlock(this.GetType(), "onLoad", "<script language=\"javascript\" type=\"text/javascript\">function SearchHighlight() {}</script>"); } if (_userMayManageSupportQueueContents) { // fill support queue management area with data. SupportQueueCollection supportQueues = CacheManager.GetAllSupportQueues(); cbxSupportQueues.DataSource = supportQueues; cbxSupportQueues.DataBind(); SupportQueueEntity containingQueue = SupportQueueGuiHelper.GetQueueOfThread(_thread.ThreadID); if (containingQueue != null) { cbxSupportQueues.SelectedValue = containingQueue.QueueID.ToString(); // get claim info SupportQueueThreadEntity supportQueueThreadInfo = SupportQueueGuiHelper.GetSupportQueueThreadInfo(_thread.ThreadID, true); if ((supportQueueThreadInfo != null) && supportQueueThreadInfo.ClaimedByUserID.HasValue) { // claimed by someone lblClaimDate.Text = supportQueueThreadInfo.ClaimedOn.Value.ToString("dd-MMM-yyyy HH:mm.ss", DateTimeFormatInfo.InvariantInfo); lnkClaimerThread.Visible = true; lblNotClaimed.Visible = false; lnkClaimerThread.Text = supportQueueThreadInfo.ClaimedByUser.NickName; lnkClaimerThread.NavigateUrl += supportQueueThreadInfo.ClaimedByUserID.ToString(); btnClaim.Visible = false; btnRelease.Visible = true; } else { // not claimed lblClaimDate.Text = string.Empty; btnClaim.Visible = true; btnRelease.Visible = false; } } } phSupportQueueManagement.Visible = _userMayManageSupportQueueContents; if ((_thread.Memo.Length > 0) && _userMayEditMemo) { // convert memo contents to HTML so it's displayed above the thread. string parserLog, messageTextXml; bool errorsOccured = false; string memoAsHTML = TextParser.TransformUBBMessageStringToHTML(_thread.Memo, ApplicationAdapter.GetParserData(), out parserLog, out errorsOccured, out messageTextXml); lblMemo.Text = memoAsHTML; } phMemo.Visible = _userMayEditMemo; bool isBookmarked = UserGuiHelper.CheckIfThreadIsAlreadyBookmarked(SessionAdapter.GetUserID(), threadID); bool isSubscribed = UserGuiHelper.CheckIfThreadIsAlreadySubscribed(SessionAdapter.GetUserID(), threadID); btnBookmarkThread.Visible = !isBookmarked && _userMayDoBasicThreadOperations; btnUnbookmarkThread.Visible = isBookmarked && _userMayDoBasicThreadOperations; bool sendReplyNotifications = CacheManager.GetSystemData().SendReplyNotifications; btnSubscribeToThread.Visible = !isSubscribed && _userMayDoBasicThreadOperations && sendReplyNotifications; btnUnsubscribeFromThread.Visible = isSubscribed && _userMayDoBasicThreadOperations && sendReplyNotifications; // fill the page's content lnkThreads.Text = HttpUtility.HtmlEncode(forum.ForumName); lnkThreads.NavigateUrl += "?ForumID=" + _thread.ForumID; lblForumName_Header.Text = forum.ForumName; lblSectionName.Text = CacheManager.GetSectionName(forum.SectionID); // Check if the current user is allowed to add new messages to the thread. // these controls are not visible by default, show them if necessary if (_userMayDoForumSpecificThreadManagement || _userMayDoSystemWideThreadManagement) { if (!_thread.IsClosed && _userMayAddNewMessages) { lnkCloseThread.Visible = true; lnkCloseThread.NavigateUrl += "?ThreadID=" + threadID + "&StartAtMessage=" + _startMessageNo; } lnkEditThreadProperties.Visible = true; lnkEditThreadProperties.NavigateUrl += "?ThreadID=" + threadID; } if (_userMayDoSystemWideThreadManagement) { lnkMoveThread.Visible = true; lnkMoveThread.NavigateUrl += "?ThreadID=" + threadID; lnkDeleteThread.Visible = true; lnkDeleteThread.NavigateUrl += "?ThreadID=" + threadID; } btnThreadDone.Visible = _thread.MarkedAsDone; btnThreadNotDone.Visible = !_thread.MarkedAsDone; btnThreadDone.Enabled = _userMayMarkThreadAsDone; btnThreadNotDone.Enabled = _userMayMarkThreadAsDone; if (_userMayEditMemo) { lnkEditMemo.Visible = true; lnkEditMemo.NavigateUrl += "?ThreadID=" + threadID + "&StartAtMessage=" + _startMessageNo; } // These controls are visible by default. Hide them when the user can't create threads on this forum if (_userCanCreateThreads) { lnkNewThreadBottom.NavigateUrl += "?ForumID=" + _thread.ForumID + "&StartAtMessage=" + _startMessageNo; lnkNewThreadTop.NavigateUrl += "?ForumID=" + _thread.ForumID + "&StartAtMessage=" + _startMessageNo; } else { lnkNewThreadBottom.Visible = false; lnkNewThreadTop.Visible = false; } if (_userMayAddNewMessages) { lnkNewMessageBottom.NavigateUrl += "?ThreadID=" + threadID + "&StartAtMessage=" + _startMessageNo; lnkNewMessageTop.NavigateUrl += "?ThreadID=" + threadID + "&StartAtMessage=" + _startMessageNo; } else { lnkNewMessageBottom.Visible = false; lnkNewMessageTop.Visible = false; } lblSeparatorTop.Visible = (_userMayAddNewMessages && _userCanCreateThreads); lblSeparatorBottom.Visible = (_userMayAddNewMessages && _userCanCreateThreads); // The amount of postings in this thread are in the dataview row, which should contain just 1 row. int maxAmountMessagesPerPage = SessionAdapter.GetUserDefaultNumberOfMessagesPerPage(); int amountOfMessages = ThreadGuiHelper.GetTotalNumberOfMessagesInThread(threadID); int amountOfPages = ((amountOfMessages - 1) / maxAmountMessagesPerPage) + 1; int currentPageNo = (_startMessageNo / maxAmountMessagesPerPage) + 1; lblCurrentPage.Text = currentPageNo.ToString(); lblTotalPages.Text = amountOfPages.ToString(); lnkPrintThread.NavigateUrl += "?ThreadID=" + threadID; plPageListBottom.AmountMessages = amountOfMessages; plPageListBottom.StartMessageNo = _startMessageNo; plPageListBottom.ThreadID = threadID; plPageListTop.AmountMessages = amountOfMessages; plPageListTop.StartMessageNo = _startMessageNo; plPageListTop.ThreadID = threadID; // Get messages and bind it to the repeater control. Use the startmessage to get only the message visible on the current page. MessagesInThreadTypedList messages = ThreadGuiHelper.GetAllMessagesInThreadAsTypedList(threadID, currentPageNo, maxAmountMessagesPerPage); rptMessages.DataSource = messages; rptMessages.DataBind(); } }