<?php
/*======================================================================*\
|| #################################################################### ||
|| # vBulletin 5.1.1 - Licence Number VBCUP51ZOY
|| # ---------------------------------------------------------------- # ||
|| # Copyright �2000-2014 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 # ||
|| #################################################################### ||
\*======================================================================*/

if (VB_AREA != 'Install' AND !isset($GLOBALS['vbulletin']->db))
{
	exit;
}

class vB_Upgrade_final extends vB_Upgrade_Version
{
	/*Constants=====================================================================*/

	/**
	* The short version of the script
	*
	* @var	string
	*/
	public $SHORT_VERSION = 'final';

	/**
	* The long version of the script
	*
	* @var	string
	*/
	public $LONG_VERSION  = 'final';

	/*Properties====================================================================*/

	/**
	* Step #1 - Import Settings XML
	*
	*/
	function step_1()
	{
		vB_Upgrade::createAdminSession();
		build_channel_permissions();
		vB::getUserContext()->rebuildGroupAccess();
		vB_Channel::rebuildChannelTypes();
		//build_product_datastore(); Why do this here ?

		if (VB_AREA == 'Upgrade')
		{
			$this->show_message($this->phrase['final']['import_latest_options']);
			require_once(DIR . '/includes/adminfunctions_options.php');

			if (!($xml = file_read(DIR . '/install/vbulletin-settings.xml')))
			{
				$this->add_error(sprintf($this->phrase['vbphrase']['file_not_found'], 'vbulletin-settings.xml'), self::PHP_TRIGGER_ERROR, true);
				return;
			}

			$this->show_message(sprintf($this->phrase['vbphrase']['importing_file'], 'vbulletin-settings.xml'));
			xml_import_settings($xml);

			$this->show_message($this->phrase['core']['import_done']);
		}
		else if (vB_Upgrade::isCLI())
		{
			$library = vB_Upgrade::fetch_library($this->registry,$this->phrase, '', false, null, false, array('execute' => false));
			$library->loadDSSettingsfromConfig();
			$this->show_message($this->phrase['core']['import_done']);
		}
		else
		{
			$this->skip_message();
		}
	}

	/**
	* Step #2 - Import Admin Help XML
	*
	*/
	function step_2()
	{
		$this->show_message($this->phrase['final']['import_latest_adminhelp']);
		require_once(DIR . '/includes/adminfunctions_help.php');

		if (!($xml = file_read(DIR . '/install/vbulletin-adminhelp.xml')))
		{
			$this->add_error(sprintf($this->phrase['vbphrase']['file_not_found'], 'vbulletin-adminhelp.xml'), self::PHP_TRIGGER_ERROR, true);
			return;
		}

		$this->show_message(sprintf($this->phrase['vbphrase']['importing_file'], 'vbulletin-adminhelp.xml'));

		xml_import_help_topics($xml);
		$this->show_message($this->phrase['core']['import_done']);
	}

	/**
	* Step #3 - Import Language XML
	*
	*/
	function step_3()
	{
		$this->show_message($this->phrase['final']['import_latest_language']);
		require_once(DIR . '/includes/adminfunctions_language.php');

		if (!($xml = file_read(DIR . '/install/vbulletin-language.xml')))
		{
			$this->add_error(sprintf($this->phrase['vbphrase']['file_not_found'], 'vbulletin-language.xml'), self::PHP_TRIGGER_ERROR, true);
			return;
		}

		$this->show_message(sprintf($this->phrase['vbphrase']['importing_file'], 'vbulletin-language.xml'));

		xml_import_language($xml, -1, '', false, true, !defined('SUPPRESS_KEEPALIVE_ECHO'));
		$this->show_message($this->phrase['core']['import_done']);

		// Try to import custom languages
		// List files in customlanguages dir
		$customlangdir = DIR . '/install/customlanguages';
		if (is_dir($customlangdir))
		{
			if ($dh = opendir($customlangdir))
			{
				while (($file = readdir($dh)) !== false)
				{
					if (strpos($file, 'vbulletin-custom-language-') === 0)
					{
						if (!($xml = file_read($customlangdir . '/' . $file)))
						{
							$this->add_error(sprintf($this->phrase['vbphrase']['file_not_found'], $file), self::PHP_TRIGGER_ERROR, false);
						}
						else
						{
							// returns languageid, or false if the custom language wasn't imported
							$check = xml_import_language($xml, 0, '', true, true, !defined('SUPPRESS_KEEPALIVE_ECHO'), true);
							if ($check === false)
							{
								$this->show_message(sprintf($this->phrase['vbphrase']['skipping_file'], $file));
							}
							else
							{
								$this->show_message(sprintf($this->phrase['vbphrase']['importing_file'], $file));
								$this->show_message($this->phrase['core']['import_done']);

								// Set the default language to the imported custom language.
								// This isn't perfect, since we may have more than one custom
								// language, but it's better to set the last imported custom language
								// to be the default than to leave English as the default
								// if they downloaded the package with a different language than English
								vB::getDatastore()->setOption('languageid', $check, true);
							}
						}
					}
				}

				closedir($dh);
			}
		}

		build_language_datastore();
	}

	/**
	* Step #4 - Import widgets XML
	*
	*/
	function step_4()
	{
		$this->show_message($this->phrase['final']['import_latest_widgets']);
		$widgetFile = DIR . '/install/vbulletin-widgets.xml';
		if (!($xml = file_read($widgetFile)))
		{
			$this->add_error(sprintf($this->phrase['vbphrase']['file_not_found'], 'vbulletin-widgets.xml'), self::PHP_TRIGGER_ERROR, true);
			return;
		}

		$this->show_message(sprintf($this->phrase['vbphrase']['importing_file'], 'vbulletin-widgets.xml'));

		$xml_importer = new vB_Xml_Import_Widget();
		$xml_importer->importFromFile($widgetFile);

		$this->show_message($this->phrase['core']['import_done']);
	}

	/**
	* Step #5 - Import pagetemplates XML
	*
	*/
	function step_5()
	{
		$pageTemplateFile = DIR . '/install/vbulletin-pagetemplates.xml';
		if (!($xml = file_read($pageTemplateFile)))
		{
			$this->add_error(sprintf($this->phrase['vbphrase']['file_not_found'], 'vbulletin-pagetemplates.xml'), self::PHP_TRIGGER_ERROR, true);
			return;
		}

		$this->show_message(sprintf($this->phrase['vbphrase']['importing_file'], 'vbulletin-pagetemplates.xml'));

		// TODO: there might be some upgrades in which we do want to add some widgetinstances
		$options = (VB_AREA == 'Upgrade') ? 0 : vB_Xml_Import::OPTION_ADDWIDGETS;
		$xml_importer = new vB_Xml_Import_PageTemplate('vbulletin', $options);
		$xml_importer->importFromFile($pageTemplateFile);

		// Update widget instance config items that use phrases (phrase:<phrasevarname>)
		// This updates all widget config items that need it, including pre-existing widget instances
		// This is needed because 500a30 step 1 imports the page templates, and the
		// phrase placeholders are not replaced in the admin config because the target phrase(s)
		// do not yet exist in the phrase table at that point. See VBV-12214.
		$pageTemplateImporter = new vB_Xml_Import_PageTemplate('vbulletin', 0);
		$pageTemplateImporter->replacePhrasePlaceholdersInWidgetConfigs();

		$this->show_message($this->phrase['core']['import_done']);
	}

	/**
	* Step #6 - Import pages XML
	*
	*/
	function step_6()
	{
		vB_Upgrade::createAdminSession();

		// Importing pages
		$pageFile = DIR . '/install/vbulletin-pages.xml';
		if (!($xml = file_read($pageFile)))
		{
			$this->add_error(sprintf($this->phrase['vbphrase']['file_not_found'], 'vbulletin-pages.xml'), self::PHP_TRIGGER_ERROR, true);
			return;
		}

		$this->show_message(sprintf($this->phrase['vbphrase']['importing_file'], 'vbulletin-pages.xml'));

		$page_importer = new vB_Xml_Import_Page('vbulletin', 0);
		$page_importer->importFromFile($pageFile);
		build_language();

		$this->show_message($this->phrase['core']['import_done']);
	}

	/**
	* Step #7 - Import channels XML
	*
	*/
	function step_7()
	{
		vB_Upgrade::createAdminSession();

		// Import channels
		$channelFile = DIR . '/install/vbulletin-channels.xml';
		if (!($xml = file_read($channelFile)))
		{
			$this->add_error(sprintf($this->phrase['vbphrase']['file_not_found'], 'vbulletin-channels.xml'), self::PHP_TRIGGER_ERROR, true);
			return;
		}

		$this->show_message(sprintf($this->phrase['vbphrase']['importing_file'], 'vbulletin-channels.xml'));

		$channel_importer = new vB_Xml_Import_Channel('vbulletin', 0);
		$channel_importer->importFromFile($channelFile);

		// rebuild caches after adding channels
		build_channel_permissions();
		vB_Channel::rebuildChannelTypes();

		// Update widget instance config items that use channels (channelguid:<GUID>)
		$pageTemplateImporter = new vB_Xml_Import_PageTemplate('vbulletin', 0);
		$pageTemplateImporter->replaceChannelGuidsInWidgetConfigs();

		$this->show_message($this->phrase['core']['import_done']);
	}

	/**
	* Step #8 - Import routes XML
	*
	*/
	function step_8()
	{
		vB_Upgrade::createAdminSession();

		// Importing routes
		$routesFile = DIR . '/install/vbulletin-routes.xml';
		if (!($xml = file_read($routesFile)))
		{
			$this->add_error(sprintf($this->phrase['vbphrase']['file_not_found'], 'vbulletin-routes.xml'), self::PHP_TRIGGER_ERROR, true);
			return;
		}

		$this->show_message(sprintf($this->phrase['vbphrase']['importing_file'], 'vbulletin-routes.xml'));

		$route_importer = new vB_Xml_Import_Route();
		$route_importer->importFromFile($routesFile);

		// Update pages with new route ids
		$pageFile = DIR . '/install/vbulletin-pages.xml';
		$page_importer = new vB_Xml_Import_Page('vbulletin', 0);
		$parsedXML = $page_importer->parseFile($pageFile);
		$page_importer->updatePageRoutes($parsedXML);

		// Update channels with route ids
		$channelFile = DIR . '/install/vbulletin-channels.xml';
		$channel_importer = new vB_Xml_Import_Channel('vbulletin', 0);
		$parsedXML = $channel_importer->parseFile($channelFile);
		$channel_importer->updateChannelRoutes($parsedXML);

		$this->show_message($this->phrase['core']['import_done']);
	}

	/*** after building out the node and channel tables with their data, we need to
	create the Channel widget instance configuration and update ONLY IF IT HASN'T BEEN ALREADY SET **/
	function step_9()
	{
		$widgetid = $this->db->query_first("
			SELECT widgetinstanceid FROM `" . TABLE_PREFIX . "widgetinstance`
			WHERE widgetinstanceid = 1 AND adminconfig = ''
			");
		if (empty($widgetid))
		{
			$this->skip_message();
			return;
		}

		$contenttype = $this->db->query_first("
			SELECT contenttypeid
			FROM " . TABLE_PREFIX . "contenttype
			WHERE class = 'Channel'
		");
		$channelContentTypeId = $contenttype['contenttypeid'];

		$widgetConfig = array(
			'channel_node_ids' => array(),
		);


		$rootChannelResult = $this->db->query_read("
			SELECT *
			FROM " . TABLE_PREFIX . "node
			WHERE
				parentid = 1
				AND
				contenttypeid = $channelContentTypeId
		");
		while ($rootChannel = $this->db->fetch_array($rootChannelResult))
		{
			$widgetConfig['channel_node_ids'][] = $rootChannel['nodeid'];

			$subChannelResult = $this->db->query_read($q = "
				SELECT *
				FROM " . TABLE_PREFIX . "node
				WHERE
					parentid = $rootChannel[nodeid]
					AND
					contenttypeid = $channelContentTypeId
			");

			while ($subChannel = $this->db->fetch_array($subChannelResult))
			{
				$widgetConfig['channel_node_ids'][] = $subChannel['nodeid'];
			}
		}

		$this->run_query(
		sprintf($this->phrase['vbphrase']['update_table'], TABLE_PREFIX . 'widgetinstance'),
		"
		UPDATE `" . TABLE_PREFIX . "widgetinstance`
			SET adminconfig = '" . $this->db->escape_string(serialize($widgetConfig)) . "'
			WHERE widgetinstanceid = 1 AND adminconfig = ''
			"
		);

	}

	/*** create the channel routes
	 **/
	function step_10()
	{
		$this->show_message('Creating new routes');
		$contenttype = $this->db->query_first("
			SELECT contenttypeid
			FROM " . TABLE_PREFIX . "contenttype
			WHERE class = 'Channel'
		");
		$channelContentTypeId = $contenttype['contenttypeid'];

		$contenttype = $this->db->query_first("
			SELECT contenttypeid FROM " . TABLE_PREFIX . "contenttype
			WHERE class = 'Text'");
		$textTypeId = $contenttype['contenttypeid'];

		// fetch info for blog channels
		$blogParentId = vB_Library::instance('Blog')->getBlogChannel();
		$blogRoute = vB::getDbAssertor()->getRow('routenew', array(
			vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
			vB_dB_Query::CONDITIONS_KEY => array('guid' => 'vbulletin-4ecbdacd6aac05.50909926')
		));
		$blogPageTemplate = vB_Page::getBlogChannelPageTemplate();
		$blogsPage = vB::getDbAssertor()->getField('page', array(
			vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
			vB_dB_Query::CONDITIONS_KEY => array('guid' => vB_Page::PAGE_BLOG)
		));

		$rootChannelResult = $this->db->query_read("
			SELECT n.nodeid, n.title, n.parentid, c.category
			FROM " . TABLE_PREFIX . "node n
			INNER JOIN " . TABLE_PREFIX . "channel c ON n.nodeid = c.nodeid
			WHERE
				parentid IN (1, $blogParentId)
				AND
				contenttypeid = $channelContentTypeId
				AND
				routeid = 0
		");
		$queue = array();
		$prefixes = array(1 => '', $blogParentId => $blogRoute['prefix']);
		while ($channel = $this->db->fetch_array($rootChannelResult))
		{
			$channel['page_parentid'] = ($channel['parentid'] == $blogParentId) ? $blogsPage : 1;
			$queue[] = $channel;
		}

		$defaultPageTemplates = vB::getDbAssertor()->assertQuery('pagetemplate', array(
			vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
			vB_dB_Query::CONDITIONS_KEY => array(
				array('field' => 'guid', 'value' => array(
					vB_Page::TEMPLATE_CHANNEL,
					vB_Page::TEMPLATE_CONVERSATION,
					vB_Page::TEMPLATE_CATEGORY
				))
			)
		));

		foreach ($defaultPageTemplates AS $pageTemplate)
		{
			$pageTemplates[$pageTemplate['guid']] = $pageTemplate['pagetemplateid'];
		}

		$phraseLib = vB_Library::instance('phrase');
		while ($channel = array_shift($queue))
		{
			$hyphenedTitle = strtolower(str_replace(' ', '-', $channel['title']));

			// create main page for channel
			if ($channel['category'] > 0)
			{
				$pagetemplateid = $pageTemplates[vB_Page::TEMPLATE_CATEGORY];
			}
			else if ($channel['parentid'] == $blogParentId)
			{
				$pagetemplateid = $blogPageTemplate;
			}
			else
			{
				$pagetemplateid = $pageTemplates[vB_Page::TEMPLATE_CHANNEL];
			}
			$parentid = (int) $channel['page_parentid'];

			// Create phrases for page of channel title
			$guid = vB_Xml_Export_Page::createGUID(array());
			$guidforphrase = vB_Library::instance('phrase')->cleanGuidForPhrase($guid);
			$phraseLib->save('pagemeta',
				'page_' . $guidforphrase . '_title',
				array(
					'text' => array($channel['title']),
					'ismaster' => 1,
					'product' => 'vbulletin',
					't' => 0,
					'oldvarname' => 'page_' . $guidforphrase . '_title',
					'oldfieldname' => 'global',
				)
			);
			$phraseLib->save('pagemeta',
				'page_' . $guidforphrase . '_metadesc',
				array(
					'text' => array($channel['description']),
					'ismaster' => 1,
					'product' => 'vbulletin',
					't' => 0,
					'oldvarname' => 'page_' . $guidforphrase . '_metadesc',
					'oldfieldname' => 'global',
				)
			);

			$this->db->query_write(
			"
			INSERT INTO `" . TABLE_PREFIX . "page`
				(`parentid`, `pagetemplateid`, `title`, `pagetype`, `guid`)
				VALUES
				($parentid, $pagetemplateid, '" . $this->db->escape_string($channel['title']) . "', '" . vB_Page::TYPE_CUSTOM .  "', '" . $guid . "');
				"
			);
			$pageid = $this->db->insert_id();
			$mainpageid = $pageid;

			$newprefix = '';
			if (!empty($prefixes[$channel['parentid']]))
			{
				$newprefix .= $prefixes[$channel['parentid']] . '/';
			}
			$newprefix .= $hyphenedTitle;
			$newprefix =  $this->db->escape_string($newprefix);;
			$duplicateRegex = $this->db->query_first("
				SELECT COUNT(*) AS num
				FROM " . TABLE_PREFIX . "routenew
				WHERE regex = '$newprefix'
			");
			if ($duplicateRegex['num'] > 0)
			{
				// we cannot have duplicate regex
				$newprefix .= $channel['nodeid'];
			}

			$prefixes[$channel['nodeid']] = $newprefix;


			$this->db->query_write(
			"
			REPLACE INTO `" . TABLE_PREFIX . "routenew`
				(`prefix`, `regex`, `class`, `controller`, `action`, `template`, `arguments`, `contentid`, `guid`)
				VALUES
				('$newprefix', '$newprefix(?:/page(?P<pagenum>[0-9]+))?', 'vB5_Route_Channel', 'page', 'index', '', '" . serialize(array('channelid'=>$channel['nodeid'], 'pagenum'=>'$pagenum', 'pageid'=>$pageid)) . "', {$channel['nodeid']}, '" . vB_Xml_Export_Route::createGUID(array()) . "');
				"
			);
			$pageRouteId = $this->db->insert_id();

			$this->db->query_write(
			"
			UPDATE `" . TABLE_PREFIX . "page`
				SET `routeid` = $pageRouteId
				WHERE pageid = $pageid;
				"
			);
			$this->db->query_write(
			"
			UPDATE `" . TABLE_PREFIX . "node`
				SET `routeid` = $pageRouteId
				WHERE nodeid = {$channel['nodeid']} AND contenttypeid = $channelContentTypeId;
				"
			);

			// create conversation page for channels that are not a category
			if ($channel['category'] == 0)
			{
				$pagetemplateid = $pageTemplates[vB_Page::TEMPLATE_CONVERSATION];
				$parentid = $mainpageid;

				$guid = vB_Xml_Export_Page::createGUID(array());
				$guidforphrase = vB_Library::instance('phrase')->cleanGuidForPhrase($guid);
				$phraseLib->save('pagemeta',
					'page_' . $guidforphrase . '_title',
					array(
						'text' => array($channel['title']),
						'ismaster' => 1,
						'product' => 'vbulletin',
						't' => 0,
						'oldvarname' => 'page_' . $guidforphrase . '_title',
						'oldfieldname' => 'global',
					)
				);
				$phraseLib->save('pagemeta',
					'page_' . $guidforphrase . '_metadesc',
					array(
						'text' => array($channel['description']),
						'ismaster' => 1,
						'product' => 'vbulletin',
						't' => 0,
						'oldvarname' => 'page_' . $guidforphrase . '_metadesc',
						'oldfieldname' => 'global',
					)
				);

				$this->db->query_write(
				"
				REPLACE INTO `" . TABLE_PREFIX . "page`
					(`parentid`, `pagetemplateid`, `title`, `pagetype`, `guid`)
					VALUES
					($parentid, $pagetemplateid, '" . $this->db->escape_string($channel['title']) . "', '" . vB_Page::TYPE_DEFAULT .  "', '" . $guid . "');
					"
				);
				$pageid = $this->db->insert_id();

				// TODOS:
				// 1) The regex below should be using the defined constant in vB5_Route_Conversation so that we
				// don't have to update it every time.
				// 2) It may need to a check for conversation vs article route in the future.
				// 3) Ensure that $newprefix is preg_quoted
				// 4) Add contentpage component of the regex.
				// Just putting in a minimal-change fix for now, VBV-11356
				$this->db->query_write(
				"
				REPLACE INTO `" . TABLE_PREFIX . "routenew`
				(`prefix`, `regex`, `class`, `controller`, `action`, `template`, `arguments`, `contentid`, `guid`)
				VALUES
				('$newprefix', '$newprefix/(?P<nodeid>[0-9]+)(?P<title>(-[^!@\\\\#\\\\$%\\\\^&\\\\*\\\\(\\\\)\\\\+\\\\?/:;\"\\'\\\\\\\\,\\\\.<>= ]*)*)(?:/page(?P<pagenum>[0-9]+))?',
				'vB5_Route_Conversation', 'page', 'index', '', '" . serialize(
				array(
					'nodeid'	=> '$nodeid',
					'pagenum'	=> '$pagenum',
					'channelid'	=> $channel['nodeid'],
					'pageid'	=> $pageid
				)) . "', {$channel['nodeid']}, '" . vB_Xml_Export_Route::createGUID(array()) . "');
				"
				);
				$pageRouteId = $this->db->insert_id();
				$this->db->query_write(
				"
				UPDATE `" . TABLE_PREFIX . "page`
					SET `routeid` = $pageRouteId
					WHERE pageid = $pageid;
					"
				);
				$this->db->query_write(
				"
				UPDATE `" . TABLE_PREFIX . "node`
					SET `routeid` = $pageRouteId
					WHERE parentid = {$channel['nodeid']} AND contenttypeid = $textTypeId;
					"
				);
			}

			// add subchannels to queue
			$subChannelResult = $this->db->query_read($q = "
				SELECT nodeid, title, parentid
				FROM " . TABLE_PREFIX . "node
				WHERE
					parentid = {$channel['nodeid']}
					AND
					contenttypeid = $channelContentTypeId
					AND
					routeid = 0
			");

			$subChannels = array();
			while ($subChannel = $this->db->fetch_array($subChannelResult))
			{
				$subChannel['page_parentid'] = $mainpageid;
				$queue[] = $subChannel;
			}
		}
	}

	/** Add routes to node table**/
	function step_11()
	{
		$contenttype = $this->db->query_first("
			SELECT contenttypeid
			FROM " . TABLE_PREFIX . "contenttype
			WHERE class = 'Channel'
		");
		$channelContentTypeId = $contenttype['contenttypeid'];

		$this->show_message(sprintf($this->phrase['vbphrase']['update_table'], TABLE_PREFIX . 'node'));

		// updating channel routes
		$this->db->query_write("
			UPDATE " . TABLE_PREFIX . "node c
			SET c.routeid = (SELECT MAX(r.routeid) FROM " . TABLE_PREFIX . "routenew r WHERE r.contentid = c.nodeid AND class='vB5_Route_Channel')
			WHERE c.routeid = 0 AND c.contenttypeid = $channelContentTypeId
			"
		);
	}

	/**
	* Check Product Dependencies
	*
	*/
	function step_12()
	{
		if (VB_AREA == 'Install')
		{
			$this->skip_message();
			return;
		}

		$this->show_message($this->phrase['final']['verifying_product_dependencies']);

		require_once(DIR . '/includes/class_upgrade_product.php');
		$this->product = new vB_Upgrade_Product($this->registry, $this->phrase['vbphrase'], true, $this->caller);

		$dependency_list = array();
		$product_dependencies = $this->db->query_read("
			SELECT pd.*
			FROM " . TABLE_PREFIX . "productdependency AS pd
			INNER JOIN " . TABLE_PREFIX . "product AS p ON (p.productid = pd.productid)
			WHERE
				pd.productid IN ('dummy') # // Any Integrated 3rd party products
					AND
				p.active = 1
			ORDER BY
				pd.dependencytype, pd.parentproductid, pd.minversion
		");
		while ($product_dependency = $this->db->fetch_array($product_dependencies))
		{
			$dependency_list["$product_dependency[productid]"][] = array(
				'dependencytype'  => $product_dependency['dependencytype'],
				'parentproductid' => $product_dependency['parentproductid'],
				'minversion'      => $product_dependency['minversion'],
				'maxversion'      => $product_dependency['maxversion'],
			);
		}

		$product_list = fetch_product_list(true);
		$disabled = array();

		foreach($dependency_list AS $productid => $dependencies)
		{
			$this->show_message(sprintf($this->phrase['final']['verifying_product_x'], $productid));
			$this->product->productinfo['productid'] = $productid;
			$disableproduct = false;
			try
			{
				$this->product->import_dependencies($dependencies);
			}
			catch(vB_Exception_AdminStopMessage $e)
			{
				$message = $this->stop_exception($e);
				$this->show_message($message);
				$disableproduct = true;
			}

			if ($disableproduct)
			{
				$disabled[] = $productid;
				$this->product->disable();
				$this->add_adminmessage(
					'disabled_product_x_y_z',
					array(
						'dismissable' => 1,
						'script'      => '',
						'action'      => '',
						'execurl'     => '',
						'method'      => '',
						'status'      => 'undone',
					),
					true,
					array($product_list["$productid"]['title'], $productid, $message)
				);
				$this->show_message(sprintf($this->phrase['final']['product_x_disabled'], $productid));
			}
		}

		if (!should_install_suite())
		{
			if (!$disabled['vbblog'] AND $product_list['vbblog']['active'])
			{
				$this->product = new vB_Upgrade_Product($this->registry, $this->phrase['vbphrase'], true, $this->caller);
				$this->product->productinfo['productid'] = 'vbblog';
				$this->product->disable();
				$this->show_message(sprintf($this->phrase['final']['product_x_disabled'], 'vbblog'));
			}
		}

	}

	/**
	 * Import Style XML
	 *
	 * @param	array	contains id to startat processing at
	 *
	 */
	function step_13($data = null)
	{
		$perpage = 1;
		$startat = intval($data['startat']);
		require_once(DIR . '/includes/functions_databuild.php');
		require_once(DIR . '/includes/adminfunctions_template.php');

		if (!($xml = file_read(DIR . '/install/vbulletin-style.xml')))
		{
			$this->add_error(sprintf($this->phrase['vbphrase']['file_not_found'], 'vbulletin-style.xml'), self::PHP_TRIGGER_ERROR, true);
			return;
		}

		if ($startat == 0)
		{
			$this->show_message(sprintf($this->phrase['vbphrase']['importing_file'], 'vbulletin-style.xml'));
		}

		$info = xml_import_style($xml, -1, -1, '', false, 1, false, $startat, $perpage);

		if (!$info['done'])
		{
			$this->show_message($info['output']);
			return array('startat' => $startat + $perpage);
		}
		else
		{
			vB_Upgrade::createAdminSession();
			build_bbcode_video(true);
			$this->show_message($this->phrase['core']['import_done']);
		}
	}

	/**
	 * Reset all caches
	 */
	function step_14()
	{
		/*
		 * There are two reasons for reset cache in this class:
		 *  1- we want to run this once, no matter what version we are upgrading
		 *  2- we need to make sure that db has been updated with cache table
		 */

		$this->show_message($this->phrase['final']['resetting_cache']);

		// we need to restore original cache values, reverting the change in upgrade.php
		$config =& vB::getConfig();
		if (!empty($config['Backup']['Cache']['class']))
		{
			foreach ($config['Backup']['Cache']['class'] AS $key => $class)
			{
				$config['Cache']['class'][$key] = $class;
			}
		}

		// now reset all cache types
		vB_Cache::resetAllCache();

		//And set the fastDS cache for rebuild. We don't want to rebuild here- we might be running in CLI,
		// and a rebuild does nothing for the website. Also, we may be on two different server.
		//We want to force a fastDS rebuild, but we can't just call rebuild. There may be dual web servers,
		// and calling rebuild only rebuilds one of them.
		$options = vB::getDatastore()->getValue('miscoptions');
		$options['dsdate'] = vB::getRequest()->getTimeNow();
		$options['tmdate'] = vB::getRequest()->getTimeNow();
		$options['phrasedate'] = vB::getRequest()->getTimeNow();
		vB::getDatastore()->build('miscoptions', serialize($options), 1);
	}

	/**
	 * Rebuild filesystem template cache
	 */
	function step_15()
	{
		if (vB::getDatastore()->getOption('cache_templates_as_files'))
		{
			$this->show_message($this->phrase['final']['rebuild_filesystem_template_cache']);
			vB_Upgrade::createAdminSession();
			$templateApi = vB_Api::instanceInternal('template');
			$templateApi->deleteAllTemplateFiles();
			$templateApi->saveAllTemplatesToFile();
		}
		else
		{
			$this->skip_message();
		}
	}

	/**
	 *	Import the password schemes
	 */
	function step_16()
	{
		$this->show_message($this->phrase['final']['import_password_schemes']);
		vB_Library::instance('login')->importPasswordSchemes();
	}

	/**
	* Step #16 Template Merge
	* THIS SHOULD ALWAYS BE THE LAST STEP
	* If this step changes vbulletin-upgrade.js must also be updated in the process_bad_response() function
	*
	* @param	array	contains start info
	*
	*/
	function step_17($data = null)
	{
		if ($data['response'] == 'timeout')
		{
			$this->show_message($this->phrase['final']['step_timed_out']);
			return;
		}

		$this->show_message($this->phrase['final']['merge_template_changes']);
		$startat = intval($data['startat']);
		require_once(DIR . '/includes/class_template_merge.php');

		$products = array("'vbulletin'");

		$merge_data = new vB_Template_Merge_Data($this->registry);
		$merge_data->start_offset = $startat;
		$merge_data->add_condition($c = "tnewmaster.product IN (" . implode(', ', $products) . ")");

		$merge = new vB_Template_Merge($this->registry);
		$merge->time_limit = 4;
		$output = array();
		$completed = $merge->merge_templates($merge_data, $output);

		if ($output)
		{
			foreach($output AS $message)
			{
				$this->show_message($message);
			}
		}

		if ($completed)
		{
			// Style rebuild sometimes needs a session.
			vB_Upgrade::createAdminSession();

			if ($error = build_all_styles(0, 0, '', true))
			{
				$this->add_error($error, self::PHP_TRIGGER_ERROR, true);
				return false;
			}
		}
		else
		{
			return array('startat' => $startat + $merge->fetch_processed_count() );
		}
	}
}

/*======================================================================*\
|| ####################################################################
|| # Downloaded: 07:00, Thu May 15th 2014
|| # CVS: $RCSfile$ - $Revision: 35750 $
|| ####################################################################
\*======================================================================*/

