<?php if (!defined('VB_ENTRY')) die('Access denied.');

/*======================================================================*\
|| #################################################################### ||
|| # vBulletin 4.0.3
|| # ---------------------------------------------------------------- # ||
|| # Copyright ©2000-2010 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 # ||
|| #################################################################### ||
\*======================================================================*/

/**
 * @package vBForum
 * @subpackage Search
 * @author Kevin Sours, vBulletin Development Team
 * @version $Revision: 30635 $
 * @since $Date: 2009-05-04 17:00:33 -0700 (Mon, 04 May 2009) $
 * @copyright vBulletin Solutions Inc.
 */

require_once (DIR . '/vb/search/type.php');
require_once (DIR . '/vb/legacy/post.php');
require_once (DIR . '/packages/vbforum/search/result/post.php');
require_once (DIR . '/packages/vbforum/search/result/thread.php');

/**
* There is a type file for each search type. This is the one for posts
*
* @package vBulletin
* @subpackage Search
*/
class vBForum_Search_Type_Post extends vB_Search_Type
{
// ###################### Start fetch_validated_list ######################
/**
* vBForum_Search_Type_Post::fetch_validated_list()
* When displaying results we get passed a list of id's. This
* function determines which are viewable by the user.
*
* @param object $user
* @param array $ids : the postid's returned from a search
* @param array $gids : the group id's for the posts
* @return array (array of viewable posts, array of rejected posts)
*/
	public function fetch_validated_list($user, $ids, $gids)
	{
		require_once(DIR . '/includes/functions_forumlist.php');
		cache_moderators_once();

		global $vbulletin;
		$map = array();
		foreach ($ids AS $i => $id)
		{
			$map[$gids[$i]][] = $id;
		}

		$threads = vB_Legacy_Thread::create_array(array_unique($gids));
		$rejected_groups = array();
		foreach ($threads as $thread)
		{
			if (!$thread->can_search($user))
			{
				$rejected_groups[] = $thread->get_field('threadid');
				unset($map[$thread->get_field('threadid')]);
			}
		}

		$searchids = array();
		foreach ($map as $gid => $value)
		{
			$searchids = array_merge($searchids, $value);
		}

		$list = array_fill_keys($ids, false);
		if (count($searchids))
		{
			$posts = vB_Legacy_Post::create_array($searchids, $threads);
			foreach ($posts AS $id => $post)
			{
				$item = vBForum_Search_Result_Post::create_from_object($post);
				if ($item->can_search($user))
				{
					$list[$id] = $item;
				}
			}
		}
		return array('list' => $list, 'groups_rejected' => $rejected_groups);
	}

// ###################### Start prepare_render ######################
/**
* vBForum_Search_Type_Post::prepare_render()
* This function does setup preparatory to  rendering the search resuls.
* In this case that means checking moderation options for this user.
*
* @param object $user
* @param object $results
* @return nothing
*/
	public function prepare_render($user, $results)
	{
		foreach ($results AS $result)
		{
			$forumid = $result->get_post()->get_thread()->get_field('forumid');

			$this->mod_rights['managethread'] = ($this->mod_rights['managethread'] OR
				$user->canModerateForum($item['forumid'], 'canmanagethreads'));

			$this->mod_rights['managepost'] = ($this->mod_rights['managepost'] OR
				($user->canModerateForum($item['forumid'], 'candeleteposts') OR
					$user->canModerateForum($item['forumid'], 'canremoveposts')));

			$this->mod_rights['approvepost'] = ($this->mod_rights['approvepost'] OR
				$user->canModerateForum($item['forumid'], 'canmoderateposts'));

			$this->mod_rights['approveattachment'] = ($this->mod_rights['approveattachment'] OR
				$user->canModerateForum($item['forumid'], 'canmoderateattachments'));
		}
	}

// ###################### Start get_display_name ######################
/**
* vBForum_Search_Type_Post::get_display_name()
* Each search type has some responsibilities, one of which is to give
* its display name.
*
* @return string
*/
	public function get_display_name()
	{
		return new vB_Phrase('search', 'searchtype_posts');
	}

// ###################### Start create_item ######################
/**
* vBForum_Search_Type_Post::create_item()
* This is how the type objects are created
*
* @param integer $id
* @return vBForum_Search_Type_Post object
*/
	public function create_item($id)
	{
		return vBForum_Search_Result_Post::create($id);
	}

// ###################### Start additional_pref_defaults ######################
/**
* Each search type has some responsibilities, one of which is to tell
* what the default search preferences are.
* vBForum_Search_Type_Post::additional_pref_defaults()
*
* @return array
*/
	public function additional_pref_defaults()
	{
		return array(
			'childforums' => 1,
			'replyless'   => 0,
			'replylimit'  => 0,
			'query'       => '',
			'forumchoice' => 0,
			'prefixchoice'=> 0,
			'titleonly'   => 0,
			'nocache'     => '',
			'searchuser'  => '',
			'starteronly' => 0,
			'showposts'   => 0,
			'exactname'   => '',
			'searchdate'  => 0,
			'beforeafter' => 0,
			'sortby'      => 'dateline',
			'order' 	     => 'descending',
			'tag'         => '',
			'showposts'	  => 0);
	}

// ###################### Start can_group ######################
/**
* vBForum_Search_Type_Post::can_group()
* Each search type has some responsibilities, one of which is to tell
* whether it is groupable- Forums, for example are not, but posts are.
* They are naturally grouped by thread.
*
* @return
*/
	public function can_group()
	{
		return true;
	}

// ###################### Start group_by_default ######################
/**
* vBForum_Search_Type_Post::group_by_default()
* Each search type has some responsibilities, one of which is to tell
* whether it is grouped by default
*
* @return
*/
	public function group_by_default()
	{
		return true;
	}

// ###################### Start listUi ######################
/**
 * vBForum_Search_Type_Post::listUi()
 *This function generates the search elements for the user to search for posts
 * @param mixed $prefs : the array of user preferences / propogated form values.
 * @param mixed $contenttypeid : the content type for which we are going to
 *    search
 * @param array registers : any additional elements to be registered. These are
 * 	just passed to the template
 * @param string $template_name : name of the template to use for display. We have
 *		a default template.
 * @return $html: complete html for the search elements
 */
	public function listUi($prefs = null, $contenttypeid = null, $registers = null,
		$template_name = null)
	{
		global $vbulletin, $vbphrase;


		if (!isset($template_name))
		{
			$template_name = 'search_input_post';
		}

		if (!isset($contenttypeid))
		{
			$contenttypeid = $this->get_contenttypeid();
		}

		$template = vB_Template::create($template_name);

		if ($prefs['searchthreadid'])
		{
			if ($thread = vB_Legacy_Thread::create_from_id($prefs['searchthreadid']))
			{
				$template->register('thread_title', $thread->get_field('title'));
			}
		}

		$template->register('securitytoken', $vbulletin->userinfo['securitytoken']);
		$template->register('contenttypeid', $contenttypeid);
		$template->register('search_forum_options', $this->showForumOptions($prefs['forumchoice']));
		$template->register('tag_cloud', fetch_tagcloud('selectlist', $prefs['tags']));

		$prefix_selected = array (
			'anythread' => (empty($prefs['prefixchoice']) OR in_array('', $prefs['prefixchoice']) ) ?
				'selected="selected"' : '',
			'anyprefix' => ($prefs['prefixchoice'] AND in_array('-2', $prefs['prefixchoice'])) ?
				'selected="selected"' : '',
			'none' => ($prefs['prefixchoice'] AND in_array('-1', $prefs['prefixchoice'])) ?
				'selected="selected"' : ''
		);

		$template->register('prefix_selected', $prefix_selected);
		$template->register('search_prefix_options', vB_Search_Searchtools::getPrefixOptions($prefs['prefixchoice'], false));

		$this->setPrefs($template, $prefs,
			array(
				'select'=> array('sortby', 'order', 'forumchoice',
					'prefixchoice', 'titleonly', 'starteronly', 'searchdate',
					'beforeafter'),
				'cb' => array('childforums', 'exactname', 'nocache'),
				'value' => array('query', 'searchuser', 'tag', 'searchthreadid'),
				'rb' => array('showposts')
		 	)
		);

		vB_Search_Searchtools::searchIntroRegisterHumanVerify($template);

		if (isset($registers) and is_array($registers) )
		{
			foreach($registers as $key => $value)
			{
				$template->register($key, htmlspecialchars_uni($value));
			}
		}
		return $template->render();
	}
// ###################### Start showForumSelect ######################
/**
 * vBForum_Search_Type_Post::showForumSelect()
 * This function generates the select scrolling list for forums, use in search for posts
 */
	private function showForumOptions($forumchoice=array())
	{
		global $vbulletin, $vbphrase, $show;

		//this will fill out $searchforumids as well as set the depth param in $vbulletin->forumcache
		global $searchforumids;
		fetch_search_forumids_array();

		$options = array();
		$non_searchable_forums = array();
		foreach ($searchforumids AS $forumid)
		{
			$forum = & $vbulletin->forumcache["$forumid"];
			if (trim($forum['link']))
			{
				continue;
			}

			//note that this code relies on the fact that searchforumids is ordered so that
			//parents appear before their childern (actually the what the display works depends
			//heavily on that fact so its not much of an assumption here).

			//if the forum isn't searchable, then don't show it.
			if(!($forum['options'] & $vbulletin->bf_misc_forumoptions['indexposts']))
			{
				$non_searchable_forums[$forumid] = $forumid;
			}

			//unless it has children that are searchable.
			else
			{
				unset($non_searchable_forums[$forum['parentid']]);
			}

			$optionvalue = $forumid;
			$optiontitle = "$forum[depthmark] $forum[title_clean]";

			if (
				!($vbulletin->userinfo['forumpermissions'][$forumid] & $vbulletin->bf_ugp_forumpermissions['canviewthreads'])
			)
			{
				$optiontitle .= '*';
				$show['cantsearchposts'] = true;
			}

			$optionselected = '';

			if ($forumchoice AND in_array($forumid, $forumchoice))
			{
				$optionselected = 'selected="selected"';
				$haveforum = true;
			}

			$options[$forumid] = render_option_template($optiontitle, $forumid, $optionselected,
				'd' . min(4, $forum['depth']));
		}

		foreach ($non_searchable_forums AS $forumid)
		{
			unset($options[$forumid]);
		}

		$options = implode("", $options);
		$options =
			render_option_template($vbphrase['search_all_open_forums'], '',
				$haveforum ? '' : 'selected="selected"') .
			render_option_template($vbphrase['search_subscribed_forums'], 'subscribed') .
			$options;
		return $options;
	}


// ###################### Start get_inlinemod_options ######################
/**
* vBForum_Search_Type_Post::get_inlinemod_options()
* This tells inline moderation what options are applicable to posts
*
* @return array of options
*/
	public function get_inlinemod_options()
	{
		global $vbphrase, $show;
		$options = array();

		$mod_options = array();

		if ($this->mod_rights['managepost'])
		{
			$mod_options[$vbphrase['delete_posts']] = 'deleteposts';
			$mod_options[$vbphrase['undelete_posts']] = 'undeleteposts';
		}

		if ($this->mod_rights['deletethread'] OR $this->mod_rights['movethread'])
		{
			$mod_options[$vbphrase['delete_threads_as_spam']] = 'spamthread';
		}

		if ($this->mod_rights['approvepost'])
		{
			$mod_options[$vbphrase['approve_posts']] = 'approveposts';
			$mod_options[$vbphrase['unapprove_posts']] = 'unapproveposts';
		}

		if ($this->mod_rights['approveattachment'])
		{
			$mod_options[$vbphrase['approve_attachments']] = 'approveattachments';
			$mod_options[$vbphrase['unapprove_attachments']] = 'unapproveattachments';
		}

		if ($this->mod_rights['managethread'])
		{
			$mod_options[$vbphrase['move_posts']] = 'moveposts';
			$mod_options[$vbphrase['merge_posts']] = 'mergeposts';
			$mod_options[$vbphrase['copy_posts']] = 'copyposts';
		}

		//if we have any mod options then we add the rest
		if ($mod_options)
		{
			$options[$vbphrase['option']] = $mod_options;
			$basic_options = array();
			$basic_options[$vbphrase['view_selected_posts']] = 'viewpost';
			$basic_options[$vbphrase['clear_post_list']] = 'clearpost';
			$options ["____________________"] = $basic_options;
		}
		return $options;
	}

// ###################### Start get_inlinemod_type ######################
/**
* vBForum_Search_Type_Post::get_inlinemod_type()
* Tell what type of object this is for inline moderation
*
* @return string
*/
	public function get_inlinemod_type()
	{
		return 'post';
	}

	public function get_inlinemod_action()
	{
		return 'inlinemod.php';
	}

	public function add_advanced_search_filters($criteria, $registry)
	{
		if ($registry->GPC['searchthreadid'])
		{
			$criteria->add_group_filter($registry->GPC['searchthreadid']);

			//some special handling for searching in threads
			$vbulletin->GPC['forumchoice'] = array();
			$vbulletin->GPC['replyless'] = false;
			$vbulletin->GPC['replylimit'] = false;
		}

		if ($registry->GPC_exists['forumchoice'])
		{
			$this->add_forumid_filter($criteria, $registry->GPC['forumchoice'],
				$registry->GPC['childforums']);
		}

		if ($registry->GPC_exists['prefixchoice'])
		{
			$this->add_prefix_filter($criteria, $registry->GPC['prefixchoice']);
		}
		else if ($prefixchoice = $criteria->get_equals_filter('prefix'))
		{
			$this->add_prefix_filter($criteria, $prefixchoice);
		}
		
		if ($registry->GPC_exists['pollidmin'])
		{
			$this->add_pollid_filter($criteria, $registry->GPC['pollidmin'], vB_Search_Core::OP_GT);
		}

		if ($registry->GPC['pollidmax'])
		{
			$this->add_pollid_filter($criteria, $registry->GPC['pollidmin'], vB_Search_Core::OP_LT);
		}

		if ($registry->GPC['pollid'])
		{
			$this->add_pollid_filter($criteria, $registry->GPC['pollid'], vB_Search_Core::OP_EQ);
		}

		if ($registry->GPC['replylimit'])
		{
			$op = $registry->GPC['replyless'] ? vB_Search_Core::OP_LT : vB_Search_Core::OP_GT;
			$criteria->add_filter('replycount', $op, $registry->GPC['replylimit'], true);

			$criteria->add_display_strings('replycount',
				vB_Search_Searchtools::getCompareString($registry->GPC['replyless'])
				. $registry->GPC['replylimit'] . ' ' . $vbphrase['replies']);
		}
	}

	public function get_db_query_info($fieldname)
	{
		$result['corejoin']['thread'] = sprintf(self::$thread_join, TABLE_PREFIX,
				vB_Types::instance()->getContentTypeId("vBForum_Post"));

		$result['groupjoin']['thread'] = sprintf(self::$thread_group_join, TABLE_PREFIX,
				vB_Types::instance()->getContentTypeId("vBForum_Thread"));


		$result['table'] = 'thread';

		$fields = array('forumid', 'prefixid', 'replycount', 'views');
		if (in_array($fieldname, $fields))
		{
			$result['field'] = $fieldname;
		}
		else if ($fieldname == 'threadstart')
		{
			$result['field'] = 'dateline';
		}
		else if ($fieldname == 'pollid')
		{
			$result['field'] = 'pollid';
		}
		else if ($fieldname == 'views')
		{
			$result['field'] = 'views';
		}
		else if ($fieldname == 'replycount')
		{
			$result['field'] = 'replycount';
		}
		else if ($fieldname == 'lastpost')
		{
			$result['field'] = 'lastpost';
		}
		else if ($fieldname == 'forum')
		{
			$result['join']['forum'] = sprintf(self::$forum_thread_join, TABLE_PREFIX);
			$result['table'] = 'forum';
			$result['field'] = 'title';
		}
		else
		{
			return false;
		}

		return $result;
	}

	/**
	*	Add a filter for forums to search.
	*
	*	@param array $forumids
	* @param boolean $include_children -- If the children should be included.
	*/
	protected function add_forumid_filter($criteria, $forumids, $include_children)
	{
		if (in_array(' ', $forumids) OR in_array('', $forumids))
		{
			return;
		}

		global $vbulletin, $vbphrase;

		$all_forumids = fetch_search_forumids($forumids, $include_children);
		if ($all_forumids)
		{
			$criteria->add_filter('forumid', vB_Search_Core::OP_EQ, $all_forumids);

			$forum_strings =  vB_Search_Searchtools::getDisplayString('forum', $vbphrase['forum'], 'title',
				'forumid', $forumids,	vB_Search_Core::OP_EQ, false);
			$criteria->add_display_strings('forumid', $forum_strings .
 				( $include_children ? ' ' . $vbphrase['and_child_forums'] : ''));
		}
		else
		{
			$criteria->add_error('invalidid', $vbphrase['forum'], $vbulletin->options['contactuslink']);
		}
	}

	protected function add_prefix_filter($criteria, $prefixids)
	{
		global $vbphrase;
		if (!is_array($prefixids))
		{
			$prefixids = array($prefixids);
		}

		//if "everything" is selected, we don't need to filter.
		if (in_array(' ', $prefixids) OR in_array('', $prefixids))
		{
			return;
		}

		$restrictive = true;
		$noprefix_pos = array_search('-1',  $prefixids);
		$anyprefix = in_array('-2', $prefixids);

		//if we select thread with a prefix and threads without a prefix,
		//that's everything -- no filter
		if ($noprefix_pos !== false AND $anyprefix)
		{
			return;
		}

		//if we want any prefix don't worry about any "non special" values
		//we'll take well, anything.  The special values should already
		//be accounted for.
		if ($anyprefix)
		{
			$criteria->add_filter('prefixid', vB_Search_Core::OP_NEQ, '', false);
			return;
		}

		if ($noprefix_pos !== false)
		{
			//convert to the actual database value for no prefix
			$prefixids[$noprefix_pos] = '';

			//if "no prefix" is selected, then this isn't a restrictive filter
			//otherwise it is.
			$restrictive = false;
		}

		$criteria->add_filter('prefixid', vB_Search_Core::OP_EQ, $prefixids, $restrictive);

		$prefixes = array();
		foreach ($prefixids as $prefixid)
		{
			$prefixes[] = $vbphrase["prefix_{$prefixid}_title_rich"];
		}

		$criteria->add_display_strings($field, $vbphrase['prefix'] . ":" .
			implode( ', ' , $prefixes));
	}

	/***
	* This is needed by the poll widget, which passes "pollid > 0"
	*
	***/
	protected function add_pollid_filter($criteria, $pollids, $op)
	{
		global $vbphrase;
		if (!is_array($pollids) and $pollids and $op = vB_Search_Core::OP_EQ)
		{
			$criteria->add_filter('pollid', $op, $pollids, $restrictive);

			$criteria->add_display_strings($field, $vbphrase['poll'] . ' ' . $vbphrase['at_least']
				. ' ' . $pollid);
			return;
		}

		if (!is_array($pollids))
		{
			$pollids = array($pollids);
		}

		//if "everything" is selected, bail -- we don't need to filter.
		if (in_array(' ', $pollids) OR in_array('', $pollids))
		{
			return;
		}

		$restrictive = true;
		$pos = array_search('-1',  $pollids);

		if ($pos !== false)
		{
			//convert to the actual database value for no prefix
			$pollids[$pos] = '';

			//if "no prefix" is selected, then this isn't a restrictive filter
			//otherwise it is.
			$restrictive = false;
		}

		$criteria->add_filter('pollid', $op, $pollids, $restrictive);

		$criteria->add_display_strings($field, $vbphrase['poll'] . vB_Search_Searchtools::getCompareString($op) .
			implode( ', ' , $pollids));
	}


	protected $package = "vBForum";
	protected $class = "Post";
	protected $group_package = "vBForum";
	protected $group_class = "Thread";

	protected $type_globals = array (
		'searchthreadid'      => TYPE_INT,
		'showposts'      => TYPE_INT,
		'forumchoice'	  => TYPE_ARRAY,
		'starteronly'    => TYPE_INT,
		'prefixchoice'	  => TYPE_ARRAY,
		'childforums'	  => TYPE_BOOL,
		'replyless'  => TYPE_BOOL,
		'replylimit' => TYPE_NOHTML
	);

	private static $thread_join =
	  " INNER JOIN %sthread AS thread ON (
				searchcore.contenttypeid = %u  AND searchcore.groupid = thread.threadid)";

	private static $thread_group_join =
	  " INNER JOIN %sthread AS thread ON (
				searchgroup.contenttypeid = %u  AND searchgroup.groupid = thread.threadid)";

	private static $forum_thread_join =
		" INNER JOIN %sforum AS forum ON (thread.forumid = forum.forumid)";
}

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