<?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_Page extends vB5_Route
{
	private $page = null;

	private function getPage()
	{
		// not sure that we need *both* the fast cache and a local variable here
		if ($this->page === null AND isset($this->arguments['pageid']))
		{
			$cache = vB_Cache::instance(vB_Cache::CACHE_FAST);
			$hashkey = 'vbPage_' . $this->arguments['pageid'];
			$this->page = $cache->read($hashkey);

			if (empty($this->page))
			{
				$this->page = vB::getDbAssertor()->getRow('page', ['pageid' => intval($this->arguments['pageid'])]);
				$cache->write($hashkey, $this->page, 86400, 'pageChg_' . $this->arguments['pageid']);
			}
		}

		return $this->page;
	}


	public function __construct($routeInfo, $matches, $queryString = '', $anchor = '')
	{
		parent::__construct($routeInfo, $matches, $queryString, $anchor);
		if (!empty($this->arguments['pageid']))
		{
			$this->setPageKey('pageid');

			// Should see if we can move this out of the constructor.  May not be relevant when generating the
			// url instead of actually displaying the page.
			$page = $this->getPage();
			if ($page)
			{
				switch($page['guid'])
				{
					case vB_Page::PAGE_SOCIALGROUP:
						$this->checkStyle(vB_Channel::DEFAULT_SOCIALGROUP_PARENT);
						break;
					case vB_Page::PAGE_BLOG:
						$this->checkStyle(vB_Channel::DEFAULT_BLOG_PARENT);
						break;
				}
			}
		}

		// RSS for channel pages (blog, group). Assuming that if a channelid argument is set, it's a channel
		// and the RSS xml exporter can create one for this. This should also set head links
		if (isset($this->arguments['channelid']))
		{
			$channel = vB_Library::instance('Content_Channel')->getBareContent($this->arguments['channelid']);
			if (is_array($channel))
			{
				$channel = array_pop($channel);
			}

			if ($channel['rss_enabled'])
			{
				$this->arguments['rss_enabled'] = $channel['rss_enabled'];
				$this->arguments['rss_route'] = $channel['rss_route'];
				$this->arguments['rss_title'] = $channel['title'];
				// because conversation routes also add their parent channel's rss info into the arguments,
				// this flag helps us tell channels apart from conversations when we're adding the RSS icon next to the page title
				$this->arguments['rss_show_icon_on_pagetitle'] = true;
			}
		}
	}

	protected function checkStyle($channelguid)
	{
		$channel = vB_Api::instanceInternal('content_channel')->fetchChannelByGUID($channelguid);
		if (!empty($channel['styleid']))
		{
			$forumOptions = vB::getDatastore()->getValue('bf_misc_forumoptions');
			if ($forumOptions['styleoverride'] & $channel['options'])
			{
				// the channel must force the style
				$this->arguments['forceStyleId'] = $channel['styleid'];
			}
			else
			{
				// the channel suggests to use this style
				$this->arguments['routeStyleId'] = $channel['styleid'];
			}
		}
	}

	public function getUserAction() : array|false
	{
		$page = $this->getPage();
		if ($page)
		{
			switch($page['guid'])
			{
				// Not sure why we don't display the user action for social groups.
				case vB_Page::PAGE_SOCIALGROUP:
					return false;
					break;
				case vB_Page::PAGE_BLOG:
					return ['viewing_blog_home'];
					break;
				case vB_Page::PAGE_ONLINE:
				case vB_Page::PAGE_MEMBERLIST:
					return ['viewing_whos_online'];
					break;
				case vB_Page::PAGE_SEARCH:
				case vB_Page::PAGE_SEARCHRESULT:
					return ['searching_forums'];
					break;
				case vB_Page::PAGE_HOME:
				default:
					return ['viewing_x', $this->getPageTitle($page), $this->getFullUrl('fullurl')];
					break;
			}
		}
		return false;
	}


	protected function setBreadcrumbs()
	{
		// If a page is set to home, we should not show any breadcrumbs.
		if ($this->ishomeroute)
		{
			$this->breadcrumbs = [];
		}
		else
		{
			// TODO: A custom pages' hierarchy support for breadcrumbs has been requested.
			// Here, we would also pull a hierachical list of parent pages' and build up
			// the breadcrumbs for such a case.
			$page = $this->getPage();
			if ($page)
			{
				$this->breadcrumbs = [
					0 => [
						'title' => $this->getPageTitle($page),
						'url' => '',
					]
				];
			}
			else
			{
				parent::setBreadcrumbs();
			}
		}
	}

	public function getUrl()
	{
		$url = '';
		if ($this->prefix)
		{
			$url = '/' . $this->prefix;
		}

		if (isset($this->arguments['pagenum']) AND is_numeric($this->arguments['pagenum']) AND $this->arguments['pagenum'] > 1)
		{
			$url .= '/page' . intval($this->arguments['pagenum']);
		}

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

		return $url;
	}

	public function getCanonicalRoute()
	{
		if (!isset($this->canonicalRoute) AND !empty($this->arguments['pageid']))
		{
			$page = $this->getPage();
			$data = [];

			if (isset($this->arguments['pagenum']) AND is_numeric($this->arguments['pagenum']) AND $this->arguments['pagenum'] > 1)
			{
				$data['pagenum'] = $this->arguments['pagenum'];
			}

			$this->canonicalRoute = self::getRoute($page['routeid'], $data, $this->queryParameters);
		}

		return $this->canonicalRoute;
	}

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

		//some pages have there own regex that needs to be preserved.
		if (!isset($data['regex']))
		{
			$data['regex'] = $data['prefix'];
		}

		$data['class'] = __CLASS__;

		/************************************************************************************************************/
		/*
		 * TODO: This class is being used for search routes since rev 58051 (fix for VBV-176) which doesn't
		 * seem correct since they use a different controller and action.
		 *
		 * This is a temporal fix to prevent overwriting the controller and action, but eventually we'll need
		 * to use a different route class. I prefer the route class to always set the controller and action.
		 */
		if (!isset($data['controller']))
		{
			$data['controller']	= 'page';
		}
		if (!isset($data['action']))
		{
			$data['action']	= 'index';
		}
		/************************************************************************************************************/

		//in this case the contentid is the pageid
		$arguments = [];
		if (!empty($data['arguments']))
		{
			$arguments = vb_unserialize($data['arguments']);
		}
		$arguments['pageid'] = $data['contentid'];
		$data['arguments']	= serialize($arguments);

		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";
		}

		//if we have an associated channel, update the route on the node record
		//it should always be the oldRoute, but we'll double check and not change it
		//if it isn't.
		$args = vb_unserialize($newRouteInfo['arguments']);
		if (!empty($args['channelid']))
		{
			$db->update(
				'vBForum:node',
				['routeid' => $newRouteInfo['routeid']],
				['nodeid' => $args['channelid'], 'routeid' => $oldRouteInfo['routeid']]
			);

			$events[] = 'nodeChg_' . $args['channelid'];
		}

		vB_Cache::allCacheEvent($events);
	}

	public static function exportArguments($arguments)
	{
		if (!empty($arguments['channelid']))
		{
			self::channelIdtoGuid($arguments);
		}

		self::pageIdtoGuid($arguments);
		return $arguments;
	}

	public static function importArguments($arguments)
	{
		// Some pages may have a channel associated (e.g. Groups, Blogs)
		if (!empty($arguments['channelGuid']))
		{
			self::channelGuidToId($arguments);
		}
		self::pageGuidToId($arguments);
		return $arguments;
	}

	public static function importContentId($arguments)
	{
		return $arguments['pageid'];
	}

	public function getHeadLinks() : array
	{
		$headlinks = parent::getHeadLinks();

		$datastore = vB::getDatastore();
		if ($datastore->getOption('externalrss') AND !empty($this->arguments['rss_enabled']))
		{
			// specific channel's rss link iff it's set (which it will be if this is a channel like blog or social group)
			$headlinks[] = $this->createRssHeadlink($datastore->getOption('bbtitle') . ' -- ' . $this->arguments['rss_title'], $this->arguments['rss_route']);
		}

		return $headlinks;
	}

	private function getPageTitle(array $page)
	{
		$library = vB_Library::instance('phrase');
		// use phrased page title & meta desc for breadcrumb
		$guidforphrase = $library->cleanGuidForPhrase($page['guid']);
		$titlephrase = 'page_' . $guidforphrase . '_title';

		// This route class is called by the short codes so not invoking them out of an abundance of caution
		$phrases = $library->renderPhrasesNoShortcode([
			$titlephrase
		]);

		// if we don't have a phrase defined, fall back to the older page title field
		// (probably shouldn't happen, probably still does)
		if ($phrases[0] != $titlephrase)
		{
			return $phrases[0];
		}
		else
		{
			return $page['title'];
		}
	}

}

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