Файл: library/XenForo/ContentPermission/Node.php
Строк: 593
<?php
/**
* Handles building content permissions for nodes.
*
* Any type of node permission may be applied to any node. However, when calculated,
* only the known permissions for the specified node type are used. For example,
* gallery permissions could be applied to a forum, causing those permissions to be
* inherited to any child galleries. However, when compiled, the forum only has
* forum permissions.
*
* @package XenForo_Permissions
*/
class XenForo_ContentPermission_Node implements XenForo_ContentPermission_Interface
{
/**
* Tracks whether we've initialized the node data. Many calls to
* {@link rebuildContentPermissions()} may happen on one object.
*
* @var boolean
*/
protected $_nodesInitialized = false;
/**
* Permission model
*
* @var XenForo_Model_Permission
*/
protected $_permissionModel = null;
/**
* Global perms that apply to this call to rebuild the permissions. These permissions
* can be manipulated if necessary and the global permissions will actually be modified.
*
* @var array
*/
protected $_globalPerms = array();
/**
* List of node types.
*
* @var array
*/
protected $_nodeTypes = array();
/**
* The node tree hierarchy. This data is traversed to build permissions.
*
* @var array Format: [parent][node id] => node info
*/
protected $_nodeTree = array();
/**
* @var null|array If not null, [node id] => info
*/
protected $_nodes = null;
/**
* All node permission entries for the tree, grouped by system, user group, and user.
*
* @var array
*/
protected $_nodePermissionEntries = array();
/**
* Builds the node permissions for a user collection.
*
* @param XenForo_Model_Permission $permissionModel Permission model that called this.
* @param array $userGroupIds List of user groups for the collection
* @param integer $userId User ID for the collection, if there are custom permissions
* @param array $permissionsGrouped List of all valid permissions, grouped
* @param array $globalPerms The global permissions that apply to this combination
*
* @return array
*/
public function rebuildContentPermissions(
$permissionModel, array $userGroupIds, $userId, array $permissionsGrouped, array &$globalPerms
)
{
$this->_permissionModel = $permissionModel;
$this->_globalPerms = $globalPerms;
$this->_nodeSetup();
$finalPermissions = $this->_buildNodeTreePermissions($userId, $userGroupIds, $globalPerms, $permissionsGrouped);
$globalPerms = $this->_globalPerms;
return $finalPermissions;
}
/**
* Sets up the necessary information about the node tree, existing permission entries,
* etc. Only runs if not initialized.
*/
protected function _nodeSetup()
{
if ($this->_nodesInitialized)
{
return;
}
$nodeModel = $this->_getNodeModel();
$this->_nodeTypes = $nodeModel->getAllNodeTypes();
$this->_nodeTree = $nodeModel->getNodeHierarchy();
$this->_nodePermissionEntries = $this->_permissionModel->getAllContentPermissionEntriesByTypeGrouped('node');
$this->_nodesInitialized = true;
}
/**
* Allows the node data to be injected manually. Generally only needed for testing.
*
* @param array $nodeTypes
* @param array $nodeTree
* @param array $nodePermissionEntries
*/
public function setNodeDataManually(array $nodeTypes, array $nodeTree, array $nodePermissionEntries)
{
$this->_nodeTypes = $nodeTypes;
$this->_nodeTree = $nodeTree;
$this->_nodePermissionEntries = $nodePermissionEntries;
$this->_nodesInitialized = true;
}
protected function _getNodesFromTree(array $tree)
{
$nodes = array();
foreach ($tree AS $treeNodes)
{
foreach ($treeNodes AS $node)
{
$nodes[$node['node_id']] = $node;
}
}
return $nodes;
}
public function getContentPermissionDetails(
$permissionModel, array $userGroupIds, $userId, $contentId, array $permission,
array $permissionsGrouped, array $globalPerms
)
{
$this->_globalPerms = $globalPerms;
$this->_permissionModel = $permissionModel;
$this->_nodeSetup();
if ($this->_nodes === null)
{
$this->_nodes = $this->_getNodesFromTree($this->_nodeTree);
}
$path = array();
$nodeId = $contentId;
while ($nodeId && isset($this->_nodes[$nodeId]))
{
array_unshift($path, $nodeId);
$nodeId = $this->_nodes[$nodeId]['parent_node_id'];
}
$output = array();
$permissionId = $permission['permission_id'];
$permissionGroupId = $permission['permission_group_id'];
$nodeTree = array();
foreach ($path AS $nodeId)
{
$node = $this->_nodes[$nodeId];
$nodeTree[$node['parent_node_id']][$nodeId] = $node;
}
$oldNodeTree = $this->_nodeTree;
$this->_nodeTree = $nodeTree;
$finalPermissions = $this->_buildNodeTreePermissions($userId, $userGroupIds, $globalPerms, $permissionsGrouped);
$this->_nodeTree = $oldNodeTree;
foreach ($path AS $nodeId)
{
$node = $this->_nodes[$nodeId];
$groupEntries = $this->_getUserGroupNodeEntries($nodeId, $userGroupIds);
$userEntries = $this->_getUserNodeEntries($nodeId, $userId);
$nodeWideEntries = $this->_getNodeWideEntries($nodeId);
$groups = array();
foreach ($userGroupIds AS $userGroupId)
{
$groups[$userGroupId] =
isset($groupEntries[$userGroupId][$permissionGroupId][$permissionId])
? $groupEntries[$userGroupId][$permissionGroupId][$permissionId]
: false;
}
$final = null;
if (isset($this->_nodeTypes[$node['node_type_id']]))
{
if ($this->_nodeTypes[$node['node_type_id']]['permission_group_id'] == $permissionGroupId)
{
$final = $finalPermissions[$nodeId][$permissionId];
}
else if ($permissionGroupId == 'general' && $permissionId == 'viewNode')
{
$final = $finalPermissions[$nodeId]['view'];
}
}
$output[$nodeId] = array(
'title' => $node['title'],
'user' => isset($userEntries[$permissionGroupId][$permissionId]) ? $userEntries[$permissionGroupId][$permissionId] : false,
'content' => isset($nodeWideEntries[$permissionGroupId][$permissionId]) ? $nodeWideEntries[$permissionGroupId][$permissionId] : false,
'groups' => $groups,
'final' => $final
);
}
return $output;
}
/**
* Recursively builds node tree permissions for the specified combination.
* Note that nodes will have permissions for all node types, but the final
* permissions for a node *only* include that node's permissions.
*
* @param integer $userId
* @param array $userGroupIds
* @param array $basePermissions Base permissions, coming from global or parent; [group][permission] => allow/unset/etc
* @param array $permissionsGrouped List of all valid permissions, grouped
* @param integer $parentId ID of the parent node.
*
* @return array Final permissions (true/false), format: [node id][permission] => value
*/
protected function _buildNodeTreePermissions(
$userId, array $userGroupIds, array $basePermissions, array $permissionsGrouped, $parentId = 0
)
{
if (!isset($this->_nodeTree[$parentId]))
{
return array();
}
if (!isset($basePermissions['general']['viewNode']))
{
if (isset($this->_globalPerms['general']['viewNode']))
{
$basePermissions['general']['viewNode'] = $this->_globalPerms['general']['viewNode'];
}
else
{
$basePermissions['general']['viewNode'] = 'unset';
}
}
$basePermissions = $this->_adjustBasePermissionAllows($basePermissions);
$finalPermissions = array();
foreach ($this->_nodeTree[$parentId] AS $node)
{
if (!isset($this->_nodeTypes[$node['node_type_id']]))
{
continue;
}
$nodeType = $this->_nodeTypes[$node['node_type_id']];
$nodeId = $node['node_id'];
$groupEntries = $this->_getUserGroupNodeEntries($nodeId, $userGroupIds);
$userEntries = $this->_getUserNodeEntries($nodeId, $userId);
$nodeWideEntries = $this->_getNodeWideEntries($nodeId);
$nodePermissions = $this->_permissionModel->buildPermissionCacheForCombination(
$permissionsGrouped, $nodeWideEntries, $groupEntries, $userEntries,
$basePermissions, $passPermissions
);
if (!isset($nodePermissions['general']['viewNode']))
{
$nodePermissions['general']['viewNode'] = 'unset';
}
if ($nodeType['permission_group_id'])
{
$nodePermissions[$nodeType['permission_group_id']]['view'] = $nodePermissions['general']['viewNode'];
$finalNodePermissions = $this->_permissionModel->canonicalizePermissionCache(
$nodePermissions[$nodeType['permission_group_id']]
);
if (isset($finalNodePermissions['view']) && !$finalNodePermissions['view'])
{
// forcable deny viewing perms to children if this isn't viewable
$passPermissions['general']['viewNode'] = 'deny';
}
}
else
{
$finalNodePermissions = array();
}
$finalPermissions[$nodeId] = $finalNodePermissions;
$finalPermissions += $this->_buildNodeTreePermissions(
$userId, $userGroupIds, $passPermissions, $permissionsGrouped, $nodeId
);
}
return $finalPermissions;
}
/**
* Get all user group entries that apply to this node for the specified user groups.
*
* @param integer $nodeId
* @param array $userGroupIds
*
* @return array
*/
protected function _getUserGroupNodeEntries($nodeId, array $userGroupIds)
{
$rawUgEntries = $this->_nodePermissionEntries['userGroups'];
$groupEntries = array();
foreach ($userGroupIds AS $userGroupId)
{
if (isset($rawUgEntries[$userGroupId], $rawUgEntries[$userGroupId][$nodeId]))
{
$groupEntries[$userGroupId] = $rawUgEntries[$userGroupId][$nodeId];
}
}
return $groupEntries;
}
/**
* Gets all user entries that apply to this node for the specified user ID.
*
* @param $nodeId
* @param $userId
*
* @return array
*/
protected function _getUserNodeEntries($nodeId, $userId)
{
$rawUserEntries = $this->_nodePermissionEntries['users'];
if ($userId && isset($rawUserEntries[$userId], $rawUserEntries[$userId][$nodeId]))
{
return $rawUserEntries[$userId][$nodeId];
}
else
{
return array();
}
}
/**
* Get node-wide permissions for this node.
*
* @param $nodeId
*
* @return array
*/
protected function _getNodeWideEntries($nodeId)
{
if (isset($this->_nodePermissionEntries['system'][$nodeId]))
{
return $this->_nodePermissionEntries['system'][$nodeId];
}
else
{
return array();
}
}
/**
* Adjusts base (inherited) content_allow values to allow only. This
* allows them to be revoked.
*
* @param array $basePermissions
*
* @return array Adjusted base perms
*/
protected function _adjustBasePermissionAllows(array $basePermissions)
{
foreach ($basePermissions AS $group => $p)
{
foreach ($p AS $id => $value)
{
if ($value === 'content_allow')
{
$basePermissions[$group][$id] = 'allow';
}
}
}
return $basePermissions;
}
/**
* Force a node-wide reset to override content allow settings from the permissions.
* This is used to cause reset to take priority over content allow from a parent, but
* not content allow from this node.
*
* @param array $nodeWideEntries
* @param array $nodePermissions
*
* @return array Updated node permissions
*/
protected function _forceNodeWideResetOverride(array $nodeWideEntries, array $nodePermissions)
{
foreach ($nodeWideEntries AS $nwGroupId => $nwGroup)
{
foreach ($nwGroup AS $nwPermId => $nwValue)
{
if ($nwValue === 'reset'
&& isset($nodePermissions[$nwGroupId][$nwPermId])
&& $nodePermissions[$nwGroupId][$nwPermId] === 'content_allow'
)
{
$nodePermissions[$nwGroupId][$nwPermId] = 'reset';
}
}
}
return $nodePermissions;
}
/**
* Gets the node model object.
*
* @return XenForo_Model_Node
*/
protected function _getNodeModel()
{
return XenForo_Model::create('XenForo_Model_Node');
}
}