<?php

/* ======================================================================*\
  || #################################################################### ||
  || # 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 # ||
  || #################################################################### ||
  \*====================================================================== */

class vB_Xml_Import_Channel extends vB_Xml_Import
{
	protected $referencedRoutes;
	protected $referencedParents;
	protected $contenttypes;
	protected $bf_moderatorpermissions;
	protected $bf_administratorpermissions;
	protected $bf_albumpermissions;
	protected $bf_forumpermissions;

	/**
	 * Checks if all referenced routes are already defined
	 * Also sets a class attribute to be used while importing
	 */
	protected function checkRoutes()
	{
		$requiredRoutes = array();

		$channels = is_array($this->parsedXML['channel'][0]) ? $this->parsedXML['channel'] : array($this->parsedXML['channel']);
		foreach ($channels AS $channel)
		{
			if (isset($channel['node']['routeguid']))
			{
				$requiredRoutes[] = $channel['node']['routeguid'];
			}
		}

		$existingRoutes = $this->db->getRows('routenew', array('guid' => $requiredRoutes));
		foreach ($existingRoutes AS $route)
		{
			$this->referencedRoutes[$route['guid']] = $route['routeid'];
		}

		if (!$this->options & vB_Xml_Import::OPTION_IGNOREMISSINGROUTES)
		{
			$missingRoutes = array_diff($requiredRoutes, array_keys($this->referencedRoutes));
			if (!empty($missingRoutes))
			{
				throw new Exception('Reference to undefined routes(s): ' . implode(' ', $missingRoutes));
			}
		}
	}

	/**
	 * Checks if all referenced parents are already defined or imported
	 * Also sets a class attribute to be used while importing
	 */
	protected function checkParents()
	{
		$requiredChannels = array();

		$channels = is_array($this->parsedXML['channel'][0]) ? $this->parsedXML['channel'] : array($this->parsedXML['channel']);
		foreach ($channels AS $channel)
		{
			if (isset($channel['node']['parentguid']))
			{
				$requiredChannels[] = $channel['node']['parentguid'];
			}
		}

		$existingChannels = $this->db->getRows('channel', array('guid' => $requiredChannels));
		foreach ($existingChannels AS $channel)
		{
			$this->referencedParents[$channel['guid']] = $channel['nodeid'];
		}

		if (!$this->options & vB_Xml_Import::OPTION_IGNOREMISSINGPARENTS)
		{
			$missingChannels = array_diff($requiredChannels, array_keys($this->referencedParents));
			if (!empty($missingChannels))
			{
				throw new Exception('Reference to undefined parent channel(s): ' . implode(' ', $missingChannels));
			}
		}
	}

	public function import($filepath)
	{
		$this->parsedXML = vB_Xml_Import::parseFile($filepath);
		$this->checkRoutes();
		$this->checkParents();
		$channels = is_array($this->parsedXML['channel'][0]) ? $this->parsedXML['channel'] : array($this->parsedXML['channel']);

		//On an upgrade from Pre-vB5 the datastore won't exist yet. So we can't get the bitfields from there.
		$parsedRaw = vB_Xml_Import::parseFile(DIR . '/includes/xml/bitfield_vbulletin.xml');
		$forumpermissions = $moderatorpermissions = $createpermissions = $adminpermissions = $albumpermissions = array();

		foreach ($parsedRaw['bitfielddefs']['group'] AS $group)
		{
			if ($group['name'] == 'ugp')
			{
				foreach($group['group'] AS $bfgroup)
				{
					switch ($bfgroup['name'])
					{
						case 'forumpermissions':
							foreach ($bfgroup['bitfield'] AS $bitfield)
							{
								$forumpermissions[$bitfield['name']] = intval($bitfield['value']);
							}
							break;
						case 'adminpermissions':
							foreach ($bfgroup['bitfield'] AS $bitfield)
							{
								$adminpermissions[$bitfield['name']] = intval($bitfield['value']);
							}
							break;
						case 'albumpermissions':
							foreach ($bfgroup['bitfield'] AS $bitfield)
							{
								$albumpermissions[$bitfield['name']] = intval($bitfield['value']);
							}
							break;
						case 'createpermissions':
							foreach ($bfgroup['bitfield'] AS $bitfield)
							{
								$createpermissions[$bitfield['name']] = intval($bitfield['value']);
							}
					}
				}
			}
			else if ($group['name'] == 'misc')
			{
				foreach($group['group'] AS $bfgroup)
				{
					if ($bfgroup['name'] == 'moderatorpermissions')
					{
						foreach ($bfgroup['bitfield'] AS $bitfield)
						{
							$moderatorpermissions[$bitfield['name']] = intval($bitfield['value']);
						}
					}
				}
			}
		}

		$this->bf_administratorpermissions = $adminpermissions;
		$this->bf_moderatorpermissions = $moderatorpermissions;
		$this->bf_forumpermissions = $forumpermissions;
		$this->bf_albumpermissions = $albumpermissions;
		$this->contenttypes = $createpermissions;
		$rootPerms = $this->buildRootPerms();

		$channelLib = vB_Library::instance('content_channel');
		$channelPerm = vB_ChannelPermission::instance();
		foreach ($channels AS $channel)
		{
			$data = array_merge($channel, $channel['node']);
			unset($data['node']);
			if (isset($channel['node']['routeguid']) AND isset($this->referencedRoutes[$channel['node']['routeguid']]))
			{
				$data['routeid'] = $this->referencedRoutes[$channel['node']['routeguid']];
			}

			if
			(
				isset($channel['node']['parentguid']) AND !empty($channel['node']['parentguid']) AND
					($parentId = $this->referencedParents[$channel['node']['parentguid']] OR
					 $parentId = vB_Xml_Import::getImportedId(vB_Xml_Import::TYPE_CHANNEL, $channel['node']['parentguid']))
			)
			{
				$data['parentid'] = $parentId;
			}

			$channelId = 0;
			$condition = array('guid' => $channel['guid']);

			// TODO: after talking with Ed, we decided it might be best not to allow node removal at all. Need to confirm with Kevin
			if ($oldChannel = $this->db->getRow('channel', $condition))
			{
				$channelId = $oldChannel['nodeid'];
			}

			if (empty($channelId))
			{
				// We cannot use the API method for adding the root channel, since it requires a parentid.
				if (!isset($data['parentid']))
				{
					$channelContentTypeId = vB_Api::instanceInternal('contenttype')->fetchContentTypeIdFromClass('Channel');
					$userId = vB::getCurrentSession()->get('userid');

					// get all columns but the key
					$channelTable = $this->db->fetchTableStructure('vbforum:channel');
					$channelTableColumns = array_diff($channelTable['structure'], array($channelTable['key']));

					$nodeTable = $this->db->fetchTableStructure('vbforum:node');
					$nodeTableColumns = array_diff($nodeTable['structure'], array($nodeTable['key'], $channelTable['key']));

					// insert root node
					$nodeValues = array(
						'userid' => $userId,
						'contenttypeid' => $channelContentTypeId
					);
					foreach($nodeTableColumns AS $col)
					{
						if (isset($channel['node'][$col]))
						{
							$nodeValues[$col] = $channel['node'][$col];
						}
					}
					if (isset($data['routeid']))
					{
						$nodeValues['routeid'] = $data['routeid'];
					}

					$channelId = $this->db->insertIgnore('node', $nodeValues);

					if (is_array($channelId))
					{
						$channelId = array_pop($channelId);
					}

					// continue only if the node could be inserted
					if ($channelId)
					{
						$values = array();
						foreach($channelTableColumns AS $col)
						{
							if (isset($channel[$col]))
							{
								$values[$col] = $channel[$col];
							}
						}
						$values['nodeid'] = $channelId;
						$this->db->insert('channel', $values);
					}

					// insert in closure
					$this->db->insert('closure', array('parent' => $channelId, 'child' => $channelId, 'depth' => 0));

					foreach ($rootPerms AS $groupPerm)
					{
						$channelPerm->setPermissions($channelId, $groupPerm['groupid'], $groupPerm);
					}
				}
				else
				{
					$channelId = $channelLib->add($data, array('nodeonly' => true, 'skipFloodCheck' => true, 'skipNotification' => true));
				}
			}
			else
			{
				/* Updated for VBV-2536.
				This runs on every upgrade and was just blindly overwriting all the existing
				channel data with the default values in the channels.xml file, really not cool. */
				// $channelLib->update($channelId, $data);

				/* Apparently routeid gets changed every update .... */
				if (isset($channel['node']['routeguid']) AND isset($this->referencedRoutes[$channel['node']['routeguid']]))
				{
					$data['routeid'] = $this->referencedRoutes[$channel['node']['routeguid']];
				}
			}

			if (is_array($channelId))
			{
				$channelId = array_pop($channelId);
			}

			/* VBV-2536, Same as above, $oldChannel chack this is to
			stop the import blindly resetting permissions on every update */
			if (!$oldChannel AND !empty($channelId))
			{
				/* We don't know what usergroups exist currently. There may be the groups 1-8 we create on a new install,
				 or there may not. If a group exists it may have a different meaning from what we create on a new install.
				So here's what we have decided:
				1) At root if there is at least one forum permission record:
					a) you get any permissions you have in any existing root forum.
					b) If you have "albumpermissions -> canalbum" you can create albums and photos.
					c) If you have adminpermissions->canadminforums you can create channels.
					d) If you can forumpermissions->canpostnew you can create all the other types
				2) If the forumpermissions doesn't exist or is empty, treat this as a new install. Take the permissions
					from the xml file, taking the id as systemgroupid.
				3) At root everybody can create private messages.
				4) 	For the blog and social groups channels, we have newly created usergroups with systemgroupid. Those two channels
				are specified in the xml file.
				5) For all other usergroups in those two channels-
					a) if you have moderator permission canmoderateposts, no record. Just inherit.
					b) otherwise, get your root permissions plus channel
				6) For the report channel- if you have moderator permission canmoderateposts anywhere on the site
					you inherit permissions. Otherwise you have none.
				*/

				vB_Xml_Import::setImportedId(vB_Xml_Import::TYPE_CHANNEL, $channel['guid'], $channelId);

				switch ($data['guid'])
				{
					case vB_Channel::DEFAULT_BLOG_PARENT :
					case vB_Channel::DEFAULT_SOCIALGROUP_PARENT :
						foreach ($rootPerms AS $groupPerm)
						{
							if ($groupPerm['createpermissions'] > 0)
							{
								$thisPerm = $groupPerm;
								$thisPerm['createpermissions'] = $groupPerm['createpermissions'] | $this->contenttypes['vbforum_channel'];
							}

							$channelPerm->setPermissions($channelId, $groupPerm['groupid'], $thisPerm);
						}
					break;
					case vB_Channel::REPORT_CHANNEL :
						foreach ($rootPerms AS $groupPerm)
						{
							if ($rootPerms['adminpermissions'] > 0 )
							{
								$channelPerm->setPermissions($channelId, $groupPerm['groupid'], $groupPerm);
							}
							else
							{
								$channelPerm->setPermissions($channelId, $groupPerm['groupid'], $this->fetchEmptyPermissions());
							}
						}
					break;
					//If it's anything else we don't set permissions.
				}
			}
		}
	}

	/**
	 * Returns a permission record with nothing in it
	 *
	 * @return	array	empty permission array
	 */
	protected function fetchEmptyPermissions()
	{
		return array(
			'maxothertags'         => 0,
			'maxstartertags'       => 0,
			'maxtags'              => 0,
			'maxattachments'       => 0,
			'edit_time'            => 1,
			'adminpermissions'     => 0,
			'createpermissions'    => 0,
			'forumpermissions'     => 0,
			'moderatorpermissions' => 0,
		);
	}

	public function updateChannelRoutes()
	{
		$importedChannels = vB_Xml_Import::getImportedId(vB_Xml_Import::TYPE_CHANNEL);
		$importedRoutes = vB_Xml_Import::getImportedId(vB_Xml_Import::TYPE_ROUTE);
		$channels = is_array($this->parsedXML['channel'][0]) ? $this->parsedXML['channel'] : array($this->parsedXML['channel']);

		foreach ($channels AS $channel)
		{
			$routeGUID = $channel['node']['routeguid'];
			if (isset($importedChannels[$channel['guid']]) AND isset($importedRoutes[$routeGUID]))
			{
				$this->db->update(
					'node',
					array('routeid' => $importedRoutes[$routeGUID]),
					array('nodeid'	=> $importedChannels[$channel['guid']])
				);
			}
		}
	}

	protected function buildRootPerms()
	{
		$perms = array();
		$options = vB::getDatastore()->getValue('options');
		$groupList = vB_Api::instanceInternal('usergroup')->fetchUsergroupList(true);

		foreach($groupList as $group)
		{
			$groupid = $group['usergroupid'];
			$perms[$groupid] = array('groupid' => $groupid);
			foreach (array('moderatorpermissions', 'createpermissions', 'require_moderate',
						 'edit_time', 'maxtags', 'maxstartertags', 'maxothertags', 'maxattachments') AS $fieldName)
			{
				$perms[$groupid][$fieldName] = 0;
			}
			$perms[$groupid]['forumpermissions'] = $group['forumpermissions'];

			if (($group['adminpermissions'] & $this->bf_administratorpermissions['ismoderator'] )
				OR ($group['systemgroupid'] == vB_Api_UserGroup::CHANNEL_MODERATOR_SYSGROUPID) OR ($group['systemgroupid'] == vB_Api_UserGroup::SG_MODERATOR_SYSGROUPID))
			{
				$perms[$groupid]['moderatorpermissions'] =  $this->bf_moderatorpermissions['caneditposts'] |
				$this->bf_moderatorpermissions['candeleteposts'] | $this->bf_moderatorpermissions['canopenclose'] |
				$this->bf_moderatorpermissions['canmanagethreads'] | $this->bf_moderatorpermissions['caneditthreads'] |
				$this->bf_moderatorpermissions['canmoderateposts'] | $this->bf_moderatorpermissions['canmoderateattachments'] |
				$this->bf_moderatorpermissions['canviewprofile'] | $this->bf_moderatorpermissions['canremoveposts'] |
				$this->bf_moderatorpermissions['caneditpoll'] | $this->bf_moderatorpermissions['cansetfeatured'] |
				$this->bf_moderatorpermissions['canmoderatetags'];
			}
			elseif (($group['systemgroupid'] == vB_Api_UserGroup::CHANNEL_OWNER_SYSGROUPID) OR ($group['systemgroupid'] == vB_Api_UserGroup::SG_OWNER_SYSGROUPID))
			{
				$perms[$groupid]['moderatorpermissions'] =  $this->bf_moderatorpermissions['caneditposts'] |
					$this->bf_moderatorpermissions['candeleteposts'] | $this->bf_moderatorpermissions['canopenclose'] |
					$this->bf_moderatorpermissions['canmanagethreads'] | $this->bf_moderatorpermissions['caneditthreads'] |
					$this->bf_moderatorpermissions['canmoderateposts'] | $this->bf_moderatorpermissions['canmoderateattachments'] |
					$this->bf_moderatorpermissions['canviewprofile'] | $this->bf_moderatorpermissions['canremoveposts'] |
					$this->bf_moderatorpermissions['caneditpoll'] | $this->bf_moderatorpermissions['cansetfeatured'] |
					$this->bf_moderatorpermissions['canmoderatetags'] | $this->bf_moderatorpermissions['canaddowners'];
			}
			else
			{
				$perms[$groupid]['moderatorpermissions'] =  0;
			}
			$perms[$groupid]['albumpermissions'] = $group['albumpermissions'];

			if ($group['adminpermissions'] & $this->bf_administratorpermissions['cancontrolpanel'])
			{
				$perms[$groupid]['moderatorpermissions'] |= $this->bf_moderatorpermissions['canbanusers'];
				$perms[$groupid]['moderatorpermissions'] |= $this->bf_moderatorpermissions['canaddowners'];
			}

			$perms[$groupid]['adminpermissions'] = $group['adminpermissions'];
		}

		if (empty($options))
		{
			$options = array();
		}
		if (!isset($options['tagmaxuser']))
		{
			$options['tagmaxuser'] = 5;
		}
		if (!isset($options['tagmaxstarter']))
		{
			$options['tagmaxstarter'] = 5;
		}
		if (!isset($options['noeditedbytime']))
		{
			$options['noeditedbytime'] = 24;
		}
		if (!isset($options['attachlimit']))
		{
			$options['attachlimit'] = 5;
		}
		$options['maxtags']  = $options['tagmaxuser']  + $options['tagmaxstarter'];

		try
		{
			$rootPermQry = vB::getDbAssertor()->assertQuery('vBInstall:getRootForumPerms', array());
			if ($rootPermQry->valid())
			{
				foreach($rootPermQry AS $rootPerm)
				{
					$perms[$rootPerm['usergroupid']]['forumpermissions'] |= $rootPerm['forumpermissions'];
				}
			}
		}
		catch(Exception $e)
		{
			//this happens when this is not an upgrade. There is nothing to do.
		}

		foreach ($perms AS $groupid => $permissions)
		{
			$perms[$groupid]['maxothertags'] = $options['tagmaxuser'];
			$perms[$groupid]['maxstartertags'] = $options['tagmaxstarter'];
			$perms[$groupid]['maxtags'] = $options['maxtags'];
			$perms[$groupid]['maxattachments'] = $options['attachlimit'];
			$perms[$groupid]['edit_time'] = $options['noeditedbytime'];
			foreach ($this->contenttypes AS $name => $bitfield)
			{
				switch($name)
				{
					case 'vbforum_channel':
						if (($perms[$groupid]['adminpermissions'] & $this->bf_administratorpermissions['canadminforums']) OR ($perms[$groupid]['adminpermissions'] & $this->bf_administratorpermissions['cancontrolpanel']))
						{
							$perms[$groupid]['createpermissions'] |= $bitfield;
						}
						break;
					case 'vbforum_gallery':
					case 'vbforum_photo':
						if ($perms[$groupid]['albumpermissions'] & $this->bf_albumpermissions['canalbum'])
						{
							$perms[$groupid]['createpermissions'] |= $bitfield;
						}
						break;
					case 'vbforum_privatemessage':
						$perms[$groupid]['createpermissions'] |= $bitfield;
						break;
					default :
						if ($perms[$groupid]['forumpermissions'] & $this->bf_forumpermissions['canpostnew'])
						{
							$perms[$groupid]['createpermissions'] |= $bitfield;
						}
				}
			}
		}

		return $perms;
	}
}

/*======================================================================*\
|| ####################################################################
|| # 
|| # CVS: $RCSfile$ - $Revision: 40911 $
|| ####################################################################
\*======================================================================*/
