<?php
/**
 * <pre>
 * Invision Power Services
 * IP.Board vVERSION_NUMBER
 * Basic IP.Content Search
 * Last Updated: $Date$
 * </pre>
 *
 * @author 		$Author:$
 * @copyright	(c) 2001 - 2009 Invision Power Services, Inc.
 * @license		http://www.invisionpower.com/community/board/license.html
 * @package		IP.Content
 * @version		$Rev: $
 */

if ( ! defined( 'IN_IPB' ) )
{
	print "<h1>Incorrect access</h1>You cannot access this file directly. If you have recently upgraded, make sure you upgraded all the relevant files.";
	exit();
}

class search_engine_ccs extends search_engine
{
	/**
	 * Category handler
	 *
	 * @access	protected
	 * @var		object
	 */
	protected $categories;
	
	/**
	 * Constructor
	 *
	 * @access	public
	 * @param	object
	 * @return	void
	 */
	public function __construct( ipsRegistry $registry )
	{
		/* Hard limit */
		IPSSearchRegistry::set( 'set.hardLimit', ( ipsRegistry::$settings['search_hardlimit'] ) ? ipsRegistry::$settings['search_hardlimit'] : 200 );

		/* Language */
		ipsRegistry::instance()->getClass('class_localization')->loadLanguageFile( array( 'public_lang' ), 'ccs' );
		
		/* Load the caches */
		ipsRegistry::cache()->getCache('ccs_databases');
		ipsRegistry::cache()->getCache('ccs_fields');
		
		/* CCS Functions */
		$classToLoad		= IPSLib::loadLibrary( IPSLib::getAppDir('ccs') . '/sources/functions.php', 'ccsFunctions' );
		ipsRegistry::setClass( 'ccsFunctions', new $classToLoad( ipsRegistry::instance() ) );
			
		parent::__construct( $registry );
	}
	
	/**
	 * Decide what type of search we're using
	 *
	 * @access	public
	 * @return	array
	 */
	public function search()
	{
		$_key	= IPSSearchRegistry::get('ccs.searchInKey');
		
		if( $_key == 'pages' )
		{
			return $this->_searchPages();
		}
		else if( strpos( $_key, '_comments' ) )
		{
			$_dbId	= intval( str_replace( array( 'database_', '_comments' ), '', $_key ) );
			
			return $this->_searchComments( $_dbId );
		}
		else
		{
			$_dbId	= intval( str_replace( 'database_', '', $_key ) );
			
			return $this->_searchDatabase( $_dbId );
		}
	}

	/**
	 * Perform search against pages
	 *
	 * @access	public
	 * @return	array
	 */
	public function _searchPages()
	{
		/* INIT */ 
		$count				= 0;
		$results			= array();
		$sort_by			= IPSSearchRegistry::get('in.search_sort_by');
		$sort_order			= IPSSearchRegistry::get('in.search_sort_order');
		$search_term		= IPSSearchRegistry::get('in.clean_search_term');
		$content_title_only	= IPSSearchRegistry::get('opt.searchTitleOnly');
		$order_dir			= ( $sort_order == 'asc' ) ? 'asc' : 'desc';
		$rows				= array();
		$count				= 0;
		$c					= 0;
		$got				= 0;
		$sortKey			= '';
		$sortType			= '';

		if ( IPSSearchRegistry::get('opt.noPostPreview') OR IPSSearchRegistry::get('display.onlyTitles') )
		{
			$group_by = 'page_id';
		}

		/* Sorting */
		switch( $sort_by )
		{
			default:
			case 'date':
				$sortKey	= 'page_last_edited';
				$sortType	= 'numerical';
			break;
			case 'title':
				$sortKey	= 'page_name';
				$sortType	= 'string';
			break;
		}

		/* Fetch data */
		$this->DB->build( array( 
								'select'   => "page_id, page_name, page_last_edited",
								'from'	   => 'ccs_pages',
								'where'	   => $this->_buildWhereStatement( $search_term, $content_title_only, 'page' ),
								'limit'    => array( 0, IPSSearchRegistry::get('set.hardLimit') + 1 ),
								'group'    => $group_by,
								)	);
		$this->DB->execute();
		
		/* Fetch count */
		$count = intval( $this->DB->getTotalRows( $DB ) );
		
		if ( $count > IPSSearchRegistry::get('set.hardLimit') )
		{
			$count = IPSSearchRegistry::get('set.hardLimit');
			
			IPSSearchRegistry::set('set.resultsCutToLimit', true );
		}

		/* Fetch to sort */
		while ( $r = $this->DB->fetch() )
		{
			$_rows[ $r['page_id'] ] = $r;
		}
		
		/* Set vars */
		IPSSearch::$ask	= $sortKey;
		IPSSearch::$aso	= strtolower( $order_dir );
		IPSSearch::$ast	= $sortType;
		
		/* Sort */
		if ( count( $_rows ) )
		{
			usort( $_rows, array( "IPSSearch", "usort" ) );

			/* Build result array */
			foreach( $_rows as $r )
			{
				$c++;
				
				if ( IPSSearchRegistry::get('in.start') AND IPSSearchRegistry::get('in.start') >= $c )
				{
					continue;
				}
				
				$rows[ $got ] = $r['page_id'];
							
				$got++;
				
				/* Done? */
				if ( IPSSearchRegistry::get('opt.search_per_page') AND $got >= IPSSearchRegistry::get('opt.search_per_page') )
				{
					break;
				}
			}
		}

		/* Return it */
		return array( 'count' => $count, 'resultSet' => $rows );
	}
	
	/**
	 * Perform a search against comments
	 *
	 * @access	public
	 * @param	int			Database id
	 * @return	array
	 */
	public function _searchComments( $databaseId )
	{
		/* INIT */ 
		$database			= $this->caches['ccs_databases'][ $databaseId ];
		$count				= 0;
		$results			= array();
		$sort_by			= IPSSearchRegistry::get('in.search_sort_by');
		$sort_order			= IPSSearchRegistry::get('in.search_sort_order');
		$search_term		= IPSSearchRegistry::get('in.clean_search_term');
		$content_title_only	= IPSSearchRegistry::get('opt.searchTitleOnly');
		$order_dir			= ( $sort_order == 'asc' ) ? 'asc' : 'desc';
		$rows				= array();
		$count				= 0;
		$c					= 0;
		$got				= 0;
		$sortKey			= '';
		$sortType			= '';

		if ( IPSSearchRegistry::get('opt.noPostPreview') OR IPSSearchRegistry::get('display.onlyTitles') )
		{
			$group_by = 'c.comment_id';
		}

		/* Sorting */
		switch( $sort_by )
		{
			default:
			case 'date':
				$sortKey  = 'comment_date';
				$sortType = 'numerical';
			break;
		}

		/* Fetch data */
		$this->DB->build( array( 
									'select'   => "c.comment_id, c.comment_user, c.comment_date",
									'from'	   => array( 'ccs_database_comments' => 'c' ),
									'where'	   => $this->_buildWhereStatement( $search_term, $content_title_only, 'comment', $databaseId ),
									'limit'    => array( 0, IPSSearchRegistry::get('set.hardLimit') + 1 ),
									'group'    => $group_by,
									'add_join' => array(
														array(
																'from'   => array( $database['database_database'] => 'r' ),
																'where'  => "r.primary_id_field=c.comment_record_id",
																'type'   => 'left',
															),
														)
						)	);
		$this->DB->execute();
		
		/* Fetch count */
		$count = intval( $this->DB->getTotalRows( $DB ) );
		
		if ( $count > IPSSearchRegistry::get('set.hardLimit') )
		{
			$count = IPSSearchRegistry::get('set.hardLimit');
			
			IPSSearchRegistry::set('set.resultsCutToLimit', true );
		}

		/* Fetch to sort */
		while ( $r = $this->DB->fetch() )
		{
			$_rows[ $r['comment_id'] ] = $r;
		}
		
		/* Set vars */
		IPSSearch::$ask	= $sortKey;
		IPSSearch::$aso	= strtolower( $order_dir );
		IPSSearch::$ast	= $sortType;
		
		/* Sort */
		if ( count( $_rows ) )
		{
			usort( $_rows, array( "IPSSearch", "usort" ) );

			/* Build result array */
			foreach( $_rows as $r )
			{
				$c++;
				
				if ( IPSSearchRegistry::get('in.start') AND IPSSearchRegistry::get('in.start') >= $c )
				{
					continue;
				}
				
				$rows[ $got ] = $r['comment_id'];
							
				$got++;
				
				/* Done? */
				if ( IPSSearchRegistry::get('opt.search_per_page') AND $got >= IPSSearchRegistry::get('opt.search_per_page') )
				{
					break;
				}
			}
		}

		/* Return it */
		return array( 'count' => $count, 'resultSet' => $rows );
	}
	
	/**
	 * Perform a search against database records
	 *
	 * @access	public
	 * @param	int			Database id
	 * @return	array
	 */
	public function _searchDatabase( $databaseId )
	{
		/* INIT */ 
		$database			= $this->caches['ccs_databases'][ $databaseId ];
		$count				= 0;
		$results			= array();
		$sort_by			= IPSSearchRegistry::get('in.search_sort_by');
		$sort_order			= IPSSearchRegistry::get('in.search_sort_order');
		$search_term		= IPSSearchRegistry::get('in.clean_search_term');
		$content_title_only	= IPSSearchRegistry::get('opt.searchTitleOnly');
		$order_dir			= ( $sort_order == 'asc' ) ? 'asc' : 'desc';
		$rows				= array();
		$count				= 0;
		$c					= 0;
		$got				= 0;
		$sortKey			= '';
		$sortType			= '';

		if ( IPSSearchRegistry::get('opt.noPostPreview') OR IPSSearchRegistry::get('display.onlyTitles') )
		{
			$group_by = 'r.primary_id_field';
		}

		/* Sorting */
		switch( $sort_by )
		{
			default:
			case 'date_added':
				$sortKey	= 'record_saved';
				$sortType	= 'numerical';
			break;
			case 'date_updated':
				$sortKey	= 'record_updated';
				$sortType	= 'numerical';
			break;
			case 'rating':
				$sortKey	= 'rating_real';
				$sortType	= 'numerical';
			break;
			case 'views':
				$sortKey	= 'record_views';
				$sortType	= 'numerical';
			break;
		}

		/* And deal with custom fields... */
		if( is_array($this->caches['ccs_fields'][ $database['database_id'] ]) AND count($this->caches['ccs_fields'][ $database['database_id'] ]) )
		{
			foreach( $this->caches['ccs_fields'][ $database['database_id'] ] as $_field )
			{
				if( in_array( $_field['field_type'], array( 'input', 'textarea', 'radio', 'select' ) ) )
				{
					if( $sort_by == 'field_' . $_field['field_id'] )
					{
						$sortKey	= 'field_' . $_field['field_id'];
						$sortType	= 'string';

						break;
					}
				}
			}
		}

		/* Fetch data */
		$this->DB->build( array( 
								'select'   => "r.*",
								'from'	   => array( $database['database_database'] => 'r' ),
								'where'	   => $this->_buildWhereStatement( $search_term, $content_title_only, 'record', $databaseId ),
								'limit'    => array( 0, IPSSearchRegistry::get('set.hardLimit') + 1 ),
								'group'    => $group_by,
								)	);
		$this->DB->execute();
		
		/* Fetch count */
		$count = intval( $this->DB->getTotalRows( $DB ) );
		
		if ( $count > IPSSearchRegistry::get('set.hardLimit') )
		{
			$count = IPSSearchRegistry::get('set.hardLimit');
			
			IPSSearchRegistry::set('set.resultsCutToLimit', true );
		}

		/* Fetch to sort */
		while ( $r = $this->DB->fetch() )
		{
			$_rows[ $r['primary_id_field'] ] = $r;
		}
		
		/* Set vars */
		IPSSearch::$ask	= $sortKey;
		IPSSearch::$aso	= strtolower( $order_dir );
		IPSSearch::$ast	= $sortType;
		
		/* Sort */
		if ( count( $_rows ) )
		{
			usort( $_rows, array( "IPSSearch", "usort" ) );

			/* Build result array */
			foreach( $_rows as $r )
			{
				$c++;
				
				if ( IPSSearchRegistry::get('in.start') AND IPSSearchRegistry::get('in.start') >= $c )
				{
					continue;
				}
				
				$rows[ $got ] = $r['primary_id_field'];
							
				$got++;
				
				/* Done? */
				if ( IPSSearchRegistry::get('opt.search_per_page') AND $got >= IPSSearchRegistry::get('opt.search_per_page') )
				{
					break;
				}
			}
		}

		/* Return it */
		return array( 'count' => $count, 'resultSet' => $rows );
	}

	
	/**
	 * Builds the where portion of a search string
	 *
	 * @access	private
	 * @param	string	$search_term		The string to use in the search
	 * @param	bool	$content_title_only	Search only title records
	 * @param	string	$type				Content type
	 * @param	int		$databaseId			Database id for record/comment searches
	 * @return	string
	 */
	private function _buildWhereStatement( $search_term, $content_title_only=false, $type='page', $databaseId=0 )
	{
		/* INI */
		$where_clause = array();
		
		/* If we have a search term... */
		if( $search_term )
		{
			$search_term	= trim($search_term);
		}

		/* Set filters based on content type */
		switch( $type )
		{
			case 'page':
			default:
				$where_clause[]	= "page_content_type='page'";
				$where_clause[]	= "page_type IN('bbcode','html')";
				
				if( $search_term )
				{
					if( $content_title_only )
					{
						$where_clause[] = "page_name LIKE '%{$search_term}%'";
					}
					else
					{
						$where_clause[] = "(page_name LIKE '%{$search_term}%' OR page_description LIKE '%{$search_term}%' OR page_meta_description LIKE '%{$search_term}%' OR page_meta_keywords LIKE '%{$search_term}%')";
					}
				}

				/* Date Restrict */
				if( $this->search_begin_timestamp && $this->search_end_timestamp )
				{
					$where_clause[] = $this->DB->buildBetween( "page_last_edited", $this->search_begin_timestamp, $this->search_end_timestamp );
				}
				else
				{
					if( $this->search_begin_timestamp )
					{
						$where_clause[] = "page_last_edited > {$this->search_begin_timestamp}";
					}
					
					if( $this->search_end_timestamp )
					{
						$where_clause[] = "page_last_edited < {$this->search_end_timestamp}";
					}
				}
			break;
			
			case 'comment':
				$this->categories	= ipsRegistry::getClass('ccsFunctions')->getCategoriesClass( $databaseId );
				
				if( count($this->categories->categories) )
				{
					$where_clause[]	= "r.category_id IN(" . implode( ',', array_keys( $this->categories->categories ) ) . ")";
				}
				
				if( $search_term )
				{
					$where_clause[] = "c.comment_post LIKE '%{$search_term}%'";
				}
				
				if( !$this->memberData['g_is_supmod'] )
				{
					$where_clause[] = "c.comment_approved=1";
					$where_clause[] = "r.record_approved=1";
				}

				/* Date Restrict */
				if( $this->search_begin_timestamp && $this->search_end_timestamp )
				{
					$where_clause[] = $this->DB->buildBetween( "c.comment_date", $this->search_begin_timestamp, $this->search_end_timestamp );
				}
				else
				{
					if( $this->search_begin_timestamp )
					{
						$where_clause[] = "c.comment_date > {$this->search_begin_timestamp}";
					}
					
					if( $this->search_end_timestamp )
					{
						$where_clause[] = "c.comment_date < {$this->search_end_timestamp}";
					}
				}
			break;
			
			case 'record':
				$this->categories	= ipsRegistry::getClass('ccsFunctions')->getCategoriesClass( $databaseId );
				
				if( count($this->categories->categories) )
				{
					$where_clause[]	= "r.category_id IN(" . implode( ',', array_keys( $this->categories->categories ) ) . ")";
				}
				
				if( $search_term )
				{
					/* Get fields class */
					require_once( IPSLib::getAppDir( 'ccs' ) . '/sources/databases/fields.php' );
					$_fieldsClass	= new ccs_database_fields( $this->registry );
					$_fieldsClass->getHandlers();
					
					if( $content_title_only )
					{
						$_titleField	= $this->caches['ccs_database'][ $databaseId ]['database_field_title'];
						
						$where_clause[] = "r.{$_titleField} LIKE '%{$search_term}%'";
					}
					else
					{
						$where_clause[] = '(' . $_fieldsClass->getSearchWhere( $this->caches['ccs_fields'][ $databaseId ], $search_term ) . ')';
					}
				}
				
				if( !$this->memberData['g_is_supmod'] )
				{
					$where_clause[] = "r.record_approved=1";
				}

				/* Date Restrict */
				if( $this->search_begin_timestamp && $this->search_end_timestamp )
				{
					$where_clause[] = $this->DB->buildBetween( "r.record_updated", $this->search_begin_timestamp, $this->search_end_timestamp );
				}
				else
				{
					if( $this->search_begin_timestamp )
					{
						$where_clause[] = "r.record_updated > {$this->search_begin_timestamp}";
					}
					
					if( $this->search_end_timestamp )
					{
						$where_clause[] = "r.record_updated < {$this->search_end_timestamp}";
					}
				}
			break;
		}
		
		/* Add in AND where conditions */
		if( isset( $this->whereConditions['AND'] ) && count( $this->whereConditions['AND'] ) )
		{
			$where_clause = array_merge( $where_clause, $this->whereConditions['AND'] );
		}
		
		/* ADD in OR where conditions */
		if( isset( $this->whereConditions['OR'] ) && count( $this->whereConditions['OR'] ) )
		{
			$where_clause[] = '( ' . implode( ' OR ', $this->whereConditions['OR'] ) . ' )';
		}

		/* Build and return the string */
		return implode( " AND ", $where_clause );
	}
	
	/**
	 * Remap standard columns (Apps can override )
	 *
	 * @access	public
	 * @param	string	$column		sql table column for this condition
	 * @return	string				column
	 * @return	void
	 */
	public function remapColumn( $column )
	{
		if( $column == 'member_id' )
		{
			if( IPSSearchRegistry::get('ccs.searchInKey') == 'pages' )
			{
				return '999999999999';
			}
			else if( strpos( IPSSearchRegistry::get('ccs.searchInKey'), '_comments' ) !== false )
			{
				return 'comment_user';
			}
		}
		
		return $column;
	}
		
	/**
	 * Returns an array used in the searchplugin's setCondition method
	 *
	 * @access	public
	 * @param	array 	$data	Array of forums to view
	 * @return	array 	Array with column, operator, and value keys, for use in the setCondition call
	 **/
	public function buildFilterSQL( $data )
	{
		/* INIT */
		$return		= array();

		/* Set up some defaults */
		IPSSearchRegistry::set( 'opt.noPostPreview'  , false );
		IPSSearchRegistry::set( 'opt.onlySearchPosts', false );

		return array();
	}

	/**
	 * Can handle boolean searching
	 *
	 * @access	public
	 * @return	boolean
	 */
	public function isBoolean()
	{
		return false;
	}
}