<?php
/**
 * <pre>
 * Status Updates Class
 * Holds various functions
 * Last Updated: $Date: 2013-10-16 12:57:41 -0400 (Wed, 16 Oct 2013) $
 * </pre>
 *
 * @author		$author$
 * @copyright	(c) 2001 - 2009 Invision Power Services, Inc.
 * @license		http://www.invisionpower.com/company/standards.php#license
 * @package		IP.Board
 * @author		MattMecham
 * @link		http://www.invisionpower.com
 * @version		$Rev: 12380 $
 *
 * Example: Create new status update
 * <code>
 * $status = new memberStatus( ipsRegistry::instance() )
 *
 * $status->setAuthor( $this->memberData );
 *
 * if ( $status->canCreate() )
 * {
 *		$status->setContent("This is my status update");
 *		$status->create();
 * }
 * </code>
 *
 * Example: Reply to a status update
 * <code>
 * $status = new memberStatus( ipsRegistry::instance() )
 *
 * $status->setAuthor( $this->memberData );
 * $status->setStatusData( $status->fetchMemberLatest( $memberId ) );
 *
 * if ( $status->canReply() )
 * {
 *		$status->setContent("This is my reply");
 *		$status->reply();
 * }
 *
 * Both valid
 * $this->setStatusData( $arrayOfData );
 * $this->setStatusData( $statusId );
 *
 * $this->setReplyData( $arrayOfData );
 * $this->setReplyData( $replyId );
 * </code>
 */

/**
 * Rules:
 * Member and Super Mods can create and delete a member's status
 * Super mods and members NOT banned, restricted posting, moderated posting, ignored by member can reply.
 * Super mods, Reply owner and status owner can delete a reply
 */
class memberStatus
{
	/**
	 * Registry Object Shortcuts
	 *
	 * @var	object
	 */
	protected $registry;
	protected $DB;
	protected $settings;
	protected $request;
	protected $lang;
	protected $member;
	protected $memberData;
	protected $cache;
	protected $caches;
	
	/**
	 * Status set via status Id entry
	 *
	 * @var		array
	 */
	protected $_statusData			= array();
	
	/**
	 * Owner data of status set via status Id entry
	 *
	 * @var		array
	 */
	protected $_statusOwner			= array();
	
	/**
	 * Bypass all permission checks
	 *
	 * Use with care, this will allow the class to be used within an API
	 *
	 * @var		boolean
	 */
	protected $_bypassPermChecks = false;
	
	protected $_statusCount      = 0;
	/**
	 * Allowed items to be saved in the get/set array
	 *
	 * @var		array
	 */
	protected $_internalData		= array( 'IsApproved' => 1, 'IsImport' => 0, 'Creator' => 'ipb', 'ExternalUpdates' => array() );
	protected $_allowedInternalData	= array( 'Author',
										   'Friend',
										   'StatusData',
										   'StatusOwner',
										   'IsImport',
										   'Creator',
										   'ReplyData',
										   'ReplyOwner',
										   'ExternalUpdates',
										   'IsApproved',
										   'Content' );
	
	/**
	 * CONSTRUCTOR
	 *
	 * @param	object	Registry
	 * @return	@e void
	 */
	public function __construct( ipsRegistry $registry )
	{
		/* Make registry 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();
		
		/* Just to be sure */
		$this->setBypassPermissionCheck( false );
		
		/* Just for now */
		$this->settings['su_parse_url']   = 1;
		$this->settings['tc_parse_tags']  = 1;
		$this->settings['tc_parse_names'] = 1;
	}
	
	/**
	 * Gets the count stored by 'fetch'
	 * @return number
	 */
	public function getStatusCount()
	{
		return intval( $this->_statusCount );
	}
	
	/**
	 * Set the bypass permission flag
	 *
	 * @param	boolean
	 * @return	@e void
	 */
	public function setBypassPermissionCheck( $bool=false )
	{
		$this->_bypassPermChecks = ( $bool === true ) ? true : false;
	}
	
	/**
	 * Master check to see if statuses are enabled globally
     *
     * @return	boolean
     */
    public function isEnabled()
    {
    	return ( $this->settings['su_enabled'] ) ? TRUE : FALSE;
    }
    
    /**
	 * Is status locked?
	 *
	 * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
	 * @return	bool
	 */
	public function isLocked( $status=null )
	{
		$status  = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
		
		if ( $status['status_replies'] >= $this->settings['su_max_replies'] )
		{
			return 3;
		}
		
		return ( $status['status_is_locked'] ) ? $status['status_is_locked'] : 0;
	}
	
    /**
     * Fetch status actions
     * Default filters are last 10 status actions from everyone sorted in desc date order.
     * 'not_theirs' pulls all actions that are not on their own statuses
     * @param	mixed	[Array of member data OR member ID INT for member updating their status - will use ->getAuthor() if null]
     * @param	array	Array of sort/filter data ( member_id [int], status_id [int], offset [int], limit [int], unix_cutoff [int], sort_dir [asc,desc], sort_field [string], 'not_theirs' [0,1] )
     */
    public function fetchActions( $author=null, $filters=array() )
    {
    	$author   = ( $author === null ) ? $this->getAuthor() : ( is_array( $author ) ? $author : $this->setAuthor( intval( $author ) ) );
    	$where    = array();
    	$actions  = array();
    	$mids     = array();
    	
    	if ( isset( $filters['member_id'] ) )
    	{
    		$where[] = "s.action_member_id=" . intval( $filters['member_id'] );
    	}
    	
    	if ( isset( $filters['unix_cutoff'] ) )
    	{
    		$where[] = "s.action_date >=" . intval( $filters['unix_cutoff'] );
    	}
    	
    	if ( isset( $filters['status_id'] ) )
    	{
    		$where[] = "s.action_status_id =" . intval( $filters['status_id'] );
    	}
    	
    	if ( isset( $filters['not_theirs'] ) AND isset( $filters['member_id'] ) AND $filters['not_theirs'] )
    	{
    		$where[] = "s.action_status_owner !=" . intval( $filters['member_id'] );
    	}
    	
    	if ( isset( $filters['custom'] ) )
    	{
    		$where[] = "s.action_custom =" . intval( $filters['custom'] );
    	}
    	
    	$sort_dir   = ( $filters['sort_dir'] == 'asc' ) ? 'asc' : 'desc';
    	$sort_field = ( isset( $filters['sort_field'] ) ) ? $filters['sort_field'] : 'action_date';
    	$offset     = ( isset( $filters['offset'] ) ) ? intval( $filters['offset'] ) : 0;
    	$limit      = ( isset( $filters['limit'] ) ) ? intval( $filters['limit'] ) : 10;
    	
    	/* Grab them */
    	$this->DB->build( array( 'select'   => 's.*',
							     'from'	    => array( 'member_status_actions' => 's' ),
							     'where'    => ( count( $where ) ) ? implode( ' AND ', $where ) : '',
							     'order'    => 's.' . $sort_field . ' ' . $sort_dir,
							     'limit'    => array( $offset, $limit ),
							     'add_join' => array(array(  'select'	=> 'u.*',
															 'from'	    => array( 'member_status_updates' => 'u' ),
															 'where'    => 'u.status_id=s.action_status_id',
															 'type'	    => 'left' ),
							    					 array(  'select'	=> 'm.*',
															 'from'	    => array( 'members' => 'm' ),
															 'where'    => 'm.member_id=s.action_member_id',
															 'type'	    => 'left' ),
													 array(  'select'   => 'pp.*',
															 'from'	    => array( 'profile_portal' => 'pp' ),
															 'where'    => 'pp.pp_member_id=m.member_id',
															 'type'	    => 'left' ) ) ) );
															 
		$o = $this->DB->execute();
		
		while( $row = $this->DB->fetch( $o ) )
		{
			/* Format some data */
			$row['action_date_formatted_short'] = $this->registry->getClass('class_localization')->getDate( $row['action_date'], 'SHORT' );
			
			/* Format member */
			$row = IPSMember::buildDisplayData( $row, array( 'reputation' => 0, 'warn' => 0 ) );
			
			/* Status owner */
			if ( $row['status_member_id'] )
			{
				$mids[ $row['status_member_id'] ] = $row['status_member_id'];
			}
			
			$actions[ $row['action_id'] ] = $row;
		}
		
		/* members? */
		if ( count( $mids ) )
		{
			$members = IPSMember::load( $mids );
			
			foreach( $actions as $id => $row )
			{
				if ( $row['status_member_id'] )
				{
					$actions[ $id ]['owner'] = IPSMember::buildDisplayData( $members[ $row['status_member_id'] ], array( 'reputation' => 0, 'warn' => 0 ) );
				}
			}
		}
		
		/* Phew */
		return $actions;
	}
    	
    /**
     * Fetch status updates
     * Default filters are last 10 status updates from everyone sorted in desc date order.
     * The 'latest_only' flag will grab the last 10 member's updates. Otherwise it'll just grab the last 10 updates which could be from 1 or more people
     * The 'member_id' field will only fetch status updates made by member_id (int) UNLESS type set
     * The 'owner_id' field will fetch status updates made by author_id (INT)
     * The 'status_id' field will only fetch status updates made by status_id (int)
     * The 'friends_only' flag will only fetch status updates from authors friends
     * The 'type' flag can contain 'all', 'mine', 'theirs' - default is 'mine'.
     * The 'isApproved' flag can contain true, false or null (true, approved only, false, unapproved only, null both)
     * The 'ignorePermissions' flag can be set to true (do not verify we can view) or false (pull only statuses we can see based on other filters), defaulting to false
     * getCount - returns a count of all matches without the limit
     *
     * @param	mixed	[Array of member data OR member ID INT for member updating their status - will use ->getAuthor() if null]
     * @param	array	Array of sort/filter data ( member_id [int], latest_only [0,1], offset [int], limit [int], unix_cutoff [int], sort_dir [asc,desc], sort_field [string], type [string] )
     */
    public function fetch( $author=null, $filters=array() )
    {
    	$author   = ( $author === null ) ? $this->getAuthor() : ( is_array( $author ) ? $author : $this->setAuthor( intval( $author ) ) );
    	$where    = array();
    	$statuses = array();
    	$mids     = array();
    	$friends  = array();
    	$authors  = array();
    	
    	/* Set up some filters */
    	$filters['type'] = ( empty( $filters['type'] ) ) ? 'mine' : $filters['type'];
    	
    	if ( isset( $filters['member_id'] ) )
    	{
    		$where[] = "s.status_member_id=" . intval( $filters['member_id'] );
    		
    		if ( $filters['type'] == 'mine' )
    		{
    			$where[] = "s.status_author_id=" . intval( $filters['member_id'] );
    		}
    	}
    	
   		if ( isset( $filters['relatedTo'] ) && ! empty( $filters['relatedTo'] ) )
    	{
    		$where[] = "s.status_member_id=" . intval( $filters['relatedTo'] ) . ' OR s.status_author_id=' . intval( $filters['relatedTo'] );
    	}
    	
    	if ( isset( $filters['ownerOnly'] ) )
    	{
    		$where[] = "s.status_author_id=s.status_member_id";
    	}
    	
    	if ( isset( $filters['status_id'] ) )
    	{
    		$where[] = "s.status_id=" . intval( $filters['status_id'] );
    	}
    	
    	if ( isset( $filters['unix_cutoff'] ) )
    	{
    		$where[] = "s.status_date >=" . intval( $filters['unix_cutoff'] );
    	}
    	
    	if ( isset( $filters['status_is_latest'] ) )
    	{
    		$where[] = "s.status_is_latest =" . intval( $filters['status_is_latest'] );
    	}
    	
    	if ( isset( $filters['isApproved'] ) && $filters['isApproved'] !== null )
    	{
    		if ( $filters['isApproved'] === true )
    		{
    			$where[] = "s.status_approved=1";
    		}
    		else if ( $filters['isApproved'] === false )
    		{
    			$where[] = "s.status_approved=0";
    		}
    	}
    	
    	if ( !empty( $filters['friends_only'] ) )
    	{
    		/* Hard limit to 300 friends */
    		$this->DB->build( array( 'select' => 'friends_friend_id',
    								 'from'   => 'profile_friends',
    								 'where'  => 'friends_member_id=' . $author['member_id'],
    								 'limit'  => array( 0, 300 ) ) );
    		$this->DB->execute();
    		
    		while( $row = $this->DB->fetch() )
    		{
    			$friends[ $row['friends_friend_id'] ] = $row['friends_friend_id'];
    		}
    		
    		if ( ! count( $friends ) )
    		{
    			$friends[0] = 0;
    		}
    		
    		$where[] = "s.status_member_id IN (" . implode( ",", $friends ) . ")";
    	}

    	/* Don't return statuses we can't see */
    	if ( !isset( $filters['ignorePermissions'] ) || $filters['ignorePermissions'] != true )
    	{
	    	if ( ! $author['g_is_supmod'] )
	    	{
	    		if( IPSMember::isOnModQueue( $author['member_id'] ) )
	    		{
	    			$where[]	= "s.status_approved=1";
	    		}
	    		else
	    		{
	    			$where[]	= "(s.status_approved=1 OR s.status_member_id=" . $author['member_id'] . ')';
	    		}
	    	}
	    }
    	
    	$sort_dir   = ( isset( $filters['sort_dir'] ) AND $filters['sort_dir'] == 'asc' ) ? 'asc' : 'desc';
    	$sort_field = ( isset( $filters['sort_field'] ) ) ? $filters['sort_field'] : 'status_date';
    	$offset     = ( isset( $filters['offset'] ) ) ? intval( $filters['offset'] ) : 0;
    	$limit      = ( isset( $filters['limit'] ) ) ? intval( $filters['limit'] ) : 10;
    	$member_id  = !empty( $filters['member_id'] ) ? $filters['member_id'] : 0;
    	
    	if ( ! empty( $filters['getCount'] ) )
    	{
    		$count = $this->DB->buildAndFetch( array( 'select'   => 'count(*) as cnt',
												      'from'	 => array( 'member_status_updates' => 's' ),
												      'where'    => ( count( $where ) ) ? implode( ' AND ', $where ) : '',
												      'add_join' => array(array(
																				  'from'	 => array( 'members' => 'm' ),
																				  'where'    => 'm.member_id=s.status_author_id',
																				  'type'	 => 'left' ),
																		  array(
																				  'from'	 => array( 'profile_portal' => 'pp' ),
																				  'where'    => 'pp.pp_member_id=m.member_id',
																				  'type'	 => 'left' ) ) ) );
																		  
			$this->_statusCount = intval( $count['cnt'] );
    	}
    	
    	/* Grab them */
    	$this->DB->build( array( 'select'   => 's.*',
							     'from'	    => array( 'member_status_updates' => 's' ),
							     'where'    => ( count( $where ) ) ? implode( ' AND ', $where ) : '',
							     'order'    => 's.' . $sort_field . ' ' . $sort_dir,
							     'limit'    => array( $offset, $limit ),
							     'add_join' => array(array(  'select'	=> 'm.*',
															 'from'	    => array( 'members' => 'm' ),
															 'where'    => 'm.member_id=s.status_author_id',
															 'type'	    => 'left' ),
													 array(  'select'   => 'pp.*',
															 'from'	    => array( 'profile_portal' => 'pp' ),
															 'where'    => 'pp.pp_member_id=m.member_id',
															 'type'	    => 'left' ) ) ) );
															 
		$o = $this->DB->execute();
		
		while( $row = $this->DB->fetch( $o ) )
		{
			/* Format some data */
			$row['status_replies']              = intval( $row['status_replies'] );
			$row['status_date_formatted_short'] = $this->registry->getClass('class_localization')->getDate( $row['status_date'], 'SHORT' );
			$row['_isLocked']	                = $this->isLocked( $row );
			$row['_userCanReply']               = $this->canReply( $author, $row, $row );
			$row['_canDelete']			        = $this->canDeleteStatus( $author, $row );
			$row['_canLock']			        = $this->canLockStatus( $author, $row );
			$row['_canUnlock']			        = $this->canUnlockStatus( $author, $row );
			$row['_creatorImg']					= $this->creatorImage( $row );
			$row['_creatorText']				= $this->creatorText( $row );
			$row['_canApprove']			        = $this->canApproveStatus( $author, $row );
			
			/* For someone else? */
			if ( $row['status_member_id'] != $row['status_author_id'] )
			{
				$authorIds[ $row['status_id'] ] = $row['status_member_id'];
				$mids[$row['status_member_id']] = $row['status_member_id'];
			}
			/* Format member */
			$row = IPSMember::buildDisplayData( $row, array( 'reputation' => 0, 'warn' => 0 ) );
			
			/* Replies */
			if ( $row['status_replies'] AND strstr( $row['status_last_ids'], 'a:' ) )
			{
				$replies = unserialize( $row['status_last_ids'] );
				
				if ( is_array( $replies ) AND count( $replies ) )
				{
					ksort( $replies );
					
					foreach( $replies as $r )
					{
						$mids[ $r['reply_member_id'] ] = $r['reply_member_id'];
						$r['reply_date_formatted']     = $this->registry->getClass('class_localization')->getDate( $r['reply_date'], 'SHORT' );
						$r['_canDelete']			   = $this->canDeleteReply( $author, $r, $row );
						$r['reply_status_id']		   = $row['status_id'];
						$row['replies'][ $r['reply_id'] ] = $r;
					}
				}
			}
			else
			{
				$row['replies'] = array();
			}
			
			$statuses[ $row['status_id'] ] = $row;
		}
		
		/* members? */
		if ( count( $mids ) )
		{
			$members = IPSMember::load( $mids );
			
			foreach( $statuses as $id => $row )
			{
				if ( $row['status_member_id'] != $row['status_author_id'] )
				{
					$statuses[ $id ]['owner'] = IPSMember::buildDisplayData( $members[ $row['status_member_id'] ], array( 'reputation' => 0, 'warn' => 0 ) );
				}
				
				if ( is_array( $row['replies'] ) )
				{
					foreach( $row['replies'] as $rid => $r )
					{
						if ( $members[ $r['reply_member_id'] ] )
						{
							if ( ! isset( $members[ $r['reply_member_id'] ]['_done'] ) )
							{
								$members[ $r['reply_member_id'] ] = IPSMember::buildDisplayData( $members[ $r['reply_member_id'] ], array( 'reputation' => 0, 'warn' => 0 ) );
								$members[ $r['reply_member_id'] ]['_done'] = 1;
							}
							
							$statuses[ $id ]['replies'][ $rid ] = array_merge( $statuses[ $id ]['replies'][ $rid ], $members[ $r['reply_member_id'] ] );
						}
					}
				}
			}
		}
		
		/* Phew */
		return $statuses;
    }
    
    /**
     * Fetch all replies to a status
     * Default filters are sorted on reply_date ASC
     *
     * @param	mixed	[Array of member data OR member ID INT for member updating their status - will use ->getAuthor() if null]
     * @param	array	Array of sort/filter data ( member_id [int], latest_only [0,1], offset [int], limit [int], unix_cutoff [int], sort_dir [asc,desc], sort_field [string] )
     */
    public function fetchAllReplies( $status=null, $filters=array() )
    {
    	$status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
    	$where    = array();
    	$replies = array();
    	
    	$sort_dir   = ( $filters['sort_dir'] == 'desc' ) ? 'desc' : 'asc';
    	$sort_field = ( isset( $filters['sort_field'] ) ) ? $filters['sort_field'] : 'reply_date';
    	$offset     = ( isset( $filters['offset'] ) ) ? intval( $filters['offset'] ) : 0;
    	$limit      = ( isset( $filters['limit'] ) ) ? intval( $filters['limit'] ) : 100;
        	
    	/* Grab them */
    	$this->DB->build( array( 'select'   => 's.*',
							     'from'	    => array( 'member_status_replies' => 's' ),
							     'where'    => 's.reply_status_id=' . intval( $status['status_id'] ),
							     'order'    => 's.' . $sort_field . ' ' . $sort_dir,
							     'limit'    => array( $offset, $limit ),
							     'add_join' => array(array(  'select'	=> 'm.*',
															 'from'	    => array( 'members' => 'm' ),
															 'where'    => 'm.member_id=s.reply_member_id',
															 'type'	    => 'left' ),
													 array(  'select'   => 'pp.*',
															 'from'	    => array( 'profile_portal' => 'pp' ),
															 'where'    => 'pp.pp_member_id=m.member_id',
															 'type'	    => 'left' ) ) ) );
															 
		$o = $this->DB->execute();
		
		while( $row = $this->DB->fetch( $o ) )
		{
			/* Format some data */
			$row['reply_date_formatted'] = $this->registry->getClass('class_localization')->getDate( $row['reply_date'], 'SHORT' );
			$row['_canDelete']			 = $this->canDeleteReply( $this->getAuthor(), $row, $status );
			
			/* Format member */
			$row = IPSMember::buildDisplayData( $row, array( 'reputation' => 0, 'warn' => 0 ) );
			
			$replies[ $row['reply_id'] ] = $row;
		}
		
		/* Phew */
		return $replies;
    }
    
    /**
     * Create a status update for a member
     *
     * @param	array	[Array of member data for member updating their status - will use ->getAuthor() if null]
     * @param   array   [Array of member data for owner of status update. If null and StatusOwner empty, getAuthor will be used]
     * @return	array	Status information
     */
    public function create( $author=null, $owner=null )
    {
    	$author = ( $author === null ) ? $this->getAuthor() : $author;
    	$_owner = $this->getStatusOwner();
    	$owner  = ( $owner  === null ) ? ( ! empty( $_owner['member_id'] ) ? $_owner : $author ) : $owner;
    	$data	= array();
    	
    	if ( $this->canCreate( $author, $owner ) )
    	{
    		if ( $this->getContent() )
    		{
    			$content = $this->_cleanContent( $this->getContent() );
    			$hash    = IPSText::contentToMd5( $content );
    			
    			/* Check for this status update already created */
    			$test = $this->fetchByHash( $owner['member_id'], $hash );
    			
    			if ( $test['status_id'] )
    			{
    				/* Already imported this one */
    				return FALSE;
    			}
    					
    			$data = array( 'status_member_id' => $owner['member_id'],
    						   'status_author_id' => $author['member_id'],
							   'status_date'	  => time(),
							   'status_content'   => $this->_parseContent( $content, $this->_internalData['Creator'] ),
							   'status_hash'      => $hash,
							   'status_replies'	  => 0,
    						   'status_author_ip' => $this->member->ip_address,
    						   'status_approved'  => ( ( $owner['pp_setting_moderate_comments'] && $owner['member_id'] != $author['member_id'] ) || IPSMember::isOnModQueue( $author['member_id'] ) ) ? 0 : $this->getIsApproved(),
							   'status_imported'  => intval( $this->_internalData['IsImport'] ),
							   'status_creator'   => trim( addslashes( $this->_internalData['Creator'] ) ),
							   'status_last_ids'  => '' );

				/* Data Hook Location */
				IPSLib::doDataHooks( $data, 'statusUpdateNew' );
		
    			$this->DB->insert( 'member_status_updates', $data );
    			
    			$status_id = $this->DB->getInsertId();
    			
    			$data['status_id']	= $status_id;
    			 
    			if ( $owner['member_id'] != $author['member_id'] )
    			{
    				$this->_sendCommentNotification( $author, $owner, $data );
    			}
    			else
    			{
	    			$this->_recordAction( 'new', $author, $data );
	    			
	    			$this->rebuildOwnerLatest( $owner );
	    			
	    			/* Fire off external updates */
	    			$eU = $this->getExternalUpdates();
	    			
	    			if ( ! $this->_internalData['IsImport'] AND is_array( $eU ) )
	    			{
	    				$this->_triggerExternalUpdates( $eU, $status_id, $owner, $content );
	    			}
	    			
	    			//-----------------------------------------
	    			// Notify owner's friends as configured
	    			//-----------------------------------------
	    			
	    			$friends	= array();
	    			
	    			if ( $this->settings['friends_enabled'] AND $author['member_id'] == $owner['member_id'] )
	    			{
		    			$this->DB->build( array( 'select' => 'friends_member_id, friends_approved', 'from' => 'profile_friends', 'where' => 'friends_friend_id=' . $owner['member_id'] ) );
		    			$this->DB->execute();
		    			
		    			while( $_friend = $this->DB->fetch() )
		    			{
		    				if ( $_friend['friends_approved'] )
		    				{
		    					$friends[ $_friend['friends_member_id'] ] = $_friend['friends_member_id'];
		    				}
		    			}
					}
					
    				if( count($friends) )
					{
						//-----------------------------------------
						// Notifications library
						//-----------------------------------------
						
						$classToLoad	= IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/classes/member/notifications.php', 'notifications' );
						$notifyLibrary	= new $classToLoad( $this->registry );
						
		    			$friends = IPSMember::load( $friends );
		    			
		    			$statusUrl = $this->registry->output->buildSEOUrl( 'app=members&amp;module=profile&amp;section=status&amp;type=single&amp;status_id=' . $status_id, 'publicNoSession', array( $owner['member_id'], $owner['members_seo_name'] ), 'members_status_single' );
		    			
		    			/* Build message as few times as possible */
		    			$ndata = array( 'NAME'		=> '#friend.name#',
				    					'OWNER'		=> $owner['members_display_name'],
										'STATUS'	=> $data['status_content'],
										'URL'		=> $this->registry->output->buildSEOUrl( 'app=core&amp;module=usercp&amp;tab=core&amp;area=notifications', 'publicNoSession' ) );
										
		    			/* Get languages */
		    			$langMessages = array();
		    			
		    			foreach( $friends as $friend )
		    			{
		    				if ( ! isset( $langMessages[ $friend['language'] ] ) )
		    				{
		    					IPSText::getTextClass('email')->getTemplate( 'new_status', $friend['language'] );
		    					IPSText::getTextClass('email')->buildMessage( $ndata );
						
		    					$_subject = sprintf(  IPSText::getTextClass('email')->subject,
													  $this->registry->output->buildSEOUrl( 'showuser=' . $owner['member_id'], 'publicNoSession', $owner['members_seo_name'], 'showuser' ),
													  $owner['members_display_name'],
													  $statusUrl );
																			
			    				$langMessages[ $friend['language'] ] = array( 'subject' => $_subject, 'message' => IPSText::getTextClass('email')->message );
		    				}
		    			}
		    			
		    			foreach( $friends as $friend )
		    			{
		    				$message = $langMessages[ $friend['language'] ]['message'];
		    				$message = str_replace( '#friend.name#', $friend['members_display_name'], $message );
		    				
							$notifyLibrary->setMember( $friend );
							$notifyLibrary->setFrom( $author );
							$notifyLibrary->setNotificationKey( 'friend_status_update' );
							$notifyLibrary->setNotificationUrl( $statusUrl );
							$notifyLibrary->setNotificationText( $message );
							$notifyLibrary->setNotificationTitle( $langMessages[ $friend['language'] ]['subject'] );
							
							try
							{
								$notifyLibrary->sendNotification();
							}
							catch( Exception $e ){}
						}
					}
	    		}
    		}
    		
    		return $data;
    	}
    	
    	return FALSE;
    }

    /**
     * Create a status update for a member
     *
     * @param	array	[Array of member data for member updating their status - will use ->getAuthor() if null]
     * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
     * @return	array	Reply information
     */
    public function reply( $author=null, $status=null )
    {
    	$author = ( $author === null ) ? $this->getAuthor() : $author;
    	$status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
    	$data	= array();
    	
    	if ( $this->canReply( $author, $status, $status ) )
    	{
    		if ( $this->getContent() AND $status['status_id'] )
    		{
    			$data = array( 'reply_status_id'  => $status['status_id'],
							   'reply_member_id'  => $author['member_id'],
							   'reply_date'	      => time(),
							   'reply_content'    => $this->_cleanContent( $this->getContent() ) );

				/* Data Hook Location */
				IPSLib::doDataHooks( $data, 'statusCommentNew' );
				
    			$this->DB->insert( 'member_status_replies', $data );
				
				$data['reply_id'] = $this->DB->getInsertId();
				
				$this->_recordAction( 'reply', $author, $status, $data );
				 
    			$this->rebuildStatus( $status );
    			$this->rebuildOwnerLatest( $author );
    			
    			$this->_sendNotification( $author, $status, $data );
    		}
    		
    		return $data;
    	}
    	
    	return FALSE;
    }
    
    /**
     * Triggers external postings Twitter, etc
     *
     * @param	array		Update to...
     * @param	int			Status ID just posted
     * @param	array		[Array of member data for member updating their status - will use ->getAuthor() if null]
     * @param	string  	[Content to update]
     * @todo [Future]		At some point it could be expanded into a mini framework with plugins
     */
    protected function _triggerExternalUpdates( $updates, $status_id=0, $author=null, $content=null )
    {
    	$author = ( $author === null ) ? $this->getAuthor() : $author;
    	$content = ( $content ) ? $this->_cleanContent( $content ) : $this->_cleanContent( $this->getContent() );
    	
    	/* Fail safe */
    	if ( ! $author['member_id'] OR ! $content )
    	{
    		return false;
    	}
    	
    	$url = $this->registry->output->buildSEOUrl( 'app=members&amp;module=profile&amp;section=status&amp;type=single&amp;status_id=' . $status_id, 'publicNoSession', array( $author['member_id'], $author['members_seo_name'] ), 'members_status_single' );
    		    			    	
    	/* Twitter */
    	if ( $updates['twitter'] )
    	{
    		if ( IPSLib::twitter_enabled() AND $author['twitter_id'] )
    		{
    			$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/twitter/connect.php', 'twitter_connect' );
    			$twitter = new $classToLoad( $this->registry, $author['twitter_token'], $author['twitter_secret'] );
				
				$twitter->updateStatusWithUrl( $content, $url, FALSE );
    		}
    	}
    
    	/* Facebook */
    	if ( $updates['facebook'] )
    	{
    		if ( IPSLib::fbc_enabled() AND $author['fb_uid'] )
    		{
    			$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/facebook/connect.php', 'facebook_connect' );
    			$facebook = new $classToLoad( $this->registry );
				$facebook->updateStatusWithUrl( $content, $url, FALSE );
    		}
    	}
    }
    
    /**
     * Send notification
     *
     * Emails users of new status replies
     * Emails owner (if selected) and anyone else who replied (if selected)
     *
     * @param	array		Author array
     * @param	array		Status array
     * @param	array		Reply array
     * @return	boolean
     */
    protected function _sendNotification( $author, $status, $reply )
    {
    	if ( $author['member_id'] AND $status['status_id'] AND $reply['reply_id'] )
    	{
    		/* Get members */
    		$members = IPSMember::load( array( $author['member_id'], $reply['reply_member_id'], $status['status_author_id'] ), 'core' );
    		
    		$_author  = $members[ $author['member_id'] ];
    		$_owner   = $members[ $status['status_author_id'] ];
    		$_replier = $members[ $reply['reply_member_id'] ];
    		
    		if ( $_author['member_id'] AND $_owner['member_id'] AND $_replier['member_id'] )
    		{
				/* Make sure we have the correct language pack */
	    		$this->registry->class_localization->loadLanguageFile( array( 'public_profile' ), 'members', $_owner['language'], true );
    			
	    		/* Init vars */
	    		$_ids		= array();
	    		$members	= array();
	    		$statusUrl	= $this->registry->output->buildSEOUrl( 'app=members&amp;module=profile&amp;section=status&amp;type=single&amp;status_id=' . $status['status_id'], 'publicNoSession', array( $_owner['member_id'], $_owner['members_seo_name'] ), 'members_status_single' );
	    		
	    		/* Did the status owner want an email? */
	    		if ( $_owner['member_id'] != $_replier['member_id'] )
	    		{
					/* Load notifications library */
					$classToLoad	= IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/classes/member/notifications.php', 'notifications' );
					$notifyLibrary	= new $classToLoad( $this->registry );
					
					IPSText::getTextClass('email')->getTemplate( 'status_reply', $_owner['language'] );
					
					$data = array( 'NAME'	=> $_owner['members_display_name'],
	    						   'OWNER'	=> $_owner['members_display_name'],
								   'POSTER'	=> $_replier['members_display_name'],
								   'BLURB'	=> $this->lang->words['email_blurb_yours'],
								   'STATUS'	=> $status['status_content'],
								   'URL'	=> $this->registry->output->buildSEOUrl( 'app=core&amp;module=usercp&amp;tab=core&amp;area=notifications', 'publicNoSession' ),
								   'TEXT'	=> $reply['reply_content']  );
					
					IPSText::getTextClass('email')->buildMessage( $data	);
					
					IPSText::getTextClass('email')->subject	= sprintf(
																		IPSText::getTextClass('email')->subject,
																		$this->registry->output->buildSEOUrl( 'showuser=' . $_replier['member_id'], 'public', $_replier['members_seo_name'], 'showuser' ),
																		$_replier['members_display_name'],
																		$statusUrl
																	);
	
					$notifyLibrary->setMember( $_owner );
					$notifyLibrary->setFrom( $_replier );
					$notifyLibrary->setNotificationKey( 'reply_your_status' );
					$notifyLibrary->setNotificationUrl( $statusUrl );
					$notifyLibrary->setNotificationText( IPSText::getTextClass('email')->message );
					$notifyLibrary->setNotificationTitle( IPSText::getTextClass('email')->subject );
					
					try
					{
						$notifyLibrary->sendNotification();
					}
					catch( Exception $e ){}
	    		}
	    		
	    		/* Now fetch everyone else who has replied */
	    		$this->DB->build( array( 'select'   => 'reply_member_id',
	    								 'from'     => 'member_status_replies',
	    								 'where'    => 'reply_status_id=' . $status['status_id'] . ' AND reply_member_id NOT IN (' . intval ( $_replier['member_id'] ) . ',' . intval ( $_owner['member_id'] ) . ')' ) );
	    		$this->DB->execute();
	    		
	    		while( $row = $this->DB->fetch() )
	    		{
	    			$_ids[ $row['reply_member_id'] ] = $row['reply_member_id'];
	    		}

	    		if ( count( $_ids ) )
	    		{
	    			$members = IPSMember::load( $_ids, 'core' );
	    		}
	    		
	    		if ( is_array( $members ) AND count( $members ) )
	    		{
					/* Load notifications library */
					$classToLoad	= IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/classes/member/notifications.php', 'notifications' );
					$notifyLibrary	= new $classToLoad( $this->registry );

	    			foreach( $members as $id => $member )
	    			{
	    				/* Make sure we have the correct language pack */
		    			$this->registry->class_localization->loadLanguageFile( array( 'public_profile' ), 'members', $member['language'], true );
    					
	    				$blurb   = '';
	    				$subject = '';
	    				
	    				/* User made a reply to their own status */
	    				if ( $_replier['member_id'] == $_owner['member_id'] )
	    				{
	    					$blurb   = $this->lang->words['email_blurb_theirs'];
	    					$subject = sprintf( $this->lang->words['email_title_blurb_other'], $_owner['members_display_name']);
	    				}
	    				else
	    				{
	    					$blurb   = sprintf( $this->lang->words['email_blurb_other'], $_owner['members_display_name']);
	    					$subject = sprintf( $this->lang->words['email_title_blurb_other'], $_owner['members_display_name']);
	    				}
						
						$data = array( 'NAME'		=> $member['members_display_name'],
			    					   'OWNER'		=> $_owner['members_display_name'],
									   'POSTER'		=> $_replier['members_display_name'],
									   'BLURB'		=> $blurb,
									   'STATUS'		=> $status['status_content'],
									   'URL'		=> $this->registry->output->buildSEOUrl( 'app=core&amp;module=usercp&amp;tab=core&amp;area=notifications', 'publicNoSession' ),
									   'TEXT'		=> $reply['reply_content']  );
																			
						IPSText::getTextClass('email')->getTemplate( 'status_reply', $member['language'] );
						
						IPSText::getTextClass('email')->buildMessage( $data	);
						
						if ( $_replier['member_id'] == $_owner['member_id'] )
						{
							IPSText::getTextClass('email')->subject	= sprintf(
																				$this->lang->words['subject__their_status_reply'],
																				$this->registry->output->buildSEOUrl( 'showuser=' . $_replier['member_id'], 'public', $_replier['members_seo_name'], 'showuser' ),
																				$_replier['members_display_name'],
																				$statusUrl
																			);
						}
						else
						{
							IPSText::getTextClass('email')->subject	= sprintf(
																				$this->lang->words['subject__other_status_reply'],
																				$this->registry->output->buildSEOUrl( 'showuser=' . $_replier['member_id'], 'public', $_replier['members_seo_name'], 'showuser' ),
																				$_replier['members_display_name'],
																				$this->registry->output->buildSEOUrl( 'showuser=' . $_owner['member_id'], 'public', $_owner['members_seo_name'], 'showuser' ),
																				$_owner['members_display_name'],
																				$statusUrl
																			);
						}

						$notifyLibrary->setMember( $member );
						$notifyLibrary->setFrom( $_replier );
						$notifyLibrary->setNotificationKey( 'reply_any_status' );
						$notifyLibrary->setNotificationUrl( $statusUrl );
						$notifyLibrary->setNotificationText( IPSText::getTextClass('email')->message );
						$notifyLibrary->setNotificationTitle( IPSText::getTextClass('email')->subject );
						
						try
						{
							$notifyLibrary->sendNotification();
						}
						catch( Exception $e ){}
					}
				}

				return TRUE;
			}
    	}
    	
    	return FALSE;
    }
    
	/**
     * Send comment notification
     *
     * Emails users of new status replies
     * Emails owner (if selected) and anyone else who replied (if selected)
     *
     * @param	array		Author array
     * @param	array		Status array
     * @param	array		Reply array
     * @return	boolean
     */
    protected function _sendCommentNotification( $author, $owner, $status )
    {
 		$classToLoad	= IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/classes/member/notifications.php', 'notifications' );
		$notifyLibrary	= new $classToLoad( $this->registry );
		
		$memberUrl = $this->registry->output->buildSEOUrl( 'showuser=' . $owner['member_id']  . '&amp;tab=status', 'public', $owner['members_seo_name'], 'showuser' );
		
		IPSText::getTextClass('email')->getTemplate( 'new_comment_added', $owner['language'] );
	
		IPSText::getTextClass('email')->buildMessage( array( 'MEMBERS_DISPLAY_NAME'	=> $owner['members_display_name'],
															 'COMMENT_NAME'			=> $author['members_display_name'],
															 'LINK'					=> $memberUrl
													 )		);
		
		IPSText::getTextClass('email')->subject	= sprintf(  IPSText::getTextClass('email')->subject,
															$memberUrl,
															$this->registry->output->buildSEOUrl( 'showuser=' . $author['member_id'], 'public', $author['members_seo_name'], 'showuser' ),
															$author['members_display_name'] );
		
		$notifyLibrary->setMember( $owner );
		$notifyLibrary->setFrom( $author );
		$notifyLibrary->setNotificationKey( 'profile_comment' );
		$notifyLibrary->setNotificationUrl( $memberUrl );
		$notifyLibrary->setNotificationText( IPSText::getTextClass('email')->message );
		$notifyLibrary->setNotificationTitle( IPSText::getTextClass('email')->subject );
		try
		{
			$notifyLibrary->sendNotification();
		}
		catch( Exception $e ){}
    	
    	return FALSE;
    }
    
    /**
     * Lock status
     *
     * @param	array	[Array of member data for member updating their status - will use ->getAuthor() if null]
     * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
     * @return	boolean
     */
    public function lockStatus( $author=null, $status=null )
    {
    	$author = ( $author === null ) ? $this->getAuthor() : $author;
    	$status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
    	
    	if ( $status['status_id'] )
		{
			if ( $this->canLockStatus( $author, $status ) )
			{
				/* Value? */
				$val = ( $author['g_is_supmod'] AND $author['member_id'] != $status['status_member_id'] ) ? 2 : 1;
				
				/* Update status */
				$this->DB->update( 'member_status_updates', array( 'status_is_locked' => $val ), 'status_id=' . $status['status_id'] );
				
				/* Rebuild latest */
				$this->rebuildOwnerLatest( $author );
				
				return TRUE;
			}
		}
		
		return FALSE;
    }
    
    /**
     * Unlock status
     *
     * @param	array	[Array of member data for member updating their status - will use ->getAuthor() if null]
     * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
     * @return	boolean
     */
    public function unlockStatus( $author=null, $status=null )
    {
    	$author = ( $author === null ) ? $this->getAuthor() : $author;
    	$status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
    	
    	if ( $status['status_id'] )
		{
			if ( $this->canUnlockStatus( $author, $status ) )
			{
				/* Update status */
				$this->DB->update( 'member_status_updates', array( 'status_is_locked' => 0 ), 'status_id=' . $status['status_id'] );
				
				/* Rebuild latest */
				$this->rebuildOwnerLatest( $author );
				
				return TRUE;
			}
		}
		
		return FALSE;
    }
    
    /**
     * Approve status
     *
     * @param	array	[Array of member data for member updating their status - will use ->getAuthor() if null]
     * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
     * @return	boolean
     */
    public function approveStatus( $author=null, $status=null )
    {
    	$author = ( $author === null ) ? $this->getAuthor() : $author;
    	$status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
    	 
    	if ( $status['status_id'] )
    	{
    		if ( $this->canApproveStatus( $author, $status ) )
    		{
    			/* Update status */
    			$this->DB->update( 'member_status_updates', array( 'status_approved' => 1 ), 'status_id=' . $status['status_id'] );
    
    			/* Rebuild latest */
    			$this->rebuildOwnerLatest( $author );
    
    			return TRUE;
    		}
    	}
    
    	return FALSE;
    }

    /**
     * Delete all user's status'
     *
     * @param	array	[Array of member data for member updating their status - will use ->getAuthor() if null]
     * @return	boolean
     */
    public function deleteAllMemberStatus( $author=null )
    {
    	$author   = ( $author === null ) ? $this->getAuthor() : $author;
    	$statuses = array();
    	
    	$this->DB->build( array( 'select' => '*',
    							 'from'   => 'member_status_updates',
    							 'where'  => 'status_member_id=' . $author['member_id'] ) );
    							 
    	$this->DB->execute();
    	
    	while( $row = $this->DB->fetch() )
    	{
    		$statuses[] = $row['status_id'];
    	}
    	
    	/* Delete data */
    	if ( count( $statuses ) )
    	{
    		$id = implode( ',', $statuses );
    		
    		/* Delete replies */
			$this->DB->delete( 'member_status_replies', 'reply_status_id IN(' . $id . ')' );
			
			/* Delete actions */
			$this->DB->delete( 'member_status_actions', 'action_status_id IN(' . $id . ')' );
			
			/* Delete statuses */
    		$this->DB->delete( 'member_status_updates', 'status_id IN(' . $id . ')' );
    	}
    	
    	/* Flush caches */
		$this->rebuildOwnerLatest( $author );
    }

     /**
     * Delete all user's replies
     *
     * @param	array	[Array of member data for member updating their status - will use ->getAuthor() if null]
     * @return	boolean
     */
    public function deleteAllReplies( $author=null )
    {
    	$author    = ( $author === null ) ? $this->getAuthor() : $author;
    	$statusIds = array();
    	$replyIds  = array();
    	$memberIds = array();
    	$stom      = array();
    	
    	/* Fetch reply IDs */
    	$this->DB->build( array( 'select' => '*',
    							 'from'   => 'member_status_replies',
    							 'where'  => 'reply_member_id=' . $author['member_id'] ) );
    							 
    	$this->DB->execute();
    	
    	while( $row = $this->DB->fetch() )
    	{
    		$replyIds[]  = $row['reply_id'];
    		$statusIds[] = $row['reply_status_id'];
    	}
    	
    	/* If nothing, goowf */
    	if ( ! count( $statusIds ) OR ! count( $replyIds ) )
    	{
    		return FALSE;
    	}
    	
    	/* Now fetch status owners to recache them */
    	$this->DB->build( array( 'select' => 'status_id, status_member_id',
    							 'from'   => 'member_status_updates',
    							 'where'  => 'status_id IN(' . implode( ',', $statusIds ) . ')' ) );
    							 
    	$this->DB->execute();
    	
    	while( $row = $this->DB->fetch() )
    	{
    		$memberIds[]               = $row['status_member_id'];
    		$stom[ $row['status_id'] ] = $row['status_member_id'];
    	}
    	
    	/* Got member ids? */
    	$members = IPSMember::load( $memberIds, 'all' );
    	
    	/* Delete data */
    	$id = implode( ',', $replyIds );
    	
    	/* Delete replies */
		$this->DB->delete( 'member_status_replies', 'reply_id IN(' . $id . ')' );
		
		/* Delete actions */
		$this->DB->delete( 'member_status_actions', 'action_reply_id IN(' . $id . ')' );
		
		foreach( $statusIds as $sid )
		{
			/* Rebuild this status */
			$this->rebuildStatus( $sid );
			
			/* Rebuild latest */
			$this->rebuildOwnerLatest( $members[ $stom[ $sid ] ] );
		}
    }
    
    /**
     * Delete status
     *
     * @param	array	[Array of member data for member updating their status - will use ->getAuthor() if null]
     * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
     * @return	boolean
     */
    public function deleteStatus( $author=null, $status=null )
    {
    	$author = ( $author === null ) ? $this->getAuthor() : $author;
    	$status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
    	
    	if ( $status['status_id'] )
		{
			if ( $this->canDeleteStatus( $author, $status ) )
			{
				/* Delete status */
				$this->DB->delete( 'member_status_updates', 'status_id=' . $status['status_id'] );
				
				/* Delete replies */
				$this->DB->delete( 'member_status_replies', 'reply_status_id=' . $status['status_id'] );
				
				/* Delete actions */
				$this->DB->delete( 'member_status_actions', 'action_status_id=' . $status['status_id'] );
				
				/* Rebuild latest */
				$this->rebuildOwnerLatest( $author );
				
				return TRUE;
			}
		}
		
		return FALSE;
    }
    
    /**
     * Delete reply
     *
     * @param	array	[Array of member data for member updating their status - will use ->getAuthor() if null]
     * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
	 * @param	array	[Array of status reply information OR reply ID OR uses $this->_internalData['ReplyData'] if none]
     * @return	boolean
     */
    public function deleteReply( $author=null, $status=null, $reply=null )
    {
    	$author = ( $author === null ) ? $this->getAuthor() : $author;
    	$status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
    	$reply   = ( $reply  === null ) ? $this->_internalData['ReplyData']  : ( ( is_array( $reply ) ) ? $reply : $this->_loadReply( $reply ) );
    	
    	if ( $status['status_id'] AND $reply['reply_id'] )
		{
			if ( $this->canDeleteReply( $author, $reply, $status ) )
			{
				/* Delete replies */
				$this->DB->delete( 'member_status_replies', 'reply_id=' . $reply['reply_id'] );
				
				/* Delete actions */
				$this->DB->delete( 'member_status_actions', 'action_reply_id=' . $reply['reply_id'] );
				
				/* Rebuild this status */
				$this->rebuildStatus( $status );
				
				/* Rebuild latest */
				$this->rebuildOwnerLatest( $author );
				
				return TRUE;
			}
		}
		
		return FALSE;
    }
    
    /**
     * Rebuild status data
     *
     * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
     * @return	boolean
     */
    public function rebuildStatus( $status=null )
    {
    	$status = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
    	$last   = array();
    	
    	if ( $status['status_id'] )
		{
			/* Fetch the number of replies */
			$count = $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as count',
													  'from'   => 'member_status_replies',
													  'where'  => 'reply_status_id=' . intval( $status['status_id'] ) ) );
													  
			/* Fetch last 3 replies */
			$this->DB->build( array( 'select' => 'reply_id, reply_member_id, reply_date, reply_content',
									 'from'   => 'member_status_replies',
									 'order'  => 'reply_date DESC',
									 'limit'  => array( 0, 3 ),
									 'where'  => 'reply_status_id=' . intval( $status['status_id'] ) ) );
									 
			$this->DB->execute();
			
			while( $row = $this->DB->fetch() )
			{
				$last[ $row['reply_date'] ] = $row;
			}
			
			if ( count( $last ) )
			{
				ksort( $last );
			}
			 
			/* Reset data */
			$this->DB->update( 'member_status_updates', array( 'status_replies' => intval( $count['count'] ), 'status_last_ids' => serialize( $last ) ), 'status_id=' . $status['status_id'] );
		}
    	
    	return TRUE;
    }
    
    /**
     * Rebuilds status data such as last status update, etc.
     *
     * @param	array	[Array of member data for member updating their status - will use ->getAuthor() if null]
     * @return	boolean
     */
    public function rebuildOwnerLatest( $author=null )
    {
    	$author = ( $author === null ) ? $this->getAuthor() : $author;
    	$last   = array();
    	
    	/* Reset last flag for everyone */
    	$this->DB->update( 'member_status_updates', array( 'status_is_latest' => 0 ), 'status_member_id=' . intval( $author['member_id'] ) );
    	
    	/* Fetch the latest update */
    	$status = $this->fetchMemberLatest( $author['member_id'] );
		    									   
		if ( $status['status_id'] )
		{
			/* Fetch the number of replies */
			$count = $this->DB->buildAndFetch( array( 'select' => 'COUNT(*) as count',
													  'from'   => 'member_status_replies',
													  'where'  => 'reply_status_id=' . intval( $status['status_id'] ) ) );
													  
			/* Fetch last 3 replies */
			$this->DB->build( array( 'select' => 'reply_id, reply_member_id, reply_date, reply_content',
									 'from'   => 'member_status_replies',
									 'order'  => 'reply_date DESC',
									 'limit'  => array( 0, 3 ),
									 'where'  => 'reply_status_id=' . intval( $status['status_id'] ) ) );
									 
			$this->DB->execute();
			
			while( $row = $this->DB->fetch() )
			{
				$last[ $row['reply_date'] ] = $row;
			}
			
			ksort( $last );
			 
			/* Reset data */
			$this->DB->update( 'member_status_updates', array( 'status_is_latest' => 1, 'status_replies' => intval( $count['count'] ), 'status_last_ids' => serialize( $last ) ), 'status_id=' . $status['status_id'] );
		}
		
		return TRUE;
    }
    
    /**
     * Fetch the status update by hash
     *
     * @param	int		Member ID
     */
    public function fetchByHash( $memberId, $hash )
    {
    	$status = array();
    	
    	$this->DB->build( array( 'select'   => 's.*',
							     'from'	    => array( 'member_status_updates' => 's' ),
							     'where'    => 's.status_member_id=' . intval( $memberId ) . ' AND s.status_imported=1 AND s.status_hash=\'' . addslashes( $hash ) . '\'',
							     'add_join' => array(array(  'select'	=> 'm.*',
															 'from'	    => array( 'members' => 'm' ),
															 'where'    => 'm.member_id=s.status_member_id',
															 'type'	    => 'left' ),
													 array(  'select'   => 'pp.*',
															 'from'	    => array( 'profile_portal' => 'pp' ),
															 'where'    => 'pp.pp_member_id=m.member_id',
															 'type'	    => 'left' ) ) ) );
															 
		$this->DB->execute();
		
		$status = $this->DB->fetch();
		
		return is_array( $status ) ? $status : array();
    }
    
    /**
     * Fetch the member's latest status update
     *
     * @param	int		Member ID
     */
    public function fetchMemberLatest( $memberId )
    {
    	$status = array();
    	$member = array();
    	
    	$this->DB->build( array( 'select'   => 's.*',
							     'from'	    => array( 'member_status_updates' => 's' ),
							     'where'    => 's.status_member_id=' . intval( $memberId ) . ' AND s.status_author_id=' . intval( $memberId ),
							     'order'    => 's.status_date DESC',
							     'limit'    => array( 0, 1 ),
							     'add_join' => array(array(  'select'	=> 'm.*',
															 'from'	    => array( 'members' => 'm' ),
															 'where'    => 'm.member_id=s.status_member_id',
															 'type'	    => 'left' ),
													 array(  'select'   => 'pp.*',
															 'from'	    => array( 'profile_portal' => 'pp' ),
															 'where'    => 'pp.pp_member_id=m.member_id',
															 'type'	    => 'left' ) ) ) );
															 
		$this->DB->execute();
		
		$row = $this->DB->fetch();
		
		if ( is_array( $row ) AND count( $row ) )
		{
			foreach( $row as $k => $v )
			{
				if ( substr( $k, 0, 7 ) != 'status_' )
				{
					$member[ $k ] = $v;
				}
				
				$status[ $k ] = $v;
			}
		}
		
		$this->_internalData['StatusData']  = $status;
		$this->_internalData['StatusOwner'] = $member;
		
		return is_array( $status ) ? $status : array();
    }
    
    /**
     * Fetch creator image
     *
     * @return	string (img URL)
     */
    public function creatorImage( $status )
    {
    	/* Got a creator? */
    	if ( ! $status['status_creator'] )
    	{
    		$status['status_creator'] = 'ipb';
    	}
    	
    	$creator = IPSText::alphanumericalClean( $status['status_creator'] );
    	
    	/* Image exists? */
    	if ( ! is_file( IPS_PUBLIC_PATH . 'style_status/' . $creator . '.png' ) )
    	{
    		$creator = 'ipb';
    	}
    	
    	return $this->settings['public_dir'] . 'style_status/' . $creator . '.png';
    }
    
    /**
     * Fetch creator text
     *
     * @return	string
     */
    public function creatorText( $status )
    {
    	/* Got a creator? */
    	if ( ! $status['status_creator'] )
    	{
    		$status['status_creator'] = 'ipb';
    	}
    	
    	if ( ! isset( $this->lang->words['status_creator_' . $status['status_creator'] ] ) )
    	{
    		$status['status_creator'] = 'ipb';
    	}
    	
    	return $this->lang->words['status_creator_' . $status['status_creator'] ];
    }
    
	/**
	 * Perm Check: Can post a status update
	 *
	 * @return	bool
	 */
	public function canCreate( $author=null, $owner=null )
	{
		$author = ( $author === null ) ? $this->getAuthor() : $author;
		
		if ( $author['restrict_post'] )
		{
			return FALSE;
		}
		
		if ( $owner !== null AND $author['member_id'] != $owner['member_id'] )
		{
			/* Not a new status by user */
			return $this->canComment( $author, $owner );
		}
		
		if ( ! $author['member_id'] )
		{
			return FALSE;
		}
		
		if ( ! $this->isEnabled() )
		{
			return FALSE;
		}
		
		if ( ! $author['g_mem_info'] OR $author['gbw_no_status_update'] OR $author['bw_no_status_update'] )
		{
			return FALSE;
		}
		
		return TRUE;
	}
	
	/**
	 * Perm Check: Can post a comment
	 *
	 * @param	array	[Array of author data, uses getAuthor if none]
	 * @param	array	[Array of status owner information uses $this->_internalData['StatusOwner'] if none]
	 * @return	bool
	 */
	public function canComment( $author=null, $owner=null, $status=null )
	{
		$author = ( $author === null ) ? $this->getAuthor() : $author;
		$owner  = ( $owner === null )  ? $this->_internalData['StatusOwner'] : $owner;
		
		/* Are we allowed to comment? */
		if ( ! $author['g_reply_other_topics'] )
		{
			return false;
		}
		
		if ( $author['restrict_post'] )
		{
			if ( $author['restrict_post'] == 1 )
			{
				return false;
			}
			
			$post_arr = IPSMember::processBanEntry( $author['restrict_post'] );
			
			if ( time() >= $post_arr['date_end'] )
			{
				/* Update this member's profile */
				IPSMember::save( $author['member_id'], array( 'core' => array( 'restrict_post' => 0 ) ) );
			}
			else
			{
				return false;
			}
		}
				
		if ( IPSMember::checkIgnoredStatus( $author['member_id'], $owner['member_id'], 'messages' ) )
		{
			return false;
		}
		
		return true;
	}
	
	/**
	 * Perm Check: Can post a status update
	 *
	 * @param	array	[Array of author data, uses getAuthor if none]
	 * @param	array	[Array of status owner information uses $this->_internalData['StatusOwner'] if none]
	 * @param	array	[Array of status data information uses $this->_internalData['StatusData'] if none]
	 * @return	bool
	 */
	public function canReply( $author=null, $owner=null, $status=null )
	{
		$author = ( $author === null ) ? $this->getAuthor() : $author;
		$owner  = ( $owner === null )  ? $this->_internalData['StatusOwner'] : $owner;
		$status = ( $status === null ) ? $this->_internalData['StatusData']  : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
		
		if ( ! $author['member_id'] )
		{
			return FALSE;
		}
		
		if ( ! $this->isEnabled() )
		{
			return FALSE;
		}
		
		if ( ! $author['g_mem_info'] OR $author['gbw_no_status_update'] OR $author['bw_no_status_update'] )
		{
			return FALSE;
		}
		
		if ( ! $this->_okToPost( $author, $owner ) )
		{
			return FALSE;
		}
		
		if ( $status['status_replies'] >= $this->settings['su_max_replies'] )
		{
			return FALSE;
		}
		
		if ( $status['status_is_locked'] )
		{
			if ( ! $author['g_is_supmod'] )
			{
				if ( $status['status_is_locked'] == 1 AND ( $author['member_id'] == $status['status_member_id'] ) )
				{
					return TRUE;
				}
				else
				{
					return FALSE;
				}
			}
		}
		
		return TRUE;
	}
	
	/**
	 * Perm Check: Can delete a status update
	 *
	 * @param	array	[Array of author data, uses getAuthor if none]
	 * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
	 * @return	bool
	 */
	public function canDeleteStatus( $author=null, $status=null )
	{
		$author  = ( $author === null ) ? $this->getAuthor() : $author;
		$status  = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
		
		if ( ! $author['member_id'] OR ! $status['status_id'] )
		{
			return FALSE;
		}
		
		if ( ! $this->isEnabled() )
		{
			return FALSE;
		}
		
		/* is left by someone else */
		if ( ( $status['status_member_id'] != $status['status_author_id'] ) && $status['status_author_id'] == $author['member_id'] )
		{
			return true;
		}
		
		if ( ! $author['g_is_supmod'] )
		{
			if ( $author['gbw_no_status_update'] OR $author['bw_no_status_update'] )
			{
				return FALSE;
			}
			
			if ( $author['member_id'] != $status['status_member_id'] )
			{
				return FALSE;
			}
		}
		
		return TRUE;
	}
	
	/**
	 * Perm Check: Can delete a reply
	 *
	 * @param	array	[Array of author data, uses getAuthor if none]
	 * @param	array	[Array of status reply information OR reply ID OR uses $this->_internalData['ReplyData'] if none]
	 * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
	 * @return	bool
	 */
	public function canDeleteReply( $author=null, $reply=null, $status=null )
	{
		$author  = ( $author === null ) ? $this->getAuthor() : $author;
		$reply   = ( $reply  === null ) ? $this->_internalData['ReplyData']  : ( ( is_array( $reply ) ) ? $reply : $this->_loadReply( $reply ) );
		$status  = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
		
		if ( ! $author['member_id'] OR ! $status['status_id'] )
		{
			return FALSE;
		}
		
		if ( ! $this->isEnabled() )
		{
			return FALSE;
		}
		
		if ( ! $author['g_is_supmod'] )
		{
			if ( $author['gbw_no_status_update'] OR $author['bw_no_status_update'] )
			{
				return FALSE;
			}
			
			if ( $author['member_id'] == $status['status_member_id'] )
			{
				return TRUE;
			}
			
			if ( $author['member_id'] == $reply['reply_member_id'] )
			{
				return TRUE;
			}
			
			return FALSE;
		}
		
		return TRUE;
	}
	
	/**
	 * Perm Check: Can approve a status
	 *
	 * @param	array	[Array of author data, uses getAuthor if none]
	 * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
	 * @return	bool
	 */
	public function canApproveStatus( $author=null, $status=null )
	{
		$author  = ( $author === null ) ? $this->getAuthor() : $author;
		$status  = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
	
		if ( ! $author['member_id'] OR ! $status['status_id'] )
		{
			return FALSE;
		}
	
		if ( ! $this->isEnabled() )
		{
			return FALSE;
		}
	
		if ( ! $author['g_is_supmod'] )
		{
			if ( $author['member_id'] == $status['status_member_id'] && !IPSMember::isOnModQueue( $author['member_id'] ))
			{
				return TRUE;
			}
				
			return FALSE;
		}
		else
		{
			return true;
		}
	
		/* is left by someone else */
		if ( ( $status['status_member_id'] != $status['status_author_id'] ) && $status['status_member_id'] != $author['member_id'] )
		{
			return false;
		}
	
		return TRUE;
	}
	
	/**
	 * Perm Check: Can lock a status
	 * status_is_locked: 1 means user lock, so owner can unlock, 2 means admin lock, so cannot unlock.
	 *
	 * @param	array	[Array of author data, uses getAuthor if none]
	 * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
	 * @return	bool
	 */
	public function canLockStatus( $author=null, $status=null )
	{
		$author  = ( $author === null ) ? $this->getAuthor() : $author;
		$status  = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
		
		if ( ! $author['member_id'] OR ! $status['status_id'] )
		{
			return FALSE;
		}
		
		if ( ! $this->isEnabled() )
		{
			return FALSE;
		}
		
		if ( ! $author['g_is_supmod'] )
		{
			if ( $author['member_id'] == $status['status_member_id'] )
			{
				return TRUE;
			}
			
			return FALSE;
		}
		else
		{
			return true;
		}
		
		/* is left by someone else */
		if ( ( $status['status_member_id'] != $status['status_author_id'] ) && $status['status_member_id'] != $author['member_id'] )
		{
			return false;
		}
		
		return TRUE;
	}
	
	/**
	 * Perm Check: Can lock a status
	 * status_is_locked: 1 means user lock, so owner can unlock, 2 means admin lock, so cannot unlock.
	 *
	 * @param	array	[Array of author data, uses getAuthor if none]
	 * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
	 * @return	bool
	 */
	public function canUnlockStatus( $author=null, $status=null )
	{
		$author  = ( $author === null ) ? $this->getAuthor() : $author;
		$status  = ( $status === null ) ? $this->_internalData['StatusData'] : ( ( is_array( $status ) ) ? $status : $this->_loadStatus( $status ) );
		
		if ( ! $author['member_id'] OR ! $status['status_id'] OR ! $status['status_is_locked'] )
		{
			return FALSE;
		}
		
		if ( ! $this->isEnabled() )
		{
			return FALSE;
		}
		
		if ( ! $author['g_is_supmod'] )
		{
			if ( $author['member_id'] == $status['status_member_id'] AND ( $status['status_is_locked'] == 1 ) )
			{
				return TRUE;
			}
			
			return FALSE;
		}
		
		return TRUE;
	}
	
	/**
	 * Record a custom action
	 *
	 * @param	string	Type (new or reply)
	 * @param	array   text, url, app
	 * @param	array	[Array of author data, uses getAuthor if none]
	 */
	public function recordAction( $type, $custom=array(), $author=null )
	{
		$author  = ( $author === null ) ? $this->getAuthor() : $author;
		
		if ( $author['member_id'] AND $custom['text'] )
		{
			$this->DB->insert( 'member_status_actions', array( 'action_member_id'    => $author['member_id'],
															   'action_date'	     => time(),
															   'action_custom'		 => 1,
															   'action_custom_text'  => $custom['text'],
															   'action_custom_url'   => $custom['url'],
															   'action_app'			 => $custom['app'],
															   'action_key'          => 'custom' ) );
		}
	}
	
	/**
	 * Record an action
	 *
	 * @param	string	Type (new or reply)
	 * @param	array	[Array of author data, uses getAuthor if none]
	 * @param	array	[Array of status information OR status ID OR uses $this->_internalData['StatusData'] if none]
	 * @param	array	[Array of status reply information OR reply ID OR uses $this->_internalData['ReplyData'] if none]
	 */
	protected function _recordAction( $type, $author=null, $status=null, $reply=null )
	{
		$author  = ( $author === null ) ? $this->getAuthor() : $author;
		$status  = ( $status === null ) ? $this->_internalData['StatusData'] : ( is_array( $status ) ) ? $status : ( $status ? $this->_loadStatus( $status ) : array() );
		$reply   = ( $reply  === null ) ? $this->_internalData['ReplyData']  : ( ( is_array( $reply ) ) ? $reply : $this->_loadReply( $reply ) );
		
		if ( $author['member_id'] AND $status['status_id'] )
		{
			$this->DB->insert( 'member_status_actions', array( 'action_status_id'    => $status['status_id'],
															   'action_reply_id'     => intval( $reply['reply_id'] ),
															   'action_member_id'    => $author['member_id'],
															   'action_status_owner' => $status['status_member_id'],
															   'action_date'	     => time(),
															   'action_custom'		 => 0,
															   'action_key'          => ( $type == 'new' ) ? 'new' : 'reply' ) );
		}
	}
	
	/**
	 * Load a status from the DB
	 *
	 * @param	int			Status ID
	 * @return	array		Array OR FALSE
	 */
	protected function _loadStatus( $statusId )
	{
		$status = array();
		$member = array();
		
		$this->DB->build( array( 'select'   => 's.*',
							     'from'	    => array( 'member_status_updates' => 's' ),
							     'where'    => 's.status_id=' . intval( $statusId ),
							     'add_join' => array(array(  'select'	=> 'm.*',
															 'from'	    => array( 'members' => 'm' ),
															 'where'    => 'm.member_id=s.status_member_id',
															 'type'	    => 'left' ),
													 array(  'select'   => 'pp.*',
															 'from'	    => array( 'profile_portal' => 'pp' ),
															 'where'    => 'pp.pp_member_id=m.member_id',
															 'type'	    => 'left' ) ) ) );
															 
		$this->DB->execute();
		
		$row = $this->DB->fetch();
		
		if( ! $row )
		{
			return FALSE;
		}
		
		foreach( $row as $k => $v )
		{
			if ( substr( $k, 0, 7 ) != 'status_' )
			{
				$member[ $k ] = $v;
			}
			
			$status[ $k ] = $v;
		}
		
		$this->_internalData['StatusData']  = $status;
		$this->_internalData['StatusOwner'] = $member;
		
		return ( isset( $status['status_id'] ) ) ? $status : FALSE;
	}
	
	/**
	 * Load a reply from the DB
	 *
	 * @param	int			Status ID
	 * @return	array		Array OR FALSE
	 */
	protected function _loadReply( $replyId )
	{
		$reply  = array();
		$member = array();
		
		$this->DB->build( array( 'select'   => 's.*',
							     'from'	    => array( 'member_status_replies' => 's' ),
							     'where'    => 's.reply_id=' . intval( $replyId ),
							     'add_join' => array(array(  'select'	=> 'm.*',
															 'from'	    => array( 'members' => 'm' ),
															 'where'    => 'm.member_id=s.reply_member_id',
															 'type'	    => 'left' ),
													 array(  'select'   => 'pp.*',
															 'from'	    => array( 'profile_portal' => 'pp' ),
															 'where'    => 'pp.pp_member_id=m.member_id',
															 'type'	    => 'left' ) ) ) );
															 
		$this->DB->execute();
		
		$row = $this->DB->fetch();
		
		if( ! $row )
		{
				return FALSE;
		}
		
		foreach( $row as $k => $v )
		{
			if ( substr( $k, 0, 6 ) != 'reply_' )
			{
				$member[ $k ] = $v;
			}
			
			$reply[ $k ] = $v;
		}
		
		$this->_internalData['ReplyData']  = $reply;
		$this->_internalData['ReplyOwner'] = $member;
		
		return ( isset( $reply['reply_id'] ) ) ? $reply : FALSE;
	}
	
	/**
	 * Clean up content suitable for posting
	 *
	 * @param	string		In
	 * @return	string		Out
	 */
	protected function _cleanContent( $content )
	{
		/* Just cut off after X chars. */
		$content = IPSText::truncate( IPSText::getTextClass('bbcode')->stripBadWords( $content ), $this->settings['su_max_chars'] );
		
		return $content;
	}
	
	/**
	 * Auto parse some stuff
	 *
	 * Eventually could abstract it out but for now, this will do. Mkay.
	 */
	protected function _parseContent( $content, $creator='' )
	{
		/* Auto parse tags */
		if ( $this->settings['su_parse_url'] )
		{
			$content = preg_replace_callback( '#(^|\s|\(|>|\](?<!\[url\]))((?:http|https|news|ftp)://\w+[^\),\s\<\[]+)#is', array( $this, '_autoParseUrls' ), $content );
		}
		
		/* Twittah? */
		if ( $creator == 'twitter' )
		{
			if ( $this->settings['tc_parse_tags'] )
			{
				$content = preg_replace_callback( '#(^|\s)(\\#([a-z_A-Z0-9:_-]+))#', array( $this, '_autoParseTags' ), $content );
			}
			
			if ( $this->settings['tc_parse_names'] )
			{
				$content = preg_replace_callback('#(^|\s)@([a-z_A-Z0-9]+)#', array( $this, '_autoParseNames' ), $content );
			}
		}
		
		return $content;
	}
	
	/**
	 * Callback to auto-parse @names
	 *
	 * @param	array		Matches from the regular expression
	 * @return	string		Converted text
	 */
	protected function _autoParseNames( $matches )
	{
		return $this->_autoParseUrls( array( '', $matches[1], 'http://www.twitter.com/' . urlencode( $matches[2] ), '@' . $matches[2] ) );
	}
	
	/**
	 * Callback to auto-parse #tags
	 *
	 * @param	array		Matches from the regular expression
	 * @return	string		Converted text
	 */
	protected function _autoParseTags( $matches )
	{
		return $this->_autoParseUrls( array( '', $matches[1], 'http://twitter.com/search?q=%23' . urlencode( $matches[3] ), $matches[2] ) );
	}
	
	/**
	 * Callback to auto-parse urls
	 * I totally stole this from Brandon's code but do NOT tell him.
	 *
	 * @param	array		Matches from the regular expression
	 * @return	string		Converted text
	 */
	protected function _autoParseUrls( $matches )
	{
		/* We use this function in other areas also */
		$matches[3] = ( $matches[3] ) ? $matches[3] : $matches[2];
		
		//-----------------------------------------
		// Adding rel='nofollow'?
		//-----------------------------------------
		
		$rels	= array();
		$rel	= '';

		if( $this->settings['posts_add_nofollow'] )
		{
			$rels[]	= "nofollow";
		}
		
		if( $this->settings['links_external'] )
		{
			$rels[]	= "external";
		}
		
		if( count($rels) )
		{
			$rel = " rel='" . implode( ' ', $rels ) . "'";
		}
		
		return $matches[1] . '<a href="' . $matches[2] . '"' . $rel . ' class="su_links">' . $matches[3] . '</a>';
	}
	
	/**
     * Check for mod posts or restricted posts or ignored
     *
     * @param	array	[Array of author data, uses getAuthor if none]
	 * @param	array	[Array of status owner information uses $this->_internalData['StatusOwner'] if none]
     * @return	bool
     */
    protected function _okToPost( $author=null, $owner=null )
    {
    	$author = ( $author === null ) ? $this->getAuthor() : $author;
		$owner  = ( $owner === null )  ? $this->_internalData['StatusOwner'] : $owner;
		
    	/* Restricted Posting */
    	if ( $author['restrict_post'] )
		{
			if ( $author['restrict_post'] == 1 )
			{
				return FALSE;
			}
			
			$post_arr = IPSMember::processBanEntry( $author['restrict_post'] );
			
			if( time() >= $post_arr['date_end'] )
			{
				/* Update this member's profile */
				IPSMember::save( $author['member_id'], array( 'core' => array( 'restrict_post' => 0 ) ) );
			}
			else
			{
				return FALSE;
			}
		}

		/* Moderated Posting */
		if ( $author['mod_posts'] )
		{
			if ( $author['mod_posts'] == 1 )
			{
				return FALSE;
			}
			else
			{
				$mod_arr = IPSMember::processBanEntry( $author['mod_posts'] );
				
				if( time() >= $mod_arr['date_end'] )
				{
					/* Update this member's profile */
					IPSMember::save( $author['member_id'], array( 'core' => array( 'mod_posts' => 0 ) ) );
				}
				else
				{
					return FALSE;
				}
			}
		}
		
		/* Member is ignoring you! */
		if ( IPSMember::checkIgnoredStatus( $author['member_id'], $owner['member_id'], 'messages' ) )
		{
			return false;
		}
		
		return TRUE;
    }
    
	/**
	 * Various getters and setters
	 *
	 * @param	string
	 * @param	mixed		void, or an array of arguments
	 * @return	mixed		Data
	 */
	public function __call( $method, $arguments )
	{
		$firstBit = substr( $method, 0, 3 );
		$theRest  = substr( $method, 3 );
	
		if ( in_array( $theRest, $this->_allowedInternalData ) )
		{
			if ( $firstBit == 'set' )
			{
				if ( $theRest == 'Author' OR $theRest == 'Friend' )
				{
					if ( is_array( $arguments[0] ) )
					{
						/* Ensure we have group data */
						if ( ! isset( $arguments[0]['g_id'] ) AND $arguments[0]['member_id'] )
						{
							$this->_internalData[ $theRest ] = IPSMember::load( intval( $arguments[0]['member_id'] ), 'all' );
						}
						else
						{
							$this->_internalData[ $theRest ] = $arguments[0];
						}
					}
					else
					{
						if( $arguments[0] )
						{
							/* Set up moderator stuff, too */
							$this->_internalData[ $theRest ] = IPSMember::load( intval( $arguments[0] ), 'all' );
						}
						else
						{
							$this->_internalData[ $theRest ] = IPSMember::setUpGuest();
						}
					}
				}
				else if ( $theRest == 'StatusData' )
				{
					if ( is_array( $arguments[0] ) )
					{
						$this->_internalData[ $theRest ] = $arguments[0];
					}
					else
					{
						$this->_internalData[ $theRest ] = $this->_loadStatus( intval( $arguments[0] ) );
					}
				}
				else if ( $theRest == 'ReplyData' )
				{
					if ( is_array( $arguments[0] ) )
					{
						$this->_internalData[ $theRest ] = $arguments[0];
					}
					else
					{
						$this->_internalData[ $theRest ] = $this->_loadReply( intval( $arguments[0] ) );
					}
				}
				else
				{
					$this->_internalData[ $theRest ] = $arguments[0];
					return TRUE;
				}
				
				return $this->_internalData[ $theRest ];
			}
			else
			{
				if ( ( $theRest == 'Author' OR $theRest == 'Friend' OR $theRest == 'StatusData' OR $theRest == 'ReplyData' ) AND isset( $arguments[0] ) )
				{
					return isset( $this->_internalData[ $theRest ][ $arguments[0] ] ) ? $this->_internalData[ $theRest ][ $arguments[0] ] : '';
				}
				else
				{
					return isset( $this->_internalData[ $theRest ] ) ? $this->_internalData[ $theRest ] : '';
				}
			}
		}
	}
}