<?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_Blog
 *
 * @package vBApi
 * @access public
 */
class vB_Api_Blog extends vB_Api
{
	protected $blogChannel = false;
	protected $db;
	protected $library;
	protected $groups = array(
		'owner' => vB_Api_UserGroup::CHANNEL_OWNER_SYSGROUPID,
		'mods' => vB_Api_UserGroup::CHANNEL_MODERATOR_SYSGROUPID,
		'members' => vB_Api_UserGroup::CHANNEL_MEMBER_SYSGROUPID
	);

	protected function __construct()
	{
		parent::__construct();
		$this->assertor = vB::getDbAssertor();
		$this->library = vB_Library::instance('blog');
	}

	public function createBlog($input)
	{
		return $this->createPost($input, $this->getBlogChannel(), vB_Page::getBlogConversPageTemplate(), vB_Page::getBlogChannelPageTemplate(), vB_Api_UserGroup::CHANNEL_OWNER_SYSGROUPID);
	}

	protected function createPost($input, $channelid, $channelConvTemplateid, $channelPgTemplateId, $ownerSystemGroupId)
	{
		// Check user is logged in
		$userid = vB::getCurrentSession()->get('userid');
		$userid = intval($userid);
		if (!$channelid)
		{
			$channelid = $this->getBlogChannel();
		}

		if ($userid <= 0 || !vB::getUserContext()->getChannelPermission('createpermissions', 'vBForum_Channel', $channelid))
		{
			throw new vB_Exception_Api('no_permission');
		}

		// Check input is valid
		$errors = array();

		if (!isset($input['title']) || empty($input['title']))
		{
			$errors[] = 'content_no_title';
		}
		else
		{
			// verify prefixes do not collide
			$newPrefix = vB5_Route_Channel::createPrefix($input['title'], $channelid);
			if (vB5_Route::isPrefixUsed($newPrefix))
			{
				$errors[] = 'blog_title_exists';
			}
		}

		if (!isset($input['description']) || empty($input['description']))
		{
			$errors[] = 'content_no_description';
		}

		if (!empty($errors))
		{
			$e = new vB_Exception_Api();
			foreach ($errors as $error)
			{
				$e->add_error($error);
			}

			throw $e;
		}

		vB_Api::instanceInternal('content_channel')->cleanInput($input);
		return $this->library->createPost($input, $channelid, $channelConvTemplateid, $channelPgTemplateId, $ownerSystemGroupId);
	}

	/**
	 * @uses fetch the id of the global Blog Channel
	 * @return int nodeid of actual Main Blog Channel
	 */
	public function getBlogChannel()
	{
		return $this->library->getBlogChannel();
	}

	/**
	 * Determines if the given node is a blog-related node (blog entry).
	 *
	 * @param	int	$nodeid
	 * @return	bool
	 */
	public function isBlogNode($nodeId)
	{
		$nodeId = (int) $nodeId;

		if ($nodeId < 0)
		{
			return false;
		}

		$blogChannelId = (int) $this->getBlogChannel();

		$nodeApi = vB_Api::instanceInternal('node');
		$node = $nodeApi->getNode($nodeId, true, false);

		return in_array($blogChannelId, $node['parents']);
	}

	/**
  * @uses Get info on every Blog Channel
  * @param mixed same parameter $options as listNodeContent from node API
  * @return mixed array containing the blog channel info we need
	 */
	public function getBlogInfo()
	{
		$response = array();
		$nodeApi = vB_Api::instanceInternal('node');
		$blogParentChannel = $this->getBlogChannel();
		$channelContentType  = vB_Types::instance()->getContentTypeId('vBForum_Channel');

		//Get base data
		$options = array('starter_only' => 1, 'channel' => $blogParentChannel, 'ignore_cache' => 1, 'view' => 'activity',
			'exclude_type' => array('vBForum_Channel', 'vBForum_Attach')
		);
		$nodeContent = $nodeApi->listNodeContent($blogParentChannel, 1, 20, 1, $channelContentType, $options);

		//We need the nodeids to collect some data
		$lastids = array();
		$channelids = array();
		foreach ($nodeContent AS $key => $node)
		{
			if ($node['lastcontentid'] > 0)
			{
				$lastids[] = $node['lastcontentid'];
			}
			$channelids[] = $node['nodeid'];
		}

		if (empty($channelids))
		{
			return array();
		}

		$lastNodes = vB_Api::instanceInternal('node')->getNodes($lastids);

		$lastTitles = $lastInfo = array();

		foreach ($lastNodes as $lastnode)
		{
			$lastTitles[$lastnode['nodeid']] =  $lastnode['title'];
		}

		//Get contributors
		$contributorQry = vB::getDbAssertor()->getRows('vBForum:groupintopic', array(
			vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
			'nodeid' => $channelids,
			'groupid' => vB_Api::instanceInternal('usergroup')->getModeratorGroupId()
		));
		$contributors = array();
		$userApi = vB_Api::instanceInternal('user');
		foreach ($contributorQry as $index => $contributor)
		{
			//Get contributors
			$contributorQry = vB::getDbAssertor()->assertQuery('vBForum:groupintopic', array(
				vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
				'nodeid' => $channelids,
				'groupid' => vB_Api::instanceInternal('usergroup')->getModeratorGroupId()
			));

			$contributors = array();
			foreach ($contributorQry as $contributor)
			{
				if (!isset($contributors[$contributor['nodeid']]))
				{
					$contributors[$contributor['nodeid']] = array();
				}
				$userInfo = $userApi->fetchUserinfo($contributor['userid'], array(vB_Api_User::USERINFO_AVATAR));
				$contributors[$contributor['nodeid']][$contributor['userid']] = $userInfo;
			}
			$userInfo = $userApi->fetchUserinfo($contributor['userid'], array(vB_Api_User::USERINFO_AVATAR));
			$contributors[$contributor['nodeid']][$contributor['userid']] = $userInfo;
		}

		// Obtain keys for blog pages
		$pageKeyInfo = array();
		$routes = vB::getDbAssertor()->assertQuery('routenew', array('class' => 'vB5_Route_Channel', 'contentid' =>$channelids));
		foreach ($routes as $record)
		{
			$route = vB5_Route_Channel::getRoute($record['routeid'], @unserialize($record['arguments']));
			if ($route AND ($pageKey = $route->getPageKey()))
			{
				$pageKeyInfo[$pageKey] = $record['contentid'];
			}
		}

		// ... now obtain visits
		$viewingQry = vB::getDbAssertor()->getRows('session',
		    array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, 'pagekey' => array_keys($pageKeyInfo))
		);
		$viewing = array();
		foreach ($viewingQry as $viewingUser)
		{
			$nodeId = $pageKeyInfo[$viewingUser['pagekey']];
			if (!isset($viewing[$nodeId]))
			{
				$viewing[$nodeId] = 0;
			}
			$viewing[$nodeId]++;
		}

		foreach ($nodeContent AS $index => $channel)
		{
			$nodeid = $channel['nodeid'];
			$nodeContent[$index]['contributors'] = $contributors[$nodeid] ? $contributors[$nodeid] : 0;
			$nodeContent[$index]['viewing'] = $viewing[$nodeid] ? $viewing[$nodeid] : 0 ;
			$nodeContent[$index]['lastposttitle'] = $lastTitles[$channel['lastcontentid']] ? $lastTitles[$channel['lastcontentid']] : 0;
		}

		return $nodeContent;
	}

	public function getBlogAdminPerms($nodeid)
	{
		$userContext = vB::getUserContext();

		if ($userContext->getChannelPermission('moderatorpermissions', 'canaddowners', $nodeid))
		{
			return array('canadmin' => 1, 'canmoderate' => 1);
		}
		else if ($userContext->getChannelPermission('moderatorpermissions', 'canmoderateposts', $nodeid))
		{
			return array('canadmin' => 0, 'canmoderate' => 1);
		}
		else
		{
			return array('canadmin' => 0, 'canmoderate' => 0);
		}
	}

	public function fetchOwner($nodeid)
	{
		$groups = array(vB_Api::instanceInternal('usergroup')->getOwnerGroupId(vB_Api_UserGroup::SG_OWNER_SYSGROUPID),
			vB_Api::instanceInternal('usergroup')->getOwnerGroupId(vB_Api_UserGroup::CHANNEL_OWNER_SYSGROUPID) );
		$contribuitors = vB::getDbAssertor()->assertQuery('vBForum:groupintopic', array(
				'nodeid' => $nodeid,
				'groupid' => $groups
			)
		);

		if ($contribuitors->valid())
		{
			$owner = $contribuitors->current();
			return $owner['userid'];
		}
		return false;
	}


	public function fetchContribuitors($nodeid, $count = false)
	{
		$contribuitors = vB::getDbAssertor()->getRows('vBForum:groupintopic', array(
			'nodeid' => $nodeid,
			'groupid' => vB_Api::instanceInternal('usergroup')->getModeratorGroupId(),
			vB_dB_Query::PARAM_LIMIT => $count
			),
			false,
			'userid'
		);
		return empty ($contribuitors) ? array() : array_keys($contribuitors);
	}

	/** fetch information about subscribers for a node.
	*
	* @param	int		the nodeid
	*	@param	int		page for which we want data
	*	@param	int		items per page
	*
	*	@return	mixed	array with 'count'=> total subscriber count, 'members'=> array of userid, name, avatar.
	***/
	public function fetchSubscribers($nodeid, $pageno = 1, $perpage = 20, $thumb = false)
	{
		if (!$nodeid OR !(intval($nodeid) > 0) OR !$pageno OR !(intval($pageno) > 0) OR
			!$perpage OR !(intval($perpage) > 0))
		{
			throw new vB_Exception_Api('invalid_data');
		}
		$userContext = vB::getUserContext();

		if (!$userContext->getChannelPermission('moderatorpermissions', 'canmoderateposts', $nodeid))
		{
			throw new vB_Exception_Api('no_permission');
		}

		$userApi = vB_Api::instanceInternal('user');
		$assertor = vB::getDbAssertor();
		$memberGroup = vB_Api::instanceInternal('usergroup')->fetchUsergroupBySystemID(vB_Api_UserGroup::CHANNEL_MEMBER_SYSGROUPID);
		$memberCount = $assertor->getRow('vBForum:groupintopicCount', array('groupid' => $memberGroup['usergroupid'], 'nodeid' => intval($nodeid)));
		$results = array('count' => $memberCount['count'], 'pagecount' => ceil($memberCount['count']/$perpage),  'groupid' => $memberGroup['usergroupid'],
			'members' => array());
		$offset = (intval($pageno) -1) * intval($perpage);
		$members = $assertor->assertQuery('vBForum:groupintopicPage',
			array('groupid' => $memberGroup['usergroupid'], 'nodeid' => intval($nodeid),
			vB_dB_Query::PARAM_LIMITSTART => $offset, vB_dB_Query::PARAM_LIMIT => intval($perpage)));
		foreach ($members as $member)
		{
			$avatarUrl = $userApi->fetchAvatar($member['userid'], $thumb, $member);
			if (empty($avatarUrl))
			{
				$member['avatarUrl'] = 0;
			}
			else
			{
				$member['avatarUrl'] = $avatarUrl['avatarpath'];
			}

			$results['members'][] = $member;
		}

		return $results;
	}

	/**
	 * Handles subscription in special channels for the current user.
	 * This is used basically for social groups subscription handling the join/subscribe logical but we are
	 * implementing here in case requirements change and join/subscribe gets into blogs too.
	 *
	 * @param	int		The channel id we are subscribing to
	 *
	 * @param	bool	Flag indicating if subscription went well.
	 */
	public function subscribeChannel($channelId)
	{
		if (!intval($channelId))
		{
			throw new vB_Exception_Api('invalid_node_id');
		}

		try
		{
			$nodeInfo = vB_Api::instanceInternal('node')->getNode($channelId);
		}
		catch (vB_Exception_Api $ex)
		{
			throw $ex;
		}

		// validate that we have joined this
		$userId = vB::getUserContext()->fetchUserId();
		if (empty($userId))
		{
			throw new vB_Exception_Api('not_logged_no_permission');
		}

		try
		{
			$result = vB_Api::instanceInternal('user')->getGroupInTopic(vB::getUserContext()->fetchUserId(), $channelId);
			$result = array_pop($result);
		}
		catch (vB_Exception_Api $ex)
		{
			throw $ex;
		}

		// validate the record
		if (!empty($result) AND ($result['nodeid'] == $channelId))
		{
			try
			{
				$response = vB_Api::instanceInternal('follow')->add($channelId, vB_Api_Follow::FOLLOWTYPE_CHANNELS);
			}
			catch (vB_Exception_Api $ex)
			{
				throw $ex;
			}

			return $response;
		}
		else
		{
			throw new vB_Exception_Api('invalid_special_channel_subscribe_request');
		}
	}

	/**
	 * Handles leave in special channels for the current user.
	 * This is used basically for social groups handling the join/subscribe logical but we are
	 * implementing here in case requirements change and join/subscribe gets into blogs too.
	 *
	 * @param	int		The channel id we are subscribing to
	 *
	 * @param	bool	Flag indicating if subscription went well.
	 */
	public function leaveChannel($channelId)
	{
		$userId = vB::getUserContext()->fetchUserId();
		if (!$this->isChannelModerator($channelId))
		{
			throw new vB_Exception_Api('no_leave_channel_permission');
		}

		// unfollow first...
		try
		{
			// @TODO change this to use removeFollowing if unsubscribing will use the same logic as regular channels
			// we are only removing the subscribediscussion record here...
			vB_Api::instanceInternal('follow')->delete($channelId, vB_Api_Follow::FOLLOWTYPE_CHANNELS);
			$memberGroup = vB_Api::instanceInternal('usergroup')->getMemberGroupId();

			if (!$memberGroup)
			{
				throw new vB_Exception_Api('invalid_membergroup_id');
			}

			$result = vB_Api::instanceInternal('user')->unsetGroupInTopic($userId, $channelId, $memberGroup);

			// check if we have a pending request to remove...
			$existingCheck = vB::getDbAssertor()->getRows('vBForum:getNodePendingRequest', array('userid' => array($userId),
				'nodeid' => array($channelId), 'request' => array(vB_Api_Node::REQUEST_GRANT_SUBSCRIBER, vB_Api_Node::REQUEST_SG_GRANT_SUBSCRIBER)));

			if (!empty($existingCheck) AND is_array($existingCheck))
			{
				$nodeIds = array();
				foreach ($existingCheck AS $rec)
				{
					$nodeIds[] = $rec['nodeid'];
				}

				vB::getDbAssertor()->assertQuery('vBForum:node', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_DELETE,
					'nodeid' => $nodeIds));
				vB::getDbAssertor()->assertQuery('vBForum:privatemessage', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_DELETE,
					'nodeid' => $nodeIds));
				vB::getDbAssertor()->assertQuery('vBForum:sentto', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_DELETE,
					'nodeid' => $nodeIds));
				vB::getDbAssertor()->assertQuery('vBForum:text', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_DELETE,
					'nodeid' => $nodeIds));
			}
		}
		catch (vB_Exception_Api $ex)
		{
			throw $ex;
		}

		return $result;
	}

	/**
	 * Indicates if the current user is owner or mod from given channel.
	 * Owner can't leave the channel ditto for mods (managers or contributors).
	 * Also used to indicate if current user can leave a special channel or not (social group or blog).
	 *
	 * @param	int		The channel id we are checking.
	 *
	 * @param	bool	Flag indicating if user is owner/mod
	 */
	public function isChannelModerator($channelId)
	{
		$userId = vB::getUserContext()->fetchUserId();

		$channelMembers = vB::getDbAssertor()->assertQuery('vBForum:groupintopic', array(
				'nodeid' => $channelId,
				'groupid' => array($this->groups['mods'], $this->groups['owner'])
			)
		);

		$granted = true;
		foreach ($channelMembers AS $record)
		{
			if ($userId == $record['userid'])
			{
				$granted = false;
			}
		}

		return $granted;
	}

	/**
	 * Indicates if the current user is member of a given channelid
	 *
	 * @param	int		The channel id we are checking.
	 *
	 * @return	int		Values of the member status.
	 * 					0 = no member
	 * 					1 = member
	 * 					2 = request pending
	 */
	public function isChannelMember($channelId)
	{
		if (!intval($channelId))
		{
			throw new vB_Exception_Api('invalid_node_id');
		}

		$userId = vB::getUserContext()->fetchUserId();
		if (empty($userId))
		{
			throw new vB_Exception_Api('invalid_current_userid', array($userId));
		}

		try
		{
			$result = vB_Api::instanceInternal('user')->getGroupInTopic(vB::getUserContext()->fetchUserId(), $channelId);
		}
		catch (vB_Exception_Api $ex)
		{
			throw $ex;
		}

		$memberStatus = 0;
		// try to check if we have a pending request...
		if (empty($result))
		{
			$pending = vB::getDbAssertor()->getRow('vBForum:getNodePendingRequest', array(
				'nodeid' => $channelId, 'userid' => $userId, 'request' => array(vB_Api_Node::REQUEST_SG_GRANT_MEMBER, vB_Api_Node::REQUEST_GRANT_MEMBER)
			));
			if (!empty($pending))
			{
				$memberStatus = 2;
			}
		}
		else
		{
			$memberStatus = 1;
		}

		return $memberStatus;
	}
	
	/**
	 * Returns the widget instances that are used for blog sidebar.
	 * This method should be used only for owner configuration of the blog, not rendering
	 */
	public function getBlogSidebarModules($channelId = 0)
	{
		$channelId = intval($channelId);
		
		$widgetApi = vB_Api::instance('widget');
		
		// We assume there's only one container in blog pagetemplate. If this is no longer the case, we may need to implement GUID for widgetinstances
		$blogTemplate = vB_Page::getBlogChannelPageTemplate();
		
		$modules = vB::getDbAssertor()->getRows('getBlogSidebarModules', array('blogPageTemplate' => $blogTemplate));
		
		$results = $parentConfig = $sortAgain = array();
		foreach($modules AS $module)
		{
			$title = $module['title'];
			
			//Temporarily removing the Blog Categories module as it is not implemented yet (VBV-4247)
			//@TODO: Remove this when this module gets implemented. 
			//@TODO: It would be great if we have a way to globally disable any module and not display it anywhere to avoid this kind of fix.
			if ($module['guid'] == 'vbulletin-widget_blogcategories-4eb423cfd6dea7.34930850')
			{
				continue;
			}
			//END
			
			if (isset($module['adminconfig']) AND !empty($module['adminconfig']))
			{
				// search for custom title
				$adminConfig = @unserialize($module['adminconfig']);
				if (is_array($adminConfig))
				{
					foreach($adminConfig AS $key=>$val)
					{
						if (stripos($key, 'title') !== FALSE)
						{
							$title = $val;
							break;
						}
					}
				}
			}
			
			if (!isset($parentConfig[$module['parent']]))
			{
				$parentConfig[$module['parent']] = $widgetApi->fetchConfig($module['parent'], 0, $channelId);
				if (isset($parentConfig[$module['parent']]['display_order']))
				{
					$sortAgain[] = $module['parent'];
				}
			}
			if (isset($parentConfig[$module['parent']]['display_modules']) AND !empty($parentConfig[$module['parent']]['display_modules']))
			{
				$hidden = in_array($module['widgetinstanceid'], $parentConfig[$module['parent']]['display_modules']) ? 0 : 1;
			}
			else
			{
				$hidden = 0;
			}
			
			$results[$module['widgetinstanceid']] = array(
				'title' => $title,
				'widgetid' => $module['widgetid'],
				'widgetinstanceid' => $module['widgetinstanceid'],
				'hidden' => $hidden
			);
		}
		
		if (!empty($sortAgain))
		{
			$newOrder = array();
			foreach($sortAgain AS $parent)
			{
				foreach($parentConfig[$parent]['display_order'] AS $widgetInstanceId)
				{
					$newOrder[$widgetInstanceId] = $results[$widgetInstanceId];
					unset($results[$widgetInstanceId]);
				}

				// append remaining items
				$newOrder += $results;
			}
			
			return $newOrder;
		}
		else
		{
			return $results;
		}
	}
	
	/**
	 * Saves channel configuration for blog sidebar
	 * @param int $blogId
	 * @param array $modules	An array in which each element contains:
	 *								- widgetinstanceid (int)
	 *								- hide (bool)
	 * @throws vB_Exception_Api 
	 */
	public function saveBlogSidebarModules($blogId, $modules)
	{
		if (empty($blogId) OR empty($modules))
		{
			return;
		}
		
		// check the user is owner
		$userid = vB::getCurrentSession()->get('userid');
		if ($userid != $this->fetchOwner($blogId))
		{
			throw new vB_Exception_Api('no_permission');
		}
		
		$config = array(
			'display_order' => array(),
			'display_modules' => array()
		);
		foreach($modules AS $module)
		{
			$config['display_order'][] = $module['widgetinstanceid'];
			if (!$module['hide'])
			{
				$config['display_modules'][] = $module['widgetinstanceid'];
			}
		}
		
		// get parent
		$parent = vB::getDbAssertor()->getField('widgetinstance', array(
			vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
			vB_dB_Query::COLUMNS_KEY => array('parent'),
			vB_dB_Query::CONDITIONS_KEY => array(
				'widgetinstanceid' => $config['display_order']
			)
		));
		
		vB_Api::instance('widget')->saveChannelConfig($parent, $blogId, $config);
	}
}

/*======================================================================*\
|| ####################################################################
|| # 
|| # SVN: $Revision$
|| ####################################################################
\*======================================================================*/
