tommy382
  •  tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
2011-03-12T07:39:15Z
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.
UserPostedImage

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.
      //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?

Sponsor
tommy382
  •  tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
2011-03-12T11:26:10Z
I got it working. The styled nicks look good and they are linkable too.
UserPostedImage


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:

          if (!isCrawler)
          {
              userLink.ID = "UserLink" + this.InstantId + userLink.UserID;
          }
          else
          {
            userLink.ID += userLink.ReplaceName;
          }


ForumStatistics.ascx.cs:
    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:
    /// <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:
    /// <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:
    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:
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
bbobb
  •  bbobb
  • 100% (Exalted)
  • YAF Developer
2011-03-12T12:57:53Z
 tommy382 👍 .Just a notion -you use OnlineStatusCacheTimeout . No need to update the cache so often - it only overloads your server..


squirrel
2011-03-12T13:36:09Z
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 
tommy382
  •  tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
2011-03-12T19:48:50Z
Originally Posted by: bbobb 

 tommy382 👍 .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.
tommy382
  •  tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
2011-03-12T20:02:26Z
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.
bbobb
  •  bbobb
  • 100% (Exalted)
  • YAF Developer
2011-03-12T20:13:56Z
prov_Profile Birthday
tommy382
  •  tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
2011-03-13T02:06:19Z
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;
ruek23
2011-03-14T22:13:14Z
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
tommy382
  •  tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
2011-03-16T09:40:25Z
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 👍. 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.

userLink.ID = "UserLink" + this.InstantId + userLink.UserID;


So you will to add a new property called "InstantId to the ActiveUsers.cs file.
    /// <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:
<YAF:ActiveUsers ID="ActiveUsers1" runat="server"></YAF:ActiveUsers>


On the same page, I can have another instant like this:
<YAF:ActiveUsers ID="activeUsersOneDay" runat="server" InstantId="ActiveUsersOneDay" />
ruek23
2011-03-16T10:13:31Z
Originally Posted by: tommy382 

Originally Posted by: ruek23 



userLink.ID = "UserLink" + this.InstantId + userLink.UserID;




hey tommy...
thanks
I was talking crap before hence edit 🙂
tommy382
  •  tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
2011-03-16T17:38:25Z
Originally Posted by: ruek23 


hey tommy...

i used

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 👍.
ruek23
2011-03-16T18:50:23Z
Originally Posted by: tommy382 

Originally Posted by: ruek23 


hey tommy...

i used

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 👍.



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

userLink.ID = "UserLink" + ParentControlName  + userLink.UserID;


the problem is I was still getting issues. so have taken out this bit of the code...

tommy382
  •  tommy382
  • 100% (Exalted)
  • YAF Commander Topic Starter
2011-03-16T19:01:48Z
Ok, that makes sense now. This approach is basically the same as mine "instant id" approach. It works great. 👍

About Us

The YAF.NET is an open source .NET forum project. YAF.NET is supported by an team of international developers who are build community by building community software.

Powered by Resharper Donate with PayPal button

Project Twitter Updates

Copyright © YetAnotherForum.NET & Ingo Herbote. All rights reserved