<?php
if (!defined('VB_ENTRY')) die('Access denied.');
/*======================================================================*\
|| #################################################################### ||
|| # vBulletin 5.0.0 Beta 10
|| # ---------------------------------------------------------------- # ||
|| # Copyright ©2000-2012 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 # ||
|| #################################################################### ||
\*======================================================================*/

/**
 * vB_Api_Site
 *
 * @package vBApi
 * @access public
 */

class vB_Api_Site extends vB_Api
{
	// Assertor object
	protected $assertor;

	// required fields for site
	protected $fields = array(
		'title' => vB_Cleaner::TYPE_STR,
		'url' => vB_Cleaner::TYPE_STR,
		'usergroups' => vB_Cleaner::TYPE_ARRAY,
		'newWindow' => vB_Cleaner::TYPE_BOOL,
		'subnav' => vB_Cleaner::TYPE_ARRAY,
	);

	// cleaner instance
	protected $cleanerObj;

	protected $sitescache = array();

	/**
	 * Initializes an Api Site object
	 */
	public function __construct()
	{
		$this->assertor = vB::getDbAssertor();
		$this->cleanerObj = new vB_Cleaner();
	}

	/**
	 * Stores the header navbar data.
	 *
	 * @param	int			The storing data siteid.
	 * @param	mixed		Array of elements containing data to be stored for header navbar. Elements might contain:
	 * 			title		--	string		Site title. *required
	 * 			url			--	string		Site url. *required
	 * 			usergroups	--	array		Array of ints.
	 * 			newWindow	--	boolean		Flag used to display site in new window. *required
	 * 			subnav		--	mixed		Array of subnav sites (containing same site data structure).
	 * 				id			--	int		Id of subnav site.
	 * 				title		--	string	Title of subnav site.
	 * 				url			--	string	Url of subnav site.
	 * 				usergroups	--	array	Array of ints.
	 * 				newWindow	--	boolean	Flag used to display subnav site in new window.
	 * 				subnav		--	mixed	Array of subnav sites (containing same site data structure).
	 * @return	boolean		To indicate if save was succesfully done.
	 */
	public function saveHeaderNavbar($siteId, $data)
	{
		/** Will throw exception if needed */
		$this->validate($siteId);

		/** We expect an array of elements for cleaning */
		$cleanedData = array();
		foreach ($data AS $key => $element)
		{
			$cleanedData[$key] = $this->cleanData($element);
		}

		/** Required fields check */
		$this->hasEmptyData($cleanedData);
		foreach ($cleanedData AS &$element)
		{
			$this->hasRequiredData($element);
			$this->saveNavbarPhrase($element);
		}

		/** At this point we can store the data */
		$cleanedData = serialize($cleanedData);

		$queryParams = array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_UPDATE, vB_dB_Query::CONDITIONS_KEY =>
				array(array('field' => 'siteid', 'value' => $siteId)), 'headernavbar' => $cleanedData);
		$response = $this->assertor->assertQuery('vBForum:site', $queryParams);

		return true;

	}

	/**
	 * Stores the footer navbar data.
	 *
	 * @param	int			The storing data siteid.
	 * @param	mixed		Array of data to be stored for footer navbar.
	 * 			title		--	string		Site title.
	 * 			url			--	string		Site url.
	 * 			usergroups	--	array		Array of ints.
	 * 			newWindow	--	boolean		Flag used to display site in new window.
	 * 			subnav		--	mixed		Array of subnav sites (containing same site data structure).
	 * 				id			--	int		Id of subnav site.
	 * 				title		--	string	Title of subnav site.
	 * 				url			--	string	Url of subnav site.
	 * 				usergroups	--	array	Array of ints.
	 * 				newWindow	--	boolean	Flag used to display subnav site in new window.
	 * 				subnav		--	mixed	Array of subnav sites (containing same site data structure).
	 * @return	boolean		To indicate if save was succesfully done.
	 */
	public function saveFooterNavbar($siteId, $data)
	{
		/** Will throw exception if needed */
		$this->validate($siteId);

		/** We expect an array of elements for cleaning */
		$cleanedData = array();
		foreach ($data AS $key => $element)
		{
			$cleanedData[$key] = $this->cleanData($element);
		}

		/** Required fields check */
		$this->hasEmptyData($cleanedData);
		foreach ($cleanedData AS &$element)
		{
			$this->hasRequiredData($element);
			$this->saveNavbarPhrase($element);
		}

		/** At this point we can store the data */
		$cleanedData = serialize($cleanedData);

		$queryParams = array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_UPDATE, vB_dB_Query::CONDITIONS_KEY =>
				array(array('field' => 'siteid', 'value' => $siteId)), 'footernavbar' => $cleanedData);
		$this->assertor->assertQuery('vBForum:site', $queryParams);

		return true;
	}

	/**
	 * Gets the header navbar data
	 *
	 * @param	int		Site id requesting header data.
	 * @parma	string	URL
	 * @param	int		Edit mode so allow all links if user can admin sitebuilder
	 *
	 * @return	mixed	Array of header navbar data (Described in save method).
	 */
	public function loadHeaderNavbar($siteId, $url = false, $edit = false)
	{
		/** Will throw exception if needed */
// @todo -- this is wrong, people without permission need to be able to view the data
// for displaying the navbar
//		$this->validate($siteId);

		/** @TODO remove this block when multiple sites are supported */
		if ($siteId != 1)
		{
			throw new vB_Exception_Api("invalid_siteid", array($siteId));
		}

		if (!isset($this->sitescache[$siteId]))
		{
			$queryParams = array(
				vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
				vB_dB_Query::CONDITIONS_KEY => array(array('field' => 'siteid', 'value' => $siteId))
			);
			$this->sitescache[$siteId] = $this->assertor->getRow('vBForum:site', $queryParams);
		}

		$header = unserialize($this->sitescache[$siteId]['headernavbar']);
		if (!empty($url))
		{
			$url = array_shift(explode('?', $url, 2));
		}
		$this->prepareNavbarData($header, $url, $edit);
		return $header;
	}

	/**
	 * Gets the footer navbar data
	 *
	 * @param	int		Site id requesting footer data.
	 *
	 * @return	mixed	Array of footer navbar data (Described in save method).
	 */
	public function loadFooterNavbar($siteId)
	{
		/** Will throw exception if needed */
// @todo -- this is wrong, people without permission need to be able to view the data
// for displaying the navbar
//		$this->validate($siteId);

		/** @TODO remove this block when multiple sites are supported */
		if ($siteId != 1)
		{
			throw new vB_Exception_Api("invalid_siteid", array($siteId));
		}

		if (!isset($this->sitescache[$siteId]))
		{
			$queryParams = array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
				vB_dB_Query::CONDITIONS_KEY => array(array('field' => 'siteid', 'value' => $siteId)));
			$this->sitescache[$siteId] = $this->assertor->getRow('vBForum:site', $queryParams);
		}

		$footer = unserialize($this->sitescache[$siteId]['footernavbar']);
		$this->prepareNavbarData($footer);

		return $footer;
	}

	protected function prepareNavbarData(&$data, $url = false, $edit = false)
	{
		/** @todo this is a hack, create a permanent solution to handle baseurl */
		$baseurl_short = parse_url($_SERVER['x-vb-presentation-base'], PHP_URL_PATH);
		$found_current = false;
		$found_sub_parent = false;
		$userinfo = vB_User::fetchUserinfo();
		$phraseApi = vB_Api::instance('phrase');
		foreach ($data as $k => &$item)
		{
			if (is_array($item) AND isset($item['url']))
			{
				$item['phrase'] = $item['title'];
				// we want the default language phrase, not the language from current user
				$phrases = $phraseApi->fetch($item['phrase'], 0);
				$item['title'] = $phrases[$item['phrase']];

				if (
					(!$edit OR !vB::getUserContext()->hasPermission('adminpermissions', 'canusesitebuilder'))
						AND
					(!empty($item['usergroups']) AND !in_array($userinfo['usergroupid'], $item['usergroups']))
				)
				{
					unset($data[$k]);
					continue;
				}
				$item['isAbsoluteUrl'] = (bool) preg_match('#^https?://#i', $item['url']);
				$item['normalizedUrl'] = ltrim($item['url'], '/');
				if (!empty($item['subnav']) AND is_array($item['subnav']))
				{
					$found_sub = $this->prepareNavbarData($item['subnav'], $url);
					if (!$found_current AND $found_sub)
					{
						$found_sub_parent = &$item;
						$item['current_sub'] = true;
					}
				}
				if (!$found_current AND !empty($url))
				{
					if ($item['isAbsoluteUrl'])
					{
						$itemUrl = parse_url($item['normalizedUrl'], PHP_URL_PATH);
					}
					else
					{
						$itemUrl = $baseurl_short . '/' . $item['normalizedUrl'];
					}

					if($url == $itemUrl || (strlen($url) > strlen($itemUrl) && substr($url, -strlen($itemUrl)) == $itemUrl))
					{
						$found_current = $item['current'] = true;
					}
				}
			}
		}
		if (!$found_current AND !empty($found_sub_parent))
		{
			$found_sub_parent['current'] = true;
		}
		return $found_current;
	}

	/**
	 * Check if data array is empty
	 *
	 * @param	mixed		Array of site data (described in save methods) to check.
	 *
	 * @throws 	Exception	missing_required_field if there's an empty field in site data.
	 */
	protected function hasEmptyData($data)
	{
		if (empty($data) OR !is_array($data))
		{
			throw new vB_Exception_Api('missing_required_field');
		}

		foreach ($data AS $field => $value)
		{
			//it's O.K. to have empty subnav
			if ((($field === 'subnav') OR ($field === 'usergroups') OR ($field === 'phrase')) AND (empty($value)))
			{
				continue;
			}

			if (is_array($value))
			{
				$this->hasEmptyData($value);
			}
			else
			{
				//if it's a boolean then empty is O.K.
				if (array_key_exists($field, $this->fields) AND ($this->fields[$field] == vB_Cleaner::TYPE_BOOL))
				{
					continue;
				}

				if (empty($value))
				{
					throw new vB_Exception_Api('missing_required_field');
				}
			}
		}
	}

	/**
	 * Check if data array is empty
	 *
	 * @param	mixed		Array of site data (described in save methods) to check.
	 *
	 * @throws 	Exception	missing_required_field if there's an empty field in site data.
	 */
	protected function hasRequiredData($data)
	{
		foreach ($this->fields as $field => $cleaner)
		{
			//it's O.K. to have empty subnav, usergroups or newWindow
			if (($field != 'subnav') AND ($field != 'usergroups') AND ($field != 'newWindow') AND empty($data[$field]))
			{
				throw new vB_Exception_Api('missing_required_field' );
			}
		}
	}

	/**
	 * Validate site data and perms.
	 *
	 * @siteId	int
	 *
	 * @throws	Exception	Indicating either invalid_siteid or invalid_permissions
	 */
	protected function validate($siteId)
	{
		/** @TODO remove this block when multiple sites are supported */
		if ($siteId != 1)
		{
			throw new vB_Exception_Api("invalid_siteid", array($siteId));
		}

		/** Check user perm */
		if (!vB::getUserContext()->hasPermission('adminpermissions', 'canusesitebuilder'))
		{
			throw new vB_Exception_Api('invalid_permissions');
		}
	}

	protected function cleanData($data)
	{
		/** should be an array data */
		if (!is_array($data))
		{
			throw new vB_Exception_Api('invalid_data');
		}

		foreach ($this->fields as $fieldKey => $fieldVal)
		{
			if (isset($data[$fieldKey]))
			{
				$data[$fieldKey] = $this->cleanerObj->clean($data[$fieldKey], $fieldVal);
			}
		}

		return $data;
	}

	protected function saveNavbarPhrase(&$element)
	{
		if (!isset($element['phrase']) OR empty($element['phrase'])
					OR strpos($element['phrase'], 'navbar_') !==0)
		{
			$words = explode(' ', $element['title']);
			array_walk($words, 'trim');
			$element['phrase'] = 'navbar_' . strtolower(implode('_', $words));
		}

		vB_Api::instance('phrase')->save('navbarlinks', $element['phrase'], array(
				'text' => array(0 => $element['title']),
				'oldvarname' => $element['phrase'],
				'oldfieldname' => 'navbarlinks',
				't' => 0,
				'ismaster' => 0,
				'product' => 'vbulletin'
		));

		// store phrase name instead of title
		$element['title'] = $element['phrase'];
		unset($element['phrase']);

		// do the same for subnavigation
		if (isset($element['subnav']) AND !empty($element['subnav']))
		{
			foreach($element['subnav'] AS &$subnav)
			{
				$this->saveNavbarPhrase($subnav);
			}
		}
	}
}

