<?php if (!defined('VB_ENTRY')) die('Access denied.');
/*========================================================================*\
|| ###################################################################### ||
|| # vBulletin 5.4.1
|| # ------------------------------------------------------------------ # ||
|| # Copyright 2000-2018 vBulletin Solutions Inc. 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 vB_Template_Runtime
{
	use vB_Trait_NoSerialize;

	public static $units = array(
		'%',
		'px',
		'pt',
		'em',
		'rem',
		'ch',
		'ex',
		'pc',
		'in',
		'cm',
		'mm',
		'vw',
		'vh',
		'vmin',
		'vmax',
	);

	public static function date($timestamp, $format = 'r', $doyestoday=0, $adjust=1)
	{
		if (empty($format))
		{
			$format = 'r';
		}
		return vbdate($format, intval($timestamp), $doyestoday, true, $adjust);
	}

	public static function time($timestamp)
	{
		if (empty($timestamp))
		{
			$timestamp = 0;
		}
		return vbdate(vB::getDatastore()->getOption('timeformat'), $timestamp);
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return string
	 */
	public static function datetime($timestamp, $format = 'date, time', $formatdate = '', $formattime = '')
	{
		return '';
	}

	public static function escapeJS($javascript)
	{
		return str_replace(array("'", "\n", "\r"), array("\'", ' ', ' '), $javascript);
	}

	public static function numberFormat($number, $decimals = 0)
	{
		return vb_number_format($number, $decimals);
	}

	public static function urlEncode($text)
	{
		return urlencode($text);
	}

	public static function parsePhrase($phraseName)
	{
		global $vbphrase;
		$arg_list = func_get_args();
		if (isset($vbphrase[$phraseName]))
		{
			$arg_list[0] = $vbphrase[$phraseName];
			return construct_phrase_from_array($arg_list);
		}

		$phraseArr = vB_Api::instanceInternal('phrase')->fetch(array($phraseName));
		if (isset($phraseArr[$phraseName]))
		{
			$phrase = $phraseArr[$phraseName];
			$arg_list[0] = $phrase;
			return construct_phrase_from_array($arg_list);
		}
		else
		{
			// Should we do something else here if phrase wasn't found?
			return '';
		}
	}

	public static function addStyleVar($name, $value, $datatype = 'string')
	{
		global $vbulletin;

		switch ($datatype)
		{
			case 'string':
				$vbulletin->stylevars["$name"] = array(
					'datatype' => $datatype,
					'string'   => $value,
				);
			break;
			case 'imgdir':
				$vbulletin->stylevars["$name"] = array(
					'datatype' => $datatype,
					'imagedir' => $value,
				);
			break;
		}
	}

	private static function outputStyleVar($base_stylevar, $parts = array())
	{
		global $vbulletin;

		if (isset($base_stylevar['value']) AND $base_stylevar['value'] == false)
		{
			// Invalid stylevar value
			return;
		}

		if (!empty($base_stylevar))
		{
			$stylevar_value_prefix = 'stylevar_';
			foreach ($base_stylevar AS $key => $value)
			{
				if ($key == 'datatype' OR (strpos($key, $stylevar_value_prefix)) === 0)
				{
					continue;
				}

				$stylevar_value_key = $stylevar_value_prefix . $key;
				if (empty($value) AND !empty($base_stylevar[$stylevar_value_key]))
				{
					$base_stylevar[$key] = self::fetchStyleVar($base_stylevar[$stylevar_value_key]);
				}
				unset($base_stylevar[$stylevar_value_key]); // Don't give access to the stylevars directly.
			}
		}

		// Set up the background gradient for "background" type stylevars for
		// both branches below
		if (isset($base_stylevar['datatype']) AND $base_stylevar['datatype'] == 'background')
		{
			if (
				!empty($base_stylevar['gradient_type']) AND
				!empty($base_stylevar['gradient_direction']) AND
				!empty($base_stylevar['gradient_start_color']) AND
				!empty($base_stylevar['gradient_end_color'])
			)
			{
				$base_stylevar['gradient'] = $base_stylevar['gradient_type'] . '(' .
					$base_stylevar['gradient_direction'] . ', ' .
					$base_stylevar['gradient_start_color'] . ', ' .
					$base_stylevar['gradient_end_color'] . ')';
			}
		}


		// Output a stylevar *part*, for example, myBackgroundStylevar.backgroundImage
		if (isset($parts[1]))
		{
			$types = array(
				'background' => array(
					'backgroundColor' => 'color',
					'backgroundImage' => 'image',
					'backgroundRepeat' => 'repeat',
					'backgroundPositionX' => 'x',
					'backgroundPositionY' => 'y',
					'backgroundPositionUnits' => 'units',
					'backgroundGradient' => 'gradient',
					// make short names valid too
					'color' => 'color',
					'image' => 'image',
					'repeat' => 'repeat',
					'x' => 'x',
					'y' => 'y',
					'units' => 'units',
					'gradient' => 'gradient',
					'gradient_type' => 'gradient_type',
					'gradient_direction' => 'gradient_direction',
					'gradient_start_color' => 'gradient_start_color',
					'gradient_end_color' => 'gradient_end_color',
				),

				'font' => array(
					'fontWeight' => 'weight',
					'units' => 'units',
					'fontSize' => 'size',
					'fontFamily' => 'family',
					'fontStyle' => 'style',
					'fontVariant' => 'variant',
					// make short names valid too
					'weight' => 'weight',
					'size' => 'size',
					'family' => 'family',
					'style' => 'style',
					'variant' => 'variant',
				),

				'padding' => array(
					'units' => 'units',
					'paddingTop' => 'top',
					'paddingRight' => 'right',
					'paddingBottom' => 'bottom',
					'paddingLeft' => 'left',
					// make short names valid too
					'top' => 'top',
					'right' => 'right',
					'bottom' => 'bottom',
					'left' => 'left',
				),

				'margin' => array(
					'units' => 'units',
					'marginTop' => 'top',
					'marginRight' => 'right',
					'marginBottom' => 'bottom',
					'marginLeft' => 'left',
					// make short names valid too
					'top' => 'top',
					'right' => 'right',
					'bottom' => 'bottom',
					'left' => 'left',
				),

				'border' => array(
					'borderStyle' => 'style',
					'units' => 'units',
					'borderWidth' => 'width',
					'borderColor' => 'color',
					// make short names valid too
					'style' => 'style',
					'width' => 'width',
					'color' => 'color',
				),
			);

			//handle is same for margin and padding -- allows the top value to be
			//used for all padding values
			if (isset($base_stylevar['datatype']) AND in_array($base_stylevar['datatype'], array('padding', 'margin')) AND $parts[1] <> 'units')
			{
				if (isset($base_stylevar['same']) AND $base_stylevar['same'])
				{
					$parts[1] = $base_stylevar['datatype'] . 'Top';
				}
			}

			if (isset($base_stylevar['datatype']) AND isset($types[$base_stylevar['datatype']]))
			{
				$mapping = $types[$base_stylevar['datatype']][$parts[1]];
				$output = $base_stylevar[$mapping];
			}
			else
			{
				$output = $base_stylevar;
				for ($i = 1; $i < sizeof($parts); $i++)
				{
					$output = $output[$parts[$i]];
				}
			}
		}
		// Output the full/combined value of a stylevar
		else
		{
			$output = '';

			switch($base_stylevar['datatype'])
			{
				case 'color':
					$output = $base_stylevar['color'];
					break;

				case 'background':
					$base_stylevar['x'] = !empty($base_stylevar['x']) ? $base_stylevar['x'] : '0';
					$base_stylevar['y'] = !empty($base_stylevar['y']) ? $base_stylevar['y'] : '0';
					$base_stylevar['repeat'] = !empty($base_stylevar['repeat']) ? $base_stylevar['repeat'] : '';
					$base_stylevar['units'] = !empty($base_stylevar['units']) ? $base_stylevar['units'] : '';
					switch ($base_stylevar['x'])
					{
						case 'stylevar-left':
							$base_stylevar['x'] = $vbulletin->stylevars['left']['string'];
							break;
						case 'stylevar-right':
							$base_stylevar['x'] = $vbulletin->stylevars['right']['string'];
							break;
						default:
							$base_stylevar['x'] = $base_stylevar['x'] . $base_stylevar['units'];
							break;
					}
					// The order of the background layers is important. Color is lowest,
					// then gradient, then the image is the topmost layer.
					// Keep syncronized with the other runtime outputStyleVar() implementation
					// and the previewBackground() Javascript funtion in class_stylevar.php
					$backgroundLayers = array();
					$backgroundLayers[] = (!empty($base_stylevar['image']) ? "$base_stylevar[image]" : 'none') . ' ' .
						$base_stylevar['repeat'] . ' ' . $base_stylevar['x'] . ' ' .
						$base_stylevar['y'] .
						$base_stylevar['units'];
					if (!empty($base_stylevar['gradient']))
					{
						$backgroundLayers[] = $base_stylevar['gradient'];
					}
					if (!empty($base_stylevar['color']))
					{
						$backgroundLayers[] = $base_stylevar['color'];
					}
					$output = implode(', ', $backgroundLayers);
					break;

				case 'textdecoration':
					if ($base_stylevar['none'])
					{
						$output = 'none';
					}
					else
					{
						unset($base_stylevar['datatype'], $base_stylevar['none']);
						$output = implode(' ', array_keys(array_filter($base_stylevar)));
					}
					break;

				case 'texttransform':
					$output = !empty($base_stylevar['texttransform']) ? $base_stylevar['texttransform'] : 'none';
					break;

				case 'textalign':
					// Default to left and not inherit or initial because the select menu in the stylevar editor
					// defaults to left (the first option). If they create the stylevar, see it's set to left and
					// don't edit it to actually save the value, we'll have an empty value here and should use left.
					$output = !empty($base_stylevar['textalign']) ? $base_stylevar['textalign'] : 'left';
					// if it's left/right, use the left/right stylevar value,
					// which changes to the opposite in RTL. See VBV-15458.
					if ($output == 'left')
					{
						$output = $vbulletin->stylevars['left']['string'];
					}
					else if ($output == 'right')
					{
						$output = $vbulletin->stylevars['right']['string'];
					}
					break;

				case 'font':
					$output = $base_stylevar['style'] . ' ' . $base_stylevar['variant'] . ' ' .
					$base_stylevar['weight'] . ' ' . $base_stylevar['size'] . $base_stylevar['units'] . ' ' .
					$base_stylevar['family'];
					break;

				case 'imagedir':
					$output = $base_stylevar['imagedir'];
					break;

				case 'string':
					$output = $base_stylevar['string'];
					break;

				case 'numeric':
					$output = $base_stylevar['numeric'];
					break;

				case 'size':
					$output =  $base_stylevar['size'] . $base_stylevar['units'];
					break;

				case 'url':
					$output = $base_stylevar['url'];
					break;

				case 'path':
					$output = $base_stylevar['path'];
					break;

				case 'fontlist':
					$output = implode(',', preg_split('/[\r\n]+/', trim($base_stylevar['fontlist']), -1, PREG_SPLIT_NO_EMPTY));
					break;

				case 'border':
					$output = $base_stylevar['width'] . $base_stylevar['units'] . ' ' .
						$base_stylevar['style'] . ' ' . $base_stylevar['color'];
					break;

				case 'dimension':
					$output = 'width: ' . intval($base_stylevar['width'])  . $base_stylevar['units'] .
						'; height: ' . intval($base_stylevar['height']) . $base_stylevar['units'] . ';';
					break;

				case 'padding':
				case 'margin':
					foreach (array('top', 'right', 'bottom', 'left') AS $side)
					{
						if (isset($base_stylevar[$side]) AND $base_stylevar[$side] != 'auto')
						{
							$base_stylevar[$side] = $base_stylevar[$side] . $base_stylevar['units'];
						}
					}
					if (isset($base_stylevar['same']) AND $base_stylevar['same'])
					{
						$output = $base_stylevar['top'];
					}
					else
					{
						if (vB_Template_Runtime::fetchStyleVar('textdirection') == 'ltr')
						{
							$output = $base_stylevar['top'] . ' ' . $base_stylevar['right'] . ' ' . $base_stylevar['bottom'] . ' ' . $base_stylevar['left'];
						}
						else
						{
							$output = $base_stylevar['top'] . ' ' . $base_stylevar['left'] . ' ' . $base_stylevar['bottom'] . ' ' . $base_stylevar['right'];
						}
					}
					break;
			}
		}

		return $output;
	}

	public static function fetchStyleVar($stylevar)
	{
		global $vbulletin;

		$parts = explode('.', $stylevar);
		if (empty($parts[0]) OR !isset($vbulletin->stylevars[$parts[0]]))
		{
			return;
		}
		return self::outputStyleVar($vbulletin->stylevars[$parts[0]], $parts);
	}

	public static function fetchCustomStylevar($stylevar, $user = false)
	{
		$parts = explode('.', $stylevar);

		$customstylevar = vB_Api::instanceInternal('stylevar')->get($parts[0], $user);

		// if there is no user passed and the customstylevar is empty (there is no session) fetch the sitedefault value
		// VBV-2213: Hiding customizations for users that have this setting enabled
		if (empty($customstylevar[$parts[0]]) OR $user === false)
		{
			return self::fetchStyleVar($stylevar);
		}
		return self::outputStyleVar($customstylevar[$parts[0]], $parts);
	}

	public static function runMaths($str)
	{
		//this would usually be dangerous, but none of the units make sense
		//in a math string anyway.  Note that there is ambiguty between the '%'
		//unit and the modulo operator.  We don't allow the latter anyway
		//(though we do allow bitwise operations !?)
		$units_found = array();
		foreach (self::$units AS $unit)
		{
			if (strpos($str, $unit))
			{
				$units_found[] = $unit;
			}
		}

		//mixed units.
		if (count($units_found) > 1)
		{
			return "/* ~~cannot perform math on mixed units ~~ found (" .
				implode(",", $units_found) . ") in $str */";
		}

		$str = preg_replace('#([^+\-*=/\(\)\d\^<>&|\.]*)#', '', $str);

		if (empty($str))
		{
			$str = '0';
		}
		else
		{
			//hack: if the math string is invalid we can get a php parse error here.
			//a bad expression or even a bad variable value (blank instead of a number) can
			//cause this to occur.  This fails quietly, but also sets the status code to 500
			//(but, due to a bug in php only if display_errors is *off* -- if display errors
			//is on, then it will work just fine only $str below will not be set.
			//
			//This can result is say an almost correct css file being ignored by the browser
			//for reasons that aren't clear (and goes away if you turn error reporting on).
			//We can check to see if eval hit a parse error and, if so, we'll attempt to
			//clear the 500 status (this does more harm then good) and send an error
			//to the file.  Since math is mostly used in css, we'll provide error text
			//that works best with that.

			try
			{
				$status = @eval("\$str = $str;");
			}
			catch(Error $e)
			{
				$status = false;
			}

			if ($status === false)
			{
				if (!headers_sent())
				{
					http_response_code(200);
				}
				return "/* Invalid math expression */";
			}

			if (count($units_found) == 1)
			{
				$str = $str.$units_found[0];
			}
		}
		return $str;
	}

	public static function linkBuild($type, $info = array(), $extra = array(), $primaryid = null, $primarytitle = null)
	{
		//allow strings of form of query strings for info or extra.  This allows us to hard code some values
		//in the templates instead of having to pass everything in from the php code.  Limitations
		//in the markup do not allow us to build arrays in the template so we need to use strings.
		//We still can't build strings from variables to pass here so we can't mix hardcoded and
		//passed values, but we do what we can.

		if (is_string($info))
		{
			parse_str($info, $new_vals);
			$info = $new_vals;
		}

		if (is_string($extra))
		{
			parse_str($extra, $new_vals);
			$extra = $new_vals;
		}

		return fetch_seo_url($type, $info, $extra, $primaryid, $primarytitle);
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return array
	 */
	public static function parseData()
	{
		// This used to return an empty string, which caused fatal errors during eval() when saving the template.
		// So now it returns an empty array(), which is what API functions are supposed to return anyways, see VBV-12504
		return array();
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return string
	 */
	public static function parseAction()
	{
		return '';
	}

	/**
	 * Includes a template
	 *
	 * This includeTemplate functionality is mainly included since CSS is rendered in core
	 * and we use template includes in CSS files. Otherwise this would use the presentation
	 * layer implementation.
	 *
	 * @param	string	$templateName	Template to include
	 * @param	array	$arguments	Any number items which are the name/value pairs to pass to the template as variables.
	 *
	 * @return string	Rendered template
	 */
	public static function includeTemplate($templateName, $arguments = array())
	{
		$templater = vB_Template::create($templateName);

		if (!empty($arguments))
		{
			$templater->quickRegister($arguments);
		}

		return $templater->render();
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return string
	 */
	public static function parseJSON()
	{
		return '';
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return string
	 */
	public static function includeCss()
	{
		return '';
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return string
	 */
	public static function includeCssFile()
	{
		return '';
	}

	/**
	 * Returns the full path/URL to the sprite file. This is implemented in the core
	 * runtime implementation because CSS is rendered (via css.php) in core. There
	 * is currently no implementation in the frontend runtime class.
	 *
	 * @var string Sprite template name.
	 *
	 * @return string Sprite URL
	 */
	public static function includeSpriteFile($filename)
	{
		$textdirection = vB_Template_Runtime::fetchStyleVar('textdirection');
		$styleid = vB_Template_Runtime::fetchStyleVar('styleid');

		$vboptions = vB::getDatastore()->getValue('options');

		// build sprite path
		if ($vboptions['storecssasfile'])
		{
			if (!empty($vboptions['cssfilelocation']))
			{
				$fileLocation = 'core' . $vboptions['cssfilelocation'] . '/style';
			}
			else
			{
				$fileLocation =	'core/clientscript/vbulletin_css/style';
			}
			$spritepath = $fileLocation . str_pad($styleid, 5, '0', STR_PAD_LEFT) . substr($textdirection, 0, 1) . '/';
		}
		else
		{
			$spritepath = 'sprite.php?styleid=' . $styleid . '&td=' . $textdirection . '&sprite=';
		}

		$baseurl = $vboptions['cdnurl'];
		if (!$baseurl)
		{
			$baseurl = '';
		}
		else
		{
			$baseurl .= '/';
		}

		$spritepath = $baseurl . $spritepath;

		// add sprite filename to sprite path
		if ($vboptions['storecssasfile'])
		{
			$cssfiledate = vB_Library::instance('Style')->getCssFileDate($styleid);
			$fullpath = $spritepath . $cssfiledate . '-' . $filename;
		}
		else
		{
			$miscoptions = vB::getDatastore()->getValue('miscoptions');
			if (!($cssdate = intval($miscoptions['cssdate'])))
			{
				$cssdate = time(); // fallback so we get the latest css
			}

			$joinChar = (strpos($spritepath, '?') === false) ? '?' : '&';
			$fullpath = $spritepath . $filename . "{$joinChar}ts=$cssdate";
		}

		return $fullpath;
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return string
	 */
	public static function includeJs()
	{
		return '';
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return string
	 */
	public static function includeHeadLink()
	{
		return '';
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return string
	 */
	public static function doRedirect()
	{
		return '';
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return string
	 */
	public static function buildUrlAdmincpTemp()
	{
		return '';
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return string
	 */
	public static function buildUrl()
	{
		return '';
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return string
	 */
	public static function hook($hook)
	{
		return '';
	}

	public static function vBVar($value)
	{
		return vB_String::htmlSpecialCharsUni($value);
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @return string
	 */
	public static function parseDataWithErrors()
	{
		return '';
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @return string
	 */
	public static function parseSchema($schemaInfo = array())
	{
		return '';
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param <type> $var
	 * @return string
	 */
	public static function debugExit()
	{
		return '';
	}

	/**
	 * This method is defined just to avoid errors while saving the template. The real implementation
	 * is in the presentation layer.
	 * @param string timer name
	 * @return string
	 */
	public static function debugTimer($timerName)
	{
		return '';
	}

}

/*=========================================================================*\
|| #######################################################################
|| # NulleD By - vBSupport.org
|| # CVS: $RCSfile$ - $Revision: 97255 $
|| #######################################################################
\*=========================================================================*/
