From ed0b5020a9c2e2b1c7ee12ebf7ff0c3791f3f664 Mon Sep 17 00:00:00 2001
From: Marc Alexander <admin@m-a-styles.de>
Date: Thu, 16 May 2024 20:40:56 +0200
Subject: [PATCH] [ticket/17312] Use user_last_active instead of user_lastvisit
 where possible

PHPBB3-17312
---
 phpBB/memberlist.php                          | 10 ++---
 .../data/v33x/add_user_last_active.php        | 39 +++++++++++++++--
 phpBB/phpbb/session.php                       | 43 ++++++++++++++-----
 3 files changed, 73 insertions(+), 19 deletions(-)

diff --git a/phpBB/memberlist.php b/phpBB/memberlist.php
index e9af6026d7..c36ebd697a 100644
--- a/phpBB/memberlist.php
+++ b/phpBB/memberlist.php
@@ -1036,7 +1036,7 @@ switch ($mode)
 		if ($auth->acl_get('u_viewonline'))
 		{
 			$sort_key_text['l'] = $user->lang['SORT_LAST_ACTIVE'];
-			$sort_key_sql['l'] = 'u.user_lastvisit';
+			$sort_key_sql['l'] = 'u.user_last_active';
 		}
 
 		$sort_key_text['m'] = $user->lang['SORT_RANK'];
@@ -1138,15 +1138,15 @@ switch ($mode)
 				{
 					if ($active_select === 'lt' && (int) $active[0] == 0 && (int) $active[1] == 0 && (int) $active[2] == 0)
 					{
-						$sql_where .= ' AND u.user_lastvisit = 0';
+						$sql_where .= ' AND u.user_last_active = 0';
 					}
 					else if ($active_select === 'gt')
 					{
-						$sql_where .= ' AND u.user_lastvisit ' . $find_key_match[$active_select] . ' ' . $active_time;
+						$sql_where .= ' AND u.user_last_active ' . $find_key_match[$active_select] . ' ' . $active_time;
 					}
 					else
 					{
-						$sql_where .= ' AND (u.user_lastvisit > 0 AND u.user_lastvisit < ' . $active_time . ')';
+						$sql_where .= ' AND (u.user_last_active > 0 AND u.user_last_active < ' . $active_time . ')';
 					}
 				}
 			}
@@ -1713,7 +1713,7 @@ switch ($mode)
 			{
 				$row['session_time'] = $session_ary[$row['user_id']]['session_time'] ?? 0;
 				$row['session_viewonline'] = $session_ary[$row['user_id']]['session_viewonline'] ?? 0;
-				$row['last_visit'] = (!empty($row['session_time'])) ? $row['session_time'] : $row['user_lastvisit'];
+				$row['last_visit'] = (!empty($row['session_time'])) ? $row['session_time'] : $row['user_last_active'];
 
 				$id_cache[$row['user_id']] = $row;
 			}
diff --git a/phpBB/phpbb/db/migration/data/v33x/add_user_last_active.php b/phpBB/phpbb/db/migration/data/v33x/add_user_last_active.php
index 58cc4f0240..d4506eea39 100644
--- a/phpBB/phpbb/db/migration/data/v33x/add_user_last_active.php
+++ b/phpBB/phpbb/db/migration/data/v33x/add_user_last_active.php
@@ -29,7 +29,7 @@ class add_user_last_active extends migration
 		return [
 			'add_columns'	=> [
 				$this->table_prefix . 'users'	=> [
-					'user_last_active'		=> ['TIMESTAMP', 0],
+					'user_last_active'		=> ['TIMESTAMP', 0, 'after' => 'user_lastvisit'],
 				],
 			],
 		];
@@ -39,10 +39,41 @@ class add_user_last_active extends migration
 	{
 		return [
 			'drop_columns'	=> [
-				$this->table_prefix . 'users'	=> [
-					'user_last_active'		=> ['TIMESTAMP', 0],
-				],
+				$this->table_prefix . 'users'	=> ['user_last_active'],
 			],
 		];
 	}
+
+	public function update_data()
+	{
+		return [
+			['custom', [[$this, 'set_user_last_active']]],
+		];
+	}
+
+	public function set_user_last_active($start = 0)
+	{
+		// Get maximum user id from database
+		$sql = "SELECT MAX(user_id) AS max_user_id
+			FROM {$this->table_prefix}users";
+		$result = $this->db->sql_query($sql);
+		$max_id = (int) $this->db->sql_fetchfield('max_user_id');
+		$this->db->sql_freeresult($result);
+
+		if ($start > $max_id)
+		{
+			return;
+		}
+
+		// Keep setting user_last_active time
+		$next_start = $start + 10000;
+
+		$sql = 'UPDATE ' . $this->table_prefix . 'users
+			SET user_last_active = user_lastvisit
+			WHERE user_id > ' . (int) $start . '
+				AND user_id <= ' . (int) ($next_start);
+		$this->db->sql_query($sql);
+
+		return $next_start;
+	}
 }
diff --git a/phpBB/phpbb/session.php b/phpBB/phpbb/session.php
index 7cb7e15d78..af39ecf2fa 100644
--- a/phpBB/phpbb/session.php
+++ b/phpBB/phpbb/session.php
@@ -440,10 +440,10 @@ class session
 						// Is user banned? Are they excluded? Won't return on ban, exists within method
 						$this->check_ban_for_current_session($config);
 
-						// Update user last visit time accordingly, but in a minute or so
-						if ((int) $this->data['session_time'] - (int) $this->data['user_lastvisit'] > 60)
+						// Update user last active time accordingly, but in a minute or so
+						if ((int) $this->data['session_time'] - (int) $this->data['user_last_active'] > 60)
 						{
-							$this->update_user_lastvisit();
+							$this->update_last_active_time();
 						}
 
 						return true;
@@ -690,8 +690,8 @@ class session
 			{
 				$this->session_id = $this->data['session_id'];
 
-				// Only sync user last visit time in a minute or so after last session data update or if the page changes
-				if ((int) $this->data['session_time'] - (int) $this->data['user_lastvisit'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
+				// Only update session DB a minute or so after last update or if page changes
+				if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
 				{
 					// Update the last visit time
 					$this->update_user_lastvisit();
@@ -818,22 +818,26 @@ class session
 				$this->data['user_form_salt'] = unique_id();
 				// Update the form key
 				$sql = 'UPDATE ' . USERS_TABLE . '
-					SET user_form_salt = \'' . $db->sql_escape($this->data['user_form_salt']) . '\'
+					SET user_form_salt = \'' . $db->sql_escape($this->data['user_form_salt']) . '\',
+						user_last_active = ' . (int) $this->data['session_time'] . '
 					WHERE user_id = ' . (int) $this->data['user_id'];
 				$db->sql_query($sql);
 			}
+			else
+			{
+				$this->update_last_active_time();
+			}
 		}
 		else
 		{
 			$this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now;
 
+			$this->update_user_lastvisit();
+
 			$SID = '?sid=';
 			$_SID = '';
 		}
 
-		// Update the last visit time
-		$this->update_user_lastvisit();
-
 		$session_data = $sql_ary;
 		/**
 		* Event to send new session data to extension
@@ -1807,7 +1811,26 @@ class session
 		if (isset($this->data['session_time'], $this->data['user_id']))
 		{
 			$sql = 'UPDATE ' . USERS_TABLE . '
-				SET user_lastvisit = ' . (int) $this->data['session_time'] . '
+				SET user_lastvisit = ' . (int) $this->data['session_time'] . ',
+					user_last_active = ' . (int) $this->data['session_time'] . '
+				WHERE user_id = ' . (int) $this->data['user_id'];
+			$db->sql_query($sql);
+		}
+	}
+
+	/**
+	 * Update user's last active time
+	 *
+	 * @return void
+	 */
+	public function update_last_active_time()
+	{
+		global $db;
+
+		if (isset($this->data['session_time'], $this->data['user_id']))
+		{
+			$sql = 'UPDATE ' . USERS_TABLE . '
+				SET user_last_active = ' . (int) $this->data['session_time'] . '
 				WHERE user_id = ' . (int) $this->data['user_id'];
 			$db->sql_query($sql);
 		}