<?php
/*========================================================================*\
|| ###################################################################### ||
|| # vBulletin 6.2.3 - Licence Number L3138846
|| # ------------------------------------------------------------------ # ||
|| # Copyright 2000-2026 MH Sub I, LLC dba vBulletin. All Rights Reserved.  # ||
|| # This file may not be redistributed in whole or significant part.   # ||
|| # ----------------- VBULLETIN IS NOT FREE SOFTWARE ----------------- # ||
|| # http://www.vbulletin.com | http://www.vbulletin.com/license.html   # ||
|| ###################################################################### ||
\*========================================================================*/

class vB5_Route_Profile extends vB5_Route
{
	const DEFAULT_PREFIX = 'member';
	const REGEXP = '(?P<userid>[0-9]+)(?P<username>(-[^\?/]*)*)(?:/(?P<tab>activities|subscribed|about|media|infractions|ipaddress))?(?:/page(?P<pagenum>[0-9]+))?';
	protected static $availableTabs = [
		'activities' => true,
		'subscribed' => true,
		'about' => true,
		'media' => true,
		'infractions' => true,
		'ipaddress' => true,
	];

	protected static $doNotIndexTabs = [
		'infractions' => true,
		'ipaddress' => true,
	];

	protected static $tabsWithPagination = [
		// If the tab isn't set then we are using the default tab and it *could* be paginated so allow that case.
		'' => true,
		'media' => true,
		'infractions' => true,
	];

	public function __construct($routeInfo, $matches, $queryString = '', $anchor = '')
	{
		parent::__construct($routeInfo, $matches, $queryString, $anchor);
		$this->setPageKey('pageid', 'userid');

		//this is a bad place for this... since it will get called whenever a route class is loaded and not just when the
		//user goes to the profile page.
	}

	public function getUserAction() : array|false
	{
		if (empty($this->arguments['username']))
		{
			return false;
		}

		return ['viewing_user_profile', $this->arguments['username'], $this->getFullUrl('fullurl')];
	}

	protected function initRoute($routeInfo, $matches, $queryString = '', $anchor = '')
	{
		parent::initRoute($routeInfo, $matches, $queryString, $anchor);

		// if we don't have a numeric userid at this point, make it 0
		$this->arguments['userid'] = isset($this->arguments['userid']) ? intval($this->arguments['userid']) : 0;

		//there are lots of places we can get the username from if we don't have it, check them.
		if (!empty($this->arguments['userid']) AND empty($this->arguments['username']))
		{
			//node records provide both the username and the userid but under a different value.  We generate profile links for node
			//display frequently and we'd like to avoid an extra database trip
			if (!empty($matches['authorname']))
			{
				$this->arguments['username'] = $matches['authorname'];
			}
			else
			{
				//a lot of our profile links are for the current user -- who's information we have cached.
				$currentuser = vB::getCurrentSession()->fetch_userinfo();
				if ($this->arguments['userid'] == $currentuser['userid'])
				{
					$this->arguments['username'] = $currentuser['username'];
				}

				//if all else fails try to load from the database
				else
				{
					$user = vB_User::fetchUserinfo($this->arguments['userid']);
					$this->arguments['username']  = $user['username'];
				}
			}
		}
	}

	protected function checkRoutePermissions()
	{
		$currentUser = vB::getUserContext();

		if (!$currentUser->hasPermission('genericpermissions', 'canviewmembers') AND ($this->arguments['userid'] != vB::getCurrentSession()->get('userid')))
		{
			throw new vB_Exception_NodePermission('profile');
		}
	}

	/**
	* Sets the breadcrumbs for the route
	*/
	protected function setBreadcrumbs()
	{
		// I don't think anyone would set a "member/{userid}" page as their homepage, skipping
		// the homeroute check for breacrumbs.

		//if we are coming in for a route (instead of generating a URL) then the $this->arguments['username'] is the
		//url slug, which we don't want.  The API call is cached and will be called later anyway to generate the
		//profile page so its not a bit performance hit to load this way.
		$userInfo = vB_Api::instanceInternal('user')->fetchProfileInfo($this->arguments['userid']);
		$this->breadcrumbs = [
			0 => [
				'title' => $userInfo['username'],
				'url'	=> ''
			],
		];
	}

	protected static function validInput(array &$data)
	{
		if (
			!isset($data['pageid'])
			OR !is_numeric($data['pageid'])
			OR !isset($data['prefix'])
		)
		{
			return false;
		}
		$data['pageid'] = intval($data['pageid']);

		$data['prefix'] = $data['prefix'];
		$data['regex'] = $data['prefix'] . '/' . self::REGEXP;
		$data['arguments'] = serialize([
			'userid' => '$userid',
			'pageid' => $data['pageid'],
			'tab' => '$tab'
		]);

		$data['class'] = __CLASS__;
		$data['controller']	= 'page';
		$data['action']		= 'index';
		// this field will be used to delete the route when deleting the channel (contains channel id)

		unset($data['pageid']);

		return parent::validInput($data);
	}

	protected static function updateContentRoute($oldRouteInfo, $newRouteInfo)
	{
		$db = vB::getDbAssertor();
		$events = [];

		$updateIds = self::updateRedirects($db, $oldRouteInfo['routeid'], $newRouteInfo['routeid']);
		foreach ($updateIds AS $routeid)
		{
			$events[] = "routeChg_$routeid";
		}

		vB_Cache::allCacheEvent($events);
	}

	public function getUrl()
	{
		if (!empty($this->arguments['userid']) AND !empty($this->arguments['username']))
		{
			$result = '/' . $this->prefix . '/' . $this->arguments['userid'] . '-' . vB_String::getUrlIdent($this->arguments['username']);
		}
		else
		{
			return false;
		}

		// append the tab to URL only if it's a valid tab.
		if (isset($this->arguments['tab']))
		{
			if (isset(self::$availableTabs[$this->arguments['tab']]))
			{
				$result .= '/' . $this->arguments['tab'];

				if (isset(self::$doNotIndexTabs[$this->arguments['tab']]))
				{
					$this->arguments['noindex'] = true;
				}
			}
			else
			{
				// invalid tab, unset it
				unset($this->arguments['tab']);
			}
		}

		// Append the page number if pagenum argument is set and we paginate this tab.
		$paginatedTab = (self::$tabsWithPagination[$this->arguments['tab'] ?? ''] ?? false);
		$pagenum = intval($this->arguments['pagenum'] ?? 1);
		if ($paginatedTab AND $pagenum > 1)
		{
			$result .= '/page' . $pagenum;
		}

		if (strtolower(vB_String::getCharset()) != 'utf-8')
		{
			$result = vB_String::encodeUtf8Url($result);
		}

		return $result;
	}

	public function getCanonicalRoute()
	{
		if (!isset($this->canonicalRoute))
		{
			$cache = vB_Cache::instance(vB_Cache::CACHE_FAST);
			$hashKey = 'routepageid_' . $this->arguments['pageid'];
			$page = $cache->read($hashKey);
			if (empty($page))
			{
				$page = vB::getDbAssertor()->getRow('page', ['pageid' => $this->arguments['pageid']]);
				$cache->write($hashKey, $page, 1440, 'routepageid_Chg_' . $this->arguments['pageid']);
			}
			$this->canonicalRoute = self::getRoute($page['routeid'], $this->arguments, $this->queryParameters);
		}

		return $this->canonicalRoute;
	}

	public static function exportArguments($arguments)
	{
		self::pageIdtoGuid($arguments);
		return $arguments;
	}

	public static function importArguments($arguments)
	{
		self::pageGuidToId($arguments);
		return $arguments;
	}

	protected static function getHashKey($options = [], $data = [], $extra = [])
	{
		$routeId = $options[0];
		$hashKey = 'vbRouteURL_'. $routeId;
		if (!empty($data['userid']))
		{
			$hashKey = 'vbRouteURL_'. $routeId . '_' . $data['userid'];
		}
		else if (!empty($data['username']))
		{
			// differentiate between any hash_add suffices from usernames.
			// usernames should not be able to have two semicolons in a row
			// since a raw ; is not allowed in the username (one semicolon ending
			// might be possible due to an ending htmlentity)
			$hashKey = 'vbRouteURL_'. $routeId . '_' . $data['username'] . ';;';
		}

		$hash_add = '';
		if (count($options) > 1)
		{
			// full urls and relative urls must be differentiated. Otherwise the
			// delayed replacements code might prepend the frontendurl to an already
			// absolute full url
			if (in_array('fullurl', $options) OR in_array('bburl', $options))
			{
				$hash_add .= '_full';
			}
		}

		if (!empty($hash_add))
		{
			// I don't think we need md5 here unless we have more than one or two options to key.
			$hashKey .= $hash_add;
		}
		return $hashKey;
	}
}

/*=========================================================================*\
|| #######################################################################
|| # Downloaded: 13:27, Sun Aug 30th 2026
|| # CVS: $RCSfile$ - $Revision: 122204 $
|| #######################################################################
\*=========================================================================*/
