<?php

  /**
   * Framework level roles manager implementation
   *
   * @package angie.frameworks.authentication
   * @subpackage models
   */
  class FwRoles extends BaseRoles {
    
    /**
     * Return roles by ID-s
     *
     * @param array $ids
     * @return DBResult
     */
    static function findByIds($ids) {
      if($ids) {
        return Roles::find(array(
          'conditions' => array('id IN (?)', $ids), 
          'order' => 'name', 
        ));
      } else {
        return null;
      } // if
    } // findByIds
    
    /**
     * Return default project role
     * 
     * @return Role
     */
    static function getDefault() {
      return Roles::find(array(
        'order' => 'is_default DESC', 
        'one' => true, 
      ));
    } // getDefault
    
    /**
     * Set default role
     *
     * @param Role $role
     * @return Role
     */
    static function setDefault(Role $role) {
      try {
        $roles_table = TABLE_PREFIX . 'roles';
        
        DB::beginWork("Setting up default role @ " . __CLASS__);
        
        DB::execute("UPDATE $roles_table SET is_default = ?", false);
        DB::execute("UPDATE $roles_table SET is_default = ? WHERE id = ?", true, $role->getId());
        
        DB::commit("Default role has been set @ " . __CLASS__);
      } catch(Exception $e) {
        DB::rollback("Failed to set default role @ " . __CLASS__);
        throw $e;
      } // try
    } // setDefault
    
    /**
     * Cached array of roles that have administration permissions
     *
     * @var array
     */
    static private $admin_role_ids = false;
    
    /**
     * Return all administrators
     *
     * @return DBResult
     */
    static function findAdministrators() {
      $admin_role_ids = self::getAdminRoleIds();
      
      if($admin_role_ids) {
        return Users::find(array(
          'conditions' => array('role_id IN (?) AND state >= ?', $admin_role_ids, STATE_VISIBLE), 
          'order' => 'CONCAT(first_name, last_name, email)',
        ));
      } else {
        return null;
      } // if
    } // findAdministrators
    
    /**
     * Return number of administrators in the system
     *
     * @return integer
     */
    static function countAdministrators() {
      $admin_role_ids = self::getAdminRoleIds();
      
      return $admin_role_ids ? (integer) DB::executeFirstCell('SELECT COUNT(id) FROM ' . TABLE_PREFIX . 'users WHERE role_id IN (?) AND state >= ?', $admin_role_ids, STATE_VISIBLE) : 0;
    } // countAdministrators
    
    /**
     * Returns true if $role is last role with administration permissions
     *
     * @param Role $role
     * @return boolean
     */
    static function isLastAdministratorRole(Role $role) {
      if($role->isLoaded()) {
        $admin_role_ids = self::getAdminRoleIds(true);
        return is_array($admin_role_ids) && count($admin_role_ids) == 1 && $admin_role_ids['0'] == $role->getId();
      } else {
        return false;
      } // if
    } // isLastAdministratorRole
    
    /**
     * Returns true if $user is the last administrator in the system
     *
     * @param User $user
     * @return boolean
     */
    static function isLastAdministrator(User $user) {
      return $user->isLoaded() && $user->isAdministrator() && Roles::countAdministrators() == 1;
    } // isLastAdministrator
    
    /**
     * Return ID-s of roles with administration permissions
     * Optionally returns only ID-s of admin roles that have administrators
     *
     * @param boolean $only_with_users
     * @return array
     */
    static private function getAdminRoleIds($only_with_users = false) {
      if(self::$admin_role_ids === false) {
        self::$admin_role_ids = array();

        foreach(Roles::find() as $role) {
          if($role->isAdministrator()) {
            if ($only_with_users) {
              if ($role->countUsers() > 0) {
                self::$admin_role_ids[] = $role->getId();
              } // if
            } else {
              self::$admin_role_ids[] = $role->getId();
            } // if
          } // if
        } // foreach
      } // if
      
      return self::$admin_role_ids;
    } // getAdminRoleIds
    
    /**
     * Cached array of roles that can see private objects
     *
     * @var array
     */
    private static $who_can_see_private = false;
    
    /**
     * Return array of roles that can see private objects
     * 
     * @return array
     */
    static function whoCanSeePrivate() {
      if(self::$who_can_see_private === false) {
        $roles = Roles::find();
        
        if($roles) {
          self::$who_can_see_private = array();
          
          foreach($roles as $role) {
            if($role->canSeePrivate()) {
              self::$who_can_see_private[] = $role;
            } // if
          } // foreach
        } else {
          self::$who_can_see_private = null;
        } // if
      } // if
      
      return self::$who_can_see_private;
    } // whoCanSeePrivate
    
    /**
     * Cached names of roles that can see private objects
     *
     * @var string
     */
    static private $who_can_see_private_names = false;
    
    /**
     * Return array of role names that can see private objects
     * 
     * @return array
     */
    static function whoCanSeePrivateNames() {
      if(self::$who_can_see_private_names === false) {
        $roles = Roles::whoCanSeePrivate();
        
        if($roles) {
          foreach($roles as $role) {
            self::$who_can_see_private_names[] = $role->getName();
          } // foreach
        } else {
          self::$who_can_see_private_names = null;
        } // if
      } // if
      
      return self::$who_can_see_private_names;
    } // whoCanSeePrivateNames
    
    /**
     * Cached ids of roles that can see private objects
     * 
     * @var array
     */
    static private $who_can_see_private_ids = false;
    
    /**
     * Return array of role ids who can see private objects
     *  
     * @return array
     */
    static function whoCanSeePrivateIds() {
    	if (self::$who_can_see_private_ids === false) {
    		$roles = Roles::whoCanSeePrivate();
    		
    		if ($roles) {
    			foreach ($roles as $role) {
    				self::$who_can_see_private_ids[] = $role->getId();
    			} // foreach
    		} else {
    			self::$who_can_see_private_ids = null;
    		} // if
    	} // if
    	
    	return self::$who_can_see_private_ids;
    } // whoCanSeePrivateIds
    
    /**
     * Cached permissions
     *
     * @var NamedList
     */
    static private $permissions = false;
    
    /**
     * Return list of system permission with their details
     *
     * @return NamedList
     */
    static function getPermissions() {
      if(self::$permissions === false) {
        self::$permissions = new NamedList(array(
          'has_system_access' => array(
            'name' => lang('System Access'), 
            'description' => lang("This permission defines whether user has permission to access the system. Set this to No if you don't want to delete specific user accounts but you want to restrict them access to the system."), 
          ), 
          'has_admin_access' => array(
            'name' => lang('Administration Access'), 
            'description' => lang("Set this permission to Yes if you want to give administration permissions to users with selected role. This permission overrides every other permissions and additionally gives access to administration panel to users who have it."),
            'depends_on' => 'has_system_access', 
          ), 
          'can_use_api' => array(
            'name' => lang('Use API'), 
            'description' => lang("Set this permission to Yes if you want to let users use API. API is used to let external applications work with application data (calendar applications, RSS readers, specialized tools like timer applications etc)"), 
            'depends_on' => 'has_system_access',
          ),
          'can_use_feeds' => array(
            'name' => lang('Use Feeds'), 
            'description' => lang("Set this permission to Yes if you want to let users use feeds. Feeds are used to let external applications work with application data (calendar applications, RSS readers etc)"), 
            'depends_on' => 'can_use_api',
          ),
          'can_see_private_objects' => array(
            'name' => lang('See Private Objects'), 
            'description' => lang("Set to Yes for roles that you want to be able to see objects marked as private. Usually this is set to Yes only for the members of your own company and to No for the client roles to hide sensitive, internal discussions and other confidential information."),
            'depends_on' => 'has_system_access', 
          ), 
          'can_manage_trash' => array(
            'name' => lang('Manage Trashed Data'), 
            'description' => lang('People with this permissions can list objects that are moved to trash. This permission also lets people to restore object from trash, or to permanently remove them from the system'), 
            'depends_on' => 'has_system_access', 
          ) 
        ));
        
        EventsManager::trigger('on_system_permissions', array(&self::$permissions));
      } // if
      
      return self::$permissions;
    } // getPermissions
    
    /**
     * Return defaults of a single permission
     *
     * @param string $name
     * @return array
     */
    static function getPermission($name) {
      return self::getPermissions()->get($name);
    } // getPermission
    
    /**
     * Return names of all system permissions
     *
     * @return array
     */
    static function getPermissionNames() {
      return self::getPermissions()->keys();
    } // getPermissionNames
    
    /**
     * Return roles that have all of the given permissions
     * 
     * $permissions can be a single permission name or an array of permission 
     * names
     * 
     * @param mixed $permissions
     * @return array
     */
    static function findByPermissions($permissions) {
      $roles = Roles::find();
      
      if($roles) {
        if(is_array($permissions)) {
          $check = $permissions;
        } elseif($permissions) {
          $check = array($permissions);
        } else {
          return null;
        } // if
        
        $result = array();
        
        foreach($roles as $role) {
          $has_all_permissions = true;
          
          foreach($check as $permission) {
            if(!$role->getPermissionValue($permission)) {
              $has_all_permissions = false;
              break;
            } // if
          } // foreach
          
          if($has_all_permissions) {
            $result[] = $role;
          } // if
        } // foreach
        
        return count($result) ? $result : null;
      } else {
        return null;
      } // if
    } // findByPermissions
    
  }