PageRenderTime 630ms CodeModel.GetById 81ms app.highlight 377ms RepoModel.GetById 47ms app.codeStats 3ms

/app/modules/users/models/user_model.php

https://bitbucket.org/nanomites_webdev/heroframework
PHP | 1656 lines | 911 code | 273 blank | 472 comment | 182 complexity | 6475090a07e999d2164255fac2a274ef MD5 | raw file
   1<?php
   2
   3/**
   4* User Model 
   5*
   6* Contains all the methods used to create, update, login, logout, and delete users.
   7*
   8* @author Electric Function, Inc.
   9* @copyright Electric Function, Inc.
  10* @package Hero Framework
  11*
  12*/
  13
  14class User_model extends CI_Model
  15{
  16	public $active_user;  // the logged-in use
  17	public $failed_due_to_activation; // if the login failed to the account not being activated, this == TRUE
  18	public $failed_due_to_duplicate_login; // if the login failed because someone is already logged in, this == TRUE
  19	
  20	// this will change if we are in the control panel, as we want to have independent sessions foreach
  21	// so a user can be logged in the CP but no the frontend (helps for testing - eases confusion)
  22	private $session_name = 'user_id';
  23	
  24	// should we trigger the member_register hook or create the user silently in new_user()?
  25	public $trigger_register_hook = TRUE;
  26	
  27	// are we in the control panel?
  28	private $in_admin = null;
  29	
  30	private $cache_fields;
  31	
  32	function __construct()
  33	{
  34		parent::__construct();
  35		
  36		if ($this->in_admin()) {
  37			// let's use a different session token so that we can independent sessions
  38			$this->make_admin_session();
  39		}
  40		
  41		// check for session
  42        if ($this->session->userdata($this->session_name) != '') {
  43        	// load active user into cache for future ->Get() calls
  44        	$this->set_active($this->session->userdata($this->session_name));
  45        	
  46        	// no carts in the control panel...
  47        	if (!$this->in_admin() and module_installed('store')) {
  48	        	// handle a potential cart
  49	        	$CI =& get_instance();
  50	        	$CI->load->model('store/cart_model');
  51	        	$CI->cart_model->save_cart_to_db();
  52	        }
  53        }
  54        else {
  55        	// we don't have remember_keys for admins...
  56        	if (!$this->in_admin()) {
  57	        	$this->load->helper('cookie');
  58	        	
  59	        	// we may have a remembered user
  60	        	if (get_cookie('user_remember_key',TRUE)) {
  61	        		// does this correspond with a remember key?
  62	        		$this->db->select('user_id');
  63	        		$this->db->where('user_remember_key', get_cookie('user_remember_key', TRUE));
  64	        		$result = $this->db->get('users');
  65	        		
  66	        		if ($result->num_rows() == 0) {
  67	        			// no correspondence, this key has expired
  68	        			delete_cookie('user_remember_key');
  69	        			
  70	        			$this->db->update('users',array('user_remember_key' => ''),array('user_remember_key' => get_cookie('user_remember_key', TRUE)));
  71	        		}
  72	        		else {
  73	        			$user = $result->row_array();
  74	        			
  75	        			$this->login_by_id($user['user_id']);
  76	        		}
  77	        	}
  78	        }
  79        }
  80	}
  81	
  82	/**
  83	* CP Check
  84	* 
  85	* Are we in the control panel?
  86	*
  87	* @return boolean
  88	*/
  89	private function in_admin () {
  90		if ($this->in_admin != null) {
  91			return $this->in_admin;
  92		}
  93	
  94		$CI =& get_instance();
  95		
  96		$url = $CI->uri->uri_string();
  97		// it may be at 0 or 1 depending on if we retained the initial slash...
  98		if (strpos($url, 'admincp') === 0 or strpos($url, 'admincp') === 1) {
  99			$this->in_admin = TRUE;
 100		}
 101		else {
 102			$this->in_admin = FALSE;
 103		}
 104		
 105		return $this->in_admin;
 106	}
 107	
 108	/**
 109	* Make Admin Session
 110	*
 111	* Forces the model into an admin session
 112	*
 113	* @return void 
 114	*/
 115	function make_admin_session () {
 116		$this->session_name = 'admin_id';
 117	}
 118	
 119	/**
 120	* Make Frontend Session
 121	*
 122	* Forces the model into a user session
 123	*
 124	* @return void 
 125	*/
 126	function make_frontend_session () {
 127		$this->session_name = 'user_id';
 128	}
 129	
 130	/**
 131	* Login User
 132	*
 133	* Logs a user in, sets the $_SESSION, updates the user's last login, and tracks login
 134	*
 135	* @param string $username Either the username or email of the user
 136	* @param string $password Their password
 137	* @param boolean $remember Remember the user with a cookie to re-log them in at future visits (default: FALSE)
 138	*
 139	* @return boolean FALSE upon failure, TRUE upon success
 140	*/
 141	public function login ($username, $password, $remember = FALSE) {
 142		$authenticated = FALSE;
 143		
 144		// stop SQL injection
 145		$username = addslashes($username);
 146	
 147		$this->db->where('(`user_username` = \'' . $username . '\' or `user_email` = \'' . $username . '\')');
 148		$this->db->where('user_suspended','0');
 149		$this->db->where('user_deleted','0');
 150		$query = $this->db->get('users');
 151		
 152		if ($query->num_rows() > 0) {
 153			$user_db = $query->row_array();
 154			$user = $this->get_user($user_db['user_id']);
 155			
 156			$hashed_password = ($user['salt'] == '') ? md5($password) : md5($password . ':' . $user['salt']);
 157			
 158			if ($hashed_password == $user_db['user_password']) {
 159				$authenticated = TRUE;
 160			}
 161		}
 162
 163		if ($authenticated === TRUE) { 
 164			if ($this->config->item('simultaneous_login_prevention') == '1') {
 165				// let's make sure someone isn't logged into the account right now
 166				$this->db->where('user_id',$user['id']);
 167				$this->db->where('user_activity_date >',date('Y-m-d H:i:s', time() - 60));
 168				$result = $this->db->get('user_activity');
 169				if ($result->num_rows() > 0) {
 170					$this->failed_due_to_duplicate_login = TRUE;
 171					
 172					return FALSE;
 173				}
 174			}
 175			
 176			// let's make sure they are activated if it's been more than 1 day
 177			if (!empty($user['validate_key']) and ((time() - strtotime($user['signup_date'])) > (60*60*24))) {
 178				$this->failed_due_to_activation = TRUE;
 179				
 180				return FALSE;
 181			}
 182		}
 183		else {
 184			return FALSE;
 185		}
 186		
 187		// track login
 188		$this->login_by_id($user['id'], $password); 
 189		
 190		// remember?
 191		if ($remember == TRUE) {
 192			$remember_key = random_string('unique');
 193			
 194			$result = $this->db->select('user_id')->where('user_remember_key',$remember_key)->get('users');
 195			while ($result->num_rows() > 0) {
 196				$remember_key = random_string('unique');
 197				
 198				$result = $this->db->select('user_id')->where('user_remember_key',$remember_key)->get('users');
 199			}
 200			
 201			// create the cookie with the key
 202			$this->load->helper('cookie');
 203			
 204			$cookie = array(
 205			                   'name'   => 'user_remember_key',
 206			                   'value'  => $remember_key,
 207			                   'expire' => (60*60*24*365) // 1 year
 208			               );
 209			
 210			set_cookie($cookie); 
 211			
 212			// put key in database
 213			$this->db->update('users',array('user_remember_key' => $remember_key),array('user_id' => $user['id']));
 214		}
 215		
 216		return TRUE;
 217    }
 218    
 219    /**
 220    * Login by ID
 221    *
 222    * @param int $user_id
 223    *
 224    * @return boolean 
 225    */
 226    function login_by_id ($user_id, $password = FALSE) {
 227    	$CI =& get_instance();
 228    	$CI->load->model('users/login_model');
 229		$CI->login_model->new_login($user_id);
 230		
 231		$this->db->update('users',array('user_last_login' => date('Y-m-d H:i:s')),array('user_id' => $user_id));
 232    	
 233    	$this->session->set_userdata($this->session_name,$user_id);
 234    	$this->session->set_userdata('login_time',now());
 235		
 236		$this->set_active($user_id);
 237		
 238		// track activity
 239		$this->db->insert('user_activity', array('user_id' => $user_id, 'user_activity_date' => date('Y-m-d H:i:s')));
 240		
 241		// cart functions
 242		if (module_installed('store/cart_model')) {
 243			$CI =& get_instance();
 244			$CI->load->model('store/cart_model');
 245			$CI->cart_model->user_login($this->active_user);
 246		}
 247		
 248		// salt password
 249		if (!empty($password)) {
 250			// do we have a salt?
 251			if ($this->get('salt') == '') {
 252				// salt it!
 253				$CI->load->helper('string');
 254				$salt = random_string('unique');
 255				
 256				// new password with salt
 257				$salted_password = md5($password . ':' . $salt);
 258				
 259				$this->db->update('users', array('user_salt' => $salt, 'user_password' => $salted_password), array('user_id' => $this->get('id')));
 260			}
 261		}
 262		
 263		// do we have a customer record for this user?
 264		// trigger its creation if not, else just get the active customer ID
 265		$this->active_user['customer_id'] = $this->get_customer_id($user_id);
 266		
 267		// prep hook
 268		$CI =& get_instance();
 269		// call the library here, because this may be loaded in the admin/login controller which doesn't preload
 270		// app_hooks like the other controllers
 271		$CI->load->library('app_hooks');
 272		$CI->app_hooks->data('member', $user_id);
 273		$CI->app_hooks->trigger('member_login', $user_id, $password);
 274		$CI->app_hooks->reset();
 275		
 276		return TRUE;
 277    }
 278    
 279    /**
 280    * User Logout
 281    *
 282    * @return boolean TRUE upon success
 283    */
 284    function logout () {
 285    	// delete activity
 286    	$this->db->delete('user_activity', array('user_id' => $this->get('id')));
 287    	
 288    	// unset user_id session and login_time
 289    	$this->session->unset_userdata($this->session_name,'login_time');
 290    	
 291    	// delete cookie
 292		$CI =& get_instance();
 293		$CI->load->helper('cookie');
 294		delete_cookie('user_remember_key');
 295		
 296		// prep hook
 297		// call the library here, because this may be loaded in the admin/login controller which doesn't preload
 298		// app_hooks like the other controllers
 299		$CI->load->library('app_hooks');
 300		$CI->app_hooks->data('member', $this->get('id'));
 301		$CI->app_hooks->trigger('member_logout', $this->get('id'));
 302		$CI->app_hooks->reset();
 303    	
 304    	return TRUE;
 305    }
 306    
 307    /**
 308    * Set Active User
 309    *
 310    * Sets the active user by ID, loads user data into array
 311    *
 312    * @param int $user_id
 313    *
 314    * @return boolean TRUE upon success
 315    */
 316    function set_active ($user_id) {
 317    	if (!$user = $this->get_user($user_id)) {
 318    		return FALSE;
 319    	}
 320    	
 321    	$this->active_user = $user;
 322    	
 323    	return TRUE;
 324    }
 325    
 326    /**
 327    * Is User an Admin?
 328    *
 329    * @return boolean TRUE if the current user is an administrator
 330    */
 331    function is_admin () {
 332    	if (empty($this->active_user) or $this->active_user['is_admin'] == FALSE) {
 333    		return FALSE;
 334    	}
 335    	
 336    	return TRUE;
 337    }
 338    
 339    /**
 340    * Is user logged in?
 341    *
 342    * @return boolean TRUE if user is logged in
 343    */
 344    function logged_in () {
 345    	if (empty($this->active_user)) {
 346    		return FALSE;
 347    	}
 348    	else {
 349    		return TRUE;
 350    	}
 351    }
 352    
 353    /**
 354    * Is user in this group?
 355    *
 356    * Returns TRUE if the user is in the usergroup(s) or the privileges are open to all (i.e., array is empty or contains "0"). 
 357    * To check if the user is logged out, send "-1" by itself or in an array.  It's return TRUE if the user is logged out.
 358    *
 359    * @param int|array $group A group ID, or array of group ID's (they must be in one of the groups)
 360    * @param int $user_id (Optional) Specify the user.  (default: FALSE)
 361    *
 362    * @return boolean TRUE if in the group
 363    */
 364    function in_group ($group, $user_id = FALSE) {
 365    	if (empty($group)) {
 366    		return TRUE;
 367    	}
 368    	
 369    	// sometimes, we only want to show something if the user is logged out
 370    	if ($group == '-1' or (is_array($group) and in_array('-1', $group))) {
 371    		if ($this->logged_in() === FALSE) {
 372	    		return TRUE;
 373	    	}
 374	    	else {
 375	    		return FALSE;
 376	    	}
 377	    }
 378    
 379    	if ($user_id) {
 380    		$user_array = $this->get_user($user_id);
 381    	}
 382    	else {
 383    		$user_array = $this->active_user;
 384    	}
 385    	
 386    	if (is_array($group) and in_array('0', $group)) {	
 387    		// this is a "privileges" array and it's public so anyone can see it
 388    		return TRUE;
 389    	}
 390    	elseif (is_array($group) and in_array('', $group)) {
 391    		return TRUE;
 392    	}
 393    	
 394    	if (!$this->logged_in()) {
 395    		// we aren't even logged in
 396    		
 397    		return FALSE;
 398    	}
 399    
 400    	if (is_array($group)) {
 401			// are they in any of these groups?
 402    		foreach ($group as $one_group) {
 403    			if (in_array($one_group, $user_array['usergroups'])) {
 404    				return TRUE;
 405    			}
 406    		}
 407    	}
 408    	else {
 409    		// are they in this group?
 410    		if (in_array($group, $user_array['usergroups'])) {
 411    			return TRUE;
 412    		}
 413    	}
 414    	
 415    	// nope
 416    	return FALSE;
 417    }
 418    
 419    /**
 420    * Is user NOT in this group?
 421    *
 422    * @param int|array A group ID, or array of group ID's (they must NOT be in any of the groups)
 423    * @param int $user_id (Optional) Specify the user.  Default: Current User
 424    *
 425    * @return boolean TRUE if not in the group
 426    */
 427    function not_in_group ($group, $user_id = FALSE) {
 428    	if (empty($group)) {
 429    		return FALSE;
 430    	}
 431    
 432    	if ($user_id) {
 433    		$user_array = $this->get_user($user_id);
 434    	}
 435    	else {
 436    		$user_array = $this->active_user;
 437    	}
 438    	
 439    	if (!$this->logged_in()) {
 440    		// we aren't even logged in
 441    		
 442    		return TRUE;
 443    	}
 444    	
 445    	if (is_array($group)) {
 446			// are they in any of these groups?
 447    		foreach ($group as $one_group) {
 448    			if (in_array($one_group, $user_array['usergroups'])) {
 449    				return FALSE;
 450    			}
 451    		}
 452    	}
 453    	else {
 454    		// are they in this group?
 455    		if (in_array($group, $user_array['usergroups'])) {
 456    			return FALSE;
 457    		}
 458    	}
 459    	
 460    	// nope, they aren't in any of them
 461    	return TRUE;
 462    }
 463    
 464    /**
 465    * Get user data
 466    *
 467    * @param string $parameter The name of the piece of user data (e.g., email)
 468    *
 469    * @return string User data
 470    */
 471    function get ($parameter = FALSE) {
 472    	if ($parameter) {
 473    		return $this->active_user[$parameter];
 474    	}
 475    	else {
 476    		return $this->active_user;
 477    	}
 478    }
 479    
 480    /**
 481    * Get Active Subscriptions
 482    *
 483    * Gets active subscriptions for a user
 484    *
 485    * @param int $user_id The user id
 486    *
 487    * @return array Array of active subscriptions, else FALSE if none exist
 488    */
 489    
 490    function get_active_subscriptions ($user_id) {
 491    	if (module_installed('billing')) {
 492	    	$this->load->model('billing/recurring_model');
 493	    	
 494	    	$customer_id = $this->get_customer_id($user_id);
 495	    	
 496	    	return $this->recurring_model->GetRecurrings(array('customer_id' => $customer_id));
 497	    } 
 498	    else {
 499	    	return FALSE;
 500	    }
 501    }
 502    
 503    /**
 504    * Get Subscriptions
 505    *
 506    * Gets active and cancelled subscriptions for a user
 507    *
 508    * @param int $user_id The user id
 509    *
 510    * @param array Array of subscriptions, else FALSE if none exist
 511    */
 512    
 513    function get_subscriptions ($user_id) {
 514    	if (module_installed('billing')) {
 515	    	$this->load->model('billing/subscription_model');
 516	    	
 517	    	return $this->subscription_model->get_subscriptions(array('user_id' => $user_id), TRUE);
 518	    }
 519	    else {
 520	    	return FALSE;
 521	    }
 522    }
 523    
 524    /**
 525    * Get Customer ID
 526    * 
 527    * Sometimes, we just need this, so let's not do a full blown query.
 528    *
 529    * @param int $user_id (default: active user)
 530    *
 531    * @return int|boolean $customer_id
 532    */
 533    function get_customer_id ($user_id = FALSE) {
 534    	if (!module_installed('billing')) {
 535    		return FALSE;
 536    	}
 537    	
 538    	// auto-complete $user_id?
 539    	if (empty($user_id) and $this->logged_in()) {
 540    		$user_id = $this->active_user['id'];
 541    	}
 542    
 543    	// previously, we looked for the "customer_id" in the users table
 544    	// however, this didn't let us confirm that the customer record actually exists
 545    	// so now we look up via the customers table
 546    	$this->db->select('customer_id');
 547    	$this->db->where('internal_id',$user_id);
 548    	$result = $this->db->get('customers');
 549    	
 550    	if ($result->num_rows() == 0) {
 551    		// no reason not to have a customer record
 552    		// let's create one
 553    		if (module_installed('billing')) {
 554    			// get user data
 555    			$user = (!empty($this->active_user) and $this->active_user['id'] == $user_id) ? $this->active_user : $this->get_user($user_id);
 556    			
 557    			if (empty($user)) {
 558    				// how would this happen?  probably impossible
 559    				return FALSE;
 560    			}
 561    		
 562				// do any custom fields map to billing fields?
 563				$user_custom_fields = $this->get_custom_fields();
 564				
 565				$customer = array();
 566				if (is_array($user_custom_fields)) {
 567					foreach ($user_custom_fields as $field) {
 568						if (!empty($field['billing_equiv']) and isset($user[$field['name']])) {
 569							$customer[$field['billing_equiv']] = $user[$field['name']];		
 570						}
 571					}
 572				}
 573				
 574				$CI =& get_instance();
 575				$CI->load->model('billing/customer_model');
 576		
 577				$customer['email'] = $user['email'];
 578				$customer['internal_id'] = $user['id'];
 579				$customer['first_name'] = $user['first_name'];
 580				$customer['last_name'] = $user['last_name'];
 581				
 582				$customer_id = $CI->customer_model->NewCustomer($customer);
 583				
 584				$this->db->update('users',array('customer_id' => $customer_id),array('user_id' => $user_id));
 585				
 586				return $customer_id;
 587			}
 588    	}
 589    	else {
 590    		return $result->row()->customer_id;
 591    	}
 592    }
 593    
 594    /**
 595    * Set Charge ID
 596    *
 597    * After a successful order, we put this charge ID in the user database so that when the
 598    * charge trigger is tripped, we'll process this user's cart
 599    *
 600    * @param int $user_id
 601    * @param int $charge_id
 602    *
 603    * @return boolean TRUE
 604    */
 605    function set_charge_id ($user_id, $charge_id) {
 606    	$this->db->update('users', array('user_pending_charge_id' => $charge_id), array('user_id' => $user_id));
 607    	
 608    	return TRUE;
 609    }
 610    
 611    /**
 612    * Remove Charge ID
 613    * 
 614    * @param int $user_id
 615    *
 616    * @return boolean TRUE
 617    */
 618    function remove_charge_id ($user_id) {
 619    	$this->db->update('users', array('user_pending_charge_id' => '0'), array('user_id' => $user_id));
 620    	
 621    	return TRUE;
 622    }
 623	
 624	/**
 625	* Validation
 626	*
 627	* Validates POST data to be acceptable for creating a new user
 628	*
 629	* @param boolean $editing Set to TRUE if this is an edited user (i.e., password can be blank) (default: FALSE)
 630	* @param boolean $error_array Return errors in an array or HTML formatted string (TRUE for array) (default: TRUE)
 631	*
 632	* @return array If errors, returns an array of individual errors, else returns TRUE
 633	*/
 634	function validation ($editing = FALSE, $error_array = TRUE) {	
 635		$CI =& get_instance();
 636		
 637		$CI->load->library('form_validation');
 638		$CI->load->model('custom_fields_model');
 639		$CI->load->helpers(array('unique_username','unique_email','strip_whitespace'));
 640		
 641		$CI->form_validation->set_rules('first_name','First Name','trim|required');
 642		$CI->form_validation->set_rules('last_name','Last Name','trim|required');
 643		$unique_email = ($editing == FALSE) ? '|unique_email' : '';
 644		$CI->form_validation->set_rules('email','Email','trim' . $unique_email . '|valid_email|required');
 645		
 646		$username_rules = array('trim','strip_whitespace','min_length[3]');
 647		if ($this->config->item('username_allow_special_characters') == FALSE) {
 648			$username_rules[] = 'alpha_numeric';
 649		}
 650		if ($editing == FALSE) {
 651			$username_rules[] = 'unique_username';
 652		}
 653		$CI->form_validation->set_rules('username','Username',implode('|', $username_rules));
 654		
 655		if ($editing == FALSE) {
 656			$CI->form_validation->set_rules('password','Password','min_length[5]|matches[password2]');
 657			$CI->form_validation->set_rules('password2','Repeat Password','required');
 658		}
 659		
 660		if ($CI->form_validation->run() === FALSE) {
 661			if ($error_array == TRUE) {
 662				return explode('||',str_replace(array('<p>','</p>'),array('','||'),validation_errors()));
 663			}
 664			else {
 665				return validation_errors();
 666			}
 667		}
 668		
 669		// validate custom fields
 670		$custom_fields = $this->get_custom_fields(array('not_in_admin' => TRUE));
 671		
 672		$CI->load->library('custom_fields/form_builder');
 673		$CI->form_builder->build_form_from_array($custom_fields);
 674
 675		if ($CI->form_builder->validate_post() === FALSE) {
 676			$errors = $CI->form_builder->validation_errors(($error_array == TRUE) ? TRUE : FALSE);
 677			return $errors;
 678		}
 679		
 680		return TRUE;
 681	}
 682	
 683	/**
 684	* Validate Billing Address
 685	*
 686	* @param int $user_id
 687	*
 688	* @return boolean TRUE if they have a valid billing address on file
 689	*/
 690	function validate_billing_address ($user_id) {
 691		$address = $this->get_billing_address($user_id);
 692		
 693		$required = array(
 694							'first_name',
 695							'last_name',
 696							'address_1',
 697							'city',
 698							'country',
 699							'postal_code'
 700						);
 701						
 702		foreach ($required as $item) {
 703			if (empty($address[$item])) {
 704				return FALSE;
 705			}
 706		}
 707		
 708		return TRUE;
 709	}
 710	
 711	/**
 712	* Get Billing Address
 713	*
 714	* @param int $user_id
 715	*
 716	* @return array Billing address
 717	*/
 718	function get_billing_address ($user_id) {
 719		$customer_id = $this->get_customer_id($user_id);
 720		
 721		if (empty($customer_id)) {
 722			return FALSE;
 723		}
 724		
 725		$CI =& get_instance();
 726		$CI->load->model('billing/customer_model');
 727		
 728		$customer = $this->customer_model->GetCustomer($customer_id);
 729		
 730		$address = array(
 731						'first_name' => $customer['first_name'],
 732						'last_name' => $customer['last_name'],
 733						'company' => $customer['company'],
 734						'address_1' => $customer['address_1'],
 735						'address_2' => $customer['address_2'],
 736						'city' => $customer['city'],
 737						'country' => $customer['country'],
 738						'postal_code' => $customer['postal_code'],
 739						'state' => $customer['state'],
 740						'phone_number' => $customer['phone']
 741					);
 742		
 743		return $address;
 744	}
 745	
 746	/**
 747	* Is email address unique?
 748	*
 749	* @param string $email The email address being tested
 750	*
 751	* @return boolean TRUE upon being OK, FALSE if not
 752	*/
 753	function unique_email ($email) {	
 754		$this->db->select('user_id');
 755		$this->db->where('user_email',$email);
 756		$this->db->where('user_deleted','0');
 757		$result = $this->db->get('users');
 758		
 759		if ($result->num_rows() > 0) {
 760			return FALSE;
 761		}
 762		else {
 763			return TRUE;
 764		}
 765	}
 766	
 767	/**
 768	* Is username unique?
 769	*
 770	* @param string $username The username being tested
 771	*
 772	* @return boolean TRUE upon being OK, FALSE if not
 773	*/
 774	function unique_username ($username) {
 775		// protected usernames
 776		$protected = array('admin','administrator','root','','1','2','3','4','5','6','7','8','9');
 777		if (in_array($username, $protected)) {
 778			return FALSE;
 779		}
 780	
 781		$this->db->select('user_id');
 782		$this->db->where('user_username',$username);
 783		$this->db->where('user_deleted','0');
 784		$result = $this->db->get('users');
 785		
 786		if ($result->num_rows() > 0) {
 787			return FALSE;
 788		}
 789		else {
 790			return TRUE;
 791		}
 792	}
 793	
 794	/**
 795	* Add a Usergroup
 796	*
 797	* @param int $user_id
 798	* @param int $group_id
 799	*
 800	* @return array New usergroup array
 801	*/	
 802	function add_group ($user_id, $group_id) {
 803		$user = $this->get_user($user_id);
 804		
 805		if (is_array($user['usergroups']) and !empty($user['usergroups']) and in_array($group_id, $user['usergroups'])) {
 806			// already a member
 807			return FALSE;
 808		}
 809		
 810		if ($user['usergroups'] == FALSE) {
 811			$user['usergroups'] = array();
 812		}
 813		
 814		$user['usergroups'][] = $group_id;
 815		
 816		$usergroups = '|' . implode('|',$user['usergroups']) . '|';
 817		
 818		$this->db->update('users',array('user_groups' => $usergroups),array('user_id' => $user_id));
 819		
 820		return $usergroups;
 821	}
 822	
 823	/**
 824	* Remove a Usergroup
 825	*
 826	* @param int $user_id
 827	* @param int $group_id
 828	*
 829	* @return array New usergroup array
 830	*/
 831	function remove_group ($user_id, $group_id) {
 832		$user = $this->get_user($user_id);
 833		
 834		if (is_array($user['usergroups']) and !empty($user['usergroups'])) {
 835			foreach ($user['usergroups'] as $key => $val) {
 836				if ($val == $group_id) {
 837					unset($user['usergroups'][$key]);
 838				}
 839			}
 840			
 841			$usergroups = '|' . implode('|',$user['usergroups']) . '|';
 842			
 843			$this->db->update('users',array('user_groups' => $usergroups),array('user_id' => $user_id));
 844		}
 845		
 846		return $usergroups;
 847	}
 848	
 849	/**
 850	* Resend Validation Email
 851	*
 852	* Resends the validation email, unless there's no email to be sent
 853	*
 854	* @param int $user_id
 855	*
 856	* @return TRUE
 857	*/
 858	function resend_validation_email ($user_id) {
 859		$user = $this->get_user($user_id);
 860		
 861		if (empty($user)) {
 862			return FALSE;
 863		}
 864
 865		if (!empty($user['validate_key'])) {
 866			$validation_link = site_url('users/validate/' . $user['validate_key']);
 867			
 868			$CI =& get_instance();
 869			$CI->app_hooks->data('member', $user['id']);
 870			
 871			$CI->app_hooks->data_var('validation_link', $validation_link);
 872			$CI->app_hooks->data_var('validation_code', $user['validate_key']);
 873			
 874			$CI->app_hooks->trigger('member_validate_email');
 875			
 876			return TRUE;
 877		}
 878		
 879		return FALSE;
 880	}
 881	
 882	/**
 883	* New User
 884	*
 885	* Create a new user, including custom fields
 886	*
 887	* @param string $email Email Address
 888	* @param string $password Password to use
 889	* @param string $username Username
 890	* @param string $first_name First name
 891	* @param string $last_name Last name
 892	* @param array $groups Array of group ID's to be entered into (default: FALSE)
 893	* @param int $affiliate Affiliate ID of referrer (default: FALSE)
 894	* @param boolean $is_admin Check to make an administrator (default: FALSE)
 895	* @param array $custom_fields An array of custom field data, matching in name (default: array())
 896	* @param boolean $require_validation Should we require email validation? (default: FALSE)
 897	*
 898	* @return int $user_id
 899	*/
 900	function new_user($email, $password, $username, $first_name, $last_name, $groups = FALSE, $affiliate = FALSE, $is_admin = FALSE, $custom_fields = array(), $require_validation = FALSE) {
 901		if (empty($groups)) {
 902			$this->load->model('users/usergroup_model');
 903			
 904			$group = $this->usergroup_model->get_default();
 905			
 906			$groups = array($group);
 907		}
 908		
 909		if ($require_validation == TRUE) {
 910			$validate_key = random_string('unique');
 911			
 912			$result = $this->db->select('user_id')->where('user_validate_key',$validate_key)->get('users');
 913			while ($result->num_rows() > 0) {
 914				$validate_key = random_string('unique');
 915				
 916				$result = $this->db->select('user_id')->where('user_validate_key',$validate_key)->get('users');
 917			}
 918		}
 919		else {
 920			$validate_key = '';
 921		}
 922		
 923		// generate hashed password
 924		$CI =& get_instance();
 925		$CI->load->helper('string');
 926		$salt = random_string('unique');
 927		$hashed_password = md5($password . ':' . $salt);
 928		
 929		$insert_fields = array(
 930								'user_is_admin' => ($is_admin == TRUE) ? '1' : '0',
 931								'user_groups' => '|' . implode('|',$groups) . '|',
 932								'user_first_name' => $first_name,
 933								'user_last_name' => $last_name,
 934								'user_username' => $username,
 935								'user_email' => $email,
 936								'user_password' => $hashed_password,
 937								'user_salt' => $salt,
 938								'user_referrer' => ($affiliate != FALSE) ? $affiliate : '0',
 939								'user_signup_date' => date('Y-m-d H:i:s'),
 940								'user_last_login' => '0000-00-00 00:00:00',
 941								'user_suspended' => '0',
 942								'user_deleted' => '0',
 943								'user_remember_key' => '',
 944								'user_validate_key' => $validate_key
 945							);
 946		
 947		if (is_array($custom_fields)) {					
 948			foreach ($custom_fields as $name => $value) {
 949				$insert_fields[$name] = $value;
 950			}
 951		}
 952												
 953		$this->db->insert('users',$insert_fields);
 954		$user_id = $this->db->insert_id();
 955		
 956		// create customer record
 957		if (module_installed('billing')) {
 958			$CI->load->model('billing/customer_model');
 959			
 960			$customer = array();
 961			$customer['email'] = $email;
 962			$customer['internal_id'] = $user_id;
 963			$customer['first_name'] = $first_name;
 964			$customer['last_name'] = $last_name;
 965			
 966			// do any custom fields map to billing fields?
 967			$user_custom_fields = $this->get_custom_fields();
 968			
 969			if (is_array($user_custom_fields)) {
 970				foreach ($user_custom_fields as $field) {
 971					if (!empty($field['billing_equiv']) and isset($custom_fields[$field['name']])) {
 972						$customer[$field['billing_equiv']] = $custom_fields[$field['name']];		
 973					}
 974				}
 975			}
 976			
 977			// we don't want to set a country if we don't have a state/province, because we risk
 978			// internal US/Canada province validation
 979			if (isset($customer['country']) and (!isset($customer['state']) or (isset($customer['state']) and empty($customer['state'])))) {
 980				unset($customer['country']);
 981			}
 982			
 983			$customer_id = $CI->customer_model->NewCustomer($customer);
 984			
 985			$this->db->update('users',array('customer_id' => $customer_id),array('user_id' => $user_id));
 986		}
 987		
 988		// prep hook
 989		// only run this hook if the App_hooks library is loaded
 990		// it may not be if this is the user created during the install wizard
 991		if (class_exists('App_hooks') and $this->trigger_register_hook == TRUE) {
 992			$CI =& get_instance();
 993			$CI->app_hooks->data('member', $user_id);
 994			$CI->app_hooks->data_var('password', $password);
 995			
 996			// trip the validation email?
 997			if (!empty($validate_key)) {
 998				$validation_link = site_url('users/validate/' . $validate_key);
 999				
1000				$CI->app_hooks->data_var('validation_link', $validation_link);
1001				$CI->app_hooks->data_var('validation_code', $validate_key);
1002				
1003				$CI->app_hooks->trigger('member_validate_email');
1004			}
1005			
1006			$CI->app_hooks->trigger('member_register', $user_id, $password);
1007			$CI->app_hooks->reset();
1008		}
1009		
1010		return $user_id;
1011	}
1012	
1013	/**
1014	* Update User
1015	*
1016	* Updates a user, including custom fields
1017	*
1018	* @param int $user_id The current user ID #
1019	* @param string $email Email Address
1020	* @param string $password Password to use
1021	* @param string $username Username
1022	* @param string $first_name First name
1023	* @param string $last_name Last name
1024	* @param array $groups Array of group ID's to be entered into (default: FALSE)
1025	* @param boolean $is_admin Check to make an administrator (default: FALSE)
1026	* @param array $custom_fields An array of custom field data, matching in name (default: array())
1027	*
1028	* @return int $user_id
1029	*/
1030	function update_user($user_id, $email, $password, $username, $first_name, $last_name, $groups = FALSE, $is_admin = FALSE, $custom_fields = array()) {
1031		$old_user = $this->get_user($user_id);
1032		
1033		if (empty($old_user)) {
1034			return FALSE;
1035		}
1036		
1037		// can we update the username?
1038		if ($old_user['username'] != $username) {
1039			// check if username is in use by someone else
1040			$users = $this->db->select('user_id')
1041							  ->from('users')
1042							  ->where('user_username',$username)
1043							  ->get();
1044							  
1045			if ($users->num_rows() > 0) {
1046				// already in use
1047				// keep old username
1048				$username = $old_user['username'];
1049			}							  
1050		}
1051		
1052		// can we update the email?
1053		if ($old_user['email'] != $email) {
1054			// check if email is in use by someone else
1055			$users = $this->db->select('user_id')
1056							  ->from('users')
1057							  ->where('user_email',$email)
1058							  ->get();
1059							  
1060			if ($users->num_rows() > 0) {
1061				// already in use
1062				// keep old email
1063				$email = $old_user['email'];
1064			}							  
1065		}
1066		
1067		$update_fields = array(
1068								'user_is_admin' => ($is_admin == TRUE) ? '1' : '0',
1069								'user_groups' => @'|' . implode('|',$groups) . '|',
1070								'user_first_name' => $first_name,
1071								'user_last_name' => $last_name,
1072								'user_username' => $username,
1073								'user_email' => $email
1074							);
1075							
1076		if (!empty($password)) {
1077			$this->update_password($user_id, $password);
1078		}
1079							
1080		foreach ($custom_fields as $name => $value) {
1081			$update_fields[$name] = $value;
1082		}
1083												
1084		$this->db->update('users',$update_fields,array('user_id' => $user_id));
1085		
1086		if (module_installed('billing')) {
1087			// update email in customers table
1088			$this->db->update('customers',array('email' => $email),array('internal_id' => $user_id));
1089			
1090			// do any custom fields map to billing fields?
1091			$user_custom_fields = $this->get_custom_fields();
1092			
1093			$customer = array();
1094			if (is_array($user_custom_fields)) {
1095				foreach ($user_custom_fields as $field) {
1096					if (!empty($field['billing_equiv']) and isset($custom_fields[$field['name']])) {
1097						$customer[$field['billing_equiv']] = $custom_fields[$field['name']];		
1098					}
1099				}
1100			}
1101			
1102			$customer_id = $this->get_customer_id($user_id);
1103			
1104			if (!empty($customer)) {
1105				$this->db->update('customers', $customer, array('internal_id' => $user_id));
1106			}
1107		}
1108		
1109		// hook call
1110		$CI =& get_instance();
1111		$CI->app_hooks->data('member', $user_id);
1112		$CI->app_hooks->trigger('member_update', $user_id, $old_user);
1113		$CI->app_hooks->reset();
1114		
1115		return TRUE;
1116	}
1117	
1118	/**
1119	* Update Billing Address
1120	*
1121	* @param int $user_id
1122	* @param array $address_fields New Address
1123	*
1124	* @return boolean 
1125	*/
1126	function update_billing_address ($user_id, $address_fields) {
1127		if (!module_installed('billing')) {
1128			return FALSE;
1129		}
1130	
1131		$CI =& get_instance();
1132		$CI->load->model('billing/customer_model');
1133		
1134		$customer_id = $this->get_customer_id($user_id);
1135		
1136		if ($customer_id != FALSE) {
1137			$CI->customer_model->UpdateCustomer($customer_id, $address_fields);
1138		}
1139		else {
1140			// user doesn't have a customer record, let's create one
1141			$address_fields['internal_id'] = $user_id;
1142			$customer_id = $CI->customer_model->NewCustomer($address_fields);
1143			
1144			// link this to customer account
1145			$this->db->update('users', array('customer_id' => $customer_id), array('user_id' => $user_id));
1146		}
1147		
1148		return TRUE;
1149	}
1150	
1151	/**
1152	* Delete User
1153	*
1154	* @param int $user_id The user ID #
1155	*
1156	* @return boolean TRUE
1157	*/
1158	function delete_user ($user_id) {
1159		$this->db->update('users',array('user_deleted' => '1'),array('user_id' => $user_id));
1160		
1161		// hook call
1162		$CI =& get_instance();
1163		$CI->app_hooks->data('member', $user_id);
1164		$CI->app_hooks->trigger('member_delete', $user_id);
1165		$CI->app_hooks->reset();
1166		
1167		return TRUE;
1168	}
1169	
1170	/**
1171	* Update Password
1172	*
1173	* @param int $user_id
1174	* @param string $new_password
1175	*
1176	* @return boolean 
1177	*/
1178	function update_password ($user_id, $new_password) {
1179		$CI =& get_instance();
1180		$CI->load->helper('string');
1181		$salt = random_string('unique');
1182		$hashed_password = md5($new_password . ':' . $salt);
1183	
1184		$this->db->update('users',array('user_password' => $hashed_password, 'user_salt' => $salt),array('user_id' => $user_id));
1185		
1186		// prep hook
1187		$CI =& get_instance();
1188		$CI->app_hooks->data('member', $user_id);
1189		$CI->app_hooks->data_var('new_password', $new_password);
1190		$CI->app_hooks->trigger('member_change_password', $user_id, $new_password);
1191		$CI->app_hooks->reset();
1192		
1193		return TRUE;
1194	}
1195	
1196	/**
1197	* Reset Password
1198	*
1199	* @param int $user_id
1200	*
1201	* @return boolean TRUE
1202	*/
1203	function reset_password ($user_id) {
1204		$user = $this->get_user($user_id);
1205		
1206		if (empty($user)) {
1207			return FALSE;
1208		}
1209		
1210		// reset the password
1211		$this->load->helper('string');
1212		
1213		$password = random_string('alnum',9);
1214		$this->db->update('users',array('user_password' => md5($password), 'user_salt' => ''),array('user_id' => $user['id']));
1215	
1216		// hook call
1217		$CI =& get_instance();
1218		$CI->app_hooks->data('member', $user['id']);
1219		$CI->app_hooks->data_var('new_password', $password);
1220		
1221		$CI->app_hooks->trigger('member_forgot_password');
1222		$CI->app_hooks->reset();
1223		
1224		return TRUE;
1225	}
1226	
1227	/**
1228	* Suspend User
1229	*
1230	* @param int $user_id The user ID #
1231	*
1232	* @return boolean  
1233	*/
1234	function suspend_user ($user_id) {
1235		$this->db->update('users',array('user_suspended' => '1'),array('user_id' => $user_id));
1236		
1237		// hook call
1238		$CI =& get_instance();
1239		$CI->app_hooks->data('member', $user_id);
1240		$CI->app_hooks->trigger('member_suspend', $user_id);
1241		$CI->app_hooks->reset();
1242		
1243		return TRUE;
1244	}
1245	
1246	/**
1247	* Unsuspend User
1248	*
1249	* @param int $user_id The user ID #
1250	*
1251	* @return boolean TRUE
1252	*/
1253	function unsuspend_user ($user_id) {
1254		$this->db->update('users',array('user_suspended' => '0'),array('user_id' => $user_id));
1255		
1256		// hook call
1257		$CI =& get_instance();
1258		$CI->app_hooks->data('member', $user_id);
1259		$CI->app_hooks->trigger('member_unsuspend', $user_id);
1260		$CI->app_hooks->reset();
1261		
1262		return TRUE;
1263	}
1264	
1265	/**
1266	* Get User
1267	*
1268	* @param int $user_id The user ID #
1269	* @param boolean $any_status Should even deleted records be retrieved?
1270	*
1271	* @return array User fields
1272	*/
1273	function get_user ($user_id, $any_status = FALSE) {
1274		$filters = array('id' => $user_id);
1275		
1276		$user = $this->get_users($filters, $any_status);
1277		
1278		if (empty($user)) {
1279			return FALSE;
1280		}
1281		else {
1282			return $user[0];
1283		}
1284	}
1285	
1286	/**
1287	* Count Users
1288	*
1289	* @param array $filters In same format as get_users()
1290	*
1291	* @return int Number of users matching filters
1292	*/
1293	function count_users ($filters) {
1294		return $this->get_users($filters, FALSE, TRUE);
1295	}
1296	
1297	/**
1298	* Get Users
1299	*
1300	* @param int $filters['id'] The user ID to select
1301	* @param int $filters['group'] The group ID to filter by
1302	* @param int $filters['suspended'] Set to 1 to retrieve suspended users
1303	* @param string $filters['email'] The email address to filter by
1304	* @param string $filters['name'] Search by first and last name
1305	* @param string $filters['username'] Member username
1306	* @param string $filters['first_name'] Search by first name
1307	* @param string $filters['last_name'] Search by last name
1308	* @param date $filters['signup_start_date'] Select after this signup date
1309	* @param date $filters['signup_end_date'] Select before this signup date
1310	* @param string $filters['keyword'] Search by ID, Name, Email, or Username
1311	* @param string $filters['sort'] Field to sort by
1312	* @param string $filters['sort_dir'] ASC or DESC
1313	* @param int $filters['limit'] How many records to retrieve
1314	* @param int $filters['offset'] Start records retrieval at this record
1315	* @param boolean $any_status Set to TRUE to allow for deleted users, as well (default: FALSE)
1316	* @param boolean $counting Should we just count the number of users that match the filters? (default: FALSE)
1317	*
1318	* @return array Each user in an array of users
1319	*/
1320	function get_users ($filters = array(), $any_status = FALSE, $counting = FALSE) {
1321		$fields = $this->get_custom_fields();
1322		
1323		// keyword search
1324		if (isset($filters['keyword'])) {
1325			$this->db->where('user_deleted','0');
1326			$this->db->where('user_id', $filters['keyword']);
1327			$this->db->or_like('user_username', $filters['keyword']);
1328			$this->db->or_like('user_email', $filters['keyword']);
1329			$this->db->or_like('user_last_name', $filters['keyword']);
1330			$this->db->or_like('user_first_name', $filters['keyword']);
1331		}
1332		
1333		// other filters
1334		if (isset($filters['id'])) {
1335			$this->db->where('user_id',$filters['id']);
1336		}
1337		
1338		if (isset($filters['group'])) {
1339			$this->db->where('(user_groups LIKE \'%|' . $filters['group'] . '|%\' or user_groups = \'|' . $filters['group'] . '|\')');
1340		}
1341		
1342		if (isset($filters['suspended'])) {
1343			$this->db->where('user_suspended',$filters['suspended']);
1344		}
1345		
1346		if (isset($filters['email'])) {
1347			$this->db->like('user_email',$filters['email']);
1348		}
1349		
1350		if (isset($filters['username'])) {
1351			$this->db->like('user_username',$filters['username']);
1352		}
1353		
1354		if (isset($filters['name'])) {
1355			if (is_numeric($filters['name'])) {
1356				// we are passed a member id
1357				$this->db->where('users.user_id',$filters['name']);
1358			} else {
1359				$this->db->where('(`user_first_name` LIKE \'%' . mysql_real_escape_string($filters['name']) . '%\' OR `user_last_name` LIKE \'%' . mysql_real_escape_string($filters['name']) . '%\')');
1360			}
1361		}
1362		
1363		if (isset($filters['first_name'])) {
1364			$this->db->like('user_first_name',$filters['first_name']);
1365		}
1366		
1367		if (isset($filters['last_name'])) {
1368			$this->db->like('user_last_name',$filters['last_name']);
1369		}
1370		
1371		if (isset($filters['is_admin'])) {
1372			$this->db->where('users.user_is_admin',$filters['is_admin']);
1373		}
1374		
1375		if (isset($filters['signup_date_start'])) {
1376			$date = date('Y-m-d H:i:s', strtotime($filters['signup_date_start']));
1377			$this->db->where('users.user_signup_date >=', $date);
1378		}
1379		
1380		if (isset($filters['signup_date_end'])) {
1381			$date = date('Y-m-d H:i:s', strtotime($filters['signup_date_end']));
1382			$this->db->where('users.user_signup_date <=', $date);
1383		}
1384		
1385		// custom field params
1386		if (is_array($fields)) {
1387			foreach ($fields as $field) {
1388				if (isset($filters[$field['name']])) {
1389					$this->db->like('users.' . $field['name'],$filters[$field['name']]);
1390				}
1391			}
1392		}
1393		
1394		if ($any_status == FALSE) {
1395			$this->db->where('user_deleted','0');
1396		}
1397		
1398		// standard ordering and limiting
1399		if ($counting == FALSE) {
1400			$order_by = (isset($filters['sort'])) ? $filters['sort'] : 'user_username';
1401			$order_dir = (isset($filters['sort_dir'])) ? $filters['sort_dir'] : 'ASC';
1402			$this->db->order_by($order_by, $order_dir);
1403			
1404			if (isset($filters['limit'])) {
1405				$offset = (isset($filters['offset'])) ? $filters['offset'] : 0;
1406				$this->db->limit($filters['limit'], $offset);
1407			}
1408		}
1409		
1410		if ($counting === TRUE) {
1411			$this->db->select('users.user_id');
1412			$result = $this->db->get('users');
1413			$rows = $result->num_rows();
1414			$result->free_result();
1415			return $rows;
1416		}
1417		else {
1418			$this->db->from('users');
1419			
1420			$result = $this->db->get();
1421		}
1422		
1423		if ($result->num_rows() == 0) {
1424			return FALSE;
1425		}
1426		
1427		// get custom fields
1428		$CI =& get_instance();
1429		$CI->load->model('custom_fields_model');
1430		$custom_fields = $CI->custom_fields_model->get_custom_fields(array('group' => '1'));
1431		
1432		$users = array();
1433		foreach ($result->result_array() as $user) {
1434			$groups = explode('|',$user['user_groups']);
1435			$user_groups = array();
1436			foreach ($groups as $group) {
1437				if (!empty($group)) {
1438					$user_groups[] = $group;
1439				}
1440			}
1441		
1442			$this_user = array(
1443							'id' => $user['user_id'],
1444							'is_admin' => ($user['user_is_admin'] == '1') ? TRUE : FALSE,
1445							'customer_id' => $user['customer_id'],
1446							'salt' => $user['user_salt'],
1447							'usergroups' => $user_groups,
1448							'first_name' => $user['user_first_name'],
1449							'last_name' => $user['user_last_name'],
1450							'username' => $user['user_username'],
1451							'email' => $user['user_email'],
1452							'referrer' => $user['user_referrer'],
1453							'signup_date' => local_time($user['user_signup_date']),
1454							'last_login' => local_time($user['user_last_login']),
1455							'suspended' => ($user['user_suspended'] == 1) ? TRUE : FALSE,
1456							'admin_link' => site_url('admincp/users/profile/' . $user['user_id']),
1457							'remember_key' => $user['user_remember_key'],
1458							'validate_key' => $user['user_validate_key'],
1459							'cart' => (empty($user['user_cart'])) ? FALSE : unserialize($user['user_cart']),
1460							'pending_charge_id' => (!empty($user['user_pending_charge_id'])) ? $user['user_pending_charge_id'] : FALSE
1461							);
1462							
1463			foreach ($custom_fields as $field) {
1464				$this_user[$field['name']] = $user[$field['name']];
1465			}
1466			reset($custom_fields);
1467							
1468			$users[] = $this_user;
1469							
1470		}
1471		
1472		$result->free_result();
1473		
1474		return $users;
1475	}
1476	
1477	/**
1478	* New User Custom Field
1479	*
1480	* Creates the user-field-specific custom field record
1481	*
1482	* @param int $custom_field_id
1483	* @param string $billing_equiv If this field represents a billing address field (e.g, "address_1"), specify here:  options: address_1/2, state, country, postal_code, company (default: '')
1484	* @param boolean $admin_only Is this an admin-only field? (default: FALSE)
1485	* @param boolean $registration_form Should we show this in the registration form? (default: TRUE)
1486	*
1487	* @return int $custom_field_id
1488	*/
1489	function new_custom_field ($custom_field_id, $billing_equiv = '', $admin_only = FALSE, $registration_form = TRUE) {
1490		$insert_fields = array(
1491							'custom_field_id' => $custom_field_id,
1492							'subscription_plans' => '',
1493							'products' => '',
1494							'user_field_billing_equiv' => $billing_equiv,
1495							'user_field_admin_only' => ($admin_only == TRUE) ? '1' : '0',
1496							'user_field_registration_form' => ($registration_form == TRUE) ? '1' : '0'
1497							);
1498							
1499		$this->db->insert('user_fields',$insert_fields);
1500		
1501		return $this->db->insert_id();
1502	}
1503	
1504	/**
1505	* Update User Custom Field
1506	*
1507	* Updates the user_fields table with user custom field-specific information
1508	*
1509	* @param int $user_field_id The custom field ID to edit
1510	* @param string $billing_equiv If this field represents a billing address field (e.g, "address_1"), specify here:  options: address_1/2, state, country, postal_code, company (default: '')
1511	* @param boolean $admin_only Is this an admin-only field? (default: FALSE)
1512	* @param boolean $registration_form Should we show this in the registration form? (default: TRUE)
1513	*
1514	* @return boolean TRUE
1515	*/
1516	function update_custom_field ($custom_field_id, $billing_equiv = '', $admin_only = FALSE, $registration_form = TRUE) {	
1517		$result = $this->db->select('user_field_id')
1518							 ->from('user_fields')
1519							 ->where('custom_field_id',$custom_field_id)
1520							 ->get();
1521							 
1522		if ($result->num_rows() == 0) {
1523			// no record yet
1524			$field_id = $this->new_custom_field($custom_field_id, $billing_equiv, $admin_only, $registration_form);
1525		}
1526				
1527		$update_fields = array(
1528							'subscription_plans' => '',
1529							'products' => '',
1530							'user_field_billing_equiv' => $billing_equiv,
1531							'user_field_admin_only' => ($admin_only == TRUE) ? '1' : '0',
1532							'user_field_registration_form' => ($registration_form == TRUE) ? '1' : '0'
1533							);
1534							
1535		$this->db->update('user_fields',$update_fields,array('custom_field_id' => $custom_field_id));
1536		
1537		return TRUE;
1538	}
1539	
1540	/**
1541	* Delete User Custom Field
1542	*
1543	* ge custom field record and modify database
1544	*
1545	* @param int $id The ID of the field
1546	* @param string The database table to reflect the changes, else FALSE
1547	*
1548	* @return boolean TRUE
1549	*/
1550	function delete_custom_field ($user_field_id) {
1551		// get custom_field_id
1552		$field = $this->get_custom_field($user_field_id);
1553		
1554		$this->load->model('custom_fields_model');
1555		$this->custom_fields_model->delete_custom_field($field['custom_field_id'], 'users');
1556		
1557		$this->db->delete('user_fields',array('user_field_id' => $user_field_id));
1558		
1559		return TRUE;
1560	}
1561	
1562	/**
1563	* Get User Custom Field
1564	*
1565	* @param int $custom_field_id
1566	*
1567	* @return boolean $custom_field or FALSE
1568	*/
1569	function get_custom_field ($id) {
1570		$return = $this->get_custom_fields(array('id' => $id));
1571		
1572		if (empty($return)) {
1573			return FALSE;
1574		}
1575		
1576		return $return[0];
1577	}
1578	
1579	/**
1580	* Get User Custom Fields
1581	*
1582	* Retrieves custom fields ordered by custom_field_order
1583	* 
1584	* @param int $filters['id'] A custom field ID
1585	* @param boolean $filters['registration_form'] Set to TRUE to retrieve registration form fields
1586	* @param boolean $filters['not_in_admin'] Set to TRUE to not retrieve admin-only fields
1587	*
1588	* @return array $fields The custom fields
1589	*/
1590	function get_custom_fields ($filters = array()) {
1591		$cache_string = md5(implode(',',$filters));
1592		
1593		if (isset($this->cache_fields[$cache_string])) {
1594			return $this->cache_fields[$cache_string];
1595		}
1596	
1597		$this->load->model('custom_fields_model');
1598	
1599		if (isset($filters['id'])) {
1600			$this->db->where('user_field_id',$filters['id']);
1601		}
1602		
1603		if (isset($filters['registration_form']) and $filters['registration_form'] == TRUE) {
1604			$this->db->where('user_field_registration_form','1');
1605		}
1606		
1607		if (isset($filters['not_in_admin']) and $filters['not_in_admin'] == TRUE) {
1608			$this->db->where('user_field_admin_only','0');
1609		}
1610	
1611		$this->db->join('user_fields','custom_fields.custom_field_id = user_fields.custom_field_id','left');
1612		$this->db->order_by('custom_fields.custom_field_order','ASC');
1613		
1614		$this->db->select('user_fields.user_field_id');
1615		$this->db->select('user_fields.user_field_billing_equiv');
1616		$this->db->select('user_fields.user_field_admin_only');
1617		$this->db->select('user_fields.user_field_registration_form');
1618		$this->db->select('custom_fields.*');
1619		
1620		$this->db->where('custom_field_group','1');
1621		
1622		$result = $this->db->get('custom_fields');
1623		
1624		if ($result->num_rows() == 0) {
1625			return FALSE;
1626		}
1627		
1628		$billing_installed = module_installed('billing');
1629		
1630		$fields = array();
1631		foreach ($result->result_array() as $field) {
1632			$fields[] = array(
1633							'id' => $field['user_field_id'],
1634							'custom_field_id' => $field['custom_field_id'],
1635							'friendly_name' => $field['custom_field_friendly_name'],
1636							'name' => $field['custom_field_name'],
1637							'type' => $field['custom_field_type'],
1638							'options' => (!empty($field['custom_field_options'])) ? unserialize($field['custom_field_options']) : array(),
1639							'help' => $field['custom_field_help_text'],
1640							'order' => $field['custom_field_order'],
1641							'width' => $field['custom_field_width'],
1642							'default' => $field['custom_field_default'],
1643							'required' => ($field['custom_field_required'] == 1) ? TRUE : FALSE,
1644							'validators' => (!empty($field['custom_field_validators'])) ? unserialize($field['custom_field_validators']) : array(),
1645							'data' => (!empty($field['custom_field_data'])) ? unserialize($field['custom_field_data']) : array(),
1646							'billing_equiv' => ($billing_installed === TRUE) ? $field['user_field_billing_equiv'] : '',
1647							'admin_only' => ($field['user_field_admin_only'] == '1') ? TRUE : FALSE,
1648							'registration_form' => ($field['user_field_registration_form'] == '1') ? TRUE : FALSE
1649						);
1650		}
1651		
1652		$this->cache_fields[$cache_string] = $fields;
1653		
1654		return $fields;
1655	}
1656}