<?php
/**
 * <pre>
 * Invision Power Services
 * IP.Board v3.2.0
 * Custom Profile Fields
 * Last Updated: $Date: 2012-09-21 10:15:19 -0400 (Fri, 21 Sep 2012) $
 * </pre>
 *
 * @author 		$Author: mark $
 * @copyright	(c) 2001 - 2009 Invision Power Services, Inc.
 *
 * @package		IP.Board
 *
 * @version		$Rev: 11360 $
 *
 */
 
if ( ! defined( 'IN_IPB' ) )
{
	print "<h1>Incorrect access</h1>You cannot access this file directly. If you have recently upgraded, make sure you upgraded all the relevant files.";
	exit();
}

class customProfileFields
{
	/**
	 * Member ID
	 *
	 * @var		int
	 */
	public $member_id		= 0;
	
	/**
	 * Member ID
	 *
	 * @var		int
	 */
	public $mem_data_id		= 0;
	
	/**
	 * Array of incoming data
	 *
	 * @var		array
	 */
	public $in_fields		= array();
	
	/**
	 * Final/parsed data
	 *
	 * @var		array
	 */
	public $out_fields		= array();
	
	/**
	 * Chosen output data
	 *
	 * @var		array
	 */
	public $out_chosen		= array();
	
	/**
	 * Member record
	 *
	 * @var		array
	 */	
	public $member_data		= array();
	
	/**
	 * Field names
	 *
	 * @var		array
	 */
	public $field_names		= array();
	
	/**
	 * Field descriptions
	 *
	 * @var		array
	 */	
	public $field_desc		= array();
	
	/**
	 * Error fields
	 *
	 * @var		array
	 */
	public $error_fields	= array();
	
	/**
	 * Error messages
	 *
	 * @var		array
	 */	
	public $error_messages	= array();
	
	/**
	 * What type of parse this is
	 *
	 * @var		string
	 */
	public $type			= '';
	
	/**
	 * Is an admin
	 *
	 * @var		bool
	 */
	public $admin			= false;
	
	/**
	 * Is an admin
	 *
	 * @var		bool
	 */
	public $supmod			= false;	
	
	/**
	 * Initialized yet
	 *
	 * @var		bool
	 */
	public $init			= false;	
	
	/**
	 * Cache data
	 *
	 * @var		array
	 */
	public $cache_data		= array();
	
	/**
	 * Custom fields library
	 *
	 * @var		object
	 */
	public $cfields;
	
	/**
	 * Skin group to use for view parsing
	 *
	 * @var		string
	 */
	public $skinGroup		= '';
	
	/**
	 * Database handle
	 *
	 * @var		object
	 */
	protected $DB;
	
	/**
	 * Already added fields
	 * Prevents MSSQL getting confused
	 *
	 * @var		array
	 */
	protected $_addedFields = array();
	
	/* Ability to pull fields from groups */
	protected $fromGroups = array();
	
	/* Ability to exlude fields from groups */
	protected $notGroups = array();
	
	/**
	 * CONSTRUCTOR
	 *
	 * @return	@e void
	 */
	public function __construct()
	{
		/* Shortcuts */
		$this->DB		  = ipsRegistry::DB();
		$this->cache_data = ipsRegistry::cache()->getCache( 'profilefields' );
		
		/* User Setup */
		$this->member_id  = ipsRegistry::member()->getProperty( 'member_id' );
		$this->admin      = intval( ipsRegistry::member()->getProperty( 'g_access_cp' ) );
		$this->supmod     = intval( ipsRegistry::member()->getProperty( 'g_is_supmod' ) );
	}
	
	/**
	 * Set only to show from x groups
	 *
	 * @param	mixed	string or array
	 */
	public function setFromGroups( $in )
	{
		$this->fromGroups = ( is_array( $in ) ) ? $in : array( $in );
	}
	
	/**
	 * Set only to show from x groups
	 *
	 * @param	mixed	string or array
	 */
	public function setNotGroups( $in )
	{
		$this->notGroups = ( is_array( $in ) ) ? $in : array( $in );
	}
	
	/**
	 * Initializes cache, loads kernel class, and formats data for kernel class
	 *
	 * @param	string	$type	Set to view for displaying the field normally or edit for displaying in a form
	 * @param	bool	$mlist	Whether this is the memberlist or not
	 * @return	@e void
	 */
	public function initData( $type='view', $mlist=0, $attributes=array() )
	{	
		/* Store Type */
		$this->type = $type;

		/* Get Member */
		if( ! count( $this->member_data ) and $this->mem_data_id && ! $mlist )
		{
			$this->member_data = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'pfields_content', 'where' => 'member_id='.intval($this->mem_data_id) ) );
		}
		
		if( count( $this->member_data ) )
		{
			$this->mem_data_id = isset( $this->member_data['member_id'] ) ? $this->member_data['member_id'] : 0;
		}
		
		if( ! $this->init )
		{
			/* Cache data... */
			if( ! is_array( $this->cache_data ) )
			{
				$this->DB->build( array( 'select' => '*', 'from' => 'pfields_data', 'order' => 'pf_group_id,pf_position' ) );
				$this->DB->execute();
				
				while ( $r = $this->DB->fetch() )
				{
					$this->cache_data[ $r['pf_id'] ] = $r;
				}
			}
		}
			
		/* Get names... */
		if( is_array( $this->cache_data ) and count( $this->cache_data ) )
		{
			foreach( $this->cache_data as $id => $data )
			{
				/* Field names and descriptions */
				$this->field_names[ $id ] = $data['pf_title'];
				$this->field_desc[ $id ]  = $data['pf_desc'];
				
				/* In Fields */
				foreach( $this->cache_data as $id => $data )
				{
					$data['pf_key']       = ( ! empty( $data['pf_key'] ) ) ? $data['pf_key'] : '_key_' . $data['pf_id'];
					$data['pf_group_key'] = $data['pf_group_key'] ? $data['pf_group_key'] : '_other';
					
					if( $mlist )
					{
						$this->in_fields[ $id ] = '';
						
						if( !empty(ipsRegistry::$request[ 'field_' . $id ]) )
						{
							if( is_string(ipsRegistry::$request[ 'field_' . $id ]) )
							{
								$this->in_fields[ $id ] = urldecode(ipsRegistry::$request[ 'field_' . $id ]);
							}
							else if( is_array(ipsRegistry::$request[ 'field_' . $id ]) )
							{
								foreach( ipsRegistry::$request[ 'field_' . $id ] as $k => $v )
								{
									$this->in_fields[ $id ][ $k ] = urldecode($v);
								}
							}
						}
					}
					else
					{
						$_val	= '';

						if( is_string(ipsRegistry::$request[ 'field_' . $id ]) )
						{
							$_val	= urldecode(ipsRegistry::$request[ 'field_' . $id ]);
						}
						else if( is_array(ipsRegistry::$request[ 'field_' . $id ]) )
						{
							foreach( ipsRegistry::$request[ 'field_' . $id ] as $k => $v )
							{
								$_val[ $k ] = urldecode($v);
							}
						}
							
						$this->in_fields[ $id ] = isset( $this->member_data['field_'.$id] ) ? $this->member_data['field_' . $id] : $_val;
					}					
				}						
			}
		}

		/* Clean up on aisle #4 */
		$this->out_fields = array();
		$this->out_chosen = array();

		/* Format data for kernel class */
		foreach( $this->cache_data as $k => $v )
		{
			/* Add any option to dropdown */
			if( $v['pf_type'] == 'drop' && $mlist )
			{
				$v['pf_content'] = '0=|' . $v['pf_content'];
			}			
			/* Field Info */
			$this->cache_data[$k]['id']    = $v['pf_id'];
			$this->cache_data[$k]['type']  = $v['pf_type'];
			$this->cache_data[$k]['data']  = $v['pf_content'];
			$this->cache_data[$k]['value'] = $this->in_fields[$k];
			
			/* Field Restrictions */
			$this->cache_data[$k]['restrictions'] = array(
					
															'max_size'	=> isset( $v['pf_max_input'] )		? $v['pf_max_input']	: '',
															'min_size'	=> isset( $v['pf_min_input'] )		? $v['pf_min_input']	: '',
															'not_null'	=> isset( $v['pf_not_null'] )		? $v['pf_not_null']		: '',
															'format'	=> isset( $v['pf_input_format'] )	? $v['pf_input_format']	: '',
															'urlfilter'	=> isset( $v['pf_filtering'] )		? $v['pf_filtering']	: '',
														);
														
			if ( !empty( $attributes ) )
			{
				if ( !isset( $this->cache_data[$k]['attributes'] ) )
				{
					$this->cache_data[$k]['attributes'] = $attributes;
				}
				else
				{
					$this->cache_data[$k]['attributes'] = array_merge( $this->cache_data[$k]['attributes'], $attributes );
				}
			}
		}

		/* Kernel profile field class */
		$_NOW = IPSDebug::getMemoryDebugFlag();
		
		$classToLoad = IPSLib::loadLibrary( IPS_KERNEL_PATH . 'classCustomFields.php', 'classCustomFields' );
		
		IPSDebug::setMemoryDebugFlag( "Get CustomFields Kernel Class", $_NOW );
		
		$this->cfields_obj = new $classToLoad( $this->cache_data, $type, $attributes );
		$this->cfields     = $this->cfields_obj->cfields;

		$this->init = 1;
	}

	/**
	 * Parses fields for saving into the database, results are stored in $this->out_fields
	 *
	 * @param	array	$field_data	Array that contains the fields to parse, usually $this->request
	 * @return	@e void
	 */
	public function parseToSave( $field_data, $mode='normal' )
	{
		/* Parse the fields */
		$save_fields = $this->cfields_obj->getFieldsToSave( $field_data );
		
		/* Save the raw error data */
		$this->error_fields = $save_fields['errors'];

		/* Reformat the errors into nicer output */
		if( is_array( $this->error_fields ) && count( $this->error_fields ) )
		{
			/* Make sure error message texts are loaded */
			ipsRegistry::getClass( 'class_localization' )->loadLanguageFile( array( 'public_profile' ), 'members' );
			
			foreach( $this->error_fields as $id => $err )
			{
				/* Can we view this field? */
				if( ! $this->_checkFieldAuth( $this->cfields[ str_replace( 'field_', '', $id ) ], $mode ) )
				{
					continue;		
				}

				$_error_messages = array();
				
				foreach( $err as $e )
				{
					$_error_messages[] = ipsRegistry::getClass( 'class_localization' )->words[ 'profile_field_error__' . $e ];
				}

				$this->error_messages[$id] = $this->cache_data[ str_replace( 'field_', '', $id ) ]['pf_title'] . ': ' . implode( ', ', $_error_messages );
			}
		}

		/* Loop through our custom fields */
		foreach( $this->cfields as $id => $field )
		{
			/* Can we view this field? */
			if( ! $this->_checkFieldAuth( $field, $mode ) )
			{
				continue;
			}
			
			/* Now add any missing content fields */
			if ( ! isset( $this->member_data[ 'field_' . $id ] ) OR IN_ACP )
			{
				if ( ! $this->DB->checkForField( "field_$id", 'pfields_content' ) AND ! isset( $this->_addedFields[ $id ] ) )
				{
					$this->DB->addField( 'pfields_content', "field_$id", 'text' );
					
					$this->_addedFields[ $id ] = $id;
				}
			}

			$this->out_fields[ 'field_' . $id ] = IPSText::getTextClass( 'bbcode' )->stripBadWords( $save_fields['save_array']['field_' . $id] );
			
			//-----------------------------------------
			// Blacklisted urls
			// @link	http://community.---.com/tracker/issue-7872-url-black-list-in-profile/
			//-----------------------------------------
			
			if( $this->cache_data[$id]['restrictions']['urlfilter'] AND !IPSText::getTextClass( 'bbcode' )->checkBlacklistUrls( $save_fields['save_array']['field_' . $id] ) )
			{
				/* Make sure error message texts are loaded */
				ipsRegistry::getClass( 'class_localization' )->loadLanguageFile( array( 'public_profile' ), 'members' );
			 
				if( isset( $this->error_messages[ 'field_' . $id ] ) )
				{
					$this->error_messages['field_' . $id] .= ', ' . ipsRegistry::getClass( 'class_localization' )->words['profile_field_error__blacklist'];
				}
				else
				{
					$this->error_messages['field_' . $id] = $this->cache_data[ $id ]['pf_title'] . ': ' . ipsRegistry::getClass( 'class_localization' )->words['profile_field_error__blacklist'];
				}
			}
		}
		
		//print_r($this->error_messages);
	}
	
	/**
	 * Parses fields for viewing, results are stored in $this->out_fields
	 *
	 * @param	bool	$check_topic_format		Whether to apply topic formatting
	 * @param	string	$location				Location being called from
	 * @return	@e void
	 */
	public function parseToView( $check_topic_format=0, $location='profile' )
	{
		/* Loop through our custom fields */
		foreach( $this->cfields as $id => $field )
		{
			/* Can we view this field? */
			if ( ! $this->_checkFieldAuth( $field ) )
			{
				continue;		
			}
			
			/* Topic Format */
			if( $check_topic_format )
			{
				if ( ! $field->raw_data['pf_topic_format'] OR $location != 'topic' )
				{
					/* Override formatting with skin function? */
					if( $this->skinGroup )
					{
						/* Check for a field level skin bit first */
						$__func = 'customField__' . $field->raw_data['pf_key'];
						
						if( method_exists( ipsRegistry::getClass( 'output' )->getTemplate( $this->skinGroup ), $__func ) )
						{	
							$this->out_fields[ $field->raw_data['pf_group_key'] ] [ $field->raw_data['pf_key'] ] = ipsRegistry::getClass( 'output')->getTemplate( $this->skinGroup )->$__func( $field );
							continue;
						}
												
						/* Now check for a group level skin bit */
						$__func = 'customFieldGroup__' . $field->raw_data['pf_group_key'];
						
						if( method_exists( ipsRegistry::getClass( 'output' )->getTemplate( $this->skinGroup ), $__func ) )
						{
							$this->out_fields[ $field->raw_data['pf_group_key'] ] [ $field->raw_data['pf_key'] ] = ipsRegistry::getClass( 'output')->getTemplate( $this->skinGroup )->$__func( $field );
							continue;
						}						
						
						/* Now check for a generic skin bit */
						$__func = 'customField__generic';
						
						if( method_exists( ipsRegistry::getClass( 'output' )->getTemplate( $this->skinGroup ), $__func ) )
						{
							$this->out_fields[ $field->raw_data['pf_group_key'] ] [ $field->raw_data['pf_key'] ] = ipsRegistry::getClass( 'output')->getTemplate( $this->skinGroup )->$__func( $field );
							continue;
						}
					}
	
					if ( ! $field->raw_data['pf_topic_format'] )
					{
						continue;
					}
				}
			}

			$this->out_fields[ $field->raw_data['pf_group_key'] ][ $field->raw_data['pf_key'] ] = $field->getValue();
		
			/* Format using the format configured in the ACP */
			if( $check_topic_format )
			{
				/* Save the value */
				$current_value = isset( $this->out_fields[ $field->raw_data['pf_group_key'] ][ $field->raw_data['pf_key'] ] ) ? $this->out_fields[ $field->raw_data['pf_group_key'] ][ $field->raw_data['pf_key'] ] : '';

				if( $current_value || $current_value == '0' )
				{
					/* Get the format */
					$this->out_fields[ $field->raw_data['pf_group_key'] ] [ $field->raw_data['pf_key'] ] = $field->raw_data['pf_topic_format'];
					
					$this->out_fields[ $field->raw_data['pf_group_key'] ] [ $field->raw_data['pf_key'] ] = str_replace( '{title}'  , $field->raw_data['pf_title'], $this->out_fields[ $field->raw_data['pf_group_key'] ] [ $field->raw_data['pf_key'] ] );
					$this->out_fields[ $field->raw_data['pf_group_key'] ] [ $field->raw_data['pf_key'] ] = str_replace( '{key}'    , $this->in_fields[$field->id]  , $this->out_fields[ $field->raw_data['pf_group_key'] ] [ $field->raw_data['pf_key'] ] );
					$this->out_fields[ $field->raw_data['pf_group_key'] ] [ $field->raw_data['pf_key'] ] = str_replace( '{content}', $current_value                , $this->out_fields[ $field->raw_data['pf_group_key'] ] [ $field->raw_data['pf_key'] ] );				
				}
			}
		}
	}
	
	/**
	 * Fetch group keys and titles
	 *
	 * @return	array 	array( pf_group_key => pf_group_name )
	 */
	public function fetchGroupTitles()
	{
		$return = array();
		
		/* Loop through our custom fields */
		foreach( $this->cfields as $id => $field )
		{
			/* Can we view this field? */
			if( ! $this->_checkFieldAuth( $field ) )
			{
				continue;		
			}
			
			$return[ $field->raw_data['pf_group_key'] ] = $field->raw_data['pf_group_name'];
		}
		
		return $return;
	}
	
	/**
	 * Parses fields for editing, results are stored in $this->out_fields
	 *
	 * @param	string	[$mode]	register or normal, normal is default
	 * @return	@e void
	 */
	public function parseToEdit( $mode='normal' )
	{
		/* Loop through our custom fields */
		foreach( $this->cfields as $id => $field )
		{
			/* Can we view this field? */
			if( ! $this->_checkFieldAuth( $field, $mode ) )
			{
				continue;
			}
			
			if( $mode == 'register' && ! $field->raw_data['pf_show_on_reg'] )
			{
				continue;
			}
			
			$this->out_fields[ $id ] = $field->getValue();

		}
	}
	
	/**
	 * Checks to see if the field is viewable by the current user
	 *
	 * @param	array	$field	Array of field data
	 * @param	string	$mode	Register, or blank
	 * @return	bool
	 */
	protected function _checkFieldAuth( $field, $mode='normal' )
	{
		/* Admin / mod only? */
		if( $field->raw_data['pf_admin_only'] )
		{
			if ( ! $this->admin AND ! $this->supmod )
			{
				return false;
			}
		}
		
		/* Whitelist */
		if ( count( $this->fromGroups ) )
		{
			if ( in_array( $field->raw_data['pf_group_key'], $this->fromGroups ) )
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		
		/* Blacklist */
		if ( count( $this->notGroups ) )
		{
			if ( in_array( $field->raw_data['pf_group_key'], $this->notGroups ) )
			{
				return false;
			}
		}
		
		/* Member Edit? */
		if( $this->type == 'edit' && ! $field->raw_data['pf_member_edit'] )
		{
			if ( $mode != 'register' AND $mode != 'mlist' AND ! $this->admin AND !$this->supmod )
			{
				return false;
			}
		}
		
		/* Registering */
		if ( $mode == 'register' AND ! $field->raw_data['pf_show_on_reg'] AND ! IN_ACP ) // Bug Fix #21163
		{
			return false;
		}
		
		/* Private FIeld */
		if( $field->raw_data['pf_member_hide'] )
		{
			$pass = 0;
			
			if ( $this->admin )
			{
				$pass = 1;
			}
			else if ( $this->supmod )
			{
				$pass = 1;
			}
			else if ( ($this->member_id and ( $this->member_id == $this->mem_data_id )) OR ($this->type == 'edit' AND $mode == 'register') )
			{
				$pass = 1;
			}
			else
			{
				$pass = 0;
			}
			
			if ( ! $pass )
			{
				return false;
			}
		}

		return true;
	}
	
	/**
	 * Returns the id for the specified key
	 *
	 * @param	string	$key	Field key
	 * @return	integer
	 */
	public function getFieldIDByKey( $key )
	{
		$field = 0;
		
		foreach( $this->cache_data as $id => $_c )
		{
			if( $_c['pf_key'] == $key )
			{
				$field = $id;
				break;
			}
		}
		
		return $field;
	}
}