<?php
/**
 * <pre>
 * Invision Power Services
 * IP.Board v3.4.6
 * Moderator actions
 * Last Updated: $Date: 2013-09-04 23:48:12 -0400 (Wed, 04 Sep 2013) $
 * </pre>
 *
 * @author 		$Author: rashbrook $
 * @copyright	(c) 2001 - 2009 Invision Power Services, Inc.
 *
 * @package		IP.Board
 * @subpackage	Forums
 *
 * @version		$Revision: 12345 $
 */

if ( ! defined( 'IN_IPB' ) )
{
	print "<h1>Incorrect access</h1>You cannot access this file directly.";
	exit();
}

class app_forums_classes_topics
{
	/**#@+
	 * Registry Object Shortcuts
	 *
	 * @var		object
	 */
	protected $registry;
	protected $DB;
	protected $settings;
	protected $request;
	protected $lang;
	protected $member;
	protected $memberData;
	protected $cache;
	protected $caches;
	/**#@-*/

	protected $topicData       = array();
	protected $_memberData     = array();
	protected $moderatorData   = null;
	protected $permissionsData = array();
	protected $errorMessage	   = array();
	protected $_parsedMembers  = array();
	private   $_topicCache     = array();
	private   $_countPosts	   = 0;
	private   $_countTopics	   = 0;
	private   $_archiveReader  = null;
	private   $_parser         = null;
	/**
	 * Return errors, instead of printing
	 *
	 * @var	bool
	 */
 	public $return	= true;
	
 	/* Switch fields */
	protected $fields = array('pid'           	 => 'archive_id',
							  'author_id'    	 => 'archive_author_id',
							  'author_name'  	 => 'archive_author_name',
						      'ip_address'   	 => 'archive_ip_address',
							  'post_date' 	     => 'archive_content_date',
							  'post'		 	 => 'archive_content',
							  'queued'	 	     => 'archive_queued',
							  'topic_id'     	 => 'archive_topic_id',
							  'new_topic'     	 => 'archive_is_first',
							  'post_bwoptions'   => 'archive_bwoptions',
							  'post_key'   	 	 => 'archive_attach_key',
							  'post_htmlstate'   => 'archive_html_mode',
							  'use_sig'   		 => 'archive_show_signature',
							  'use_emo'   		 => 'archive_show_emoticons',
							  'append_edit'   	 => 'archive_show_edited_by',
							  'edit_time'   	 => 'archive_edit_time',
							  'edit_name'   	 => 'archive_edit_name',
							  'post_edit_reason' => 'archive_edit_reason',
							  '_prefix_'		 => 'archive_',
							  '_table_'			 => 'forums_archive_posts' );
	
	/**
	 * Constructor
	 *
	 * @param	object		Registry reference
	 * @return	@e void
	 */
	public function __construct( ipsRegistry $registry )
	{
		/* Make objects */
		$this->registry = $registry;
		$this->DB	    = $this->registry->DB();
		$this->settings =& $this->registry->fetchSettings();
		$this->request  =& $this->registry->fetchRequest();
		$this->lang	    = $this->registry->getClass('class_localization');
		$this->member   = $this->registry->member();
		$this->memberData =& $this->registry->member()->fetchMemberData();
		$this->cache	= $this->registry->cache();
		$this->caches   =& $this->registry->cache()->fetchCaches();
		
		/* Set default member */
		$this->setMemberData( $this->memberData );
		
		/* Check for class_forums */
		if ( ! $this->registry->isClassLoaded( 'class_forums' ) )
		{
			$classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( 'forums' ) . "/sources/classes/forums/class_forums.php", 'class_forums', 'forums' );
			$this->registry->setClass( 'class_forums', new $classToLoad( $registry ) );
			$this->registry->strip_invisible = 0;
			$this->registry->class_forums->forumsInit();
		}
		
		/* Load tagging stuff */
		if ( ! $this->registry->isClassLoaded('tags') )
		{
			require_once( IPS_ROOT_PATH . 'sources/classes/tags/bootstrap.php' );/*noLibHook*/
			$this->registry->setClass( 'tags', classes_tags_bootstrap::run( 'forums', 'topics' ) );
		}
			
		/* Unpack reputation @todo this isn't ideal here but it'll do for now */
		if ( $this->settings['reputation_enabled'] )
		{
			/* Load the class */
			$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/class_reputation_cache.php', 'classReputationCache' );
			$this->registry->setClass( 'repCache', new $classToLoad() );
		
			/* Update the filter? */
			if( isset( $this->request['rep_filter'] ) AND $this->request['rep_filter'] == 'update' )
			{
				/**
				 * Check secure key
				 * @link	http://community.---.com/tracker/issue-22078-i-can-edit-your-treshold
				 */
				if( $this->request['secure_key'] != $this->member->form_hash )
				{
					$this->registry->output->showError( 'usercp_forums_bad_key', 1021522 );
				}
				
				$_mem_cache = IPSMember::unpackMemberCache( $this->memberData['members_cache'] );
				
				if( $this->request['rep_filter_set'] == '*' )
				{
					$_mem_cache['rep_filter'] = '*';
				}
				else
				{
					$_mem_cache['rep_filter'] = intval( $this->request['rep_filter_set'] );
				}
				
				IPSMember::packMemberCache( $this->memberData['member_id'], $_mem_cache );
				
				$this->memberData['_members_cache'] = $_mem_cache;
			}
			else
			{
				$this->memberData['_members_cache'] = isset($this->memberData['members_cache']) ? IPSMember::unpackMemberCache( $this->memberData['members_cache'] ) : array();
			}
		}
	}

	/**
	 * Auto populate the data and that. Populated topicData and forumData. Also does rudimentary access checks
	 *
	 * @param	mixed	$topic	Array of topic data, or single topic id
	 * @param	bool	$return	Return errors instead of printing
	 * @return	@e void
	 */
	public function autoPopulate( $topic="", $return=true )
	{
		/* @todo Remove other calls to request['t'] - intvalled here because it's called in a million places */
		$this->request['t'] = intval( $this->request['t'] );
		
		$this->return	= $return;
		
		/* Sanitize */
		$topicId = intval( $this->request['t'] );
			
		if ( ! is_array( $topic ) )
		{
			if ( ! $topicId )
			{
				throw new Exception( 'EX_topics_no_tid' );
			}
			
			/* May have loaded topic data previously */
			if ( empty( $this->registry->class_forums->topic_cache['tid'] ) )
			{
				/* Load tagging stuff */
				if ( ! $this->registry->isClassLoaded('tags') )
				{
					require_once( IPS_ROOT_PATH . 'sources/classes/tags/bootstrap.php' );/*noLibHook*/
					$this->registry->setClass( 'tags', classes_tags_bootstrap::run( 'forums', 'topics' )  );
				}

				$this->DB->build( array(
										'select'	=> 't.*',
										'from'		=> array( 'topics' => 't' ),
										'where'		=> 't.tid=' . $topicId,
										'add_join'	=> array( $this->registry->tags->getCacheJoin( array( 'meta_id_field' => 't.tid' ) ) )
								)		);									
				$this->DB->execute();
				
				$this->setTopicData( $this->DB->fetch() );
			}
			else
			{
				$this->setTopicData( $this->registry->class_forums->topic_cache );
			}
		}
		else
		{
			$this->setTopicData( $topic );
		}
		
		$this->topicData['title'] = IPSText::stripAttachTag( $this->topicData['title'] );
		
		/* @todo Remove other calls to request['f'] - intvalled here because it's called in a million places */
		$this->request['f'] = intval( $this->topicData['forum_id'] );
		
		/* Check to see if stuff is stuffable */
		$result = $this->canView();

		if ( $result === false )
		{
			throw new Exception( $this->getErrorMessage() );
		}
		
		if ( ! empty( $this->topicData['tag_cache_key'] ) )
		{
			$this->topicData['tags'] = $this->registry->tags->formatCacheJoinData( $this->topicData );
		}
		
		/* Set up */
		$this->topicData = $this->setUpTopic( $this->topicData );
	}
	
	/**
	 * Sets up a topic
	 * @param  array $topic If not passed then $this->topicData is used
	 * @return array
	 */
	public function setUpTopic( $topic=array() )
	{
		$topic = ( count( $topic ) ) ? $topic : $this->topicData;
		
		/* Error out if the topic is not approved or soft deleted */
		$approved = $this->registry->class_forums->fetchHiddenTopicType( $topic );
		
		/* Deleted topic? */
		$topic['_isDeleted']  = ( $approved == 'sdelete' || $approved == 'pdelete' ) ? true : false;
		$topic['_isHidden']   = ( $approved == 'hidden' )  ? true : false;
		$topic['_isArchived'] = $this->isArchived( $topic );
		
		/* Posts per day restrictions? */
		$topic['_ppd_ok'] = $this->registry->getClass('class_forums')->checkGroupPostPerDay( $this->memberData, TRUE );
		
		/* Got any unread posts? */
		$topic['hasUnreadPosts'] = ( $this->registry->classItemMarking->isRead( array( 'forumID' => $topic['forum_id'], 'itemID' => $topic['tid'], 'itemLastUpdate' => $topic['last_post'] ), 'forums' ) ) ? false : true;
		
		$topic['Perms'] = array( 'canQueuePosts'  => $this->registry->class_forums->canQueuePosts( $topic['forum_id'] ),
							     'canAnswerTopic' => $this->registry->topics->canAnswerTopic( $topic ),
								 'canRateAnswer'  => $this->registry->topics->canRateAnsweredPost( $topic ) );
		
		return $topic;
	}
	
	/**
	 * Update a topic
	 * @param int $tid
	 * @param array $data
	 */
	public function updateTopic( $tid, $data )
	{
		if ( is_numeric( $tid ) && is_array( $data ) )
		{
			$this->DB->update( 'topics', $data, 'tid=' . intval( $tid ) );
		}	
	}
	
	/**
	 * Rebuild a topic
	 *
	 * @param	integer 	Topic id
	 * @return	boolean		Rebuild complete
	 */
	public function rebuildTopic( $tid )
	{
		/* Topic ID */
		$tid       = intval( $tid );
		$topicData = $this->getTopicById( $tid );
		
		/* Is this archived? */
		if ( $this->isArchived( $topicData ) )
		{
			/* Load up archive class */
			if ( ! is_object( $this->_archiveReader ) )
			{
				$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/archive/reader.php', 'classes_archive_reader' );
				$this->_archiveReader = new $classToLoad();
			
				$this->_archiveReader->setApp('forums');
			}
			
			/* Fetch replies */
			$pcount  = $this->_archiveReader->getPostCount( $tid, array( 'visible' ) ) - 1;
			
			/* Fetch queued */
			$qpcount = $this->_archiveReader->getPostCount( $tid, array( 'hidden' ) );
			
			/* Fetch soft deleted */
			$dpcount = $this->_archiveReader->getPostCount( $tid, array( 'sdelete' ) );
			
			/* Get last post info */
			$_post = $this->getPosts( array( 'onlyVisible'     => true,
										     'sortField'	   => 'post_date',
										     'sortOrder'	   => 'desc',
										     'topicId'		   => array( $tid ),
										     'limit'		   => 1,
										     'archiveToNative' => true,
										     'isArchivedTopic' => true ) );
			
			$last_post = array_pop( $_post );

			/* Get first post info */
			$_post = $this->getPosts( array( 'onlyVisible'    => true,
										     'sortField'	   => 'post_date',
										     'sortOrder'	   => 'asc',
										     'topicId'		   => array( $tid ),
										     'limit'		   => 1,
										     'archiveToNative' => true,
										     'isArchivedTopic' => true ) );
			
			$first_post = array_pop( $_post );

			$members     = IPSMember::load( array( intval( $last_post['archive_author_id'] ), intval( $first_post['archive_author_id'] ) ) );
			
			/* Merge in some data */
			$last_post['member_id']            = $members[ $last_post['archive_author_id'] ]['member_id'];
			$last_post['members_display_name'] = $members[ $last_post['archive_author_id'] ]['members_display_name'];
			$last_post['forum_id']			   = $topicData['forum_id'];
			$last_post['title']			       = $topicData['title'];
			
			$first_post['member_id']            = $members[ $first_post['archive_author_id'] ]['member_id'];
			$first_post['members_display_name'] = $members[ $first_post['archive_author_id'] ]['members_display_name'];
			$first_post['has_poll_id']			= ( $topicData['poll_start_date'] ) ? 1 : 0;

			$last_poster_name = $last_post['members_display_name'] ? $last_post['members_display_name'] : $last_post['archive_author_name'];
			$first_poster_name = $first_post['members_display_name'] ? $first_post['members_display_name'] : $first_post['author_name'];
			$_last_poster_name = $last_poster_name ? $last_poster_name : ( $pcount > 0 ? $this->lang->words['global_guestname'] : $first_poster_name );
		}
		else
		{
			/* Touch tags to rebuild cache */
			$this->registry->getClass('tags')->rebuildCache( array( 'meta_id' => $tid ) );
			
			/* Fetch replies */
			$_queued	= $this->registry->class_forums->fetchPostHiddenQuery( array( 'visible' ), '' );
			$posts		= $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as posts', 'from' => 'posts', 'where' => "topic_id={$tid} and {$_queued}" ) );
			$pcount		= intval( $posts['posts'] - 1 );
			
			/* Fetch queued */
			$_queued	= $this->registry->class_forums->fetchPostHiddenQuery( array( 'hidden' ), '' );
			$qposts		= $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as posts', 'from' => 'posts', 'where' => "topic_id={$tid} and {$_queued}" ) );
			$qpcount	= intval( $qposts['posts'] );
			
			/* Fetch soft deleted */
			$_queued	= $this->registry->class_forums->fetchPostHiddenQuery( array( 'sdelete' ), '' );
			$dposts		= $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as posts', 'from' => 'posts', 'where' => "topic_id={$tid} and {$_queued}" ) );
			$dpcount	= intval( $dposts['posts'] );
			
			/* Get last post info */
			$_post = $this->getPosts( array( 'onlyVisible'     => true,
										     'sortField'	   => 'post_date',
										     'sortOrder'	   => 'desc',
										     'topicId'		   => array( $tid ),
										     'limit'		   => 1,
										     'forceIndex'	   => 'topic_id' ) );
			
			$last_post = array_pop( $_post );

			/* Get first post info */
			$_post = $this->getPosts( array( 'onlyVisible'     => true,
										     'sortField'	   => 'post_date',
										     'sortOrder'	   => 'asc',
										     'topicId'		   => array( $tid ),
										     'limit'		   => 1,
										     'forceIndex'	   => 'topic_id' ) );
			
			$first_post = array_pop( $_post );
			
			$first_post['has_poll_id'] = ( $topicData['poll_start_date'] ) ? 1 : 0;
	
			$last_poster_name  = $last_post['members_display_name']  ? $last_post['members_display_name']  : $last_post['author_name'];
			$first_poster_name = $first_post['members_display_name'] ? $first_post['members_display_name'] : $first_post['author_name'];
			$_last_poster_name = $last_poster_name ? $last_poster_name : ( $pcount > 0 ? $this->lang->words['global_guestname'] : $first_poster_name );
		}
		
		/* Attachment count */
		$attach = $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as count', 'from' => 'attachments', 'where' => "attach_parent_id={$tid} AND attach_rel_module='post'" ) );

		/* Update topic */
		$this->DB->setDataType( array( 'starter_name', 'last_poster_name' ), 'string' );

		$this->DB->update( 'topics', array( 'last_post'			  => intval($last_post['post_date'] ? $last_post['post_date'] : $first_post['post_date']),
											'last_real_post'	  => intval($last_post['post_date'] ? $last_post['post_date'] : $first_post['post_date']),
											'last_poster_id'	  => intval($last_post['author_id'] ? $last_post['author_id'] : ( $pcount > 0 ? 0 : $first_post['author_id'] )),
											'last_poster_name'	  => $_last_poster_name,
											'poll_state'          => empty($first_post['has_poll_id']) ? 0 : 1,
											'topic_queuedposts'	  => intval($qpcount),
											'topic_deleted_posts' => intval($dpcount), 
											'posts'				  => intval($pcount),
											'starter_id'		  => intval($first_post['author_id']),
											'starter_name'		  => $first_poster_name,
											'seo_first_name'      => IPSText::makeSeoTitle( $first_poster_name ),
											'seo_last_name'       => IPSText::makeSeoTitle( $_last_poster_name ),
											'start_date'		  => intval($first_post['post_date']),
											'topic_firstpost'	  => intval($first_post['pid']),
											'topic_hasattach'	  => intval($attach['count']),
											'title_seo'			  => IPSText::makeSeoTitle( $last_post['title'] ) ), 'tid=' . $tid );

		/* Update first post */
		if ( empty( $first_post['new_topic'] ) and $first_post['pid'] )
		{
			$this->DB->update( 'posts', array( 'new_topic' => 0 ), 'topic_id=' . $tid, true );
			$this->DB->update( 'posts', array( 'new_topic' => 1 ), 'pid=' . $first_post['pid'], true );
		}
		
		/* Update forum */
		if ( ( $this->registry->class_forums->allForums[ $last_post['forum_id'] ]['last_id'] == $tid ) )
		{
			$tt = $this->DB->buildAndFetch( array(	
													'select'	=> 'title, tid, last_post, last_poster_id, last_poster_name',
													'from'		=> 'topics',
													'where'		=> 'forum_id=' . $last_post['forum_id'] . ' and ' . $this->registry->getClass('class_forums')->fetchTopicHiddenQuery( array( 'visible' ), '' ),
													'order'		=> 'last_post desc',
													'limit'		=> array( 1 ) )	);
			
			$dbs = array( 'last_title'			=> $tt['title']				? $tt['title']				: "",
						  'last_id'				=> $tt['tid']				? $tt['tid']				: 0,
						  'last_post'			=> $tt['last_post']			? $tt['last_post']			: 0,
						  'last_poster_name'	=> $tt['last_poster_name']	? $tt['last_poster_name']	: "",
						  'last_poster_id'		=> $tt['last_poster_id']	? $tt['last_poster_id']		: 0,
						  'seo_last_title'   	=> IPSText::makeSeoTitle( $tt['title'] ),
						  'seo_last_name'    	=> IPSText::makeSeoTitle( $tt['last_poster_name'] ) );
			
			if ( $this->registry->class_forums->allForums[ $this->forum['id'] ]['newest_id'] == $tid )
			{
				$sort_key = $this->registry->class_forums->allForums[ $this->forum['id'] ]['sort_key'];
				
				$tt = $this->DB->buildAndFetch( array(  'select' => 'title, tid',
														'from'	 => 'topics',
														'where'	 => 'forum_id=' . $this->forum['id'] . ' and ' . $this->registry->getClass('class_forums')->fetchTopicHiddenQuery( array( 'visible' ), '' ),
														'order'	 => 'start_date desc',
														'limit'	 => array( 1 ) ) );
															
				$dbs['newest_id']		= $tt['tid']	? $tt['tid']	: 0;
				$dbs['newest_title']	= $tt['title']	? $tt['title']	: "";
				$dbs['seo_last_title']  = IPSText::makeSeoTitle( $tt['title'] );
			}
			
			$this->DB->setDataType( array( 'last_poster_name', 'seo_last_title', 'seo_last_name', 'last_title', 'newest_title' ), 'string' );

			$this->DB->update( 'forums', $dbs, "id=" . intval($this->forum['id']) );
		}

		return true;
	}
	
	/**
	 * Load and return a topic by ID.
	 * @param   intval  $tid
	 * @return	array	Topic Data
	 */
	public function getTopicById( $tid )
	{
		/* No topic ID? Whut?! */
		if ( empty($tid) )
		{
			return array();
		}
		
		if ( empty( $this->_topicCache[ $tid ] ) OR ! is_array( $this->_topicCache[ $tid ] ) )
		{
			$this->_topicCache[ $tid ] = $this->DB->buildAndFetch( array( 'select'   => 't.*', 
																		  'from'     => array( 'topics' => 't' ), 
																		  'where'    => "t.tid=" . intval( $tid ),
																		  'add_join' => array( array( 'select'  => 'p.pid, p.start_date as poll_start_date, p.choices, p.starter_id as poll_starter_id, p.votes, p.poll_question, p.poll_only, p.poll_view_voters',
																		  							  'type'	=> 'left',
																									  'from'	=> array( 'polls' => 'p' ),
																									  'where'	=> 'p.tid=t.tid' ) ) ) );
		}

		return $this->_topicCache[ $tid ];
	}
	
	/**
	 * Load and return a post by ID.
	 * @param   intval  $pid
	 * @param	boolean	Check for archived topic
	 * @return	array	Post Data
	 */
	public function getPostById( $pid, $checkArchived=false, $additionalFilters=array() )
	{
		$post              = array();
		$additionalFilters = ( is_array( $additionalFilters ) ) ? $additionalFilters : array( $additionalFilters );
		
		if ( ! empty($pid) )
		{
			$posts = $this->getPosts( array_merge( $additionalFilters, array( 'postId'          => array( $pid ),
																	          'checkArchived'   => $checkArchived ) ) );
											 
			$post = $posts[ $pid ];
			
		}
		
		/* Bitwise options */
		$_tmp = IPSBWOptions::thaw( $post['post_bwoptions'], 'posts', 'forums' );
		
		if ( count( $_tmp ) )
		{
			foreach( $_tmp as $k => $v )
			{
				$post[ $k ] = $v;
			}
		}
		
		return $post;
	}
	
	/**
	 * getTopics
	 * Fetches topics based on different critera
	 * @param	array	Filters (see below for specifics)
	 * @return	array 
	 * 
	 * FILTERS:
	 * forumId			Get topics matching the (array) forum ids, (int) forum ID
	 * topicId			Get topics matching the (array) topic ids, (int) topic ID
	 * memberData		Set memberData (this->memberData is used otherwise)
	 * onlyViewable		Set whether this member can view them or not. (default is true ) NOTE: Will not check to see if parent topic is viewable!
	 * onlyVisible 		Set whether to skip unapproved posts where permission allows (default is true)
	 * topicType		array of 'sdelete', 'visible', 'hidden', 'pdeleted' (if you specify these, permission checks are NOT performed)
	 * archiveState		Archive status 'not', 'archived', 'working', 'exclude', 'restore'
	 * sortField		Sort key (date, pid, etc)
	 * sortOrder		asc/desc
	 * skipForumCheck	Skips the forum ID IN list check to ensure you have access to view (good for when using perms elsewhere)
	 * tidIsGreater		Where TID is greater than x
	 * tidIsLess		Where TID is less than x
	 * dateIsGreater	Where DATE is greater than UNIX
	 * getFirstPost	    Return the first post of the topic
	 * parse			Parses the first post of the topic
	 * limit, offset	Limit the amount of results in the returned query
	 * getCount			fetch count without limit
	 * getCountOnly		As above, but returns the count and does not fetch the data
	 */
	public function getTopics( $filters )
	{
		/* init */
		$filters	= $this->_setTopicFilters( $filters );
		$limit	    = null;
		$topics	    = array();
		$where		= array();
		$memberData	= ( ! empty( $filters['memberData'] ) && is_array( $filters['memberData'] ) ) ? $filters['memberData'] : $this->memberData;
		
		/* Forum Ids */
		if ( ! empty( $filters['forumId'] ) )
		{
			$filters['forumId'] = ( ! is_array( $filters['forumId'] ) ) ? array( $filters['forumId'] ) : $filters['forumId'];
			$where[] = "t.forum_id IN (" . implode( ',', $filters['forumId'] ) . ")";
		}
		
		/* Topic Ids */
		if ( ! empty( $filters['topicId'] ) )
		{
			$filters['topicId'] = ( ! is_array( $filters['topicId'] ) ) ? array( $filters['topicId'] ) : $filters['topicId'];
			$where[] = "t.tid IN (" . implode( ',', $filters['topicId'] ) . ")";
		}
		
		/* Archive state */
		if ( ! empty( $filters['archiveState'] ) && is_array( $filters['archiveState'] ) )
		{
			$where[] = $this->registry->class_forums->fetchTopicArchiveQuery( $filters['archiveState'], 't.' );
		}
		
		/* TID is greater */
		if ( ! empty( $filters['tidIsGreater'] ) )
		{
			$where[] = "t.tid > " . intval( $filters['tidIsGreater'] );
		}
		
		/* TID is less */
		if ( ! empty( $filters['tidIsLess'] ) )
		{
			$where[] = "t.tid < " . intval( $filters['tidIsLess'] );
		}
		
		/* TID is greater */
		if ( ! empty( $filters['dateIsGreater'] ) )
		{
			$where[] = "t.start_date > " . intval( $filters['dateIsGreater'] );
		}
		
		/* Visible / specific filters */
		if ( ! empty( $filters['topicType'] ) && is_array( $filters['topicType'] ) )
		{
			$where[] = $this->registry->class_forums->fetchTopicHiddenQuery( $filters['topicType'], 't.' );
		}
		else
		{
			if ( isset( $filters['onlyViewable'] ) && $filters['onlyViewable'] === true && empty( $filters['onlyVisible'] ) )
			{
				$_perms = array( 'visible' );
				
				if ( $this->registry->getClass('class_forums')->canSeeSoftDeletedTopics( false ) )
				{
					$_perms[] = 'sdelete';
				}
				
				if ( $this->registry->getClass('class_forums')->canQueuePosts( false ) )
				{
					$_perms[] = 'hidden';
				}
				
				$where[] = $this->registry->class_forums->fetchTopicHiddenQuery( $_perms, 't.' );
			}
			else
			{
				/* Show visible only */
				$where[] = $this->registry->class_forums->fetchTopicHiddenQuery( array( 'visible' ), 't.' );
			}
		}
		
		/* Forum ID check? */
		if ( ( isset( $filters['onlyViewable'] ) && $filters['onlyViewable'] === true ) && ( empty($filters['skipForumCheck']) OR $filters['skipForumCheck'] === false ) )
		{
			if ( empty( $filters['forumId'] ) )
			{
				$forumIds = $this->registry->class_forums->fetchSearchableForumIds( $memberData['member_id'] );
				
				if ( ! count( $forumIds ) )
				{
					return $where;
				}
				
				$where[] = "t.forum_id IN (" . implode( ",", $forumIds ) . ")";
			}
		}
		
		/* Did we want a count also? */
		if ( ! empty( $filters['getCount'] ) || ! empty( $filters['getCountOnly'] ) )
		{
			$count	= $this->DB->buildAndFetch( array(	'select'	=> 'COUNT(*) as topics', 
														'from'		=> array( 'topics' => 't' ), 
														'where'		=> implode( ' AND ', $where ) ) );

			$this->_countTopics = $count['topics'];
		}
		
		/* Just return? */
		if ( ! empty( $filters['getCountOnly'] ) )
		{
			return $this->_countTopics;
		}
		
		/* Offset, limit */
		if ( isset( $filters['offset'] ) OR isset( $filters['limit'] ) )
		{
			if ( $filters['offset'] > 0 || $filters['limit'] > 0 )
			{
				$limit = array( intval( $filters['offset'] ), intval( $filters['limit'] ) );
			}
		}
		
		/* Order */
		if ( ! empty( $filters['sortField'] ) )
		{
			$order = 't.' . $filters['sortField'];
			
			if ( isset( $filters['sortOrder'] ) )
			{
				$order .= ' ' . $filters['sortOrder'];
			}
		}
		
		/* Set joins */
		$joins = array( array( 'select'  	=> 'm.*',
						       'type'		=> 'left',
						       'from'		=> array( 'members' => 'm' ),
						       'where'	=> 'm.member_id=t.starter_id' ),
				        array( 'select'  	=> 'pp.*',
						       'type'		=> 'left',
						       'from'		=> array( 'profile_portal' => 'pp' ),
						       'where'	=> 'pp.pp_member_id=t.starter_id' ) );
				        
		/* Fetching first post ? */
		if ( ! empty( $filters['getFirstPost'] ) )
		{
			$joins[] = array( 'select' => 'p.*',
							  'from'   => array( 'posts' => 'p' ),
							  'where'  => 'p.pid=t.topic_firstpost',
							  'type'  => 'left' );
		}
		
		/* Fetch them */
		$this->DB->build( array( 'select'   => 't.*, t.title as real_title, t.posts as real_posts, t.last_post as topic_last_post', 
							     'from'     => array( 'topics' => 't' ), 
							     'where'    => implode( ' AND ', $where ),
								 'limit'	=> $limit ? $limit : '',
								 'order'	=> $order ? $order : 't.tid asc',
							     'add_join' => $joins ) );
							     
		$o = $this->DB->execute();
		
		while( $topic = $this->DB->fetch( $o ) )
		{
			if ( $topic['starter_id'] && $topic['member_group_id'] )
			{
				$group = $this->caches['group_cache'][ $topic['member_group_id'] ];
				
				if ( is_array( $group ) && count( $group ) )
				{
					$topic = array_merge( $group, $topic );
				}
			}
			
			/* Get the first post? */
			if ( ! empty( $filters['getFirstPost'] ) )
			{
				if ( ! empty( $filters['parse'] ) )
				{
					$this->setTopicData( $topic );
					$this->setPermissionData();
					$topic['firstPostParsed'] = $this->parsePost( $topic );	
				}
				
				$topic['_postType']     = $this->registry->class_forums->fetchHiddenType( $topic );
			}
			
			/* Member title overwrites topic */
			$topic['title']     	= $topic['real_title'];
			$topic['posts']			= $topic['real_posts'];
			$topic['_topicType']    = $this->registry->class_forums->fetchHiddenTopicType( $topic );
			
			$topics[ $topic['tid'] ] = $topic;
		}
		
		return $topics;
	}
	
	/**
	 * getTopicsCount
	 * Fetches number of topics based on different critera (useful for pagination when combined with getTopics())
	 * @return	int 
	 */
	public function getTopicsCount()
	{
		return $this->_countTopics;
	}
	
	/**
	 * getPosts
	 * Fetches posts based on different critera
	 * @param	array	Filters (see below for specifics)
	 * @return	array 
	 * 
	 * FILTERS:
	 * topicId			Get posts matching the (array) topic ids, (int) topic ID
	 * forumId			Get posts matching the (array) forum ids, (int) forum ID
	 * notForumId		Get posts NOT matching the (array) forum ids, (int) forum ID
	 * postId			Get posts matching the (array) post ids, (int) post id
	 * postDate			Get posts matching the (array) post dates, (int) post date
	 * authorId			Get posts matching the (array) author ids, (int) author id
	 * memberData		Set memberData (this->memberData is used otherwise)
	 * onlyViewable		Set whether this member can view them or not. (default is true ) NOTE: Will not check to see if parent topic is viewable!
	 * onlyVisible 		Set whether to skip unapproved posts where permission allows (default is true)
	 * postType			array of 'sdelete', 'visible', 'hidden', 'pdeleted' (if you specify these, permission checks are NOT performed)
	 * sortField		Sort key (date, pid, etc)
	 * sortOrder		asc/desc
	 * pidIsGreater		Where PID is greater than x
	 * dateIsGreater	Where DATE is greater than UNIX
	 * skipForumCheck	Skips the forum ID IN list check to ensure you have access to view (good for when using perms elsewhere)
	 * parse			Parses post content
	 * limit, offset	Limit the amount of results in the returned query
	 * getCount			fetch count without limit
	 * getCountOnly	    fetch count and return only
	 * checkArchived	Check archive tables if no matches
	 * 
	 */
	public function getPosts( $filters )
	{
		/* init */
		$filters	= $this->_setPostFilters( $filters );
		$limit	    = null;
		$posts	    = array();
		$where		= array();
		$memberData	= ( ! empty( $filters['memberData'] ) && is_array( $filters['memberData'] ) ) ? $filters['memberData'] : $this->memberData;
		
		$isArchived    = ( ! empty( $filters['isArchivedTopic'] ) ) ? true : false;
		$checkArchived = ( ! empty( $filters['checkArchived'] ) ) ? true : false;
		
		if ( $isArchived && ( $this->settings['archive_remote_sql_database'] && $this->settings['archive_remote_sql_user'] ) )
		{
			/* Load up archive class */
			if ( ! is_object( $this->_archiveReader ) )
			{
				$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/archive/reader.php', 'classes_archive_reader' );
				$this->_archiveReader = new $classToLoad();
					
				$this->_archiveReader->setApp('forums');
			}
				
			$posts = $this->_archiveReader->getPosts( $filters );
			
			/* If we just need the count let's go */
			if ( $filters['getCountOnly'] )
			{
				return $posts;
			}
				
			/* Fetch topics */
			$tids = array();
			$mids = array();

			if ( !empty( $posts ) )
			{
				foreach( $posts as $id => $post )
				{
					$tids[ $post['topic_id'] ] = $post['topic_id'];
			
					if ( $post['author_id'] )
					{
						$mids[ $post['author_id'] ] = $post['author_id'];
					}
				}
			}
				
			$topics  = $this->getTopics( array( 'topicId' => $tids ) );
			$members = IPSMember::load( $mids );
			
			if ( !empty( $posts ) )
			{
				foreach( $posts as $id => $post )
				{
					$posts[ $id ] = array_merge( $topics[ $post['topic_id'] ], $post );
					
					if ( $post['author_id'] )
					{
						$_mem = $members[ $post['author_id'] ];
						unset( $_mem['title'] );
							
						$posts[ $id ] = array_merge( $_mem, $post );
					}
			
					if ( ! empty( $filters['parse'] ) )
					{
						$this->setTopicData( $post );
						$this->setPermissionData();
						$posts[ $id ] = $this->parsePost( $posts[ $id ] );
					}
					
					$posts[ $id ]['tid']        = $post['topic_id'];
					$posts[ $id ]['_postType']  = $this->registry->class_forums->fetchHiddenType( $post );
					$posts[ $id ]['_topicType'] = $this->registry->class_forums->fetchHiddenTopicType( $post );
				}
			}
				
			return $posts;
		}
		else
		{
			/* Posts */
			if ( ! empty( $filters['postId'] ) )
			{
				$filters['postId'] = ( ! is_array( $filters['postId'] ) ) ? array( $filters['postId'] ) : $filters['postId'];
				$where[] = "p." . $this->getPostTableField( 'pid', $isArchived ) . " IN (" . implode( ',',$filters['postId'] ) . ")";
			}

			/* Posts */
			if ( ! empty( $filters['postDate'] ) )
			{
				$filters['postDate'] = ( ! is_array( $filters['postDate'] ) ) ? array( $filters['postDate'] ) : $filters['postDate'];
				$where[] = "p." . $this->getPostTableField( 'post_date', $isArchived ) . " IN (" . implode( ',',$filters['postDate'] ) . ")";
			}

			/* Topics */
			if ( ! empty( $filters['topicId'] ) )
			{
				$filters['topicId'] = ( ! is_array( $filters['topicId'] ) ) ? array( $filters['topicId'] ) : $filters['topicId'];
				$where[] = "p.". $this->getPostTableField( 'topic_id', $isArchived ) . " IN (" . implode( ',',$filters['topicId'] ) . ")";
			}
			
			/* Forum Ids */
			if ( ! empty( $filters['forumId'] ) )
			{
				$filters['forumId'] = ( ! is_array( $filters['forumId'] ) ) ? array( $filters['forumId'] ) : $filters['forumId'];
				$where[] = "t.forum_id IN (" . implode( ',', $filters['forumId'] ) . ")";
			}
			
			/* Author Ids */
			if ( ! empty( $filters['authorId'] ) )
			{
				$filters['authorId'] = ( ! is_array( $filters['authorId'] ) ) ? array( $filters['authorId'] ) : $filters['authorId'];
				$where[] = "p." . $this->getPostTableField( 'author_id', $isArchived ) . " IN (" . implode( ',',$filters['authorId'] ) . ")";
			}
			
			/* Not Forum Ids */
			if ( ! empty( $filters['notForumId'] ) )
			{
				$filters['notForumId'] = ( ! is_array( $filters['notForumId'] ) ) ? array( $filters['notForumId'] ) : $filters['notForumId'];
				$where[] = "t.forum_id NOT IN (" . implode( ',', $filters['notForumId'] ) . ")";
			}
			
			/* PID is greater */
			if ( ! empty( $filters['pidIsGreater'] ) )
			{
				$where[] = "p.". $this->getPostTableField( 'pid', $isArchived ) . " > " . intval( $filters['pidIsGreater'] );
			}
			
			/* PID is less */
			if ( ! empty( $filters['pidIsLess'] ) )
			{
				$where[] = "p.". $this->getPostTableField( 'pid', $isArchived ) . " < " . intval( $filters['pidIsLess'] );
			}
			
			/* Author is ignored */
			if ( ! empty( $filters['notIgnored'] ) )
			{
				$ignoredUsers = array();
				
				foreach( $this->member->ignored_users as $_i )
				{
					if( $_i['ignore_topics'] )
					{
						$ignoredUsers[] = $_i['ignore_ignore_id'];
					}
				}
				
				if ( ! empty( $ignoredUsers ) )
				{
					$where[] = "p." . $this->getPostTableField( 'author_id', $isArchived ) . " NOT IN (" . implode( ',',$ignoredUsers ) . ")";
				}
			}		
			
			/* Date is greater */
			if ( ! empty( $filters['dateIsGreater'] ) )
			{
				$where[] = "p.". $this->getPostTableField( 'post_date', $isArchived ) . " > " . intval( $filters['dateIsGreater'] );
			}
			
			/* Date is less */
			if ( ! empty( $filters['dateIsLess'] ) )
			{
				$where[] = "p.". $this->getPostTableField( 'post_date', $isArchived ) . " < " . intval( $filters['dateIsLess'] );
			}
			
			/* Visible / specific filters */
			if ( ! empty( $filters['postType'] ) && is_array( $filters['postType'] ) )
			{
				$where[] = $this->registry->class_forums->fetchPostHiddenQuery( $filters['postType'], 'p.' . $this->getPostTableField( '_prefix_', $isArchived ) );
			}
			else
			{
				if ( isset( $filters['onlyViewable'] ) && $filters['onlyViewable'] === true && empty( $filters['onlyVisible'] ) )
				{
					$_perms = array( 'visible' );
					
					if ( $this->registry->getClass('class_forums')->canSeeSoftDeletedPosts( false ) )
					{
						$_perms[] = 'sdelete';
					}
					
					if ( $this->registry->getClass('class_forums')->canQueuePosts( false ) )
					{
						$_perms[] = 'hidden';
					}
					
					$where[] = $this->registry->class_forums->fetchPostHiddenQuery( $_perms, 'p.' . $this->getPostTableField( '_prefix_', $isArchived ) );
				}
				else
				{
					/* Show visible only */
					$where[] = $this->registry->class_forums->fetchPostHiddenQuery( array( 'visible' ), 'p.' . $this->getPostTableField( '_prefix_', $isArchived ) );
				}
			}
			
			/* Forum ID check? */
			if ( ( isset( $filters['onlyViewable'] ) && $filters['onlyViewable'] === true ) && ( empty($filters['skipForumCheck']) OR $filters['skipForumCheck'] === false ) )
			{
				if ( empty( $filters['forumId'] ) && empty( $filters['notForumId'] ) )
				{
					$forumIds = $this->registry->class_forums->fetchSearchableForumIds( $memberData['member_id'] );
					
					if ( ! count( $forumIds ) )
					{
						return $where;
					}
					
					$where[] = "t.forum_id IN (" . implode( ",", $forumIds ) . ")";
				}
			}
			
			/* Did we want a count also? */
			if ( ! empty( $filters['getCount'] ) || ! empty( $filters['getCountOnly'] ) )
			{
				$count	= $this->DB->buildAndFetch( array(	'select'	=> 'COUNT(*) as posts', 
															'from'		=> array( $this->getPostTableField( '_table_', $isArchived ) => 'p' ), 
															'where'		=> implode( ' AND ', $where ),
															'add_join'	=> array( array( 'type'		=> 'left',
																						 'from'		=> array( 'topics' => 't' ),
																					     'where'	=> 'p.' . $this->getPostTableField( 'topic_id', $isArchived ) . '=t.tid' ) ) ) );
	
				$this->_countPosts = $count['posts'];
				
				if ( $filters['getCountOnly'] )
				{
					return $this->_countPosts;
				}
			}
			
			/* Offset, limit */
			if ( isset( $filters['offset'] ) OR isset( $filters['limit'] ) )
			{
				if ( $filters['offset'] > 0 || $filters['limit'] > 0 )
				{
					$limit = array( intval( $filters['offset'] ), intval( $filters['limit'] ) );
				}
			}
			
			/* Order */
			if ( ! empty( $filters['sortField'] ) )
			{
				if ( strstr( $filters['sortField'], '.' ) )
				{
					$order = $filters['sortField'];
				}
				else
				{
					$order = 'p.' . $this->getPostTableField( $filters['sortField'], $isArchived );
				}
				
				if ( isset( $filters['sortOrder'] ) )
				{
					$order .= ' ' . $this->getPostTableField( $filters['sortOrder'], $isArchived );
				}
			}
			
			$_joins	= array( array(  'select'  	=> 't.*, t.title as real_title',
		  						     'type'		=> 'left',
								     'from'		=> array( 'topics' => 't' ),
								     'where'	=> 'p.' . $this->getPostTableField( 'topic_id', $isArchived ) . '=t.tid' ),
						      array( 'select'  	=> 'm.*, m.title as member_title',
								     'type'		=> 'left',
								     'from'		=> array( 'members' => 'm' ),
								     'where'	=> 'm.member_id=p.' . $this->getPostTableField( 'author_id', $isArchived ) ),
						      array( 'select'  	=> 'pp.*',
								     'type'		=> 'left',
								     'from'		=> array( 'profile_portal' => 'pp' ),
								     'where'	=> 'pp.pp_member_id=p.' . $this->getPostTableField( 'author_id', $isArchived ) ) );
	
			/* Add custom fields join? */
			if ( $this->settings['custom_profile_topic'] == 1 )
			{
				$_joins[] = array( 
										'select' => 'pc.*',
										'from'   => array( 'pfields_content' => 'pc' ),
										'where'  => 'pc.member_id=p.' . $this->getPostTableField( 'author_id', $isArchived ),
										'type'   => 'left'
									);
			}
	
			/* Fetch them */
			$this->DB->build( array( 'select'   => 'p.*, p.' . $this->getPostTableField( 'ip_address', $isArchived ) . ' as post_ip',
								     'from'     => array( $this->getPostTableField( '_table_', $isArchived ) => 'p'. ( isset($filters['forceIndex'] ) ? ' USE INDEX(' . $this->getPostTableField( $filters['forceIndex'], $isArchived ). ')':'') ), 
								     'where'    => implode( ' AND ', $where ),
									 'limit'	=> $limit ? $limit : '',
									 'order'	=> $order ? $order : 'p.' . $this->getPostTableField( 'pid', $isArchived ) . ' asc',
								     'add_join' => $_joins ) );
			
			$o = $this->DB->execute();
			
			while( $post = $this->DB->fetch( $o ) )
			{
				if ( $post[ $this->getPostTableField('author_id', $isArchived ) ] && $post['member_group_id'] )
				{
					$group = $this->caches['group_cache'][ $post['member_group_id'] ];
					
					if ( is_array( $group ) && count( $group ) )
					{
						$post = array_merge( $group, $post );
					}
				}
				
				/* Post IP overrides member */
				$post['ip_address'] = $post['post_ip'];
				
				/* Member title overwrites topic */
				$post['title']	= $post['topic_title'] = $post['real_title'];
				$pid			= $post[ $this->getPostTableField('pid', $isArchived ) ];
				
				if ( $isArchived AND $filters['archiveToNative'] )
				{
					$post = $this->archivePostToNativeFields( $post );
				}
				
				if ( ! empty( $filters['parse'] ) )
				{
					$this->setTopicData( $post );
					$this->setPermissionData();
					$post = $this->parsePost( $post );	
				}
				
				$post['_postType']     = $this->registry->class_forums->fetchHiddenType( $post );
				$post['_topicType']    = $this->registry->class_forums->fetchHiddenTopicType( $post );
				
				$posts[ $pid ] = $post;
			}
			
			/* got anything? */
			if ( ! count( $posts ) && $checkArchived )
			{
				$filters['checkArchived'] = false;
				$filters['isArchived']    = true;
				
				$posts = $this->getPosts( $filters );
			}
		}
		
		return $posts;
	}
	
	/**
	 * getPostsCount
	 * Fetches number of posts based on different critera (useful for pagination when combined with getPosts())
	 * @return	int 
	 */
	public function getPostsCount()
	{
		return $this->_countPosts;
	}
	
	/**
	 * @return the $errorMessage
	 */
	public function getErrorMessage()
	{
		return $this->errorMessage;
	}

	/**
	 * @param field_type $errorMessage
	 */
	public function setErrorMessage( $errorMessage )
	{
		$this->errorMessage = $errorMessage;
	}
	
	/**
	 * Sets up the permissions for this class
	 * 
	 * @param	string	key
	 * @param	string	value
	 */
	public function setPermissionData( $k='', $v='' )
	{
		$this->registry->getClass('class_forums')->setMemberData( $this->getMemberData() );
		
		if ( empty( $k ) and empty( $v ) )
		{
			/* Auto set up */
			$this->permissionsData['softDelete']             = $this->registry->getClass('class_forums')->canSoftDeletePosts( $this->topicData['forum_id'], array() );
			$this->permissionsData['softDeleteRestore']      = $this->registry->getClass('class_forums')->can_Un_SoftDeletePosts( $this->topicData['forum_id'] );
			$this->permissionsData['softDeleteSee']          = $this->registry->getClass('class_forums')->canSeeSoftDeletedPosts( $this->topicData['forum_id'] );
			$this->permissionsData['softDeleteReason']       = $this->registry->getClass('class_forums')->canSeeSoftDeleteReason( $this->topicData['forum_id'] );
			$this->permissionsData['softDeleteContent']      = $this->registry->getClass('class_forums')->canSeeSoftDeleteContent( $this->topicData['forum_id'] );
			$this->permissionsData['TopicSoftDelete']        = $this->registry->getClass('class_forums')->canSoftDeleteTopics( $this->topicData['forum_id'], $this->topicData );
			$this->permissionsData['TopicSoftDeleteRestore'] = $this->registry->getClass('class_forums')->can_Un_SoftDeleteTopics( $this->topicData['forum_id'] );
			$this->permissionsData['TopicSoftDeleteSee']     = $this->registry->getClass('class_forums')->canSeeSoftDeletedTopics( $this->topicData['forum_id'] );
		}
		else if ( isset( $k['tid'] ) && isset( $k['forum_id'] ) )
		{
			/* Auto set up */
			$this->permissionsData['softDelete']             = $this->registry->getClass('class_forums')->canSoftDeletePosts( $k['forum_id'], array() );
			$this->permissionsData['softDeleteRestore']      = $this->registry->getClass('class_forums')->can_Un_SoftDeletePosts( $k['forum_id'] );
			$this->permissionsData['softDeleteSee']          = $this->registry->getClass('class_forums')->canSeeSoftDeletedPosts( $k['forum_id'] );
			$this->permissionsData['softDeleteReason']       = $this->registry->getClass('class_forums')->canSeeSoftDeleteReason( $k['forum_id'] );
			$this->permissionsData['softDeleteContent']      = $this->registry->getClass('class_forums')->canSeeSoftDeleteContent( $k['forum_id'] );
			$this->permissionsData['TopicSoftDelete']        = $this->registry->getClass('class_forums')->canSoftDeleteTopics( $k['forum_id'], $k );
			$this->permissionsData['TopicSoftDeleteRestore'] = $this->registry->getClass('class_forums')->can_Un_SoftDeleteTopics( $k['forum_id'] );
			$this->permissionsData['TopicSoftDeleteSee']     = $this->registry->getClass('class_forums')->canSeeSoftDeletedTopics( $k['forum_id'] );
		}
		else if ( is_array( $k ) )
		{
			$this->permissionsData = $k;
		}
		else if ( ! empty( $k )  )
		{
			$this->permissionsData[ $k ] = $v;
		}
	}
	
	/**
	 * @return the $permissionData
	 */
	public function getPermissionData( $k='' )
	{
		return ( ! empty( $k ) ) ? $this->permissionsData[ $k ] : $this->permissionsData;
	}
	
	/**
	 * @return the $topicData
	 */
	public function getTopicData( $k='' )
	{	
		return ( ! empty( $k ) ) ? $this->topicData[ $k ] : $this->topicData;
	}

	/**
	 * @param	string	key
	 * @param	string	value
	 */
	public function setTopicData( $k, $v='' )
	{
		if ( is_integer( $k ) )
		{
			$this->topicData = $this->getTopicById( $k );
		}
		else if ( is_array( $k ) )
		{
			$this->topicData = $k;
		}
		else if ( ! empty( $k ) )
		{
			$this->topicData[ $k ] = $v;
		}
	}
	
	/**
	 * @return the $_memberData
	 */
	public function getMemberData( $k='' )
	{
		return ( ! empty( $k ) ) ? $this->_memberData[ $k ] : $this->_memberData;
	}

	/**
	 * @param	string	key
	 * @param	string	value
	 */
	public function setMemberData( $k, $v='' )
	{
		if ( is_integer( $k ) )
		{
			$this->_memberData = empty( $k ) ? IPSMember::setUpGuest() : IPSMember::load( $k );
		}
		else if ( is_string($k) && $k == intval($k) )
		{
			$this->_memberData = empty( $k ) ? IPSMember::setUpGuest() : IPSMember::load( $k );
		}
		else if ( is_array( $k ) )
		{
			$this->_memberData = $k;
		}
		else if ( ! empty( $k ) )
		{
			$this->_memberData[ $k ] = $v;
		}
	}
	
	/**
	 * Loads and fetches the moderator data
	 */
	public function getModeratorData()
	{
		$forumData = $this->registry->getClass('class_forums')->getForumbyId( $this->topicData['forum_id'] );
		
		if ( $this->moderatorData === null AND $this->memberData['member_id'] AND ! $this->memberData['g_is_supmod'] )
		{
			$other_mgroups	= array();
			$_mgroup_others	= IPSText::cleanPermString( $this->memberData['mgroup_others'] );

			if( $_mgroup_others )
			{
				$other_mgroups = explode( ",", $_mgroup_others );
			}
		
			$other_mgroups[] = $this->memberData['member_group_id'];
			
			$member_group_ids = implode( ",", $other_mgroups );

			$this->moderatorData = $this->DB->buildAndFetch( array( 'select' => '*',
																    'from'	 => 'moderators',
																    'where'	 => "forum_id LIKE '%,{$forumData['id']},%' AND (member_id={$this->memberData['member_id']} OR (is_group=1 AND group_id IN({$member_group_ids})))" )	);
		
			$this->moderatorData = ( is_array( $this->moderatorData ) ) ? $this->moderatorData : array();
			
			$this->moderatorData = array_merge( $this->moderatorData, IPSBWOptions::thaw( $this->moderatorData['mod_bitoptions'], 'moderators', 'forums' ) );	
		}
		
		return $this->moderatorData;
	}

	/**
	 * Fetch the next unread topicID
	 * @param array or null $topicData
	 */
	public function getNextUnreadTopicId( $topicData=false)
	{
		$topicData   = ( ! is_array( $topicData ) ) ? $this->getTopicData() : $topicData;
		$readItems   = $this->registry->classItemMarking->fetchReadIds( array( 'forumID' => $topicData['forum_id'] ) );
		$lastMarked  = $this->registry->classItemMarking->fetchTimeLastMarked( array( 'forumID' => $topicData['forum_id'] ) );
		$approved    = $this->memberData['is_mod'] ? ' AND ' . $this->registry->getClass('class_forums')->fetchTopicHiddenQuery( array( 'visible', 'hidden' ), '' ) . ' ' : ' AND ' . $this->registry->getClass('class_forums')->fetchTopicHiddenQuery( array( 'visible' ), '' ) . ' ';
		
		/* Add in this topic ID to be sure */
		$readItems[ $topicData['tid'] ] = $topicData['tid'];
		
		/* First, attempt to fetch a topic older than this one */
		$tid = $this->DB->buildAndFetch( array( 'select' => 'tid',
												'from'   => 'topics',
												'where'  => "forum_id=" . intval( $topicData['forum_id'] ) . " {$approved} AND tid NOT IN(".implode(",",array_values($readItems)).") AND last_post < " . intval($topicData['last_post']) . " AND last_post > " . $lastMarked . " AND state != 'link'",
												'order'  => 'last_post DESC',
												'limit'  => array( 0, 1 ) )	);
		
		if ( ! $tid )
		{
			$tid = $this->DB->buildAndFetch( array( 'select' => 'tid',
													'from'   => 'topics',
													'where'  => "forum_id=" . intval( $topicData['forum_id'] ) . " {$approved} AND tid NOT IN(".implode(",",array_values($readItems)).") AND last_post > " . intval($topicData['last_post']) . " AND state != 'link'",
													'order'  => 'last_post DESC',
													'limit'  => array( 0, 1 ) )	);
		}
		
		return intval( $tid['tid'] );
	}
	
	/**
	 * Determines if we're on the last page or not ...
	 * @param array or null $topicData
	 * @return	boolean
	 */
	public function isOnLastPage( $topicData=false )
	{
		$topicData = ( ! is_array( $topicData ) ) ? $this->getTopicData() : $topicData;
		$page      = intval( $this->request['page'] );
		$perPage   = $this->settings['display_max_posts'];
		$posts     = intval( $topicData['posts'] ) + 1;
		$maxPage   = ceil( $posts / $perPage );

		if ( $posts <= $perPage )
		{
			return true;
		}
		
		if ( $maxPage == $page )
		{
			return true;
		}
		
		return false;
	}
	
	/**
	 * Return whether we can see the IP address or not
	 *
	 * @return	bool
	 */
	public function canSeeIp()
	{
		$moderator = $this->getModeratorData();
		
		if ( ! $this->memberData['g_is_supmod'] && empty( $moderator['view_ip'] ) )
		{
			return FALSE;
		}
		else
		{
			return TRUE;
		}
	}
	
	/**
	 * Is this topic archived?
	 * For this purpose, either 'working' or 'archived' returns true
	 * @param array $topicData
	 * @return boolean
	 */
	public function isArchived( $topicData )
	{
		if ( ! empty( $topicData['topic_archive_status'] ) )
		{
			$result = $this->registry->class_forums->fetchArchiveTopicType( $topicData );
			
			switch( $result )
			{
				case 'not':
				case 'exclude':
					return false;
				break;
				case 'working':
				case 'restore':
				case 'archived':
					return true;
				break;
			}
		}
		
		return false;
	}
	
	/**
	 * Can view or cannot view
	 * @param	mixed	Nothing or inline topicData
	 * @param   mixed	Nothing or inline memberData
	 */
	public function canView( $topicData=false, $memberData=false )
	{
		if ( is_array( $topicData ) && isset( $topicData['tid'] ) )
		{
			$this->setTopicData( $topicData );
		}
		
		if ( is_array( $memberData ) && isset( $memberData['member_id'] ) )
		{
			$this->setMemberData( $memberData );
		}
		
		/* get member/topic data */
		$memberData = $this->getMemberData();
		$topicData  = $this->getTopicData();
		
		/* Basic checks */
		if ( ! $topicData['tid'] )
		{
			$this->setErrorMessage( 'EX_topics_no_tid' );
			return false;
		}
		
		if ( ! $this->registry->getClass('class_forums')->getForumbyId( $topicData['forum_id'] ) )
		{
			$this->setErrorMessage( 'EX_topics_no_fid' );
			return false;
		}
		
		/* Set up member ID */
		$this->registry->class_forums->setMemberData( $memberData );
		
		/* Test */
		if ( ! $this->registry->class_forums->forumsCheckAccess( $topicData['forum_id'], 1, 'topic', $topicData, $this->return ) )
		{
			$this->setErrorMessage( 'EX_topic_not_approved' );
			return false;
		}
		
		/* Error out if the topic is not approved or soft deleted */
		$approved = $this->registry->class_forums->fetchHiddenTopicType( $topicData );
		
		if ( ! $this->registry->class_forums->canQueuePosts( $topicData['forum_id'] ) )
		{
			if ( $approved == 'hidden' )
			{
				$this->setErrorMessage( 'EX_topic_not_approved' );
				return false;
			}
		}
		
		/* Set up permissions */
		if ( ! count( $this->permissionsData ) )
		{
			$this->setPermissionData();
		}
		
		/* Deleted / Hidden? */
		if ( ( $approved == 'sdelete' or $approved == 'pdelete') AND ! $memberData['g_is_supmod'] AND ! $this->permissionsData['softDeleteSee'] )
		{
			$this->setErrorMessage( 'EX_topic_not_approved' );
			return false;
		}
		
		return true;
	}
	
	/**
	 * Return whether or not we have permission to delete the post (_getDeleteButtonData)
	 *
	 * @return	bool
	 */
	public function canDeletePost( $poster )
	{
		/* Is we archived? */
		if ( $this->isArchived( $this->getTopicData() ) )
		{
			return false;
		}
		
		$moderator = $this->getModeratorData();
		
		if ( ! $this->memberData['member_id']  )
		{
			return FALSE;
		}
		
		if ( $this->memberData['g_is_supmod'] OR $moderator['delete_post'] )
		{
			return TRUE;
		}
		
		if ( $poster['member_id'] == $this->memberData['member_id'] and ( $this->memberData['g_delete_own_posts'] ) )
		{
			return TRUE;
		}
		
		return FALSE;
	}
	
	/**
	 * Return whether or not we can edit this post
	 *
	 * @param	array 		Array of post data
	 * @return  bool
	 */
	public function canEditPost( $poster=array() )
	{
		/* Is we archived? */
		if ( $this->isArchived( $this->getTopicData() ) )
		{
			return false;
		}
		
		$moderator	= $this->getModeratorData();
		$topicData	= $this->getTopicData();
		
		if ( $this->memberData['member_id'] == "" or $this->memberData['member_id'] == 0 )
		{
			return FALSE;
		}
				
		if ( $this->memberData['g_is_supmod'] )
		{
			return TRUE;
		}
		
		if ( $moderator['edit_post'] )
		{
			return TRUE;
		}
		
		if ( ( $topicData['state'] != 'open' ) and ( ! $this->memberData['g_is_supmod'] AND ! $moderator['edit_post'] ) )
		{
			if ( $this->memberData['g_post_closed'] != 1 )
			{
				return FALSE;
			}
		}
		
		if ( $poster['member_id'] == $this->memberData['member_id'] and ($this->memberData['g_edit_posts']) )
		{
			// Have we set a time limit?
			if ($this->memberData['g_edit_cutoff'] > 0)
			{
				if ( $poster['post_date'] > ( time() - ( intval($this->memberData['g_edit_cutoff']) * 60 ) ) )
				{
					return TRUE;
				}
				else
				{
					return FALSE;
				}
			}
			else
			{
				return TRUE;
			}
		}
		
		return FALSE;
	}
	
	/**
	 * Can make a reply to this topic
	 * @return string locked / moved / no_reply / reply 
	 */
	public function getReplyStatus( $topicData=array() )
	{
		if ( empty( $topicData['tid'] ) )
		{
			$topicData = $this->getTopicData();
		}
		
		/* Is we archived? */
		if ( $this->isArchived( $topicData ) )
		{
			return 'archived';
		}
		
		/* Init */
		$forumData = $this->registry->getClass('class_forums')->getForumbyId( $topicData['forum_id'] );
		
		$status = '';
		
		if ($topicData['state'] == 'closed' OR ( $topicData['poll_state'] AND $topicData['poll_only'] ) )
		{
			/* Do we have the ability to post in closed topics or is this a poll only?*/
			if ( $this->memberData['g_post_closed'] == 1)
			{
				$status = 'locked';
			}
			else
			{
				$status = "locked";
			}
		}
		else
		{
			if ( $topicData['state'] == 'moved' )
			{
				$status = "moved";
			}
			else if ( $topicData['_isDeleted'] )
			{
				$status = 'no_reply';
			}
			else
			{
				if ( $forumData['min_posts_post'] && $forumData['min_posts_post'] > $this->memberData['posts'] && !$this->memberData['g_is_supmod'] )
				{
					$status = "locked";
				}
				else
				{
					if ( $this->memberData['member_id'] AND ( ( ( $this->memberData['member_id'] == $topicData['starter_id'] ) AND ! $this->memberData['g_reply_own_topics'] ) OR ( ( $this->memberData['member_id'] != $topicData['starter_id'] ) AND ! $this->memberData['g_reply_other_topics'] ) ) )
					{ 
						$status = "no_reply";
					}
					else if ( $this->registry->permissions->check( 'reply', $forumData ) == TRUE )
					{
						$status = "reply";
					}
					else
					{
						$status = "no_reply";
					}
				}
			}
		}
		
		return $status;
	}
	
	/**
	 * Get multimoderation data
	 *
	 * @return	array
	 */
	public function getMultiModerationData()
	{
		$return_array = array();
		$mm_array	 = $this->registry->class_forums->getMultimod( $this->topicData['forum_id'] );
		
		//-----------------------------------------
		// Print and show
		//-----------------------------------------
		
		if ( is_array( $mm_array ) and count( $mm_array ) )
		{
			foreach( $mm_array as $m )
			{
				$return_array[] = $m;
			}
		}
		
		return $return_array;
	}
	
	/**
	 * Parses and caches members
	 * @param array $member
	 */
	public function parseMember( array $member )
	{
		/* Not cached? */
		if ( ! isset( $this->_parsedMembers[ $member['author_id'] ] ) )
		{
			$member['member_id'] = !empty($member['mid']) ? $member['mid'] : $member['member_id'];
			
			/* Unset any post data */
			unset( $member['pid'], $member['append_edit'], $member['edit_time'], $member['use_sig'], $member['use_emo'],
					$member['post_edit_reason'], $member['post_date'], $member['post'], $member['queued'], $member['topic_id'],
					$member['post_htmlstate'], $member['new_topic'], $member['edit_name'], $member['post_key'], $member['title'] );
			
			/* Do we have a cached signature? */
			if ( isset( $member['cache_content_sig'] ) )
			{ 
				$member['cache_content'] = $member['cache_content_sig'];
				$member['cache_updated'] = $member['cache_updated_sig'];
			}
			else
			{
				unset( $member['cache_content'], $member['cache_updated'] );
			}
			
			/**
			 * Add group data and setup secondary groups as well
			 * @link	http://community.---.com/tracker/issue-29142-can-post-html-in-secondary-groups/
			 */
			if( !empty($this->caches['group_cache'][ $member['member_group_id'] ]) )
			{
				$member = array_merge( $member, $this->caches['group_cache'][ $member['member_group_id'] ] );
				$member = $this->member->setUpSecondaryGroups( $member );
			}

			$member = IPSMember::buildDisplayData( $member, array( 'signature' => 1, 'customFields' => 1, 'warn' => 1, 'checkFormat' => 1, 'cfLocation' => 'topic', 'photoTagSize' => array( 'thumb', 'small' ) ) );
			
			//-----------------------------------------
			// Add it to the cached list
			//-----------------------------------------
			
			$this->_parsedMembers[ $member['author_id'] ] = $member;
		}
		
		return $this->_parsedMembers[ $member['author_id'] ];
	}
	
	/**
	 * Parse the topic for forum/search results/also tagged view
	 * @param	array
	 */
	public function parseTopicForLineEntry( array $topic )
	{
		/* Set up permissions */
		if ( ! count( $this->permissionsData ) )
		{
			$this->setPermissionData();
		}
		
		$topic['real_tid']		= $topic['tid'];
		
		$topic['_isVisible']	= ( $this->registry->getClass('class_forums')->fetchHiddenTopicType( $topic ) == 'visible' ) ? true : false;
		$topic['_isHidden']		= ( $this->registry->getClass('class_forums')->fetchHiddenTopicType( $topic ) == 'hidden' ) ? true : false;
		$topic['_isDeleted']	= ( $this->registry->getClass('class_forums')->fetchHiddenTopicType( $topic ) == 'sdelete' ) ? true : false;
		$topic['_archiveFlag']	= $this->registry->class_forums->fetchArchiveTopicType( $topic );
		$topic['_isArchived']	= ( in_array( $topic['_archiveFlag'], array( 'working', 'archived', 'restore' ) ) );
		
		/* Member table and topic table overwrite */
		if ( isset( $topic['topic_title'] ) )
		{
			$topic['title'] = $topic['topic_title'];
		}
		
		if ( isset( $topic['topic_posts'] ) )
		{
			$topic['posts'] = $topic['topic_posts'];
		}
		
		if ( isset( $topic['topic_last_post'] ) )
		{
			$topic['last_post'] = $topic['topic_last_post'];
		}
		
		//-----------------------------------------
		// Rebuild SEO title on the fly, if needed
		//-----------------------------------------

		if( ! $topic['title_seo'] AND $topic['tid'] )
		{
			$topic['title_seo']	= IPSText::makeSeoTitle( $topic['title'] );
			
			$this->DB->update( 'topics', array( 'title_seo' => ( $topic['title_seo'] ) ? $topic['title_seo'] : '-' ), 'tid=' . $topic['tid'] );
		}

		//-----------------------------------------
		// Linky pinky!
		//-----------------------------------------
		
		/* We need original _tid in the skin template for linked topics, so we'll reassign here and just use that in the template, rather
			than add a bunch of HTML logic to show _tid for link and tid for regular topics */
		$topic['_tid']			= $topic['tid'];
		$topic['_forum_id']		= $topic['forum_id'];
		
		if ( $topic['state'] == 'link' )
		{
			$t_array				= explode("&", $topic['moved_to']);
			$topic['tid']			= $t_array[0];
			$topic['forum_id']		= $t_array[1];
			$topic['title']			= $topic['title'];
			$topic['views']			= '--';
			$topic['prefix']		= $this->registry->getClass('output')->getTemplate('forum')->topicPrefixWrap( $this->lang->words['pre_moved'] );
		}

		/* Fetch last marking time for this entry */
		$lastMarked = ( $topic['_isArchived'] ) ? IPS_UNIX_TIME_NOW : $this->registry->classItemMarking->fetchTimeLastMarked( array( 'forumID' => $topic['forum_id'], 'itemID' => $topic['tid'], 'itemLastUpdate' => $topic['last_post'] ) );
		
		/* Check against it */
		if ( $topic['poll_state'] AND ( $topic['last_vote'] > $topic['last_post'] ) )
		{
			$topic['_hasUnread'] = ( $lastMarked < $topic['last_vote'] ) ? true : false;
		}
		else
		{
			$topic['_hasUnread'] = ( $lastMarked < $topic['last_post'] ) ? true : false;
		}
		
		/* Determine which link normal or /unread */
		if ( $lastMarked && $topic['posts'] && $topic['_hasUnread'] )
		{
			/* They've seen this topic but not all of it or there have been new */
			$topic['_canJumpToUnread'] = true;
		}
		
		if ( $topic['_hasUnread'] )
		{
			$topic['_unreadUrl'] = $this->registry->output->buildSEOUrl( 'showtopic=' . $topic['tid'] . '&amp;view=getnewpost', 'public', $topic['title_seo'], 'showtopic' );
		}
		
		$topic['_url'] = $this->registry->output->buildSEOUrl( 'showtopic=' . $topic['tid'], 'public', $topic['title_seo'], 'showtopic' );
		
		//-----------------------------------------
		// Yawn
		//-----------------------------------------
		
		$topic['prefix']		= $topic['poll_state']		? $this->registry->getClass('output')->getTemplate('forum')->topicPrefixWrap( $this->lang->words['poll_prefix'] ) : '';
		$topic['folder_img']	= $this->registry->getClass('class_forums')->fetchTopicFolderIcon( $topic, ( $this->memberData['member_id'] and !empty($topic['author_id']) ), ( $topic['_hasUnread'] ? 0 : 1 ) );
		
		if ( $topic['starter_id'] && ! empty($topic['_starter']['member_id']) )
		{
			$topic['starter'] = $this->registry->getClass('output')->getTemplate('global')->userHoverCard( $topic['_starter'] );
		}
		else
		{
			$topic['starter'] = $this->registry->getClass('output')->getTemplate('global')->userHoverCard( array( 'member_id' => 0, 'members_display_name' => $this->settings['guest_name_pre'] . $topic['starter_name'] . $this->settings['guest_name_suf'] ) );
		}
		
		if ( $topic['last_poster_id'] && ! empty($topic['member_id']) )
		{
			$topic['last_poster_name'] = $topic['members_display_name'];
			$topic['last_poster'] = $this->registry->getClass('output')->getTemplate('global')->userHoverCard( $topic );
		}
		else
		{
			$topic['last_poster_name'] = $this->settings['guest_name_pre'] . $topic['last_poster_name'] . $this->settings['guest_name_suf'];
			$topic['last_poster']      = $this->registry->getClass('output')->getTemplate('global')->userHoverCard( array( 'member_id' => 0, 'members_display_name' => $topic['last_poster_name'] ) );
		}
		
		//-----------------------------------------
		// Pages 'n' posts
		//-----------------------------------------
		
		$pages			= 1;
		$topic['pages']	= "";
		
		if ( $this->registry->class_forums->canQueuePosts( $topic['fourm_id'] ) )
		{
			$topic['posts'] += intval($topic['topic_queuedposts']);
		}
		
		if( $this->permissionsData['softDeleteSee'] )
		{
			$topic['posts'] += intval($topic['topic_deleted_posts']);
		}
		
		if ($topic['posts'])
		{
			if ( (($topic['posts'] + 1) % $this->settings['display_max_posts']) == 0 )
			{
				$pages = ($topic['posts'] + 1) / $this->settings['display_max_posts'];
			}
			else
			{
				$number = ( ($topic['posts'] + 1) / $this->settings['display_max_posts'] );
				$pages = ceil( $number);
			}
		}
		
		if ( $pages > 1 )
		{
			for ( $i = 0 ; $i < $pages ; ++$i )
			{
				$real_no = $i * $this->settings['display_max_posts'];
				$page_no = $i + 1;
				
				if ( $page_no == 4 and $pages > 4 )
				{
					$topic['pages'][] = array( 'last'   => 1,
					 					       'st'     => ($pages - 1) * $this->settings['display_max_posts'],
					  						   'page'   => $pages,
					 							'total' => $pages );
					break;
				}
				else
				{
					$topic['pages'][] = array( 'last' => 0,
											   'st'   => $real_no,
											   'page' => $page_no,
											 	'total' => $pages );
				}
			}
		}
		
		$topic['_hasqueued'] = 0;

		$mod	= $this->memberData['forumsModeratorData'] ? $this->memberData['forumsModeratorData'] : array();
		
		if ( ( $this->memberData['g_is_supmod'] or
				($mod[ $topic['forum_id'] ]['post_q'] AND $mod[ $topic['forum_id'] ]['post_q'] == 1) ) and 
				( $topic['topic_queuedposts'] ) 
			)
		{
			$topic['_hasqueued'] = 1;
		}
		
		//-----------------------------------------
		// Topic rating
		//-----------------------------------------
		
	    $topic['_rate_img']   = '';
	    
	    if ( !empty($this->forum['forum_allow_rating']) )
		{
			if ( $topic['topic_rating_total'] )
			{
				$topic['_rate_int'] = round( $topic['topic_rating_total'] / $topic['topic_rating_hits'] );
			}
			
			//-----------------------------------------
			// Show image?
			//-----------------------------------------
			
			if ( ( $topic['topic_rating_hits'] >= $this->settings['topic_rating_needed'] ) AND ( $topic['_rate_int'] ) )
			{
				$topic['_rate_img']  = $this->registry->getClass('output')->getTemplate('forum')->topic_rating_image( $topic['_rate_int'] );
			}
		}
		
		//-----------------------------------------
		// Already switched on?
		//-----------------------------------------
		
		if ( $this->memberData['is_mod'] )
		{
			if ( $this->request['selectedtids'] )
			{
				if ( strstr( ','.$this->request['selectedtids'].',', ','.$topic['tid'].',' ) )
				{
					$topic['tidon'] = 1;
				}
				else
				{
					$topic['tidon'] = 0;
				}
			}
		}
		
		/* Tags */
		if ( ! empty( $topic['tag_cache_key'] ) )
		{
			$topic['tags'] = $this->registry->tags->formatCacheJoinData( $topic );
		}
		
		/* Answered */
		$topic['_answeredPid'] = $this->getTopicAnswerPid( $topic );
		
		return $topic;
	}
	
	/**
	 * Builds an array of post data for output
	 *
	 * @param	array	$row	Array of post data
	 * @return	array
	 */
	public function parsePost( array $post )
	{
		/* Init */
		$topicData      = $this->getTopicData();
		$forumData      = $this->registry->getClass('class_forums')->getForumById( $topicData['forum_id'] );
		$permissionData = $this->getPermissionData();

		/* Start memory debug */
		$_NOW   = IPSDebug::getMemoryDebugFlag();
		$poster = array();
		
		/* Bitwise options */
		$_tmp = IPSBWOptions::thaw( $post['post_bwoptions'], 'posts', 'forums' );

		if ( count( $_tmp ) )
		{
			foreach( $_tmp as $k => $v )
			{
				$post[ $k ] = $v;
			}
		}

		/* Is this a member? */
		if ( $post['author_id'] != 0 )
		{
			$poster = $this->parseMember( $post );
		}
		else
		{
			/* Sort out guest */
			$post['author_name']				= $this->settings['guest_name_pre'] . $post['author_name'] . $this->settings['guest_name_suf'];
			
			$poster								= IPSMember::setUpGuest( $post['author_name'] );
			$poster['members_display_name']		= $post['author_name'];
			$poster['_members_display_name']	= $post['author_name'];
			$poster['custom_fields']			= "";
			$poster['warn_img']					= "";
			$poster								= IPSMember::buildProfilePhoto( $poster );
		}
		
		/* Memory debug */
		IPSDebug::setMemoryDebugFlag( "PID: ".$post['pid'] . " - Member Parsed", $_NOW );
		
		/* Update permission */
		$this->registry->getClass('class_forums')->setMemberData( $this->getMemberData() );
		$permissionData['softDelete'] = $this->registry->getClass('class_forums')->canSoftDeletePosts( $topicData['forum_id'], $post );
		
		/* Soft delete */
		$post['_softDelete']        = ( $post['pid'] != $topicData['topic_firstpost'] ) ? $permissionData['softDelete'] : FALSE;
		$post['_softDeleteRestore'] = $permissionData['softDeleteRestore'];
		$post['_softDeleteSee']     = $permissionData['softDeleteSee'];
		$post['_softDeleteReason']  = $permissionData['softDeleteReason'];
		$post['_softDeleteContent'] = $permissionData['softDeleteContent'];
		
		$post['_isVisible']		   = ( $this->registry->getClass('class_forums')->fetchHiddenType( $post ) == 'visible' ) ? true : false;
		$post['_isHidden']		   = ( $this->registry->getClass('class_forums')->fetchHiddenType( $post ) == 'hidden' ) ? true : false;
		$post['_isDeleted']		   = ( $this->registry->getClass('class_forums')->fetchHiddenType( $post ) == 'sdelete' ) ? true : false;
		
		/* Answered post */
		try
		{
			$post['_isMarkedAnswered']  = ( $this->postIsAnswer( $post, $topicData ) ) ? true : false;
		}
		catch( Exception $e )
		{
			$post['_isMarkedAnswered']	= false;
		}

		$post['_canMarkUnanswered'] = ( $post['_isMarkedAnswered'] === true && $this->canUnanswerTopic( $topicData ) ) ? true : false;
		$post['_canAnswer']		    = ( $post['_isMarkedAnswered'] === false && $this->canAnswerTopic( $topicData ) ) ? true : false;
		
		$post['PermalinkUrlBit']    = '';
		
		/* Queued */
		if ( $topicData['topic_firstpost'] == $post['pid'] and ( $post['_isHidden'] OR $topicData['_isHidden'] ) )
		{
			$post['queued']    = 1;
			$post['_isHidden'] = true;
		}
		
		if ( $topicData['topic_queuedposts'] || $topicData['topic_deleted_posts'] )
		{
			if ( $topicData['topic_queuedposts'] && $topicData['Perms']['canQueuePosts'] )
			{
				/* We have hidden data that is viewable */
				$post['PermalinkUrlBit'] = '&amp;p=' . $post['pid'];
			}
			
			if ( $topicData['topic_deleted_posts'] && $post['_softDeleteSee'] )
			{
				/* We have hidden data that is viewable */
				$post['PermalinkUrlBit'] = '&amp;p=' . $post['pid'];
			}
		}
		
		/* Edited stuff */
		$post['edit_by'] = "";
		
		if ( ( $post['append_edit'] == 1 ) and ( $post['edit_time'] != "" ) and ( $post['edit_name'] != "" ) )
		{
			$e_time = $this->registry->class_localization->getDate( $post['edit_time'] , 'LONG' );
			
			$post['edit_by'] = sprintf( $this->lang->words['edited_by'], $post['edit_name'], $e_time );
		}
		
		/* Now parse the post */
		if ( ! isset($post['cache_content']) OR ! $post['cache_content'] )
		{
			$_NOW2   = IPSDebug::getMemoryDebugFlag();
			
			/* Grab the parser file */
			if ( $this->_parser === null )
			{
				/* Load parser */
				$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/text/parser.php', 'classes_text_parser' );
				$this->_parser = new $classToLoad();
			}
						
			/* set up parser */
			$this->_parser->set( array( 'memberData'     => array( 'member_id'       => $post['member_id'],
																   'member_group_id' => $post['member_group_id'],
																   'mgroup_others'   => $post['mgroup_others'] ),
								 		'parseBBCode'    => $forumData['use_ibc'],
								 		'parseHtml'      => ( $forumData['use_html'] and $poster['g_dohtml'] and $post['post_htmlstate'] ) ? 1 : 0,
								 		'parseEmoticons' => $post['use_emo'],
								 		'parseArea'	  	 => 'topics' ) );
			
			$post['post']	= $this->_parser->display( $post['post'] );
					
			IPSDebug::setMemoryDebugFlag( "topics::parsePostRow - bbcode parse - Completed", $_NOW2 );
			
			IPSContentCache::update( $post['pid'], 'post', $post['post'] );
		}
		else
		{
			$post['post'] = '<!--cached-' . gmdate( 'r', $post['cache_updated'] ) . '-->' . $post['cache_content'];
		}
		
		/* Buttons */
		$post['_can_delete'] = $post['pid'] != $topicData['topic_firstpost'] 
							  ? $this->canDeletePost( $post ) 
							  : FALSE;		
		$post['_can_edit']   = $this->canEditPost( $post );
		$post['_show_ip']	 = $this->canSeeIp();
		$post['_canReply']   = ( $this->getReplyStatus() == 'reply' ) ? true : false;
		
		/* Signatures */
		$post['signature'] = "";
		
		if ( ! empty( $poster['signature'] ) )
		{
			if ( $post['use_sig'] == 1 )
			{
				if ( ! $this->memberData['view_sigs'] || ( $poster['author_id'] && $this->memberData['member_id'] && ! empty( $this->member->ignored_users[ $poster['author_id'] ]['ignore_signatures'] ) && IPSMember::isIgnorable( $poster['member_group_id'], $poster['mgroup_others'] ) ) )
				{
					$post['signature'] = '<!--signature.hidden.' . $post['pid'] . '-->';
				}
				else
				{
					$post['signature'] = $this->registry->output->getTemplate( 'global' )->signature_separator( $poster['signature'], $poster['author_id'], IPSMember::isIgnorable( $poster['member_group_id'], $poster['mgroup_others'] ) );
				}
			}
		}
		
		$post['forum_id'] = $topicData['forum_id'];		
		
		/* Reputation */
		if ( $this->settings['reputation_enabled'] AND ! $this->isArchived( $topicData ) )
		{ 
			/* Load the class */
			if ( ! $this->registry->isClassLoaded( 'repCache' ) )
			{
				$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/class_reputation_cache.php', 'classReputationCache' );
				$this->registry->setClass( 'repCache', new $classToLoad() );
			}
			
			$this->memberData['_members_cache']['rep_filter'] = isset( $this->memberData['_members_cache']['rep_filter'] ) ? $this->memberData['_members_cache']['rep_filter'] : '*';
			
			$post['pp_reputation_points']	= $post['pp_reputation_points'] ? $post['pp_reputation_points'] : 0;
			$post['has_given_rep']			= $post['has_given_rep'] ? $post['has_given_rep'] : 0;
			$post['rep_points']				= $this->registry->repCache->getRepPoints( array( 'app' => 'forums', 'type' => 'pid', 'type_id' => $post['pid'], 'rep_points' => $post['rep_points'] ) );
			$post['_repignored']			= 0;
			
			if ( ! ( $this->settings['reputation_protected_groups'] && 
				    in_array( $this->memberData['member_group_id'], explode( ',', $this->settings['reputation_protected_groups'] ) ) 
				   ) &&
			 	$this->memberData['_members_cache']['rep_filter'] !== '*' 
			)
			{
				if ( $this->settings['reputation_show_content'] && $post['rep_points'] < $this->memberData['_members_cache']['rep_filter'] && $this->settings['reputation_point_types'] != 'like' )
				{
					$post['_repignored'] = 1;
				}
			}
			
			if ( $this->registry->repCache->isLikeMode() )
			{
				$post['like'] = $this->registry->repCache->getLikeFormatted( array( 'app' => 'forums', 'type' => 'pid', 'id' => $post['pid'], 'rep_like_cache' => $post['rep_like_cache'] ) );
			}
		}
		
		/* Ignore stuff */
		$post['_ignored']	= 0;

		if ( $post['author_id'] && isset( $topicData['ignoredUsers'] ) && is_array( $topicData['ignoredUsers'] ) && count( $topicData['ignoredUsers'] ) )
		{ 
			if( in_array( $post['author_id'], $topicData['ignoredUsers'] ) )
			{
				if ( ! strstr( $this->settings['cannot_ignore_groups'], ','.$post['member_group_id'].',' ) )
				{
					$post['_ignored'] = 1;
				}
			}
		}
		
		/* AD Code */
		$post['_adCode']	= '';

		if ( $this->registry->getClass('IPSAdCode')->userCanViewAds() && !$this->getTopicData('adCodeSet') && !IPS_IS_AJAX )
		{
			$post['_adCode'] = $this->registry->getClass('IPSAdCode')->getAdCode('ad_code_topic_view_code');
			if ( $post['_adCode'] )
			{
				$this->setTopicData( 'adCodeSet', true );
			}
		}
		
		/* Memory debug */
		IPSDebug::setMemoryDebugFlag( "PID: ".$post['pid']. " - Completed", $_NOW );
		
		/* Excerpt */
		$post['_excerpt'] = IPSText::truncate( str_replace( array( '<br />', '<br>', "\n", '</p>', '<p>' ), ' ', $post['post'] ), 500 );
		
		return array( 'post' => $post, 'author' => $poster );
	}
	
	/**
	 * Add the recent post
	 * @param array $post
	 */
	public function addRecentPost( $post )
	{
		if ( $post['post_author_id'] && $post['post_id'] && $post['post_topic_id'] && $post['post_forum_id'] )
		{
			$this->DB->insert( 'forums_recent_posts', array( 'post_id'        => intval( $post['post_id'] ),
															 'post_topic_id'  => intval( $post['post_topic_id'] ),
															 'post_forum_id'  => intval( $post['post_forum_id'] ),
															 'post_author_id' => intval( $post['post_author_id'] ),
															 'post_date'      => intval( $post['post_date'] )
							  )								);
		}
	}
	
	/**
	 * Delete a recent post
	 * @param array $post
	 */
	public function deleteRecentPost( $where )
	{
		$query = array();
		
		foreach( array( 'post_id', 'post_topic_id', 'post_forum_id', 'post_author_id', 'post_date' ) as $k )
		{
			if ( ! empty( $where[ $k ] ) )
			{
				$query[] = ( is_array( $where[ $k ] ) ) ? $k . ' IN (' . implode( ',', $where[ $k ] ) . ')' : $k . '=' . intval( $where[ $k ] );
			}
		}
		
		if ( count( $query ) )
		{
			$this->DB->delete( 'forums_recent_posts', implode( ' AND ', $query ) );
		}
	}
	
	/**
	 * Update a recent post
	 * @param array $post
	 */
	public function updateRecentPost( $update, $where )
	{
		$query  = array();
		
		foreach( array( 'post_id', 'post_topic_id', 'post_forum_id', 'post_author_id', 'post_date' ) as $k )
		{
			if ( ! empty( $where[ $k ] ) )
			{
				$query[] = ( is_array( $where[ $k ] ) ) ? $k . ' IN (' . implode( ',', $where[ $k ] ) . ')' : $k . '=' . intval( $where[ $k ] );
			}
		}
		
		if ( count( $update ) )
		{
			$this->DB->update( 'forums_recent_posts', $update, implode( ' AND ', $query ) );
		}
	}
	
	/**
	 * Restore posts
	 * @param array $where
	 */
	public function restoreRecentPost( $where )
	{
		$date  = IPS_UNIX_TIME_NOW - 86400;
		$PRE   = trim(ipsRegistry::dbFunctions()->getPrefix());
		$query = array();
		
		$remap = array( 'post_id'        => 'pid',
						'post_topic_id'  => 'topic_id',
						'post_author_id' => 'post_author_id' );
		
		foreach( array( 'post_id', 'post_topic_id', 'post_forum_id', 'post_author_id' ) as $k )
		{
			if ( ! empty( $where[ $k ] ) )
			{
				$query[] = ( is_array( $where[ $k ] ) ) ? $remap[ $k ] . ' IN (' . implode( ',', $where[ $k ] ) . ')' : $remap[ $k ] . '=' . intval( $where[ $k ] );
			}
		}
		
		if ( count( $query ) )
		{
			$this->DB->loadCacheFile( IPSLib::getAppDir('forums') . '/sql/' . ips_DBRegistry::getDriverType() . '_topics_queries.php', 'topics_sql_queries' );
			
			$this->DB->buildFromCache( 'restoreRecentPost', array( 'query' => $query, 'date' => $date ), 'topics_sql_queries' );
			$this->DB->allow_sub_select = true;
			$this->DB->execute();
		}
	}
	
	/**
	 * Returns either a field name from post_db or from archive_db
	 * @param string $field
	 * @param string $isArchive
	 */
	public function getPostTableField( $field, $isArchive )
	{
		if ( ! $isArchive )
		{
			if ( $field == '_prefix_' )
			{
				return '';
			}
			
			if ( $field == '_table_' )
			{
				return 'posts';
			}
			
			return $field;
		}
		else
		{
			if ( $field == '_table_' )
			{
				return 'forums_archive_posts';
			}
			
			return $this->fields[ $field ];
		}
	}
	
	/**
	 * Take an archive row and returned native friendly array
	 * @param array $post
	 * @return array
	 */
	public function archivePostToNativeFields( $post )
	{
		$native  = array( 'pid'           	 => intval( $post['archive_id'] ),
						  'author_id'    	 => intval( $post['archive_author_id'] ),
						  'author_name'  	 => $post['archive_author_name'],
					      'ip_address'   	 => $post['archive_ip_address'],
						  'post_date' 	     => intval( $post['archive_content_date'] ),
						  'post'		 	 => $post['archive_content'],
						  'queued'	 	     => $post['archive_queued'],
						  'topic_id'     	 => intval( $post['archive_topic_id'] ),
						  'new_topic'     	 => intval( $post['archive_is_first'] ),
						  'post_bwoptions'   => $post['archive_bwoptions'],
						  'post_key'   	 	 => $post['archive_attach_key'],
						  'post_htmlstate'   => $post['archive_html_mode'],
						  'use_sig'   		 => $post['archive_show_signature'],
						  'use_emo'   		 => $post['archive_show_emoticons'],
						  'append_edit'   	 => $post['archive_show_edited_by'],
						  'edit_time'   	 => $post['archive_edit_time'],
						  'edit_name'   	 => $post['archive_edit_name'],
						  'post_edit_reason' => $post['archive_edit_reason'] );
		
		return array_merge( $post, $native );
	}
	
	/**
	 * Get fields
	 * @return array
	 */
	public function getPostTableFields()
	{
		return $this->fields;
	}
	
	/**
	 * Coverts a page number to a start value
	 * @param int $page
	 */
	public function pageToSt( $page )
	{
		$page = intval( $page );
		
		return ( $page > 1 ) ? ( ( $page - 1 ) * $this->settings['display_max_posts'] ) : 0;
	}
	
	/**
	 * Coverts a st value to a page number
	 * @param int $page
	 */
	public function stToPage( $st )
	{
		$st = intval( $st );
	
		return ( $st > 0 ) ? ( ceil( $st / $this->settings['display_max_posts'] ) + 1 ) : 1;
	}
	
	/**
	 * Returns page number of post
	 * @param mixed $post
	 * @return int
	 */
	public function postToPage( $post )
	{
		//-----------------------------------------
		// Get post...
		//-----------------------------------------
		
		$post = is_array( $post ) ? $post : $this->getPostById( $post, false, array( 'onlyViewable' => true ) );

		/* Get the correct page number */
		$_perms = array( 'visible' );
					
		if ( $this->registry->getClass('class_forums')->canSeeSoftDeletedPosts( $post['forum_id'] ? $post['forum_id'] : false ) )
		{
			$_perms[] = 'sdelete';
		}
		
		if ( $this->registry->getClass('class_forums')->canQueuePosts( $post['forum_id'] ? $post['forum_id'] : false ) )
		{
			$_perms[] = 'hidden';
		}
		
		$query = $this->registry->class_forums->fetchPostHiddenQuery( $_perms );
				
		$sort_value = $post['pid'];
		$sort_field = ($this->settings['post_order_column'] == 'pid') ? 'pid' : 'post_date';
		
		if( $sort_field == 'post_date' )
		{
			if( $post['post_date'] )
			{
				$sort_value	= $post['post_date'];
			}
			else
			{
				$date = $this->DB->buildAndFetch( array( 'select' => 'post_date', 'from' => 'posts', 'where' => 'pid=' . $post['pid'] ) );

				$sort_value = $date['post_date'];
			}
		}

		$this->DB->build( array( 'select' => 'COUNT(*) as posts', 'from' => 'posts', 'where' => "topic_id={$post['topic_id']} AND {$sort_field} <=" . intval( $sort_value ) . ' AND ' . $query ) );										
		$this->DB->execute();
		
		$cposts = $this->DB->fetch();
		
		if ( (($cposts['posts']) % $this->settings['display_max_posts']) == 0 )
		{
			$pages = ($cposts['posts']) / $this->settings['display_max_posts'];
		}
		else
		{
			$number = ( ($cposts['posts']) / $this->settings['display_max_posts'] );
			$pages = ceil($number);
		}
		
		$st = ($pages - 1) * $this->settings['display_max_posts'];
		
		if( $this->settings['post_order_sort'] == 'desc' )
		{
			$st = (ceil(($cposts['posts']/$this->settings['display_max_posts'])) - $pages) * $this->settings['display_max_posts'];
		}

		return intval( $this->registry->getClass('topics')->stToPage( $st ) );
	}	
	
	/**
	 * Mark a topic as 'answered'
	 * @param mixed $topic
	 * @param mixed $post
	 */
	public function answerTopicSingle( $post, $topic=null )
	{
		if ( is_numeric( $post ) )
		{
			$post = $this->getPostById( $post );
		}
		
		if ( $topic === null )
		{
			$topic = $post['topic_id'];
		}
		
		if ( is_numeric( $topic ) )
		{
			$topic = $this->getTopicById( $topic );
		}
		
		if ( ! $topic['tid'] || ! $post['pid'] )
		{
			throw new Exception( 'missing_data' );
		}
		
		if ( $this->canAnswerTopic( $topic ) !== true )
		{
			throw new Exception( 'no_permission' );
		}
		
		/* Remove flag for all other posts in this topic */
		$sql   = IPSBWOptions::sql( 'bw_post_answered', 'post_bwoptions', 'posts', 'forums', 'invert' );
		$where = IPSBWOptions::sql( 'bw_post_answered', 'post_bwoptions', 'posts', 'forums', 'has' );
		
		$this->DB->update( 'posts', 'post_bwoptions=' . $sql, 'topic_id=' . $topic['tid'] . ' AND ' . $where, false, true );
		
		/* Update post */
		$post['bw_post_answered'] = 1;
		
		$this->DB->update( 'posts', array( 'post_bwoptions' => IPSBWOptions::freeze( $post, 'posts', 'forums' ) ), 'pid=' . $post['pid'] );
		
		/* Update topic */
		$this->DB->update( 'topics', array( 'topic_answered_pid' => $post['pid'] ), 'tid=' . $topic['tid'] );
		
		return true;
	}
	
	/**
	 * Mark a topic as 'unAnswered'
	 * @param mixed $topic
	 * @param mixed $post
	 */
	public function unAnswerTopicSingle( $post, $topic=null )
	{
		if ( is_numeric( $post ) )
		{
			$post = $this->getPostById( $post );
		}
	
		if ( $topic === null )
		{
			$topic = $post['topic_id'];
		}
	
		if ( is_numeric( $topic ) )
		{
			$topic = $this->getTopicById( $topic );
		}
	
		if ( ! $topic['tid'] || ! $post['pid'] )
		{
			throw new Exception( 'missing_data' );
		}
	
		if ( $this->canAnswerTopic( $topic ) !== true )
		{
			throw new Exception( 'no_permission' );
		}
	
		/* Remove flag this post */
		$sql   = IPSBWOptions::sql( 'bw_post_answered', 'post_bwoptions', 'posts', 'forums', 'invert' );
		$where = IPSBWOptions::sql( 'bw_post_answered', 'post_bwoptions', 'posts', 'forums', 'has' );
	
		$this->DB->update( 'posts', 'post_bwoptions=' . $sql, 'pid=' . $post['pid'] . ' AND ' . $where, false, true );
		
		/* Update topic */
		$this->DB->update( 'topics', array( 'topic_answered_pid' => 0 ), 'tid=' . $topic['tid'] );
		
		return true;
	}
	
	
	/**
	 * Get the answered PID (or false if nothing)
	 * @param mixed $topic
	 */
	public function getTopicAnswerPid( $topic )
	{
		if ( is_numeric( $topic ) )
		{
			$topic = $this->getTopicById( $topic );
		}
	
		if ( $this->registry->class_forums->answerTopicsEnabled( $topic['forum_id'] ) !== true )
		{
			return false;
		}
	
		return ( ! empty( $topic['topic_answered_pid'] ) ) ? $topic['topic_answered_pid'] : false;
	}
	/**
	 * Is this post the answer?
	 * @param mixed $post
	 * @param mixed $topic
	 */
	public function postIsAnswer( $post, $topic=null )
	{
		if ( is_numeric( $post ) )
		{
			$post = $this->getPostById( $post );
		}
		
		if ( $topic === null )
		{
			$topic = $post['topic_id'];
		}
		
		if ( is_numeric( $topic ) )
		{
			$topic = $this->getTopicById( $topic );
		}
		
		if ( ! $topic['tid'] || ! $post['pid'] )
		{
			throw new Exception( 'missing_data' );
		}
		
		if ( $this->registry->class_forums->answerTopicsEnabled( $topic['forum_id'] ) !== true )
		{
			return false;
		}
		
		return ( ! empty( $post['bw_post_answered'] ) ) ? true : false;
	}
	
	/**
	 * Can mark a post as answered
	 * @param	mixed	Array or int
	 * @return boolean
	 */
	public function canAnswerTopic( $topic )
	{
		if ( is_numeric( $topic ) )
		{
			$topic = $this->getTopicById( $topic );
		}
	
		if ( $this->registry->class_forums->answerTopicsEnabled( $topic['forum_id'] ) !== true )
		{
			return false;
		}
		
		$forumData = $this->registry->class_forums->getForumById( $topic['forum_id'] );
		
		if ( $this->memberData['g_is_supmod'] || ( $forumData['bw_enable_answers_member'] && $topic['starter_id'] && ( $topic['starter_id'] == $this->memberData['member_id'] ) ) )
		{
			return true;
		}
		else
		{
			$moderator = $this->getModeratorData();
			
			return ( $moderator['bw_can_toggle_answered_post'] ) ? true : false;
		}
	}
	
	/**
	 * Can mark a post as unanswered
	 * @param	mixed	Array or int
	 * @return boolean
	 */
	public function canUnanswerTopic( $topic )
	{
		if ( is_numeric( $topic ) )
		{
			$topic = $this->getTopicById( $topic );
		}
	
		if ( $this->registry->class_forums->answerTopicsEnabled( $topic['forum_id'] ) !== true )
		{
			return false;
		}
	
		$forumData = $this->registry->class_forums->getForumById( $topic['forum_id'] );
		
		if ( $this->memberData['g_is_supmod'] || ( $forumData['bw_enable_answers_member'] && $topic['starter_id'] && ( $topic['starter_id'] == $this->memberData['member_id'] ) ) )
		{
			return true;
		}
		else
		{
			$moderator = $this->getModeratorData();
			
			return ( $moderator['bw_can_toggle_answered_post'] ) ? true : false;
		}
	}
	
	/**
	* @param	mixed	Array or int
	 * @return boolean
	 */
	public function canRateAnsweredPost( $topic )
	{
		if ( is_numeric( $topic ) )
		{
			$topic = $this->getTopicById( $topic );
		}
		
		if ( $this->registry->class_forums->answerTopicsEnabled( $topic['forum_id'] ) !== true )
		{
			return false;
		}
		
		if ( $this->getReplyStatus( $topic ) == 'reply' )
		{
			return true;
		}
	}
	
	/**
	 * Set post filters
	 * Takes user input and cleans it up a bit
	 *
	 * @param	array		Incoming filters
	 * @return	array
	 */
	protected function _setPostFilters( $filters )
	{
		$filters['sortOrder']		= ( isset( $filters['sortOrder'] ) )	? $filters['sortOrder']	: '';
		$filters['sortField']		= ( isset( $filters['sortField'] ) )	? $filters['sortField']	: '';
		$filters['offset']			= ( isset( $filters['offset'] ) )		? $filters['offset']	: '';
		$filters['limit']			= ( isset( $filters['limit'] ) )		? $filters['limit']		: '';
		$filters['isVisible']		= ( isset( $filters['isVisible'] ) )	? $filters['isVisible']	: '';
		
		switch( $filters['sortOrder'] )
		{
			default:
			case 'desc':
			case 'descending':
			case 'z-a':
				$filters['sortOrder'] = 'desc';
			break;
			case 'asc':
			case 'ascending':
			case 'a-z':
				$filters['sortOrder'] = 'asc';
			break;
		}
		
		/* Do some set up */
		switch( $filters['sortField'] )
		{
			case 'date':
			case 'time':
				$filters['sortField']  = 'post_date';
			break;
			case 'pid':
			case 'id':
				$filters['sortField']  = 'pid';
			break;
		}
	
		
		/* Others */
		$filters['offset']       = intval( $filters['offset'] );
		$filters['limit']        = intval( $filters['limit'] );
		$filters['unixCutOff']   = ( ! empty( $filters['unixCutOff'] ) ) ? intval( $filters['unixCutOff'] ) : 0;
		
		/* So we don't have to do this twice */
		$filters['_cleaned']   = true;
		
		return $filters;
	}
	
	/**
	 * Set topic filters
	 * Takes user input and cleans it up a bit
	 *
	 * @param	array		Incoming filters
	 * @return	array
	 */
	protected function _setTopicFilters( $filters )
	{
		$filters['sortOrder']		= ( isset( $filters['sortOrder'] ) )		? $filters['sortOrder']	: '';
		$filters['sortField']		= ( isset( $filters['sortField'] ) )		? $filters['sortField']	: '';
		$filters['offset']			= ( isset( $filters['offset'] ) )			? $filters['offset']	: '';
		$filters['limit']			= ( isset( $filters['limit'] ) )			? $filters['limit']		: '';
		$filters['isVisible']		= ( isset( $filters['isVisible'] ) )		? $filters['isVisible']	: '';
		
		switch( $filters['sortOrder'] )
		{
			default:
			case 'desc':
			case 'descending':
			case 'z-a':
				$filters['sortOrder'] = 'desc';
			break;
			case 'asc':
			case 'ascending':
			case 'a-z':
				$filters['sortOrder'] = 'asc';
			break;
		}
		
		/* Do some set up */
		switch( $filters['sortField'] )
		{
			case 'date':
			case 'time':
			case 'start_date':
				$filters['sortField']  = 'start_date';
			break;
			case 'lastDate':
			case 'lastTime':
			case 'last_post':
				$filters['sortField']  = 'last_post';
			break;
			case 'tid':
			case 'id':
				$filters['sortField']  = 'tid';
			break;
		}
	
		
		/* Others */
		$filters['offset']       = intval( $filters['offset'] );
		$filters['limit']        = intval( $filters['limit'] );
		$filters['unixCutOff']   = ( ! empty( $filters['unixCutOff'] ) ) ? intval( $filters['unixCutOff'] ) : 0;
		
		/* So we don't have to do this twice */
		$filters['_cleaned']   = true;
		
		return $filters;
	}
	
}