 Rank: YAF CommanderJoined: 4/11/2008(UTC) Posts: 91
Thanks: 10 times Was thanked: 3 time(s) in 3 post(s)
|
I added more statistical data to the forum. These data are the visitors count in the last 24 hours and the visitor count in the last 30 days. These are important short term and long term trends for the site admin to have. I also listed the visitors that visited in the last 24 hours as shown below.  I would like to use Styled Nick. I can get the data from the DB but would like to use existing code to promote reusability so I added the following code that is basically like the "active users" but I used the length of one day instead of the minutes from the Board Settings. Code: //Tommy
{
DataTable activeUsersOneDay = this.Get<IDataCache>().GetOrSet(
"VisitorsInTheLast24Hours",
() => this.Get<IDBBroker>().GetActiveList(60 * 24, false, false),
TimeSpan.FromMilliseconds(this.Get<YafBoardSettings>().OnlineStatusCacheTimeout));
this.activeUsersOneDay.ActiveUserTable = activeUsersOneDay;
}
However, this code caused the control ID conflict error below. Shouldn't the "ActiveUsers" control be able to have multiple instances? What change can I make to allow that control to be able to have multiple instances in the same page?
Multiple controls with the same ID 'UserLink2' were found. FindControl requires that controls have unique IDs. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Web.HttpException: Multiple controls with the same ID 'UserLink2' were found. FindControl requires that controls have unique IDs.
Source Error:
Line 376: CodeContracts.ArgumentNotNull(id, "id"); Line 377: Line 378: Control foundControl = sourceControl.FindControl(id); Line 379: Line 380: if (foundControl == null)
|
 1 user thanked tommy382 for this useful post.
|
|
|
 Rank: YAF CommanderJoined: 4/11/2008(UTC) Posts: 91
Thanks: 10 times Was thanked: 3 time(s) in 3 post(s)
|
I got it working. The styled nicks look good and they are linkable too.  Here are my changes in case someone want to incorporate this into YAF. I just use English but one might want to define the messages in other languages and use localization XML for non-English users. ActiveUsers.cs - This fixes the control ID conflict noted above: Code:
if (!isCrawler)
{
userLink.ID = "UserLink" + this.InstantId + userLink.UserID;
}
else
{
userLink.ID += userLink.ReplaceName;
}
ForumStatistics.ascx.cs: Code: private void ForumStatistics_Load([NotNull] object sender, [NotNull] EventArgs e)
{
...
// "Active Users" Count and Most Users Count
DataRow activeStats = LegacyDb.active_stats(this.PageContext.PageBoardID);
this.ActiveUserCount.Text = this.FormatActiveUsers(activeStats);
//Tommy
{
DataTable activeUsers1Day = this.Get<IDataCache>().GetOrSet(
"VisitorsInTheLast24Hours",
() => this.Get<IDBBroker>().GetRecentUsers(60 * 24),
TimeSpan.FromMinutes(this.Get<YafBoardSettings>().ForumStatisticsCacheTimeout));
this.activeUsersOneDay.ActiveUserTable = activeUsers1Day;
DataTable activeUsers30Day = this.Get<IDataCache>().GetOrSet(
"VisitorsInTheLast30Days",
() => this.Get<IDBBroker>().GetRecentUsers(60 * 24 * 30),
TimeSpan.FromMinutes(this.Get<YafBoardSettings>().ForumStatisticsCacheTimeout));
ltrRecentUsers.Text =
string.Format("Members online within the last 24 hours is {0}, within the last 30 days is {1}.",
activeUsers1Day.Rows.Count,
activeUsers30Day.Rows.Count);
}
IDBBroker.cs: Code: /// <summary>
/// Get the list of recently logged in users.
/// </summary>
/// <param name="timeSinceLastLogin">Time since last login in minutes.</param>
/// <returns>The DataTable of the users.</returns>
DataTable GetRecentUsers(int timeSinceLastLogin);
YafDBBroker.cs: Code: /// <summary>
/// Get the list of recently logged in users.
/// </summary>
/// <param name="timeSinceLastLogin">The time since last login in minutes.</param>
/// <returns>The list of users in Datatable format.</returns>
public DataTable GetRecentUsers(int timeSinceLastLogin)
{
return
this.StyleTransformDataTable(
LegacyDb.recent_users(
YafContext.Current.PageBoardID, timeSinceLastLogin,
this.Get<YAF.Classes.YafBoardSettings>().UseStyledNicks));
}
LegacyDb.cs: Code: public static DataTable recent_users(object boardID, int timeSinceLastLogin, object styledNicks)
{
using (var cmd = MsSqlDbAccess.GetCommand("recent_users"))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("BoardID", boardID);
cmd.Parameters.AddWithValue("TimeSinceLastLogin", timeSinceLastLogin);
cmd.Parameters.AddWithValue("StyledNicks", styledNicks);
return MsSqlDbAccess.Current.GetData(cmd);
}
}
New DB Stored Procedure: Code:USE [YafForum]
GO
/****** Object: StoredProcedure [dbo].[yaf_recent_users] Script Date: 03/12/2011 03:45:32 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[yaf_recent_users](@BoardID int,@TimeSinceLastLogin int,@StyledNicks bit=0) as
begin
SELECT U.UserId,
IsCrawler = 0,
UserCount = 1,
IsHidden = (IsActiveExcluded),
Style = CASE(@StyledNicks)
WHEN 1 THEN
ISNULL ((SELECT TOP 1 G.Style
FROM [dbo].[yaf_UserGroup] AS UG
JOIN [dbo].[yaf_Group] G on G.GroupID=UG.GroupID
WHERE UG.UserID=U.UserID AND LEN(G.Style) > 2
ORDER BY G.SortOrder), '')
ELSE ''
END
FROM [dbo].[yaf_User] AS U
JOIN [dbo].[yaf_Rank] R on R.RankID=U.RankID
WHERE (U.IsApproved = '1') AND
U.BoardID = @BoardID AND
(DATEADD(mi, 0 - @TimeSinceLastLogin, GETDATE()) < U.LastVisit) AND
--Excluding guests
NOT EXISTS(
SELECT 1
FROM [dbo].[yaf_UserGroup] x
inner join [dbo].[yaf_Group] y ON y.GroupID=x.GroupID
WHERE x.UserID=U.UserID and (y.Flags & 2)<>0
)
ORDER BY U.LastVisit
end
Edited by user Wednesday, March 16, 2011 9:46:20 AM(UTC)
| Reason: Not specified
|
|
|
|
 Rank: YAF DeveloperJoined: 10/21/2008(UTC) Posts: 1,558   Location: Moscow Thanks: 51 times Was thanked: 287 time(s) in 253 post(s)
|
tommy382
[thumbup] .Just a notion -you use OnlineStatusCacheTimeout . No need to update the cache so often - it only overloads your server.. |
|
|
|
|
 Rank: YAF LeaderJoined: 1/14/2010(UTC) Posts: 924
Thanks: 249 times Was thanked: 170 time(s) in 161 post(s)
|
Tommy - that is a really neat feature -- thank you very much for adding the code here to the forums -- I'm going to implement this on our server when we get ready to move our live site to the 1.9.5.5 update.
I have one question -- as I can tell your .NET skills are much better than mine.
Our old forums were Snitz based, and had the 'Birthdays' mod installed, which would display user birthdays for a given day, and also had 'upcoming birthdays' and 'recent birthdays' -- do you think you could give me some tips when you have time on how I could implement this into our forums? Thanks! |
If you can't find it using the forum search, try my signature link -- searches this site using Google: Google is my Friend |
|
|
|
 Rank: YAF CommanderJoined: 4/11/2008(UTC) Posts: 91
Thanks: 10 times Was thanked: 3 time(s) in 3 post(s)
|
Originally Posted by: bbobb 
tommy382
[thumbup] .Just a notion -you use OnlineStatusCacheTimeout . No need to update the cache so often - it only overloads your server.. Thanks for the tips, I thought that was the 5 minutes interval but I was wrong. I updated the above code to use ForumStatisticsCacheTimeout - 5 minutes by default.
|
|
|
|
 Rank: YAF CommanderJoined: 4/11/2008(UTC) Posts: 91
Thanks: 10 times Was thanked: 3 time(s) in 3 post(s)
|
Originally Posted by: squirrel  Tommy - that is a really neat feature -- thank you very much for adding the code here to the forums -- I'm going to implement this on our server when we get ready to move our live site to the 1.9.5.5 update.
I have one question -- as I can tell your .NET skills are much better than mine.
Our old forums were Snitz based, and had the 'Birthdays' mod installed, which would display user birthdays for a given day, and also had 'upcoming birthdays' and 'recent birthdays' -- do you think you could give me some tips when you have time on how I could implement this into our forums? Thanks! Have you looked at the DB to see where the Birthday info is stored? I don't see it under the yaf_User table. Just need the 2 queries to get the "recent" and "coming" birthdays. Then write a control to display the users (styled nicks support if enabled). One tricky part is to incorporate the time zone offset. Look at how the "Birthday" field in the "profile" page did it - it did took user timezone into consideration when computing the birthday.
|
|
|
|
 Rank: YAF DeveloperJoined: 10/21/2008(UTC) Posts: 1,558   Location: Moscow Thanks: 51 times Was thanked: 287 time(s) in 253 post(s)
|
|
|
|
|
|
 Rank: YAF CommanderJoined: 4/11/2008(UTC) Posts: 91
Thanks: 10 times Was thanked: 3 time(s) in 3 post(s)
|
Update: The fix to the "duplicate control id" I posted earlier failed in some cases. Ironically, it failed on the pages w/o the statistics data (i.e. a forum category page). Not sure why the "Active Users" controls were rendered in those pages. I updated the fix above to add a unique ID instead of using the id "UserLink" + UserId since multiple instances of the styled nick userlink will cause two controls to have the same id.
So the fix is just one line: userLink.ID = "UserLink" + this.UniqueID + userLink.UserID;
|
|
|
|
 Rank: YAF LoverJoined: 3/5/2010(UTC) Posts: 57
Thanks: 9 times Was thanked: 6 time(s) in 6 post(s)
|
Thanks for this tommy382
I implemented this on my forum today... looks awesome and works very well.
Had to do some modifications for 1.9.5.5, added to the stored procedure and added the text stuff into the Language file.
all the ground work was yours though
|
|
|
|
 Rank: YAF CommanderJoined: 4/11/2008(UTC) Posts: 91
Thanks: 10 times Was thanked: 3 time(s) in 3 post(s)
|
Originally Posted by: ruek23  Thanks for this tommy382
I implemented this on my forum today... looks awesome and works very well.
Had to do some modifications for 1.9.5.5, added to the stored procedure and added the text stuff into the Language file.
all the ground work was yours though Nice to hear [thumbup]. Be careful with the implementation I posted above though - it would still fail in some cases (pay attention to the logs). My final solution is below and works perfectly. The forum has been up for several days now and got relatively high traffic and not a single log related to this control conflict issue. Instead of using this.UniqueID, use a unique instant id per "active users" control. The uniqueID could still cause control ID conflict sometimes - not sure why but log data proved it. Code:userLink.ID = "UserLink" + this.InstantId + userLink.UserID;
So you will to add a new property called "InstantId to the ActiveUsers.cs file. Code: /// <summary>
/// The Instant ID for this control.
/// </summary>
/// <remarks>
/// Multiple instants of this control can exist in the same page but
/// each must have a different instant ID. Not specifying an Instant ID
/// default to the ID being string.Empty.
/// </remarks>
public string InstantId
{
get
{
//return string.empty for null.
return (this.ViewState["InstantId"] as string) + "";
}
set
{
this.ViewState["InstantId"] = value;
}
}
Then in the page where you want to include the control, u can declare something like this: This one is existing code (w/o specifying the InstantID) so the InstantID is string.Empty by default: Code:<YAF:ActiveUsers ID="ActiveUsers1" runat="server"></YAF:ActiveUsers>
On the same page, I can have another instant like this: Code:<YAF:ActiveUsers ID="activeUsersOneDay" runat="server" InstantId="ActiveUsersOneDay" />
Edited by user Wednesday, March 16, 2011 9:41:29 AM(UTC)
| Reason: Not specified
|
|
|
|
 Rank: YAF LoverJoined: 3/5/2010(UTC) Posts: 57
Thanks: 9 times Was thanked: 6 time(s) in 6 post(s)
|
Originally Posted by: tommy382  Originally Posted by: ruek23  Code:userLink.ID = "UserLink" + this.InstantId + userLink.UserID;
hey tommy... thanks I was talking crap before hence edit :) Edited by user Wednesday, March 16, 2011 5:36:58 PM(UTC)
| Reason: Not specified
|
|
|
|
 Rank: YAF CommanderJoined: 4/11/2008(UTC) Posts: 91
Thanks: 10 times Was thanked: 3 time(s) in 3 post(s)
|
Originally Posted by: ruek23  hey tommy... i used Code:userLink.ID = "UserLink" + this.activeUsersOneDay.ID + userLink.UserID;
which means the userlink is basically associated to the control you are using it in. Which class are you using that line in? The "ActiveUsers.cs" class can't tell its instant name (the name of the object in which it's an instance of). That syntax suggests that you are using it at the ForumStatistics class? Are you sub-classing the UserLink class and change the Control.ID property? Interesting approach [thumbup].
|
|
|
|
 Rank: YAF LoverJoined: 3/5/2010(UTC) Posts: 57
Thanks: 9 times Was thanked: 6 time(s) in 6 post(s)
|
Originally Posted by: tommy382  Originally Posted by: ruek23  hey tommy... i used Code:userLink.ID = "UserLink" + this.activeUsersOneDay.ID + userLink.UserID;
which means the userlink is basically associated to the control you are using it in. Which class are you using that line in? The "ActiveUsers.cs" class can't tell its instant name (the name of the object in which it's an instance of). That syntax suggests that you are using it at the ForumStatistics class? Are you sub-classing the UserLink class and change the Control.ID property? Interesting approach [thumbup]. Sorry tommy I was at work so didn't have the code in front of me.. I tried the above by adding a property to the ActiveUsers.cs called ParentControlName which is set in the forum stats page. This is then used like Code:userLink.ID = "UserLink" + ParentControlName + userLink.UserID;
the problem is I was still getting issues. so have taken out this bit of the code...
|
|
|
|
 Rank: YAF CommanderJoined: 4/11/2008(UTC) Posts: 91
Thanks: 10 times Was thanked: 3 time(s) in 3 post(s)
|
Ok, that makes sense now. This approach is basically the same as mine "instant id" approach. It works great. [thumbup]
|
|
|
|
Users browsing this topic |
|
Forum Jump
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.