<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package fusionLib
 * @copyright Copyright (c) 2008 - 2014 fusionLib. All rights reserved.
 * @link http://fusionlib.com
 */

/**
 * flAcMgr Class
 *
 * Class to create and manage access control permissions.
 *
 * @package fusionLib
 */
class flAcMgr {
	/**
	 * The database object.
	 *
	 * @var flDb
	 */
	private $db;

	/**
	 * Construct the object.
	 *
	 * @param flDb $db The database object to use.
	 */
	function __construct($db) {
		$this->db = $db;
	}

	/**
	 * Create a permission group.
	 *
	 * @param string $name The name of the group to create.
	 * @param string $displayName The display name of the group.
	 * @param string $textDomain The optional text domain for translation.
	 * @return bool true if the group was created; else false.
	 */
	function createPermissionGroup($name, $displayName, $textDomain = '') {
		return $this->db->insert('fl_permgroup', array('grpname' => $name, 'displayname' => $displayName, 'textdomain' => $textDomain));
	}

	/**
	 * Delete a permission group and all the permissions in it.
	 *
	 * @param string $name The name of the group to delete.
	 */
	function deletePermissionGroup($name) {
		$this->db->delete('fl_permgroup', 'grpname=?', $name);
		$this->db->delete('fl_permissions', 'permission LIKE ?', $n = str_replace(array('%', '_'), array('\%', '\_'), $name) . '.%');
		$this->db->delete('fl_roleperms', 'permission LIKE ?', $n);
		$this->db->query('OPTIMIZE TABLE fl_permissions, fl_permgroup');
	}

	/**
	 * Update the display name of a permission group.
	 *
	 * @param string $name The name of the permission group to update.
	 * @param string $displayName The mew display name of the permission group.
	 * @param string $textDomain The optional text domain for translation.
	 */
	function updatePermissionGroup($name, $displayName, $textDomain = '') {
		$this->db->update('fl_permgroup', array('displayname' => $displayName, 'textdomain' => $textDomain), 'grpname=?', $name);
	}

	/**
	 * Create a permission in a permission group.
	 *
	 * @param string $name The name of the permission to add, 'group.perm'.
	 * @param string $displayName The display name of the permission.
	 * @param int $displayOrder The smaller numbers show before the larger.
	 * @param string $textDomain The optional text domain for translation.
	 * @return bool true if the permission was added; else false.
	 */
	function createPermission($name, $displayName, $displayOrder = 0, $textDomain = '') {

		// Make sure the group exists
		list($g) = explode('.', $name);
		$this->db->insert('fl_permgroup', array('grpname' => $g, 'displayname' => ucfirst($g), 'textdomain' => $textDomain));

		// Create the permission & add to the admin role
		if($this->db->insert('fl_permissions', array('permission' => $name, 'displayname' => $displayName, 'disporder' => $displayOrder, 'textdomain' => $textDomain))) {
			$this->db->insert('fl_roleperms', array('roleid' => 1, 'permission' => $name));
			return true;
		}
		else
			return false;
	}

	/**
	 * Delete a permission.
	 *
	 * If the permission group is empty then the group is deleted as well.
	 *
	 * @param string $name The name of the permission to delete.
	 */
	function deletePermission($name) {
		$this->db->delete('fl_permissions', 'permission=?', $name);
		$this->db->delete('fl_roleperms', 'permission=?', $name);

		list($g) = explode('.', $name);
		if(!$this->db->fetchOne('SELECT COUNT(*) FROM fl_permissions WHERE permission LIKE ?', str_replace(array('%', '_'), array('\%', '\_'), $g) . '.%'))
			$this->db->delete('fl_permgroup', 'grpname=?', $g);

		$this->db->query('OPTIMIZE TABLE fl_permissions, fl_permgroup');
	}

	/**
	 * Update the display name of a permission.
	 *
	 * @param string $name The name of the permission to update.
	 * @param string $displayName The mew display name of the permission.
	 * @param string $textDomain The optional text domain for translation.
	 * @return bool true if the permission updated; else false.
	 */
	function updatePermission($name, $displayName, $textDomain = '') {
		return $this->db->update('fl_permissions', array('displayname' => $displayName, 'textdomain' => $textDomain), 'permission=?', $name);
	}

	/**
	 * Get a list of all the permission grouped by their groups.
	 *
	 * @return array Array of groups and their permissions.
	 */
	function getPermissions() {
		$result = array();

		// Get the groups
		$grpRes = $this->db->query('SELECT grpname,displayname,textdomain FROM fl_permgroup ORDER BY displayname');
		while($g = $grpRes->fetch()) {
			$permRes = $this->db->query('SELECT permission,displayname,textdomain FROM fl_permissions WHERE permission LIKE ? ORDER BY disporder,displayname', $g['grpname'] . '.%');
			$permList = array();
			while($p = $permRes->fetch())
				$permList[$p['permission']] = __($p['displayname'], $p['textdomain']) . " &nbsp; &#91;$p[permission]&#93;";
			$result[$g['grpname']] = array('groupname' => __($g['displayname'], $g['textdomain']), 'permissions' => $permList);
		}

		return $result;
	}

	/**
	 * Get a list of all the permission in a specific group.
	 *
	 * @param string $group The group to get the permissions for.
	 * @return array Permissions and their display names.
	 */
	function getPermissionsInGroup($group) {
		$permRes = $this->db->query('SELECT permission,displayname,textdomain FROM fl_permissions WHERE permission LIKE ? ORDER BY displayname', $group . '.%');
		$permList = $group == 'content' ? array('__flLoggedInUser__' => __('[Logged in Users]', 'fusionLib')) : array();
		while($p = $permRes->fetch())
			$permList[$p['permission']] = __($p['displayname'], $p['textdomain']);
		return $permList;
	}

	/**
	 * Get the display name of a permission.
	 *
	 * @param string $perm The name of the permission to get the display name of.
	 * @return string the display name of the permission.
	 */
	function getPermissionDisplayName($perm) {
		if($r = $this->db->fetchRow('SELECT displayname,textdomain FROM fl_permissions WHERE permission=?', $perm))
			return __($r['displayname'], $r['textdomain']);
		else
			return '';
	}

	/**
	 * Create a new role.
	 *
	 * @param string $name The name of the role.
	 * @param string $displayName The display name.
	 * @return int The ID of the role or 0 if it failed to create.
	 */
	function createRole($name, $displayName) {
		if(!$this->db->insert('fl_roles', array('rolename' => $name, 'displayname' => $displayName)))
			return 0;
		return $this->db->insertID();
	}

	/**
	 * Delete a role.
	 *
	 * Role 1, the admin role can't be deleted.
	 *
	 * @param int $roleid ID of the role to delete.
	 */
	function deleteRole($roleid) {
		if($roleid == 1)
			return;
		$this->db->delete('fl_roles', 'roleid=?', $roleid);
		$this->db->delete('fl_roleperms', 'roleid=?', $roleid);
		$this->db->delete('fl_usersroles', 'roleid=?', $roleid);
		$this->db->delete('fl_apikeyroles', 'roleid=?', $roleid);
	}

	/**
	 * Update the display name of a role.
	 *
	 * @param int $roleid The id of the role to update.
	 * @param string $name The name of the role.
	 * @param string $displayName  The mew display name of the role.
	 */
	function updateRole($roleid, $name, $displayName) {
		$this->db->update('fl_roles', array('rolename' => $name, 'displayname' => $displayName), 'roleid=?', $roleid);
	}

	/**
	 * Get the display name of a role.
	 *
	 * @param int $roleid The ID of the role to get.
	 *
	 * @return string The display name of the role.
	 */
	function getRoleDisplayName($roleid) {
		return $this->db->fetchOne('SELECT displayname FROM fl_roles WHERE roleid=?', $roleid);
	}

	/**
	 * Get the role name and display name.
	 *
	 * @param int $roleid The ID of the role to get.
	 * @return array The 'rolename' and 'displayname' of the role.
	 */
	function getRoleInfo($roleid) {
		return $this->db->fetchRow('SELECT rolename,displayname FROM fl_roles WHERE roleid=?', $roleid);
	}

	/**
	 * Get the roles available in the system.
	 *
	 * @return array Array of role IDs and their display names.
	 */
	function getRoles() {
		$r = array();
		$result = $this->db->query('SELECT roleid,displayname FROM fl_roles ORDER BY displayname');
		while($row = $result->fetch())
			$r[$row['roleid']] = $row['displayname'];
		return $r;
	}

	/**
	 * Set the permissions for a role. Changes to the admin permissions are ignored.
	 *
	 * @param int $roleid The id of the role to set the permissions for.
	 * @param array $permissions Array of permissions for the role.
	 * @throws flAcException
	 */
	function setRolePermissions($roleid, $permissions) {
		if($roleid == 1)
			return;

		// Verify all permissions exist
		foreach($permissions as $p) {
			if(!$this->db->fetchOne('SELECT COUNT(*) FROM fl_permissions WHERE permission=?', $p))
				throw new flAcException("Unable to assign unknown permission $p to role.");
		}

		// Delete the old permissions
		$this->db->delete('fl_roleperms', 'roleid=?', $roleid);

		// Insert the new permissions
		foreach($permissions as $p)
			$this->db->insert('fl_roleperms', array('roleid' => $roleid, 'permission' => $p));
	}

	/**
	 * Get the permissions set on a role.
	 *
	 * @param int $roleid The id of the role to get the permissions for.
	 * @param string $grp Optional group to limit the permissions returned to.
	 * @return array Array of permissions.
	 */
	function getRolePermissions($roleid, $grp = null) {
		$p = array();
		if($grp === null)
			$result = $this->db->query('SELECT permission FROM fl_roleperms WHERE roleid=?', $roleid);
		else
			$result = $this->db->query('SELECT permission FROM fl_roleperms WHERE roleid=? && permission LIKE ?', $roleid, $grp . '.%');
		while($row = $result->fetch())
			$p[] = $row['permission'];
		return $p;
	}
}
