<?php

  /**
   * Authentication level single user
   *
   * @package angie.frameworks.authentication
   * @subpackage models
   */
  abstract class FwUser extends BaseUser implements IUser, INotifier, IRoutingContext, IHistory, ISearchItem, IObjectContext, IConfigContext, IState, IAvatar, IHomescreen, INotifierContext {
    
    /**
     * Return base type name
     * 
     * @param boolean $singular
     * @return string
     */
    function getBaseTypeName($singular = true) {
      return $singular ? 'user' : 'users';
    } // getBaseTypeName
    
    /**
     * 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
    
    /**
     * 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
    
    /**
     * Return language for given user
     *
     * @var Language
     */
    private $language = false;
    
    /**
     * Return users language
     *
     * @return Language
     */
    function getLanguage() {
      $language = DataObjectPool::get('Language', $this->getLanguageId());
          
      if(empty($language)) {
        $language = new Language();
        $language->setLocale(BUILT_IN_LOCALE);
        $language->setName('English');
      } // if
      return $language;
    } // getLanguage
    
    /**
     * Cached language ID value
     *
     * @var integer
     */
    private $language_id = false;
    
    /**
     * Return language ID from configuration
     * 
     * @param boolean $use_cache
     * @return integer
     */
    function getLanguageId($use_cache = true) {
      if(empty($use_cache) || $this->language_id === false) {
        $this->language_id = ConfigOptions::getValueFor('language', $this);
      } // if
      
      return $this->language_id;
    } // getLanguageId

    /**
     * Return system permission value
     *
     * @param string $name
     * @param boolean $default
     * @return boolean
     */
    function getSystemPermission($name, $default = false) {
      return $this->getRole() instanceof Role ? $this->getRole()->getPermissionValue($name, $default) : $default;
    } // getSystemPermission

    // ---------------------------------------------------
    //  Utility
    // ---------------------------------------------------
    
    /**
     * 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/navbar/edit_company_and_role.png', SYSTEM_MODULE, AngieApplication::INTERFACE_PHONE),
//          '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(),
//          '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(),
//          'onclick' => new FlyoutFormCallback('user_updated', array(
//            'success_message' => lang('Settings have been updated'), 
//          )),
//        ), true);
        
        if($this->homescreen()->canHaveOwn() && $this->homescreen()->canManageSet($user)) {
          $options->add('homescreen', array(
            'text' => lang('Home Screen'),
            'url' => $this->homescreen()->getManageUrl(),
          ));
        } // if
      } // if
      
      if($this->canChangePassword($user)) {
        $options->add('edit_password', array(
          'text' => lang('Change Password'),
          'url'  => $this->getEditPasswordUrl(),
          'icon' => $interface == AngieApplication::INTERFACE_DEFAULT ? AngieApplication::getImageUrl('icons/12x12/change-password.png', ENVIRONMENT_FRAMEWORK) : AngieApplication::getImageUrl('icons/navbar/change_password.png', SYSTEM_MODULE, AngieApplication::INTERFACE_PHONE),
					'important' => true,
          'onclick' => new FlyoutFormCallback(array(
          	'width' => 'narrow', 
            'success_message' => lang('Password has been changed'), 
          )),
        ), true);
      } // if
      
      if($this->canEdit($user) && $this->isApiUser()) {
        $options->add('api_subscriptions', array(
          'text' => lang('API Subscriptions'),
          'url' => $this->getApiSubscriptionsUrl(),
        ));
      } // if
      
      parent::prepareOptionsFor($user, $options, $interface);
    } // prepareOptionsFor
    
    /**
     * Return vCard instance that represents this user
     * 
     * @return File_IMC_Build_Vcard
     */
    function toVCard() {
			$vcard = File_IMC::build('vCard');
			
			$vcard->setName($this->getLastName(), $this->getFirstName());
			$vcard->setFormattedName($this->getName());
			$vcard->addEmail($this->getEmail());
			$vcard->addParam('TYPE', 'INTERNET');
			
			return $vcard;
    } // toVCard

    // ---------------------------------------------------
    //  Password and password policy
    // ---------------------------------------------------

    /**
     * Returns true if we have a valid password
     *
     * @param string $password
     * @return boolean
     */
    function isCurrentPassword($password) {
      if(Authentication::getPasswordPolicy()->isCurrentPassword($password, $this->getPassword(), $this->getPasswordHashedWith())) {

        // Hash using PBKDF2 if password is hashed with SHA1
        if($this->getPasswordHashedWith() == PasswordPolicy::HASHED_WITH_SHA1) {
          DB::execute('UPDATE ' . TABLE_PREFIX . 'users SET password = ?, password_hashed_with = ? WHERE id = ?', Authentication::getPasswordPolicy()->hashPassword($password, PasswordPolicy::HASHED_WITH_PBKDF2), PasswordPolicy::HASHED_WITH_PBKDF2, $this->getId());

          cache_remove($this->getCacheId());
        } // if

        return true;
      } else {
        return false;
      } // if
    } // isCurrentPassword

    /**
     * Returns true if password for this account is expired
     *
     * @return boolean
     */
    function isPasswordExpired() {
      return $this->getPasswordExpiresOn() instanceof DateValue && $this->getPasswordExpiresOn()->getTimestamp() <= DateValue::now()->getTimestamp();
    } // isPasswordExpired

    /**
     * Set password for expiry in N months
     *
     * @param integer $months
     * @param boolean $save
     * @return DateValue
     */
    function expirePasswordIn($months, $save = false) {
      if($months > 0) {
        $expire_on = DateValue::makeFromString("+$months months");
      } else {
        $expire_on = null;
      } // if

      $this->setPasswordExpiresOn($expire_on);

      if($save) {
        $this->save();
      } // if

      return $expire_on;
    } // expirePasswordIn
    
    // ---------------------------------------------------
    //  Date and Time Formats
    // ---------------------------------------------------
    
    /**
     * Cached date format value
     *
     * @var string
     */
    private $date_format = false;
    
    /**
     * Return date format
     *
     * @return string
     */
    function getDateFormat() {
      if($this->date_format === false) {
        $this->date_format = ConfigOptions::getValueFor('format_date', $this, FORMAT_DATE);
      } // if
      
      return $this->date_format;
    } // getDateFormat
    
    /**
     * Cached time format value
     *
     * @var string
     */
    private $time_format = false;
    
    /**
     * Return time format
     *
     * @return string
     */
    function getTimeFormat() {
      if($this->time_format === false) {
        $this->time_format = ConfigOptions::getValueFor('format_time', $this, FORMAT_TIME);
      } // if
      
      return $this->time_format;
    } // getTimeFormat
    
    /**
     * Cached date time format value
     *
     * @var string
     */
    private $date_time_format = false;
    
    /**
     * Return date time format
     *
     * @return string
     */
    function getDateTimeFormat() {
      if($this->date_time_format === false) {
        $this->date_time_format = $this->getDateFormat() . ' ' . $this->getTimeFormat();
      } // if
      
      return $this->date_time_format;
    } // getDateTimeFormat
    
    // ---------------------------------------------------
    //  Mailing
    // ---------------------------------------------------
    
    /**
     * Cached mailing method value
     * 
     * @var string
     */
    private $mailing_method = false;
    
    /**
     * Return prefered mailing method for this user
     * 
     * @return string
     */
    function getMailingMethod() {
    	if($this->mailing_method === false) {
    		$this->mailing_method = $this->isLoaded() ? ConfigOptions::getValueFor('mailing_method', $this) : ApplicationMailer::getDefaultMailingMethod();
    	} // if
    	
    	return $this->mailing_method;
    } // getMailingMethod
    
    /**
     * Set mailing method for this user
     * 
     * @param string $value
     */
    function setMailingMethod($value) {
    	ConfigOptions::setValueFor('mailing_method', $this, $value);
    	$this->mailing_method = $value;
    } // setMailingMethod
    
    // ---------------------------------------------------
    //  Role
    // ---------------------------------------------------
    
    /**
     * Return user role
     *
     * @return Role
     */
    function getRole() {
      return DataObjectPool::get('Role', $this->getRoleId());
    } // getRole
    
    /**
     * Set user role
     *
     * @param Role $role
     * @return Role
     */
    function setRole(Role $role) {
      $this->setRoleId($role->getId());
      
      // Reset flags
      $this->is_active = null;
      $this->is_api_user = null;
      $this->is_feed_user = null;
      $this->is_administrator = null;
      
      return $role;
    } // setRole
    
    // ---------------------------------------------------
    //  Access level indicators
    // ---------------------------------------------------
    
    /**
     * Cached is active flag value
     *
     * @var boolean
     */
    private $is_active = null;
    
    /**
     * Returns true if this particular account is active
     *
     * @return boolean
     */
    function isActive() {
      if($this->is_active === null) {
        $this->is_active = $this->getState() >= STATE_VISIBLE && $this->getSystemPermission('has_system_access');
      } // if

      return $this->is_active;
    } // isActive
    
    /**
     * Cached is api user flag
     *
     * @var boolean
     */
    private $is_api_user = null;
    
    /**
     * Returns true if this user is API user
     * 
     * @return boolean
     */
    function isApiUser() {
      if($this->is_api_user === null) {
        $this->is_api_user = $this->isAdministrator() || (boolean) $this->getSystemPermission('can_use_api');
      } // if
      
      return $this->is_api_user;
    } // isApiUser

    /**
     * Cached is feed user flag
     *
     * @var boolean
     */
    private $is_feed_user = null;
    
    /**
     * Returns true if this user is feed user
     * 
     * @return boolean
     */
    function isFeedUser() {
      if($this->is_feed_user === null) {
        $this->is_feed_user = $this->isAdministrator() || (boolean) $this->getSystemPermission('can_use_feeds');
      } // if
      
      return $this->is_feed_user;
    } // isFeedUser
    
    /**
     * Does this user have administration permissions
     *
     * @var boolean
     */
    private $is_administrator = null;
    
    /**
     * Returns true only if this person has administration permissions
     *
     * @return boolean
     */
    function isAdministrator() {
      if($this->is_administrator === null) {
        $this->is_administrator = (boolean) $this->getSystemPermission('has_admin_access');
      } // if
      return $this->is_administrator;
    } // isAdministrator
    
    /**
     * Check if this user is the last administrator
     *
     * @return boolean
     */
    function isLastAdministrator() {
      return $this->isAdministrator() && (Users::countAdministrators() == 1);
    } // isLastAdministrator
    
    /**
     * By default, no user is financial manager
     * 
     * @return boolean
     */
    function isFinancialManager() {
      return false;
    } // isFinancialManager
    
    // ---------------------------------------------------
    //  Visibility
    // ---------------------------------------------------
    
    /**
     * Returns true if this user can see $object
     *
     * @param IVisibility $object
     * @return boolean
     */
    function canSee(IVisibility $object) {
      return $object->getVisibility() >= $this->getMinVisibility();
    } // canSee
    
    /**
     * Returns true if this user have permissions to see private objects
     *
     * @return boolean
     */
    function canSeePrivate() {
      return $this->getRole() instanceof Role ? $this->getRole()->canSeePrivate() : false;
    } // canSeePrivate
    
    /**
     * Returns min visibility that this particular user can see
     * 
     * Default implementation lets user see all objects. Override in User class 
     * to implement visibility filtering that you want
     *
     * @return integer
     */
    function getMinVisibility() {
      return VISIBILITY_PRIVATE;
    } // getMinVisibility
    
    /**
     * Returns true if this user has access to reports section
     * 
     * @return boolean
     */
    function canUseReports() {
      return $this->isAdministrator();
    } // canUseReports
    
    // ---------------------------------------------------
    //  Interface implementation
    // ---------------------------------------------------
    
    /**
     * Return object context domain
     * 
     * @return string
     */
    function getObjectContextDomain() {
      return 'users';
    } // getObjectContextDomain
    
    /**
     * Return object context path
     * 
     * @return string
     */
    function getObjectContextPath() {
      return 'users/' . $this->getId();
    } // getObjectContextPath

    /**
     * Return context ID for object that implements this interface
     *
     * @return string
     */
    function getNotifierContextId() {
      return 'USER/' . $this->getId();
    } // getNotifierContextId
    
    /**
     * Return routing context name
     *
     * @return string
     */
    function getRoutingContext() {
      return 'user';
    } // getRoutingContext
    
    /**
     * Return routing context parameters
     *
     * @return mixed
     */
    function getRoutingContextParams() {
      return array('user_id' => $this->getId());
    } // getRoutingContextParams

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

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

      return $this->inspector;
    } // inspector

    /**
     * UserAvatar implementation instance for this object
     *
     * @var IUserAvatarImplementation
     */
    private $avatar;

    /**
     * Return subtasks implementation for this object
     *
     * @return IUserAvatarImplementation
     */
    function avatar() {
      if(empty($this->avatar)) {
        $this->avatar = new IUserAvatarImplementation($this);
      } // if

      return $this->avatar;
    } // avatar
    
    /**
     * Notifier helper
     *
     * @var INotifierImplementation
     */
    private $notifier = false;
    
    /**
     * Return notifier helper
     * 
     * @return INotifierImplementation
     */
    function notifier() {
    	if($this->notifier === false) {
    		$this->notifier = new INotifierImplementation($this);
    	} // if
    	
    	return $this->notifier;
    } // notifier
    
    /**
     * Cached history helper
     *
     * @var IHistoryImplementation
     */
    private $history = false;
    
    /**
     * Return history helper instance
     * 
     * @return IHistoryImplementation
     */
    function history() {
      if($this->history === false) {
        $this->history = new IHistoryImplementation($this, array('role_id', 'first_name', 'last_name', 'email', 'password'));
      } // if
      
      return $this->history;
    } // history
    
    /**
     * Cached search helper instance
     *
     * @var IUserSearchItemImplementation
     */
    private $search = false;
    
    /**
     * Return search helper instance
     * 
     * @return IUserSearchItemImplementation
     */
    function search() {
      if($this->search === false) {
        $this->search = new IUserSearchItemImplementation($this);
      } // if
      
      return $this->search;
    } // search
    
    /**
     * Cached user reminders helper instance
     *
     * @var IUserRemindersImplementation
     */
    private $reminders = false;
    
    /**
     * Return user reminders helper instance
     * 
     * @return IUserRemindersImplementation
     */
    function reminders() {
      if(AngieApplication::isFrameworkLoaded('reminders')) {
        if($this->reminders === false) {
          $this->reminders = new IUserRemindersImplementation($this);
        } // if
        
        return $this->reminders;
      } else {
        throw new NotImplementedError(__METHOD__);
      } // if
    } // reminders

    /**
     * State helper instance
     *
     * @var IUserStateImplementation
     */
    private $state = false;

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

      return $this->state;
    } // state

    /**
     * Homescreen helper instance
     *
     * @var IUserHomescreenImplementation
     */
    private $homescreen = false;

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

      return $this->homescreen;
    } // homescreen

    /**
     * Cached favorites instance
     *
     * @var IUserFavoritesImplementation
     */
    private $favorites = false;

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

      return $this->favorites;
    } // favorites
    
    // ---------------------------------------------------
    //  URLs
    // ---------------------------------------------------
    
    /**
     * Get edit password URL
     *
     * @return string
     */
    function getEditPasswordUrl() {
      return Router::assemble($this->getRoutingContext() . '_edit_password', $this->getRoutingContextParams());
    } // getEditPasswordUrl
    
    /**
     * Return API subscriptions URL
     * 
     * @return string
     */
    function getApiSubscriptionsUrl() {
      return Router::assemble($this->getRoutingContext() . '_api_client_subscriptions', $this->getRoutingContextParams());
    } // getApiSubscriptionsUrl
    
    /**
     * Return add API subscription URL
     * 
     * @return string
     */
    function getAddApiSubscriptionUrl() {
      return Router::assemble($this->getRoutingContext() . '_api_client_subscriptions_add', $this->getRoutingContextParams());
    } // getAddApiSubscriptionUrl
    
    /**
     * Return export vCard URL
     *
     * @return string
     */
    function getExportVcardUrl() {
      return Router::assemble($this->getRoutingContext() . '_export_vcard', $this->getRoutingContextParams());
    } // getExportVcardUrl

    /**
     * Return user activities RSS feed
     *
     * @param User $user
     * @return string
     */
    function getRssUrl(User $user) {
      return Router::assemble($this->getRoutingContext() . '_activity_log_rss', $this->getRoutingContextParams());
    } // getRssUrl
    
    // ---------------------------------------------------
    //  Permissions
    // ---------------------------------------------------
    
    /**
     * 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->isAdministrator();
      } 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) {
        return $user->is($this) || $user->isAdministrator();
      } else {
        return false;
      } // if
    } // canChangePassword
    
    /**
     * Returns true if $user can change this users role
     *
     * @param User $user
     * @return boolean
     */
    function canChangeRole(User $user) {
    	return $user->isAdministrator() || $user->isPeopleManager();
    } // canChangeRole
    
    /**
     * Returns true if $user can add API subscription for this user
     * 
     * @param IUser $user
     * @return boolean
     */
    function canAddApiSubscription(IUser $user) {
      return $this->canEdit($user);
    } // canAddApiSubscription
    
    // ---------------------------------------------------
    //  Authentication
    // ---------------------------------------------------
    
    /**
     * Raw password value before it is encoded
     *
     * @var string
     */
    protected $raw_password = false;
    
    /**
     * Set field value
     *
     * @param string $field
     * @param mixed $value
     * @return mixed
     */
    function setFieldValue($field, $value) {
      if($field == 'password' && !$this->isLoading()) {
        $this->raw_password = (string) $value; // Remember raw password

        $value = Authentication::getPasswordPolicy()->hashPassword($value, PasswordPolicy::HASHED_WITH_PBKDF2); // Hash password with PBKDF2

        $this->setPasswordHashedWith(PasswordPolicy::HASHED_WITH_PBKDF2);
        $this->expirePasswordIn(Authentication::getPasswordPolicy()->getAutoExpire());
      } // if
      
      // Role ID - make sure that we don't change role of the last administrator
      if($field == 'role_id' && $this->isLoaded() && $value != $this->getFieldValue('role_id')) {
        $role = Roles::findById($value);
        
        if($role instanceof Role) {
          if(Roles::isLastAdministrator($this) && !$role->isAdministrator()) {
            throw new InvalidParamError('role_id', $value, "Can't change role of last administrator to non-administrator role");
          } // if
        } else {
          throw new InvalidParamError('role_id', $value, "'$value' is not a valid system role ID");
        } // if
      } // if
      
      return parent::setFieldValue($field, $value);
    } // setFieldValue
    
    // ---------------------------------------------------
    //  System
    // ---------------------------------------------------

    /**
     * Validate before save
     *
     * @param ValidationErrors $errors
     */
    function validate(ValidationErrors &$errors) {
      if($this->validatePresenceOf('email', 5)) {
        if(is_valid_email($this->getEmail())) {
          if($this->isNew()) {
            $in_use = Users::isEmailAddressInUse($this->getEmail());
          } else {
            $in_use = Users::isEmailAddressInUse($this->getEmail(), $this->getId());
          } // if

          if($in_use) {
            $errors->addError(lang('Email address you provided is already in use'), 'email');
          } // if
        } else {
          $errors->addError(lang('Email value is not valid'), 'email');
        } // if
      } else {
        $errors->addError(lang('Email value is required'), 'email');
      } // if

      if($this->isNew() || $this->raw_password !== false) {
        Authentication::getPasswordPolicy()->validateUserPassword($this->raw_password, $errors);
      } // if

      if(!$this->validatePresenceOf('role_id')) {
        $errors->addError(lang('Role is required'), 'role_id');
      } // if
    } // validate
    
    /**
     * Save user into the database
     *
     * @return boolean
     */
    function save() {
      $modified_fields = $this->getModifiedFields();

      if($this->isNew()) {
        $this->expirePasswordIn(Authentication::getPasswordPolicy()->getAutoExpire());
      } // if
      
      parent::save();
      
      if(in_array('role_id', $modified_fields)) {
        clean_user_permissions_cache($this); // Role changed? Clear cache!
      } // if
    } // save
    
    /**
     * Delete from database
     *
     * @return boolean
     */
    function delete() {
      try {
        DB::beginWork('Removing user and cleaning up @ ' . __CLASS__);
        
        parent::delete();
        
        ConfigOptions::removeValuesFor($this);
        
        if(AngieApplication::isFrameworkLoaded('avatar')) {
          $this->avatar()->remove();
        } // if
        
        if(AngieApplication::isFrameworkLoaded('assignments')) {
          Assignments::deleteByUser($this);
        } // if
        
        if(AngieApplication::isFrameworkLoaded('subscriptions')) {
          Subscriptions::deleteByUser($this);
        } // if
        
        if(AngieApplication::isFrameworkLoaded('favorites')) {
          Favorites::deleteByUser($this);
        } // if
        
        if(AngieApplication::isFrameworkLoaded('reminders')) {
          Reminders::deleteByUser($this);
        } // if
        
        $cleanup = array();
        EventsManager::trigger('on_user_cleanup', array(&$cleanup));
        
        if(is_foreachable($cleanup)) {
          foreach($cleanup as $table_name => $fields) {
            foreach($fields as $field) {
              $condition = '';
              if(is_array($field)) {
                $id_field = array_var($field, 'id');
                $name_field = array_var($field, 'name');
                $email_field = array_var($field, 'email');
                $condition = array_var($field, 'condition');
              } else {
                $id_field = $field . '_id';
                $name_field = $field . '_name';
                $email_field = $field . '_email';
              } // if
              
              if($condition) {
                DB::execute('UPDATE ' . TABLE_PREFIX . "$table_name SET $id_field = 0, $name_field = ?, $email_field = ? WHERE $id_field = ? AND $condition", $this->getName(), $this->getEmail(), $this->getId());
              } else {
                DB::execute('UPDATE ' . TABLE_PREFIX . "$table_name SET $id_field = 0, $name_field = ?, $email_field = ? WHERE $id_field = ?", $this->getName(), $this->getEmail(), $this->getId());
              } // if
            } // foreach
          } // foreach
        } // if
        
        DB::commit('User removed and data has been cleaned up @ ' . __CLASS__);
      } catch(Exception $e) {
        DB::rollback('Failed to remove user or clean up the data @ ' . __CLASS__);
        
        throw $e;
      } // try
    } // delete
    
    /* ---------------------------
     * Feed functions
     * ----------------------------
     */ 
    
    /**
     * Returns user feed token
     *
     * @var boolean $formatted
     * @return string
     */
    function getFeedToken($formatted = true) {
    	if ($this->isFeedUser()) {
    		$feed_client_subscription = FeedClientSubscriptions::findByUser($this);

        if (!($feed_client_subscription instanceof FeedClientSubscription)) {
          $feed_client_subscription = new FeedClientSubscription();
          $feed_client_subscription->setUser($this);
          try {
            DB::beginWork('Creating new feed client subscription @ ' . __CLASS__);

            $feed_client_subscription->setAttributes(array(
              'client_name' => $this->getName() . ' ' . lang('Feed Token'),
              'is_read_only' => 1
            ));
            $feed_client_subscription->setToken(ApiClientSubscriptions::generateToken());
            $feed_client_subscription->setIsEnabled(true);

            $feed_client_subscription->save();

            DB::commit('New feed client subscription created @ ' . __CLASS__);

          } catch(Exception $e) {
            DB::rollback('Failed to create new feed client subscription @ ' . __CLASS__);
            throw $e;
          } // try
        } // if
        return $formatted ? $feed_client_subscription->getFormattedToken() : $feed_client_subscription->getToken();
    	} else {
    		throw new Exception("User doesn't have permission to use feed");
    	} //if
    } //getFeedToken
    
  }