<?php
/*
 * This file is part of the easy-CMS project.
 * easy-CMS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * easy-CMS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with easy-CMS; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/**
 * @package    Core
 * @subpackage ACL
 */

include_once('classes/core/CoreObject.class.php');
include_once('classes/core/DBManipulator.class.php');
include_once('classes/core/ACL/ACLEntry.class.php');

/**
 * An Access Control List (ACL) holds infos about permissions over objects (content, container, containersList, pages and menus).
 *
 * @access     public
 * @author	   Cedric Francoys <cedric.francoys@easy-cms.org>
 * @version	   ACL.class, Sat Feb 12 15:31:45 CET 2005
 * @package	   Core
 * @subpackage ACL
 */
class ACL extends CoreObject {
	/**
	 * @var integer
	 */
	var $id;

  /**
   * @var integer
   */
	var $deleted;

	/**
   * @var array instances of ACLEntry
   */
	var $groupsAclEntryTable;

  /**
   * @var array instances of ACLEntry
   */
	var $usersAclEntryTable;

	/**
	 * Class Constructor
	 *
	 * @param integer $id
	 * @param integer $parentId
	 * @param integer $inherit
	 */
	function ACL($id = 0, $deleted = 0) {
    $this->debug('ACL::ACL, status : function invoked', 10, DBG_OPEN);
		$this->setAclId($id);
		$this->setAclDeleted($deleted);
		$this->usersAclEntryTable = array();
		$this->groupsAclEntryTable = array();
    $this->debug('ACL::ACL, status : function completed', 10, DBG_CLOSE);
	}

	/**
   * Gets ACL Id
   *
	 * @return integer
	 */
	function getAclId() {
		return $this->id;
	}

  /**
   * Sets ACL Id
   *
	 * @param integer $id
	 */
	function setAclId($id) {
		$this->id = $id;
	}

  /**
   * Sets ACL Deleted
   *
	 * @param integer $deleted
	 */
	function setAclDeleted($deleted) {
		$this->deleted = $deleted;
	}

  /**
   * Gets ACL Deleted
   *
	 * @param integer $deleted
	 */
	function getAclDeleted() {
		return $this->deleted;
	}

	/**
	 * Gets Groups ACL Entries
	 *
	 * @return array
	 */
	function &getGroupsAclEntryTable() {
		return $this->groupsAclEntryTable;
	}

	/**
	 * Sets Groups ACL Entries
	 *
	 * @param array $groupsAclEntryTable
	 */
	function setGroupsAclEntryTable(&$groupsAclEntryTable) {
		$this->groupsAclEntryTable = &$groupsAclEntryTable;
	}

	/**
	 * Gets Users ACL Entries
	 *
	 * @return array
	 */
	function &getUsersAclEntryTable() {
		return $this->usersAclEntryTable;
	}

	/**
	 * Sets Users ACL Entries
	 *
	 * @param array $usersAclEntriesTable
	 */
	function setUsersAclEntryTable(&$usersAclEntryTable) {
		$this->usersAclEntryTable = &$usersAclEntryTable;
	}

	/**
	 * Gets Number Of Groups ACL Entries
	 *
	 * @return integer
	 */
	function getNumGroupsAclEntry() {
		return count($this->groupsAclEntryTable);
	}

	/**
	 * Gets Number Of Users ACL Entries
	 *
	 * @return integer
	 */
	function getNumUsersAclEntry() {
		return count($this->usersAclEntryTable);
	}

	/**
	 * Gets Groups Id List
	 *
	 * @return array
	 */
	function getGroupsIdList() {
		return array_keys($this->groupsAclEntryTable);
	}

	/**
	 * Gets Users Id List
	 *
	 * @return array
	 */
	function getUsersIdList() {
		return array_keys($this->usersAclEntryTable);
	}

	/**
	 * Checks Group Acl Entry
	 *
	 * Checks if given Group has an entry defined into ACL
	 *
	 * @param  integer $groupId Group Id
	 * @return boolean
	 */
	function groupAclEntryExists($groupId) {
		return isset($this->groupsAclEntryTable[$groupId]);
	}

	/**
	 * Checks User Acl Entry
	 *
	 * Checks if given User has an entry defined into ACL
	 *
	 * @param  integer $userId User Id
	 * @return boolean
	 */
	function userAclEntryExists($userId) {
		return isset($this->usersAclEntryTable[$userId]);
	}

  /**
   * Adds Group Entry Into ACL
   *
   * @param array $record Record as in ACL_GROUPS_TABLE
   */
	function addGroupEntry($record) {
    $this->debug('ACL::addGroupEntry, status : function invoked', 10, DBG_OPEN);
		$aclEntry = &new ACLEntry();
		$aclEntry->setValues($record);
		$id = $aclEntry->getId();
		$this->groupsAclEntryTable[$id] = &$aclEntry;
    $this->debug('ACL::addGroupEntry, status : function completed', 10, DBG_CLOSE);
	}

  /**
   * Adds User Entry Into ACL
   *
   * @param array $record Record as in ACL_USERS_TABLE
   */
	function addUserEntry($record) {
    $this->debug('ACL::addUserEntry, status : function invoked', 10, DBG_OPEN);
		$aclEntry = &new ACLEntry();
		$aclEntry->setValues($record);
		$id = $aclEntry->getId();
		$this->usersAclEntryTable[$id] = &$aclEntry;
    $this->debug('ACL::addUserEntry, status : function completed', 10, DBG_CLOSE);
	}

  /**
	 * Gets ACL entry for given group
	 *
	 * @author  vinadelmar <vinadelmar@users.sourceforge.net>
	 * @since   2006-02-23
	 * @version 0.1.0
	 * @param   integer $gid group id
	 * @return  mixed `ACLEntry` object or false
	 */
	function &getGroupAclEntry($gid) {
    $this->debug('ACL::getGroupAclEntry, status : function invoked', 10, DBG_OPEN);
    $result = null;
		if (!$this->groupAclEntryExists($gid)) {
      $this->debug('ACL::getGroupAclEntry, warning : no ACL entry for group ('.$gid.')', 4);
		}
		else {
		  $result = &$this->groupsAclEntryTable[$gid];
		}
    $this->debug('ACL::getGroupAclEntry, status : function completed', 10, DBG_CLOSE);
		return $result;
	}

	/**
	 * Gets User's ACL Entry
	 *
	 * @author  vinadelmar <vinadelmar@users.sourceforge.net>
	 * @since   2006-02-23
	 * @version 0.1.0
	 * @param   integer $userId
	 * @return  mixed `ACLEntry` object or false
	 */
	function &getUserAclEntry($uid) {
    $this->debug('ACL::getUserAclEntry, status : function invoked', 10, DBG_OPEN);
    $result = null;
		if (!$this->userAclEntryExists($uid)) {
      $this->debug('ACL::getUserAclEntry, warning : no ACL entry for user ('.$uid.')', 4);
		}
		else {
		  $result = &$this->usersAclEntryTable[$uid];
		}
    $this->debug('ACL::getUserAclEntry, status : function completed', 10, DBG_CLOSE);
		return $result;
	}

	/**
	 * Sets Group's ACL Entry
	 *
	 * @author  vinadelmar <vinadelmar@users.sourceforge.net>
	 * @since   2006-02-23
	 * @version 0.1.0
	 * @param   integer $gid group id
	 * @param   array arrayof rights
	 * @return  boolean
	 */
	function setGroupAclEntry($gid, $fieldsArray) {
		if (!$this->groupAclEntryExists($gid)) {
			$aclEntry = &new ACLEntry($gid);
			$this->groupsAclEntryTable[$gid] = &$aclEntry;
		}
		$this->groupsAclEntryTable[$gid]->setValues($fieldsArray);
		return true;
	}

	/**
	 * Sets User's ACL Entry
	 *
	 * @author  vinadelmar <vinadelmar@users.sourceforge.net>
	 * @since   2006-02-23
	 * @version 0.1.0
	 * @return  boolean
	 */
	function setUserAclEntry($uid, $fieldsArray) {
		if (!$this->userAclEntryExists($uid)) {
			$aclEntry = &new ACLEntry($uid);
			$this->usersAclEntryTable[$uid] = &$aclEntry;
		}
		$this->usersAclEntryTable[$uid]->setValues($fieldsArray);
		return true;
	}

	function removeGroupAclEntry($gid) {
		if (!$this->groupAclEntryExists($gid)) {
			$this->debug('ACL::groupAclEntryExists(), warning : group #'.$gid.' not defined in ACL #'.$this->getAclId().' (no action taken).');
			return false;
		}

		unset($this->groupsAclEntryTable[$gid]);
		$this->debug('ACL::groupAclEntryTable(), success : group #'.$gid.' removed from ACL #'.$this->getAclId().'.', 9);
		return true;
	}

	function removeUserAclEntry($uid) {
		if (!$this->userAclEntryExists($uid)) {
			$this->debug('ACL::removeUserAclEntry(), warning : user #'.$uid.' not defined in ACL (no action taken).');
			return false;
		}
		unset($this->usersAclEntryTable[$uid]);
		$this->debug('ACL::removeUserAclEntry(), success : user #'.$uid.' removed from ACL.', 9);
		return true;
	}

  /**
	 * Group's View Right
 	 *
 	 * Checks if group has the view right
 	 *
 	 * @param  integer $gid group id in groups list
	 * @return boolean
	 */
	function groupIdHasViewRight($gid) {
		if (!$this->groupAclEntryExists($gid)) {
			return false;
		}
		return (bool) (($this->groupsAclEntryTable[$gid]->getViewRight()>0)?true:false);
	}

	/**
	 * User's View Right
	 *
	 * Checks if user or one its groups has view right
	 *
 	 * @param  object $user instanceof `User`
	 * @return boolean
	 */
	function userHasViewRight(&$user) {
		$right = 0;

		if(!is_object($user)) {
			$this->debug('ACL::userHasViewRight(), error : user not defined.', 2);
			return false;
		}

    $userId = $user->getUserId();

    if ($this->userAclEntryExists($userId)) {
      $right = $this->usersAclEntryTable[$userId]->getViewRight();
    }

		if ($right == 0) {
	    foreach ($user->getGroupsMembershipList() as $groupId) {
	      if($right = $this->groupIdHasViewRight($groupId)) {
	      	break;
				}
			}
		}
		return (bool) (($right>0)?true:false);
	}

	/**
	 * Group's Edit Right
 	 *
 	 * Checks if group has the edit right
 	 *
 	 * @param  integer $gid group id in groups list
	 * @return boolean
	 */
	function groupIdHasEditRight($gid) {
    	if (!$this->groupAclEntryExists($gid)) {
    		return false;
    	}
    	return (bool) (($this->groupsAclEntryTable[$gid]->getEditRight()>0)?true:false);
	}

	/**
	 * User's Edit Right
	 *
	 * Checks if user or one of its groups has edit right
	 *
 	 * @param  object $user instance of User
	 * @return boolean
	 */
	function userHasEditRight(&$user) {
    	$right = 0;

    	if (!is_object($user)) {
    		$this->debug('ACL::userHasEditRight(), error : user not defined (right 0 returned).');
    		return false;
    	}

    	$userId = $user->getUserId();

    	if ($this->userAclEntryExists($userId)) {
    		$right = $this->usersAclEntryTable[$userId]->getEditRight();
    	}

    	if ($right == 0) {
    		foreach ($user->getGroupsMembershipList() as $groupId) {
    			if ($right = $this->groupIdHasEditRight($groupId)) {
    				break;
					}
				}
    	}

		return (bool) (($right>0)?true:false);
	}

	/**
	 * Group's Publish Right
 	 *
 	 * Checks if group has the publish right
 	 *
 	 * @param  integer $gid group id in groups list
	 * @return boolean
	 */
	function groupHasPublishRight($gid) {
    	if (!$this->groupAclEntryExists($gid)) {
    		return false;
    	}
    	return (bool)(($this->groupsAclEntryTable[$gid]->getPublishRight()>0)?true:false);
	}

  /**
	 * User's Publish Right
 	 *
 	 * Checks if user or one of its groups has publish right
 	 *
 	 * @param  object $user instance of User
	 * @return boolean
	 */
	function userHasPublishRight(&$user) {
		$right = 0;

		if (!is_object($user)) {
			$this->debug('ACL::userHasPublishRight(), error : user not defined (right 0 returned).');
			return false;
		}

		$userId = $user->getUserId();

		if($this->userAclEntryExists($userId)){
			$right = $this->usersAclEntryTable[$userId]->getPublishRight();
		}

		if ($right == 0) {
			foreach ($user->getGroupsMembershipList() as $groupId) {
				if ($right = $this->groupIdHasPublishRight($groupId)) {
					break;
				}
			}
		}

		return (bool) (($right>0)?true:false);
	}

	/**
	 * Group's Remove Right
 	 *
 	 * Checks if group has the remove right
 	 *
 	 * @param  integer $gid group id in groups list
	 * @return boolean
	 */
	function groupIdHasRemoveRight($gid) {
    $result = false;
		if (!$this->groupAclEntryExists($gid)) {
			$this->debug('ACL::groupIdHasRemoveRight, warning : no ACL entry for group ('.$gid.')', 4);
		}
		else {
      $result = (bool) (($this->groupsAclEntryTable[$gid]->getRemoveRight()>0)?true:false);
		}
		return $result;
	}

  /**
	 * User's Remove Right
 	 *
 	 * Checks if user or one of its groups has remove right
 	 *
 	 * @param  object $user instance of User
	 * @return boolean
	 */
	function userHasRemoveRight(&$user) {
		$right = 0;

		if (!is_object($user)) {
			$this->debug('ACL::userHasRemoveRight(), error : user not defined (right 0 returned).');
			return false;
		}

		$userId = $user->getUserId();

		if ($this->userAclEntryExists($userId)) {
			$right = $this->usersAclEntryTable[$userId]->getRemoveRight();
		}

		if ($right == 0) {
			foreach ($user->getGroupsMembershipList() as $groupId) {
				if ($right = $this->groupIdHasRemoveRight($groupId)) {
					break;
				}
			}
		}

		return (bool) (($right>0)?true:false);
	}

	/**
	 * Group's Admin Right
 	 *
 	 * Checks if group has the admin right
 	 *
 	 * @param  integer $gid group id in groups list
	 * @return boolean
	 */
	function groupIdHasAdminRight($gid) {
    	if (!$this->groupAclEntryExists($gid)) {
    		return false;
    	}
    	return (bool)(($this->groupsAclEntryTable[$gid]->getAdminRight()>0)?true:false);
	}

	/**
	 * User's Admin Right
 	 *
 	 * Checks if user or one of its groups has admin right
 	 *
 	 * @param  object $user instance of User
	 * @return boolean
	 */
	function userHasAdminRight(&$user) {
		$right = 0;

		if (!is_object($user)) {
			$this->debug('ACL::userHasAdminRight(), error : user not defined (right 0 returned).');
			return false;
		}

		$userId = $user->getUserId();

		if ($this->userAclEntryExists($userId)) {
			$right = $this->usersAclEntryTable[$userId]->getAdminRight();
		}

		if ($right == 0) {
			foreach ($user->getGroupsMembershipList() as $groupId)
				if ($right = $this->groupIdHasAdminRight($groupId))
					break;
		}

		return (bool) (($right>0)?true:false);
	}

	/**
	 * Returns ACL comparison result
	 * 
	 * @param   object $acl instance of ACL, another ACL to be compared with
	 * @return  integer < 0 if the current acl has less rights defined than acl
	 *                  > 0 if current acl has more rights defined than acl
	 *                    0 if both acls' have equals rights
	 */
	function compareTo(&$acl) {
		foreach ($acl->getGroupsAclEntryTable() as $gid => $aclEntry) {
			if(!isset($this->groupsAclEntryTable[$gid])) {
				return -1;
			}
			if(($comp = $aclEntry->compareTo($this->groupsAclEntryTable[$gid])) != 0) {
				return (-$comp);
			}
		}

		foreach ($acl->getUsersAclEntryTable() as $uid => $aclEntry) {
			if(!isset($this->usersAclEntryTable[$uid])) {
				return -1;
			}
			if(($comp = $aclEntry->compareTo($this->usersAclEntryTable[$uid])) != 0) {
				return (-$comp);
			}
		}
		foreach ($this->getGroupsAclEntryTable() as $gid => $aclEntry) {
			if(!isset($acl->groupsAclEntryTable[$gid])) {
				return 1;
			}
			if(($comp = $aclEntry->compareTo($acl->groupsAclEntryTable[$gid])) != 0) {
				return $comp;
			}
		}
		foreach ($this->getUsersAclEntryTable() as $uid => $aclEntry) {
			if(!isset($acl->usersAclEntryTable[$uid])) {
				return 1;
			}
			if(($comp = $aclEntry->compareTo($acl->usersAclEntryTable[$uid])) != 0) {
				return $comp;
			}
		}
		return 0;
	}


  /**
   * Export ACL Into XML String
   *
   * @return string XML
   * @todo   finish xml string
   */
	function toXML() {
		$xml  = '';
		$xml .= '<acl id="" parent="" inherit="">';
		$xml .= '  <users>';
		$xml .= '  </users>';
		$xml .= '  <groups>';
		$xml .= '  </groups>';
		$xml .= '</acl>';
		return $xml;
	}

	/**
	 * Set ACL Properties From Array
	 *
	 * @param array $array associative array property=>value
	 * @todo check for relevance
	 */
    function setValues(&$array) {
		if(isset($array['id']))
			$this->id = &$array['id'];
		if(isset($array['parent_id']))
			$this->parentId = &$array['parent_id'];
		if(isset($array['inherit']))
			$this->inherit = &$array['inherit'];
		return 1;
  }

	/**
	 * @todo check for relevance
	 */
	function &getValue($fieldName) {
		if($fieldName == 'id')
			return $this->id;
		return -1;
	}



	/**
	 * Set User Rights
	 *
	 * @param  integer $userId
	 * @param  integer $viewRight
	 * @param  integer $editRight
	 * @param  integer $publishRight
	 * @param  integer $removeRight
	 * @param  integer $adminRight
	 * @return integer <0 if error
	 * @todo check for relevance
	 */
	function setUserRights(&$userId, &$viewRight, &$editRight, &$publishRight, &$removeRight, &$adminRight) {
    	if (!$this->checkUser($userId)) {
			$this->debug("ACL.setUserRights, error : user ".$userId." not defined in ACL ".$this->getId()." (no action taken).", 2);
			return -1;
    	}
    	$this->usersList[$userId]->setViewRight($viewRight);
    	$this->usersList[$userId]->setEditRight($editRight);
    	$this->usersList[$userId]->setPublishRight($publishRight);
    	$this->usersList[$userId]->setRemoveRight($removeRight);
    	$this->usersList[$userId]->setAdminRight($adminRight);
    	return 1;
	}

	/**
	 * Set Group Rights
	 *
	 * @param  integer $groupId
	 * @param  integer $viewRight
	 * @param  integer $editRight
	 * @param  integer $publishRight
	 * @param  integer $removeRight
	 * @param  integer $adminRight
	 * @return integer <0 if error
	 * @todo check for relevance
	 */
	function setGroupRights(&$groupId, &$viewRight, &$editRight, &$publishRight, &$removeRight, &$adminRight) {
    	if (!$this->checkUser($groupId)) {
			$this->debug("ACL.setGroupRights, error : group ".$groupId." not defined in ACL ".$this->getId()." (no action taken).", 2);
			return -1;
    	}
    	$this->groupsList[$groupId]->setViewRight($viewRight);
    	$this->groupsList[$groupId]->setEditRight($editRight);
    	$this->groupsList[$groupId]->setPublishRight($publishRight);
    	$this->groupsList[$groupId]->setRemoveRight($removeRight);
    	$this->groupsList[$groupId]->setAdminRight($adminRight);
    	return 1;
	}


	/**
	 *
   * @todo finish or remove method
   */
	function setGroupsEntry($groupsEntry) {
	}

  /**
   * @todo finish or remove method
   */
	function setUsersEntry($usersEntry) {
	}

	/**
	 * @todo finish or remove
	 */
	function getHTMLContentList($mode) {
	}
}

?>