<?php

  /**
   * User class
   *
   * @package activeCollab.modules.system
   * @subpackage models
   */
  class User extends FwUser {
    
    /**
     * List of protected fields (can't be set using setAttributes() method)
     *
     * @var array
     */
  	protected $protect = array(
  	  'session_id',
  	  'last_login_on',
  	  'last_visit_on',
  	  'last_activity_on',
  	  'auto_assign',
  	  'auto_assign_role_id',
  	  'auto_assign_permissions',
  	  'password_reset_key',
  	  'password_reset_on'
  	);
    
    /**
     * Return users display name
     *
     * @param boolean $short
     * @return string
     */
    function getName($short = false) {
      return $this->getDisplayName($short);
    } // getName
    
    /**
     * Return first name
     *
     * If $force_value is true and first name value is not present, system will
     * use email address part before @domain.tld
     *
     * @param boolean $force_value
     * @return string
     */
    function getFirstName($force_value = false) {
      $result = parent::getFirstName();
      if(empty($result) && $force_value) {
        $email = $this->getEmail();
        return ucfirst_utf(substr_utf($email, 0, strpos_utf($email, '@')));
      } // if
      return $result;
    } // getFirstName
    
    /**
     * Current user date time
     * 
     * @var DateTimeValue
     */
    private $current_date_time = false;
    
    /**
     * Return current date time
     *
     * @return DateTimeValue
     */
    function getCurrentDateTime() {
      if($this->current_date_time === false) {
        $tmp = DateTimeValue::now()->formatForUser($this);
        $this->current_date_time = new DateTimeValue($tmp);
      } // if
      return $this->current_date_time;
    } // getCurrentDateTime
    
    /**
     * Parent company
     *
     * @var Company
     */
    private $company = false;
    
    /**
     * Return parent company
     *
     * @return Company
     */
    function getCompany() {
      if($this->company === false) {
        $this->company = Companies::findById($this->getCompanyId());
      } // if
      return $this->company;
    } // getCompany
    
    /**
     * Set user company
     *
     * @param Company $company
     * @return Company
     */
    function setCompany(Company $company) {
      if($company instanceof Company) {
        $this->setCompanyId($company->getId());
        
        $this->company = $company;
        
        return $this->company;
      } else {
        throw new InvalidInstanceError('company', $company, 'Company');
      } // if
    } // setCompany
    
    /**
     * Return company name
     *
     * @return string
     */
    function getCompanyName() {
    	return $this->getCompany() instanceof Company ? $this->getCompany()->getName() : lang('-- Unknown --');
    } // getCompanyName
    
    /**
     * Return group ID
     * 
     * @return integer
     */
    function getGroupId() {
      return $this->getCompanyId();
    } // getGroupId
    
    /**
     * Return company name
     *
     * @return string
     */
    function getGroupName() {
      return $this->getCompanyName();
    } // getGroupName
    
    /**
     * Set user role
     *
     * @param Role $role
     * @return Role
     */
    function setRole(Role $role) {
      $this->is_project_manager = null;
      $this->is_people_manager = null;
      
      return parent::setRole($role);
    } // setRole
    
    /**
     * Cached array of active projects
     *
     * @var array
     */
    protected $active_projects = false;
    
    /**
     * Return all active project this user is involved in
     * 
     * If $separate_favorites is set to true, this function will return array 
     * where first element is array of favorite projects and the second array is 
     * a list of projects that are not added to favorites by this user
     *
     * @param boolean $favorite_first
     * @param boolean $separate_favorites
     * @return array
     */
    function getActiveProjects($favorite_first = false, $separate_favorites = false) {
      if($this->active_projects === false) {
        $this->active_projects = Projects::findActiveByUser($this);
      } // if
      
      if($favorite_first) {
        if(is_foreachable($this->active_projects)) {
          $favorite = array();
          $not_favorite = array();
          
          foreach($this->active_projects as $active_project) {
            if(Favorites::isFavorite($active_project, $this)) {
              $favorite[] = $active_project;
            } else {
              $not_favorite[] = $active_project;
            } // if
          } // foreach
          
          if($separate_favorites) {
            return array($favorite, $not_favorite);
          } else {
            if(count($favorite) && count($not_favorite)) {
              return array_merge($favorite, $not_favorite);
            } elseif(count($favorite)) {
              return $favorite;
            } elseif(count($not_favorite)) {
              return $not_favorite;
            } else {
              return null;
            } // if
          } // if
          
        } else {
          return $separate_favorites ? array(null, null) : null;
        } // if
      } else {
        return $this->active_projects;
      } // if
    } // getActiveProjects
    
    /**
     * Cached display name
     *
     * @var string
     */
    private $display_name = false;
    
    /**
     * Cached short display name
     *
     * @var string
     */
    private $short_display_name = false;
    
    /**
     * Return display name (first name and last name)
     *
     * @param boolean $short
     * @return string
     */
    function getDisplayName($short = false) {
      if ($short) {
        if ($this->short_display_name === false) {
        	$this->short_display_name = Users::getUserDisplayName(array(
        	  'first_name' => $this->getFirstName(), 
        	  'last_name' => $this->getLastName(), 
        	  'email' => $this->getEmail()
        	), $short);
        } // if
        return $this->short_display_name;
      } else {
        if ($this->display_name === false) {
					$this->display_name = Users::getUserDisplayName(array(
					  'first_name' => $this->getFirstName(), 
					  'last_name' => $this->getLastName(), 
					  'email' => $this->getEmail()
					), $short);
        } // if
        return $this->display_name;
      } // if
    } // getDisplayName
    
    /**
     * Prepare list of options that $user can use
     *
     * @param IUser $user
     * @param NamedList $options
     * @param string $interface
     * @return NamedList
     */
    protected function prepareOptionsFor(IUser $user, NamedList $options, $interface = AngieApplication::INTERFACE_DEFAULT) {
      if($this->canChangeRole($user)) {
        $options->add('edit_company_and_role', array(
          'text' => lang('Company and Role'),
          'url'  => $this->getEditCompanyAndRoleUrl(),
          'icon' => $interface == AngieApplication::INTERFACE_DEFAULT ? AngieApplication::getImageUrl('icons/12x12/change-company.png', SYSTEM_MODULE, AngieApplication::INTERFACE_DEFAULT) : AngieApplication::getImageUrl('icons/navbar/edit_company_and_role.png', SYSTEM_MODULE, AngieApplication::INTERFACE_PHONE),
        	'important' => true,
          'onclick' => new FlyoutFormCallback('user_updated', array(
            'width' => 'narrow', 
            'success_message' => lang('Company and Role have been updated'), 
          )),
        ), true);
      } // if
      
    	if($this->canEdit($user)) {
        $options->add('edit_profile', array(
          'text' => lang('Update Profile'),
          'url'  => $this->getEditProfileUrl(),
        	'icon' => $interface == AngieApplication::INTERFACE_DEFAULT ? AngieApplication::getImageUrl('icons/12x12/edit.png', ENVIRONMENT_FRAMEWORK, AngieApplication::INTERFACE_DEFAULT) : '',
        	'important' => true,
          'onclick' => new FlyoutFormCallback('user_updated', array(
            'success_message' => lang('User profile has been updated'), 
          )),
        ), true);
        
        $options->add('edit_settings', array(
          'text' => lang('Change Settings'),
          'url'  => $this->getEditSettingsUrl(),
        	'icon' => $interface == AngieApplication::INTERFACE_DEFAULT ? AngieApplication::getImageUrl('icons/12x12/settings.png', ENVIRONMENT_FRAMEWORK, AngieApplication::INTERFACE_DEFAULT) : '',
          'onclick' => new FlyoutFormCallback('user_updated', array(
            'success_message' => lang('Settings have been updated'), 
          )),
        ), true);
      } // if
      
      if($this->canContact($user)) {
        $options->add('export_vcard', array(
          'text' => lang('Export vCard'),
          'url'  => $this->getExportVcardUrl(),
        	'class' => 'notinline',
        	'onclick' => new TargetBlankCallback()
        ), true);
      } // if
      
      if($user->isProjectManager()) {
        $options->add('add_to_projects', array(
          'text' => lang('Add to Projects'),
          'url'  => $this->getAddToProjectsUrl(),
        	'onclick' => new FlyoutFormCallback('user_added_to_project', array(
            'success_message' => lang('User has been added to selected projects'), 
          )),
        ), true);
      } // if
      
      if($this->canSendWelcomeMessage($user)) {
        $options->add('send_welcome_message', array(
          'text' => lang('Send Welcome Message'),
          'url'  => $this->getSendWelcomeMessageUrl(),
          'onclick' => new FlyoutFormCallback(array(
            'success_message' => lang('Welcome message has been sent'),
            'width' => 600,
          )),
        ), true);
      } // if
            
      if($this->canLoginAs($user)) {
      	$options->add('people_company_user_login_as', array(
          'text' => lang('Login As'),
          'url'  => $this->getLoginAsUrl(),
      		'onclick' => new LoginAsFormCallback(array(
            'width' => 400
          )),
        ), true);
      } // if
      
      parent::prepareOptionsFor($user, $options, $interface);
      
      // Edit is not available in user interface, only through API
      if($options->exists('edit')) {
        $options->remove('edit');
      } // if
    } // prepareOptionsFor
    
    /**
     * Cached array of visible user ID-s
     *
     * @var array
     *
    private $visible_user_ids = array();
    
    /**
     * Returns an array of visible users
     *
     * @param Company $company
     * @param integer $min_state
     * @return array
     */
    function visibleUserIds($company = null, $min_state = STATE_VISIBLE) {
      if($company instanceof Company) {
        if(!array_key_exists($company->getId(), $this->visible_user_ids)) {
          $this->visible_user_ids[$company->getId()] = Users::findVisibleUserIds($this, $company, $min_state);
        } // if
        
        return $this->visible_user_ids[$company->getId()];
      } else {
        if(!array_key_exists('all', $this->visible_user_ids)) {
          $this->visible_user_ids['all'] = Users::findVisibleUserIds($this, null, $min_state);
        } // if
        
        return $this->visible_user_ids['all'];
      } // if
    } // visibleUserIds
    
    /**
     * Cached array of visible company ID-s
     *
     * @var array
     */
    private $visible_company_ids = false;
    
    /**
     * Returns array of companies this user can see
     *
     * @return array
     */
    function visibleCompanyIds() {
      if($this->visible_company_ids === false) {
        $this->visible_company_ids = Users::findVisibleCompanyIds($this);
      } // if
      return $this->visible_company_ids;
    } // visibleCompanyIds
    
    /**
     * Return true if user is archived
     * 
     * @return boolean
     */
    function isArchived() {
      return $this->getState() == STATE_ARCHIVED ? true : false;
    } // isArchived
     
    /**
     * Return array or property => value pairs that describes this object
     *
     * $user is an instance of user who requested description - it's used to get
     * only the data this user can see
     *
     * @param IUser $user
     * @param boolean $detailed
     * @param boolean $for_interface
     * @return array
     */
    function describe(IUser $user, $detailed = false, $for_interface = false) {
    	// TODO: resiti problem kesiranja, ovo je samo privremeni fix
      $this->role = false;
      $this->display_name = false;

    	$result = parent::describe($user, $detailed, $for_interface);

    	$result['first_name'] = $this->getFirstName(true);
    	$result['last_name'] = $this->getLastName();
    	$result['display_name'] = $this->getDisplayName();
    	$result['short_display_name'] = $this->getDisplayName(true);
    	$result['email'] = $this->getEmail();
    	$result['last_visit_on'] = $this->getLastVisitOn();
    	$result['is_administrator'] = $this->isAdministrator();
    	$result['is_project_manager'] = $this->isProjectManager();
    	$result['is_people_manager'] = $this->isPeopleManager();

      $result['company_id'] = $this->getCompanyId();
    	if($detailed) {
    	  $result['company'] = $this->getCompany() instanceof Company ? $this->getCompany()->describe($user, false, $for_interface) : null;
    	} // if

      $result['is_archived'] = $this->getState() == STATE_ARCHIVED ? 1 : 0;

      if ($detailed) {
      	$result['title'] = $this->getConfigValue('title');
      	$result['phone_work'] = $this->getConfigValue('phone_work');
      	$result['phone_mobile'] = $this->getConfigValue('phone_mobile');
      	$result['im_type'] = $this->getConfigValue('im_type');
      	$result['im_value'] = $this->getConfigValue('im_value');

      	$now = new DateTimeValue();
      	$result['local_time'] = $now->formatTimeForUser($this, get_user_gmt_offset($this));
      } // if

      $result['role_id'] = $this->getRoleId();
      if($detailed) {
        $result['role'] = $this->getRole() instanceof Role ? $this->getRole()->describe($user, false, $for_interface) : null;
      } // if

      return $result;
    } // describe

    /**
     * Return array or property => value pairs that describes this object
     *
     * @param IUser $user
     * @param boolean $detailed
     * @return array
     */
    function describeForApi(IUser $user, $detailed = false) {
      // TODO: resiti problem kesiranja, ovo je samo privremeni fix
      $this->role = false;
      $this->display_name = false;

      $result = parent::describeForApi($user, $detailed);

      $result['first_name'] = $this->getFirstName(true);
      $result['last_name'] = $this->getLastName();
      $result['display_name'] = $this->getDisplayName();
      $result['short_display_name'] = $this->getDisplayName(true);
      $result['email'] = $this->getEmail();

      if($detailed) {
        $result['company_id'] = $this->getCompanyId();
        $result['role_id'] = $this->getRoleId();

        $result['last_visit_on'] = $this->getLastVisitOn();
        $result['is_administrator'] = $this->isAdministrator();
        $result['is_project_manager'] = $this->isProjectManager();
        $result['is_people_manager'] = $this->isPeopleManager();
        $result['company'] = $this->getCompany() instanceof Company ? $this->getCompany()->describeForApi($user) : null;

        $result['title'] = $this->getConfigValue('title');
        $result['phone_work'] = $this->getConfigValue('phone_work');
        $result['phone_mobile'] = $this->getConfigValue('phone_mobile');
        $result['im_type'] = $this->getConfigValue('im_type');
        $result['im_value'] = $this->getConfigValue('im_value');

        $now = new DateTimeValue();
        $result['local_time'] = $now->formatTimeForUser($this, get_user_gmt_offset($this));

        $result['role'] = $this->getRole() instanceof Role ? $this->getRole()->describeForApi($user) : null;
      } // if

      return $result;
    } // describeForApi
    
    /**
     * Return vCard content that represents this user
     * 
     * @return string
     */
    function toVCard($force_download = true, $export_to_file = false) {
    	$vcard_content = '';
    	
      $vcard = parent::toVCard();
      
      if($this->getConfigValue('title')) {
				$vcard->setTitle($this->getConfigValue('title'));
			} // if
			
			$vcard->addOrganization($this->getCompanyName());
			
			if($this->getConfigValue('phone_work')) {
				$vcard->addTelephone($this->getConfigValue('phone_work'));
				$vcard->addParam('TYPE', 'WORK');
			} // if
			
			if($this->getConfigValue('phone_mobile')) {
				$vcard->addTelephone($this->getConfigValue('phone_mobile'));
				$vcard->addParam('TYPE', 'CELL');
			} // if
	
			if($this->getConfigValue('im_type') && $this->getConfigValue('im_value')) {
				switch($this->getConfigValue('im_type')) {
					case "AIM":
						$vcard->addIM('X-AIM', $this->getConfigValue('im_value'));
						break;
					case "ICQ":
						$vcard->addIM('X-ICQ', $this->getConfigValue('im_value'));
						break;
					case "MSN":
						$vcard->addIM('X-MSN', $this->getConfigValue('im_value'));
						break;
					case "Yahoo!":
						$vcard->addIM('X-YAHOO', $this->getConfigValue('im_value'));
						break;
					case "Jabber":
						$vcard->addIM('X-JABBER', $this->getConfigValue('im_value'));
						break;
					case "Skype":
						$vcard->addIM('X-SKYPE', $this->getConfigValue('im_value'));
						break;
					case "Google":
						$vcard->addIM('X-GOOGLE-TALK', $this->getConfigValue('im_value'));
						break;
				} // switch
			} // if
			
			$avatar_url = $this->avatar()->getUrl(IUserAvatarImplementation::SIZE_PHOTO);
			$avatar = strtok(basename($avatar_url), '?');
			$type = strtoupper(substr(strrchr($avatar, '.'), 1));
			
			if($avatar != 'default.256x256.png') {
				$vcard->setPhoto(base64_encode(file_get_contents($avatar_url)));
				$vcard->addParam('TYPE', $type == 'JPG' ? 'JPEG' : $type);
				$vcard->addParam('ENCODING', 'b');
			} // if
			
			if($this->getUpdatedOn()) {
				$vcard->setRevision(date('Ymd\THis\Z', strtotime($this->getUpdatedOn())));
			} // if
			
			$vcard_content .= $vcard->fetch() . "\n";
			
      if($force_download) {
				header('Content-Type: text/x-vcard; charset=utf-8');
				header('Content-Disposition: attachment; filename="' . $this->getName() . '.vcf"');
		
		    print $vcard_content;
		    die();
			} elseif($export_to_file) {
				$file_path = WORK_PATH . '/contacts/' . $this->getName() . '.vcf';
				$file_handle = fopen($file_path, 'w+');
				if(!fwrite($file_handle, $vcard_content)) {
					throw new Exception(lang('Could not write user :name vCard into temporary vCard :file file', array('name' => $this->getName(), 'file' => $file_path)));
				} // if
				fclose($file_handle);
				@chmod($file_path, 0777);
			} else {
				return $vcard_content;
			} // if
    } // toVCard
    
    /**
     * Prefered locale
     *
     * @var string
     */
    private $locale = false;
    
    /**
     * Return prefered locale
     *
     * @param string $default
     * @return string
     */
    function getLocale($default = null) {
    	if($this->locale === false) {
    	  $language_id = ConfigOptions::getValueFor('language', $this);
    	  if($language_id) {
    	    $language = Languages::findById($language_id);
    	    if($language instanceof Language) {
    	      $this->locale = $language->getLocale();
    	    } // if
    	  } // if
    	  
    	  if($this->locale === false) {
    	    $this->locale = $default === null ? BUILT_IN_LOCALE : $default;
    	  } // if
    	} // if
    	
    	return $this->locale;
    } // getLocale
    
    /**
     * Cached last visit on value
     *
     * @var DateTimeValue
     */
    private $last_visit_on = false;
    
    /**
     * Return users last visit
     *
     * @param boolean $force
     * @return DateTimeValue
     */
    function getLastVisitOn($force = false) {
      if($this->last_visit_on === false) {
      	$this->last_visit_on = parent::getLastVisitOn();
      } // if
      
      if($force) {
        return $this->last_visit_on instanceof DateTimeValue ? $this->last_visit_on : new DateTimeValue(filectime(ENVIRONMENT_PATH . '/config/config.php'));
      } else {
        return $this->last_visit_on;
      } // if
    } // getLastVisitOn
    
    /**
     * Prepare user's IM for import
     *
     * @param string $im_type
     * @param array $im_value
     * @param string $ac_im_type
     * @param string $ac_im_value
     */
    function prepareIM($vcard_im_type, $vcard_im_value, &$ac_im_type, &$ac_im_value) {
    	if(is_foreachable($vcard_im_value[0]['value'])) {
				$value = trim($vcard_im_value[0]['value'][0][0]);

				if($value != '') {
					$ac_im_type = $vcard_im_type;
					$ac_im_value = $value;
				} // if
			} // if
    } // prepareIM
    
    /**
     * Return user specific quick add cache ID for given interface
     * 
     * @param string $interface
     * @return string
     */
    function getQuickAddCacheId($interface = AngieApplication::INTERFACE_DEFAULT) {
      return "quick_add_for_{$this->getId()}_and_{$interface}";
    } // getQuickAddCacheId
    
    // ---------------------------------------------------
    //  Utils
    // ---------------------------------------------------
    
    /**
     * Returns true if this user can see objects of given visibility
     *
     * @param integer $visibility
     * @return boolean
     */
    function canSee(IVisibility $object) {
      switch($object->getVisibility()) {
        case VISIBILITY_PRIVATE:
          return $this->isProjectManager() || (boolean) $this->getSystemPermission('can_see_private_objects');
        case VISIBILITY_NORMAL: 
        case VISIBILITY_PUBLIC:
          return true;
        default:
          throw new InvalidParamError('visibility', $object->getVisibility(), "Unknown visiibility value: " . (string) $object->getVisibility());
      } // switch
    } // canSee
    
    /**
     * Cached values of can see milestones permissions
     *
     * @var array
     */
    private $can_see_milestones = array();
    
    /**
     * Returns true if user can see milestones in $project
     *
     * @param Project $project
     * @param boolean $check_tab
     * @return boolean
     */
    function canSeeMilestones(Project $project, $check_tab = true) {
      $project_id = $project->getId();
    	if(!isset($this->can_see_milestones[$project_id])) {
    	  $this->can_see_milestones[$project_id] = Milestones::canAccess($this, $project, $check_tab);
    	} // if
    	return $this->can_see_milestones[$project_id];
    } // canSeeMilestones
    
    /**
     * Cached can see project budgets value
     *
     * @var boolean
     */
    private $can_see_project_budgets = null;
    
    /**
     * Returns true if $user can see project budgets
     * 
     * @param Project $project
     * @return boolean
     */
    function canSeeProjectBudgets() {
      if($this->can_see_project_budgets === null) {
        $this->can_see_project_budgets = $this->isAdministrator() || $this->getSystemPermission('can_see_project_budgets');
      } // if
      
      return $this->can_see_project_budgets;
    } // canSeeProjectBudgets
    
    /**
     * Returns true if this user can see contact details of other users
     *
     * @var boolean
     */
    private $can_see_contact_details = null;
    
    /**
     * Return true if $user can see contact details of other users in the system
     *
     * @return boolean
     */
    function canSeeContactDetails() {
      if($this->can_see_contact_details === null) {
        $this->can_see_contact_details = $this->isProjectManager() || $this->isPeopleManager() || $this->getSystemPermission('can_see_contact_details');
      } // if
      
      return $this->can_see_contact_details;
    } // canSeeContactDetails
    
    /**
     * Returns true if this user has access to reports section
     * 
     * @return boolean
     */
    function canUseReports() {
      return $this->isProjectManager() || $this->isPeopleManager() || $this->isFinancialManager();
    } // canUseReports
    
    /**
     * Is this user member of owner company
     *
     * @var boolean
     */
    private $is_owner = null;
    
    /**
     * Returns true if this user is member of owner company
     *
     * @return boolean
     */
    function isOwner() {
      if($this->is_owner === null) {
        $this->is_owner = $this->getCompany() instanceof Company ? $this->getCompany()->getIsOwner() : false;
      } // if
      return $this->is_owner;
    } // isOwner
    
    /**
     * Cached is people manager permission value
     *
     * @var boolean
     */
    private $is_people_manager = null;
    
    /**
     * Returns true if this user has management permissions in People section
     *
     * @return boolean
     */
    function isPeopleManager() {
      if($this->is_people_manager === null) {
        $this->is_people_manager = $this->isAdministrator() || $this->getSystemPermission('can_manage_people');
      } // if
      return $this->is_people_manager;
    } // isPeopleManager
    
    /**
     * Cached value of is project manager permissions
     *
     * @var boolean
     */
    private $is_project_manager = null;
    
    /**
     * Returns true if this user has global project management permissions
     *
     * @return boolean
     */
    function isProjectManager() {
      if($this->is_project_manager === null) {
        $this->is_project_manager = $this->isAdministrator() || $this->getSystemPermission('can_manage_projects');
      } // if
      
      return $this->is_project_manager;
    } // isProjectManager
    
    /**
     * Cached is financial manager permission value
     *
     * @var boolean
     */
    private $is_financial_manager = null;
    
    /**
     * Returns true if this user has final management permissions
     * 
     * @return boolean
     */
    function isFinancialManager() {
      if($this->is_financial_manager === null) {
        $this->is_financial_manager = AngieApplication::isModuleLoaded('invoicing') && $this->getSystemPermission('can_manage_finances') && ($this->isOwner() || $this->isAdministrator());
      } // if
      
      return $this->is_financial_manager;
    } // isFinancialManager
    
    /**
     * Cached is company manager value
     *
     * @var boolean
     */
    private $is_company_manager = null;
    
    /**
     * Return true if this user manager of parent company
     * 
     * @return boolean
     */
    function isCompanyManager() {
      if($this->is_company_manager === null) {
        return $this->getCompany() instanceof Company ? $this->getCompany()->isManager($this) : false;
      } // if
      
      return $this->is_company_manager;
    } // isCompanyManager
    
    /**
     * Cached visibility
     *
     * @var boolean
     */
    private $min_visibility = false;
    
    /**
     * Returns optimal visibility for this user
     *
     * If this user is member of owner company he will be able to see private
     * objects. If not he will be able to see only normal and public objects
     *
     * @return boolean
     */
    function getMinVisibility() {
      if($this->min_visibility === false) {
        $this->min_visibility = $this->canSeePrivate() ? VISIBILITY_PRIVATE : VISIBILITY_NORMAL;
      } // if
      return $this->min_visibility;
    } // getMinVisibility
    
    // ---------------------------------------------------
    //  Project roles and permissions
    // ---------------------------------------------------
    
    /**
     * User projects helper implementation
     *
     * @var IUserProjectsImplementation
     */
    private $projects = false;
    
    /**
     * Return projects helper instance
     *
     * @return IUserProjectsImplementation
     */
    function projects() {
      if($this->projects === false) {
        $this->projects = new IUserProjectsImplementation($this);
      } // if
      
      return $this->projects;
    } // projects
    
    /**
     * Return config option value
     *
     * @param string $name
     * @return mixed
     */
    function getConfigValue($name) {
      return ConfigOptions::getValueFor($name, $this);
    } // getConfigValue
    
    // ---------------------------------------------------
    //  Interface implementations
    // ---------------------------------------------------
    /**
     * Return object context domain
     *
     * @return string
     */
    function getObjectContextDomain() {
      return 'people';
    } // getObjectContextDomain
    
    /**
     * Return object context path
     * 
     * @return string
     */
    function getObjectContextPath() {
      return $this->getCompany()->getObjectContextPath() . '/users/' . $this->getId();
    } // getObjectContextPath
    
    /**
     * Return routing context name
     *
     * @return string
     */
    function getRoutingContext() {
      return 'people_company_user';
    } // getRoutingContext
    
    /**
     * Return routing context parameters
     *
     * @return mixed
     */
    function getRoutingContextParams() {
      return array(
        'company_id' => $this->getCompanyId(),
        'user_id' => $this->getId(),
      );
    } // getRoutingContextParams
    
    /**
     * Return modification log helper instance
     *
     * @return IHistoryImplementation
     */
    function history() {
      return parent::history()->alsoTrackFields(array('company_id'));
    } // history
    
    /**
     * Notifier helper instance
     * 
     * @var IActiveCollabNotifierImplementation
     */
    private $notifier = false;
    
    /**
     * Return notifier helper
     * 
     * @return IActiveCollabNotifierImplementation
     */
    function notifier() {
    	if($this->notifier === false) {
    		$this->notifier = new IActiveCollabNotifierImplementation($this);
    	} // if
    	
    	return $this->notifier;
    } // notifier

    /**
     * Cached inspector instance
     *
     * @var IActiveCollabUserInspectorImplementation
     */
    private $inspector = false;

    /**
     * Return inspector helper instance
     *
     * @return IActiveCollabUserInspectorImplementation
     */
    function inspector() {
      if($this->inspector === false) {
        $this->inspector = new IActiveCollabUserInspectorImplementation($this);
      } // if

      return $this->inspector;
    } // inspector
    
    // ---------------------------------------------------
    //  Permissions
    // ---------------------------------------------------
    
    /**
     * Returns true if $user can see this account
     *
     * @param IUser $user
     * @return boolean
     */
    function canView(IUser $user) {
      if($user instanceof User) {
        $min_state = Trash::canAccess($user) ? STATE_TRASHED : STATE_ARCHIVED;
        return $user->getId() == $this->getId() || $user->getCompanyId() == $this->getCompanyId() || in_array($this->getId(), Users::findVisibleUserIds($user, null, $min_state));
      } else {
        return false;
      } // if
    } // canView
    
    /**
     * Return true if $user can see and use contact details of this user
     * 
     * @param IUser $user
     * @return boolean
     */
    function canContact(IUser $user) {
      if($user instanceof User) {
        if($user->getCompanyId() == $this->getCompanyId()) {
          return true;
        } else {
          return $user->canSeeContactDetails();
        } // if
      } else {
        return false;
      } // if
    } // canContact

    /**
     * Check if $user can update this profile
     *
     * @param User $user
     * @return boolean
     */
    function canEdit(IUser $user) {
      if($user instanceof User) {
        return $user->is($this) || $user->isPeopleManager() || $this->getCompany()->isManager($user);
      } else {
        return false;
      } // if
    } // canEdit
    
    /**
     * Returns true if $user can change password of this user
     * 
     * @param User $user
     */
    function canChangePassword(IUser $user) {
      if($user instanceof User) {
        if($this->isAdministrator()) {
          return $user->is($this) || $user->isAdministrator();
        } elseif ($this->isAdministrator() && !$user->isAdministrator()) {
          return false;
        } else {
          return $user->is($this) || $user->isPeopleManager() || $this->getCompany()->isManager($user);
        } // if
      } else {
        return false;
      } // if
    } // canChangePassword
    
    /**
     * Returns true if $user can change this users role
     *
     * @param User $user
     * @return boolean
     */
    function canChangeRole(User $user) {
      if ($this->isAdministrator()) {
        return $user->is($this) || $user->isAdministrator();
      } elseif ($this->isProjectManager() || $this->isPeopleManager()) {
        return $user->isAdministrator();
      } else {
        return $user->isAdministrator() || $user->isPeopleManager();
      } // if
    } // canChangeRole
    
    /**
     * Check if $user can view recent activities of the selected user
     *
     * @param User $user
     * @return boolean
     */
    function canViewActivities($user) {
    	return $user->isAdministrator() || $user->isProjectManager();
    } // canViewActivities

    /**
     * Can $this user import contacts from vCard?
     *
     * @return boolean
     */
    function canImportVcard() {
    	return $this->isPeopleManager();
    } // canAdd
    
    /**
     * Returns true if $user can change this users permissions on a $project
     *
     * @param User $user
     * @param Project $project
     * @return boolean
     */
    function canChangeProjectPermissions(User $user, Project $project) {
      if($project->isLeader($user) || $user->isProjectManager() || $user->isAdministrator()) {
        return false;
      } // if
      
      return $project->canManagePeople($this);
    } // canChangeProjectPermissions
    
    /**
     * Check if $user can remove this user from $project
     *
     * @param User $user
     * @param Project $project
     * @return boolean
     */
    function canRemoveFromProject(User $user, Project $project) {
      if($project->isLeader($user)) {
        return false;
      } // if
      
      return $project->canManagePeople($this);
    } // canRemoveFromProject
    
    /**
     * Returns true if $user can (re)send welcome message
     *
     * @param User $user
     * @return boolean
     */
    function canSendWelcomeMessage(User $user) {
      if ($user instanceof User) {
        if ($user->is($this) || ($this->isAdministrator() && !$user->isAdministrator())) {
          return false;
        } else {
          return $user->isPeopleManager() || $this->getCompany()->isManager($user);
        } // if
      } else {
        return false;
      } // if
    } // canSendWelcomeMessage
    
    /**
     * Can $user login as $this selected one
     *
     * @param User $user
     * @return boolean
     */
    function canLoginAs(User $user) {
      // no sense to log in as yourself
      if ($this->is($user)) {
        return false;
      } // if
			
      // admin can log in as everyone
      if ($user->isAdministrator() && !$this->isAdministrator()) {
        return true;
      } // if

      // project manager can log in as as anyone except admin and
      // as people manager, unless project manager can also manage people
      if ($user->isProjectManager()) {
      	if ($this->isAdministrator() || $this->isProjectManager()) {
      		return false;
      	} //if
        if ($this->isPeopleManager()) {
          return $user->isPeopleManager();
        } // if
        
        return true;
      } // if

      // people manager can log in as anyone, except as project manager and admin
      if ($user->isPeopleManager()) {
        return !($this->isAdministrator() || $this->isProjectManager() || $this->isPeopleManager());
      } // if

      return false;
    } // canLoginAs
        
    // ---------------------------------------------------
    //  URL-s
    // ---------------------------------------------------
    
    /**
     * Return View URL
     *
     * @return string
     */
    function getViewUrl() {
    	return Router::assemble('people_company_user', array(
    	  'company_id' => $this->getCompanyId(),
    	  'user_id'    => $this->getId(),
    	));
    } // getViewUrl
    
    /**
     * Return recent activities URL
     *
     * @return string
     */
    function getRecentActivitiesUrl() {
    	return Router::assemble('people_company_user_recent_activities', array(
    	  'company_id' => $this->getCompanyId(),
    	  'user_id'    => $this->getId(),
    	));
    } // getRecentActivitiesUrl
    
    /**
     * Get edit user profile URL
     *
     * @return string
     */
    function getEditProfileUrl() {
      return Router::assemble('people_company_user_edit_profile', array(
        'company_id' => $this->getCompanyId(),
        'user_id'    => $this->getId(),
      ));
    } // getEditProfileUrl
    
    /**
     * Get edit user settings URL
     *
     * @return string
     */
    function getEditSettingsUrl() {
      return Router::assemble('people_company_user_edit_settings', array(
        'company_id' => $this->getCompanyId(),
        'user_id'    => $this->getId(),
      ));
    } // getEditSettingsUrl
    
    /**
     * Return edit company and role URL
     *
     * @return string
     */
    function getEditCompanyAndRoleUrl() {
      return Router::assemble('people_company_user_edit_company_and_role', array(
        'company_id' => $this->getCompanyId(),
        'user_id'    => $this->getId(),
      ));
    } // getEditCompanyAndRoleUrl
    
    /**
     * Get edit password URL
     *
     * @return string
     */
    function getEditPasswordUrl() {
      return Router::assemble('people_company_user_edit_password', array(
        'company_id' => $this->getCompanyId(),
        'user_id'    => $this->getId(),
      ));
    } // getEditPasswordUrl
        
    /**
     * Return delete user URL
     *
     * @return string
     */
    function getDeleteUrl() {
      return Router::assemble('people_company_user_delete', array(
        'company_id' => $this->getCompanyId(),
        'user_id'    => $this->getId(),
      ));
    } // getDeleteUrl
    
    /**
     * Return unsubscribe from object URL
     *
     * @param ProjectObject $object
     * @return string
     */
    function getUnsubscribeUrl($object) {
      return Router::assemble('project_object_unsubscribe_user', array(
        'project_slug' => $object->getProject()->getSlug(),
        'object_id' => $object->getId(),
        'user_id' => $this->getId(),
      ));
    } // getUnsubscribeUrl
    
    /**
     * Return reset password URL
     *
     * @return string
     */
    function getResetPasswordUrl() {
    	return Router::assemble('reset_password', array(
    	  'user_id' => $this->getId(),
    	  'code' => $this->getPasswordResetKey(),
    	));
    } // getResetPasswordUrl
    
    /**
     * Return add to projects URL
     *
     * @return string
     */
    function getAddToProjectsUrl() {
      return Router::assemble('people_company_user_add_to_projects', array(
        'company_id' => $this->getCompanyId(),
        'user_id'    => $this->getId(),
      ));
    } // getAddToProjectsUrl
    
    /**
     * Return user projects URL
     *
     * @return string
     */
    function getProjectsUrl() {
      return Router::assemble('people_company_user_projects', array(
        'company_id' => $this->getCompanyId(),
        'user_id'    => $this->getId(),
      ));
    } // getProjectsUrl
    
    /**
     * Return user projects URL
     *
     * @param mixed $page
     * @return string
     */
    function getProjectsArchiveUrl($page = null) {
      $params = array(
        'company_id' => $this->getCompanyId(),
        'user_id'    => $this->getId(),
      );
      
      if($page) {
        $params['page'] = $page;
      } // if
      
      return Router::assemble('people_company_user_projects_archive', $params);
    } // getProjectsArchiveUrl
    
    /**
     * Return user favorites URL
     *
     * @return string
     */
    function getFavoritesUrl() {
    	return Router::assemble('people_company_user_favorites', array(
    	  'company_id' => $this->getCompanyId(),
    	  'user_id' => $this->getId()
    	));
    } // getFavoritesUrl
    
    /**
     * Return send welcome message URL
     *
     * @return string
     */
    function getSendWelcomeMessageUrl() {
      return Router::assemble('people_company_user_send_welcome_message', array(
        'company_id' => $this->getCompanyId(),
        'user_id'    => $this->getId(),
      ));
    } // getSendWelcomeMessageUrl
    
    /**
     * Return login as URL
     *
     * @return string
     */
    function getLoginAsUrl() {
    	return Router::assemble('people_company_user_login_as', array(
    	  'company_id' => $this->getCompanyId(),
    	  'user_id'    => $this->getId(),
    	));
    } // getLoginAsUrl
    
    // ---------------------------------------------------
    //  Getters and Setters
    // ---------------------------------------------------
    
    /**
     * Set auto-assign data
     *
     * @param boolean $enabled
     * @param integer $role_id
     * @param array $permissions
     */
    function setAutoAssignData($enabled, $role_id, $permissions) {
    	if($enabled) {
    	  $this->setAutoAssign(true);
  	    if($role_id) {
  	      $this->setAutoAssignRoleId($role_id);
  	      $this->setAutoAssignPermissions(null);
  	    } else {
  	      $this->setAutoAssignRoleId(0);
  	      $this->setAutoAssignPermissions($permissions);
  	    } // if
  	  } else {
  	    $this->setAutoAssign(false);
  	    $this->setAutoAssignRoleId(0);
  	    $this->setAutoAssignPermissions(null);
  	  } // if
    } // setAutoAssignData
    
    /**
     * Return auto assign role based on auto assign role ID
     *
     * @return Role
     */
    function getAutoAssignRole() {
    	return $this->getAutoAssignRoleId() ? ProjectRoles::findById($this->getAutoAssignRoleId()) : null;
    } // getAutoAssignRole
    
    /**
     * Return auto assign permissions
     *
     * @return mixed
     */
    function getAutoAssignPermissions() {
    	$raw = parent::getAutoAssignPermissions();
    	return $raw ? unserialize($raw) : null;
    } // getAutoAssignPermissions
    
    /**
     * Set auto assign permissions
     *
     * @param mixed $value
     * @return mixed
     */
    function setAutoAssignPermissions($value) {
    	return parent::setAutoAssignPermissions(serialize($value));
    } // setAutoAssignPermissions
    
    // ---------------------------------------------------
    //  System
    // ---------------------------------------------------
    
    /**
     * Validate before save
     *
     * @param ValidationErrors $errors
     */
    function validate(ValidationErrors &$errors) {
      $company_id = $this->getCompanyId();
      if($company_id) {
        $company = Companies::findById($company_id);
        if(!($company instanceof Company)) {
          $errors->addError(lang('Selected company does not exist'), 'company_id');
        } // if
      } else {
        $errors->addError(lang('Please select company'), 'company_id');
      } // if
      
      $mailbox_emais = IncomingMailboxes::findEnabledByEmail($this->getEmail());
      if($mailbox_emais instanceof IncomingMailbox) {
        $errors->addError(lang('There is a mailbox defined with the same email address'), 'email');
      }//if
      
      parent::validate($errors);
    } // validate
    
    /**
     * Delete from database
     */
    function delete() {
      parent::delete();
      
      $this->projects()->clear();
    } // delete

    /**
     * Clear cache on save
     *
     * @return boolean
     */
    function save() {
      $clear_cache = $this->isModifiedField('state') || $this->isModifiedField('company_id');
      $clear_project_users_cache = $clear_cache || $this->isModifiedField('first_name') || $this->isModifiedField('last_name') || $this->isModifiedField('email');

      $save = parent::save();

      if($clear_cache) {
    	  cache_remove_by_pattern('visible_userids_for_*');
    	} // if

      if ($clear_project_users_cache) {
        cache_remove_by_pattern('project_users_*');
      } // if

    	return $save;
    } // save
  
  }