mirror of
https://github.com/kamilwylegala/cakephp2-php8.git
synced 2025-01-19 02:56:15 +00:00
Importing new ACL system
git-svn-id: https://svn.cakephp.org/repo/branches/1.2.x.x@4487 3807eeeb-6ff5-0310-8944-8be069107fe0
This commit is contained in:
parent
ce71e04618
commit
30134cbd9e
8 changed files with 315 additions and 330 deletions
|
@ -36,7 +36,7 @@ uses('controller' . DS . 'components' . DS . 'dbacl' . DS . 'models' . DS . 'acl
|
|||
uses('controller' . DS . 'components' . DS . 'dbacl' . DS . 'models' . DS . 'aco');
|
||||
uses('controller' . DS . 'components' . DS . 'dbacl' . DS . 'models' . DS . 'acoaction');
|
||||
uses('controller' . DS . 'components' . DS . 'dbacl' . DS . 'models' . DS . 'aro');
|
||||
uses('controller' . DS . 'components' . DS . 'dbacl' . DS . 'models' . DS . 'aros_aco');
|
||||
uses('controller' . DS . 'components' . DS . 'dbacl' . DS . 'models' . DS . 'permission');
|
||||
|
||||
/**
|
||||
* In this file you can extend the AclBase.
|
||||
|
@ -62,7 +62,7 @@ class DB_ACL extends AclBase {
|
|||
* @return unknown
|
||||
*/
|
||||
function check($aro, $aco, $action = "*") {
|
||||
$Perms = new ArosAco();
|
||||
$Perms = new Permission();
|
||||
$Aro = new Aro();
|
||||
$Aco = new Aco();
|
||||
|
||||
|
@ -92,8 +92,8 @@ class DB_ACL extends AclBase {
|
|||
|
||||
for($i = count($aroPath) - 1; $i >= 0; $i--) {
|
||||
$perms = $Perms->findAll(array(
|
||||
'ArosAco.aro_id' => $aroPath[$i]['Aro']['id'],
|
||||
'ArosAco.aco_id' => $acoPath), null,
|
||||
'Permission.aro_id' => $aroPath[$i]['Aro']['id'],
|
||||
'Permission.aco_id' => $acoPath), null,
|
||||
'Aco.lft desc'
|
||||
);
|
||||
|
||||
|
@ -104,8 +104,8 @@ class DB_ACL extends AclBase {
|
|||
if ($action == '*') {
|
||||
// ARO must be cleared for ALL ACO actions
|
||||
foreach($permKeys as $key) {
|
||||
if (isset($perm['ArosAco'])) {
|
||||
if ($perm['ArosAco'][$key] != 1) {
|
||||
if (isset($perm['Permission'])) {
|
||||
if ($perm['Permission'][$key] != 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ class DB_ACL extends AclBase {
|
|||
|
||||
return true;
|
||||
} else {
|
||||
switch($perm['ArosAco']['_' . $action]) {
|
||||
switch($perm['Permission']['_' . $action]) {
|
||||
case -1:
|
||||
return false;
|
||||
case 0:
|
||||
|
@ -135,7 +135,7 @@ class DB_ACL extends AclBase {
|
|||
* @return boolean
|
||||
*/
|
||||
function allow($aro, $aco, $action = "*", $value = 1) {
|
||||
$Perms = new ArosAco();
|
||||
$Perms = new Permission();
|
||||
$perms = $this->getAclLink($aro, $aco);
|
||||
$permKeys = $this->_getAcoKeys($Perms->loadInfo());
|
||||
$save = array();
|
||||
|
@ -146,7 +146,7 @@ class DB_ACL extends AclBase {
|
|||
}
|
||||
|
||||
if (isset($perms[0])) {
|
||||
$save = $perms[0]['ArosAco'];
|
||||
$save = $perms[0]['Permission'];
|
||||
}
|
||||
|
||||
if ($action == "*") {
|
||||
|
@ -168,9 +168,9 @@ class DB_ACL extends AclBase {
|
|||
$save['aco_id'] = $perms['aco'];
|
||||
|
||||
if ($perms['link'] != null && count($perms['link']) > 0) {
|
||||
$save['id'] = $perms['link'][0]['ArosAco']['id'];
|
||||
$save['id'] = $perms['link'][0]['Permission']['id'];
|
||||
}
|
||||
return $Perms->save(array('ArosAco' => $save));
|
||||
return $Perms->save(array('Permission' => $save));
|
||||
}
|
||||
/**
|
||||
* Deny
|
||||
|
@ -259,7 +259,7 @@ class DB_ACL extends AclBase {
|
|||
function getAclLink($aro, $aco) {
|
||||
$Aro = new Aro();
|
||||
$Aco = new Aco();
|
||||
$Link = new ArosAco();
|
||||
$Link = new Permission();
|
||||
|
||||
$obj = array();
|
||||
$obj['Aro'] = $Aro->find($Aro->_resolveID($aro));
|
||||
|
@ -275,8 +275,8 @@ class DB_ACL extends AclBase {
|
|||
'aro' => $obj['Aro']['id'],
|
||||
'aco' => $obj['Aco']['id'],
|
||||
'link' => $Link->findAll(array(
|
||||
'ArosAco.aro_id' => $obj['Aro']['id'],
|
||||
'ArosAco.aco_id' => $obj['Aco']['id']
|
||||
'Permission.aro_id' => $obj['Aro']['id'],
|
||||
'Permission.aco_id' => $obj['Aco']['id']
|
||||
))
|
||||
);
|
||||
}
|
||||
|
@ -288,11 +288,11 @@ class DB_ACL extends AclBase {
|
|||
*/
|
||||
function _getAcoKeys($keys) {
|
||||
$newKeys = array();
|
||||
$keys = $keys->value;
|
||||
$keys = $keys->extract('{n}.name');
|
||||
|
||||
foreach($keys as $key) {
|
||||
if ($key['name'] != 'id' && $key['name'] != 'aro_id' && $key['name'] != 'aco_id') {
|
||||
$newKeys[] = $key['name'];
|
||||
if (!in_array($key, array('id', 'aro_id', 'aco_id'))) {
|
||||
$newKeys[] = $key;
|
||||
}
|
||||
}
|
||||
return $newKeys;
|
||||
|
|
|
@ -36,249 +36,22 @@
|
|||
*
|
||||
*/
|
||||
loadModel();
|
||||
|
||||
class AclNode extends AppModel {
|
||||
|
||||
var $useDbConfig = ACL_DATABASE;
|
||||
|
||||
/**
|
||||
* Explicitly disable in-memory query caching for ACL models
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
var $cacheQueries = false;
|
||||
/**
|
||||
* Creates a new ARO/ACO node
|
||||
* ACL models use the Tree behavior
|
||||
*
|
||||
* @param int $link_id
|
||||
* @param mixed $parent_id
|
||||
* @param string $alias
|
||||
* @return boolean True on success, false on fail
|
||||
* @var mixed
|
||||
*/
|
||||
function create($link_id = 0, $parent_id = null, $alias = '') {
|
||||
parent::create();
|
||||
if (strtolower(get_class($this)) == "aclnode") {
|
||||
trigger_error(__("[acl_base] The AclBase class constructor has been called, or the class was instantiated. This class must remain abstract. Please refer to the Cake docs for ACL configuration."), E_USER_ERROR);
|
||||
return null;
|
||||
}
|
||||
extract ($this->__dataVars());
|
||||
|
||||
if ($parent_id == null || $parent_id === 0) {
|
||||
$parent = $this->find(null, 'MAX(rght) as rght', null, -1);
|
||||
$parent['lft'] = $parent[0]['rght'];
|
||||
|
||||
if ($parent[0]['rght'] == null || !$parent[0]['rght']) {
|
||||
$parent['lft'] = 0;
|
||||
}
|
||||
} else {
|
||||
$parent = $this->find($this->_resolveID($parent_id), null, null, 0);
|
||||
if ($parent == null || count($parent) == 0) {
|
||||
trigger_error(sprintf(__("Null parent in %s::create()", true), $class), E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
$parent = $parent[$class];
|
||||
$this->_syncTable(1, $parent['lft'], $parent['lft']);
|
||||
}
|
||||
$return = $this->save(array($class => array(
|
||||
$secondary_id => $link_id,
|
||||
'alias' => $alias,
|
||||
'lft' => $parent['lft'] + 1,
|
||||
'rght' => $parent['lft'] + 2
|
||||
)));
|
||||
$this->id = $this->getLastInsertID();
|
||||
return $return;
|
||||
}
|
||||
/**
|
||||
* Deletes the ARO/ACO node with the given ID
|
||||
*
|
||||
* @param mixed $id The id or alias of an ARO/ACO node
|
||||
* @return boolean True on success, false on fail
|
||||
*/
|
||||
function delete($id) {
|
||||
extract ($this->__dataVars());
|
||||
$db =& ConnectionManager::getDataSource($this->useDbConfig);
|
||||
|
||||
$result = $this->find($this->_resolveID($id));
|
||||
$object = $result[$class];
|
||||
if ($object == null || count($object) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$children = $this->findAll(array("{$class}.rght" => "< {$result[$class]['rght']}", "{$class}.lft" => "> {$result[$class]['lft']}"), 'id', null, null, null, -1);
|
||||
$idList = Set::extract($children, '{n}.' . $class . '.id');
|
||||
$idList[] = $result[$class]['id'];
|
||||
|
||||
// Delete associated permissions.
|
||||
$this->ArosAco->query('DELETE FROM ' . $db->fullTableName($this->ArosAco) . " WHERE {$class}_id in (" . implode(', ', $idList) . ')');
|
||||
|
||||
$table = $db->fullTableName($this);
|
||||
$this->query("DELETE FROM {$table} WHERE {$table}.lft >= {$result[$class]['lft']} AND {$table}.rght <= {$result[$class]['rght']}");
|
||||
|
||||
$shift = 1 + $result[$class]['rght'] - $result[$class]['lft'];
|
||||
$this->query('UPDATE ' . $table . ' SET ' . $db->name('rght') . ' = ' . $db->name('rght') . ' - ' . $shift . ' WHERE ' . $db->name('rght') . ' > ' . $result[$class]['rght']);
|
||||
$this->query('UPDATE ' . $table . ' SET ' . $db->name('lft') . ' = ' . $db->name('lft') . ' - ' . $shift . ' WHERE ' . $db->name('lft') . ' > ' . $result[$class]['lft']);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Sets the parent of the given node
|
||||
*
|
||||
* @param mixed $parent_id
|
||||
* @param mixed $id
|
||||
* @return boolean True on success, false on failure
|
||||
*/
|
||||
function setParent($parent_id = null, $id = null) {
|
||||
if (strtolower(get_class($this)) == "aclnode") {
|
||||
trigger_error(__("[acl_base] The AclBase class constructor has been called, or the class was instantiated. This class must remain abstract. Please refer to the Cake docs for ACL configuration."), E_USER_ERROR);
|
||||
return null;
|
||||
}
|
||||
extract ($this->__dataVars());
|
||||
|
||||
if ($id == null && $this->id == false) {
|
||||
return false;
|
||||
} else if($id == null) {
|
||||
$id = $this->id;
|
||||
}
|
||||
$object = $this->find($this->_resolveID($id), null, null, 0);
|
||||
|
||||
if ($object == null || count($object) == 0) {
|
||||
return false;
|
||||
}
|
||||
$object = $object[$class];
|
||||
$parent = $this->getParent($id);
|
||||
|
||||
if (($parent == null && $parent_id == null) || ($parent_id == $parent[$class][$secondary_id] && $parent_id != null) || ($parent_id == $parent[$class]['alias'] && $parent_id != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($parent_id == null) {
|
||||
$newParent = $this->find(null, 'MAX(rght) as lft', null, -1);
|
||||
$newParent = $newParent[0];
|
||||
$newParent['rght'] = $newParent['lft'];
|
||||
} else {
|
||||
$newParent = $this->find($this->_resolveID($parent_id), null, null, 0);
|
||||
$newParent = $newParent[$class];
|
||||
}
|
||||
|
||||
if ($parent_id != null && $newParent['lft'] <= $object['lft'] && $newParent['rght'] >= $object['rght']) {
|
||||
return false;
|
||||
}
|
||||
$this->_syncTable(0, $object['lft'], $object['lft']);
|
||||
|
||||
if ($object['lft'] < $newParent['lft']) {
|
||||
$newParent['lft'] = $newParent['lft'] - 2;
|
||||
$newParent['rght'] = $newParent['rght'] - 2;
|
||||
}
|
||||
|
||||
if ($parent_id != null) {
|
||||
$this->_syncTable(1, $newParent['lft'], $newParent['lft']);
|
||||
}
|
||||
$object['lft'] = $newParent['lft'] + 1;
|
||||
$object['rght'] = $newParent['lft'] + 2;
|
||||
$this->save(array($class => $object));
|
||||
|
||||
if ($newParent['lft'] == 0) {
|
||||
$this->_syncTable(2, $newParent['lft'], $newParent['lft']);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Get the parent node of the given Aro or Aco
|
||||
*
|
||||
* @param moxed $id
|
||||
* @return array
|
||||
*/
|
||||
function getParent($id) {
|
||||
$path = $this->getPath($id);
|
||||
if ($path == null || count($path) < 2) {
|
||||
return null;
|
||||
} else {
|
||||
return $path[count($path) - 2];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Gets the path to the given Aro or Aco
|
||||
*
|
||||
* @param mixed $id
|
||||
* @return array
|
||||
*/
|
||||
function getPath($id) {
|
||||
if (strtolower(get_class($this)) == "aclnode") {
|
||||
trigger_error(__("[acl_base] The AclBase class constructor has been called, or the class was instantiated. This class must remain abstract. Please refer to the Cake docs for ACL configuration."), E_USER_ERROR);
|
||||
return null;
|
||||
}
|
||||
extract ($this->__dataVars());
|
||||
$item = $this->find($this->_resolveID($id), null, null, 0);
|
||||
|
||||
if ($item == null || count($item) == 0) {
|
||||
return null;
|
||||
}
|
||||
return $this->findAll(array($class . '.lft' => '<= ' . $item[$class]['lft'], $class . '.rght' => '>= ' . $item[$class]['rght']), null, array($class . '.lft' => 'ASC'), null, null, 0);
|
||||
}
|
||||
/**
|
||||
* Get the child nodes of the given Aro or Aco
|
||||
*
|
||||
* @param mixed $id
|
||||
* @return array
|
||||
*/
|
||||
function getChildren($id) {
|
||||
if (strtolower(get_class($this)) == "aclnode") {
|
||||
trigger_error(__("[acl_base] The AclBase class constructor has been called, or the class was instantiated. This class must remain abstract. Please refer to the Cake docs for ACL configuration."), E_USER_ERROR);
|
||||
return null;
|
||||
}
|
||||
|
||||
extract ($this->__dataVars());
|
||||
$item = $this->find($this->_resolveID($id), null, null, 0);
|
||||
return $this->findAll(array($class . '.lft' => '> ' . $item[$class]['lft'], $class . '.rght' => '< ' . $item[$class]['rght']), null, null, null, null, null, 0);
|
||||
}
|
||||
/**
|
||||
* Gets a conditions array to find an Aro or Aco, based on the given id or alias
|
||||
*
|
||||
* @param mixed $id
|
||||
* @return array Conditions array for a find/findAll call
|
||||
*/
|
||||
function _resolveID($id) {
|
||||
extract($this->__dataVars());
|
||||
$key = (is_numeric($id) ? $secondary_id : 'alias');
|
||||
return array($this->name . '.' . $key => $id);
|
||||
}
|
||||
/**
|
||||
* Shifts the left and right values of the aro/aco tables
|
||||
* when a node is added or removed
|
||||
*
|
||||
* @param unknown_type $dir
|
||||
* @param unknown_type $lft
|
||||
* @param unknown_type $rght
|
||||
*/
|
||||
function _syncTable($dir, $lft, $rght) {
|
||||
$db =& ConnectionManager::getDataSource($this->useDbConfig);
|
||||
|
||||
if ($dir == 2) {
|
||||
$shift = 1;
|
||||
$dir = '+';
|
||||
} else {
|
||||
$shift = 2;
|
||||
|
||||
if ($dir > 0) {
|
||||
$dir = '+';
|
||||
} else {
|
||||
$dir = '-';
|
||||
}
|
||||
}
|
||||
|
||||
$db->query('UPDATE ' . $db->fullTableName($this) . ' SET ' . $db->name('rght') . ' = ' . $db->name('rght') . ' ' . $dir . ' ' . $shift . ' WHERE ' . $db->name('rght') . ' > ' . $lft);
|
||||
$db->query('UPDATE ' . $db->fullTableName($this) . ' SET ' . $db->name('lft') . ' = ' . $db->name('lft') . ' ' . $dir . ' ' . $shift . ' WHERE ' . $db->name('lft') . ' > ' . $lft);
|
||||
}
|
||||
/**
|
||||
* Private method. Infers data based on the currently-intantiated subclass.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function __dataVars() {
|
||||
$vars = array();
|
||||
$class = strtolower(get_class($this));
|
||||
if ($class == 'aro') {
|
||||
$vars['secondary_id'] = 'foreign_key';
|
||||
} else {
|
||||
$vars['secondary_id'] = 'object_id';
|
||||
}
|
||||
|
||||
$vars['class'] = ucwords($class);
|
||||
return $vars;
|
||||
}
|
||||
var $actsAs = array('Tree' => 'nested');
|
||||
}
|
||||
|
||||
?>
|
|
@ -37,13 +37,19 @@
|
|||
* @subpackage cake.cake.libs.controller.components.dbacl.models
|
||||
*
|
||||
*/
|
||||
class Aco extends AclNode{
|
||||
var $name = 'Aco';
|
||||
class Aco extends AclNode {
|
||||
/**
|
||||
* Enter description here...
|
||||
* Model name
|
||||
*
|
||||
* @var unknown_type
|
||||
* @var string
|
||||
*/
|
||||
var $hasMany = 'ArosAco';
|
||||
var $name = 'Aco';
|
||||
/**
|
||||
* Binds to ARO nodes through permissions settings
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $hasAndBelongsToMany = array('Aro' => array('with' => 'Permission'));
|
||||
}
|
||||
|
||||
?>
|
|
@ -44,12 +44,13 @@ class Aro extends AclNode {
|
|||
*
|
||||
* @var unknown_type
|
||||
*/
|
||||
var $name = 'Aro';
|
||||
var $name = 'Aro';
|
||||
/**
|
||||
* Enter description here...
|
||||
*
|
||||
* @var unknown_type
|
||||
*/
|
||||
var $hasMany = 'ArosAco';
|
||||
var $hasAndBelongsToMany = array('Aco' => array('with' => 'Permission'));
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,68 +0,0 @@
|
|||
<?php
|
||||
/* SVN FILE: $Id$ */
|
||||
|
||||
/**
|
||||
* Short description for file.
|
||||
*
|
||||
* Long description for file
|
||||
*
|
||||
* PHP versions 4 and 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework <http://www.cakephp.org/>
|
||||
* Copyright 2005-2007, Cake Software Foundation, Inc.
|
||||
* 1785 E. Sahara Avenue, Suite 490-204
|
||||
* Las Vegas, Nevada 89104
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @filesource
|
||||
* @copyright Copyright 2005-2007, Cake Software Foundation, Inc.
|
||||
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
||||
* @package cake
|
||||
* @subpackage cake.cake.libs.controller.components.dbacl.models
|
||||
* @since CakePHP(tm) v 0.10.0.1232
|
||||
* @version $Revision$
|
||||
* @modifiedby $LastChangedBy$
|
||||
* @lastmodified $Date$
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Short description for file.
|
||||
*
|
||||
* Long description for file
|
||||
*
|
||||
* @package cake
|
||||
* @subpackage cake.cake.libs.controller.components.dbacl.models
|
||||
*/
|
||||
|
||||
class ArosAco extends AppModel {
|
||||
|
||||
var $useDbConfig = ACL_DATABASE;
|
||||
/**
|
||||
* Enter description here...
|
||||
*
|
||||
* @var unknown_type
|
||||
*/
|
||||
var $cacheQueries = false;
|
||||
/**
|
||||
* Enter description here...
|
||||
*
|
||||
* @var unknown_type
|
||||
*/
|
||||
var $name = 'ArosAco';
|
||||
/**
|
||||
* Enter description here...
|
||||
*
|
||||
* @var unknown_type
|
||||
*/
|
||||
var $useTable = 'aros_acos';
|
||||
/**
|
||||
* Enter description here...
|
||||
*
|
||||
* @var unknown_type
|
||||
*/
|
||||
var $belongsTo = 'Aro,Aco';
|
||||
}
|
||||
?>
|
88
cake/libs/model/behaviors/acl.php
Normal file
88
cake/libs/model/behaviors/acl.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
class AclBehavior extends ModelBehavior {
|
||||
|
||||
var $__typeMaps = array('requester' => 'Aro', 'controlled' => 'Aco');
|
||||
|
||||
function setup(&$model, $config = array()) {
|
||||
if (is_string($config)) {
|
||||
$config = array('type' => $config);
|
||||
}
|
||||
$this->settings[$model->name] = am(array('type' => 'Requester'), $config);
|
||||
$type = $this->__typeMaps[$this->settings[$model->name]['type']];
|
||||
|
||||
if (!ClassRegistry::isKeySet($type)) {
|
||||
uses('controller' . DS . 'components' . DS . 'dbacl' . DS . 'models' . DS . 'aclnode');
|
||||
uses('controller' . DS . 'components' . DS . 'dbacl' . DS . 'models' . DS . low($type));
|
||||
$object =& new $type();
|
||||
} else {
|
||||
$object =& ClassRegistry::getObject($type);
|
||||
}
|
||||
$model->{$type} =& $object;
|
||||
if (!method_exists($model, 'parentNode')) {
|
||||
trigger_error("Callback parentNode() not defined in {$model->name}", E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Aro/Aco node for this model
|
||||
*
|
||||
* @param mixed $ref
|
||||
* @return array
|
||||
*/
|
||||
function node(&$model, $ref = null) {
|
||||
$db =& ConnectionManager::getDataSource($model->useDbConfig);
|
||||
$type = $this->__typeMaps[low($this->settings[$model->name]['type'])];
|
||||
$table = low($type) . 's';
|
||||
$prefix = $model->tablePrefix;
|
||||
$axo =& $model->{$type};
|
||||
|
||||
if (empty($ref)) {
|
||||
$ref = array('model' => $model->name, 'foreign_key' => $model->id);
|
||||
} elseif (is_string($ref)) {
|
||||
$path = explode('/', $ref);
|
||||
$start = $path[count($path) - 1];
|
||||
unset($path[count($path) - 1]);
|
||||
|
||||
$query = "SELECT {$type}0.* From {$prefix}{$table} AS {$type}0 ";
|
||||
foreach ($path as $i => $alias) {
|
||||
$j = $i - 1;
|
||||
$k = $i + 1;
|
||||
$query .= "LEFT JOIN {$prefix}{$table} AS {$type}{$k} ";
|
||||
$query .= "ON {$type}{$k}.lft > {$type}{$i}.lft && {$type}{$k}.rght < {$type}{$i}.rght ";
|
||||
$query .= "AND {$type}{$k}.alias = " . $db->value($alias) . " ";
|
||||
}
|
||||
$result = $axo->query("{$query} WHERE {$type}0.alias = " . $db->value($start));
|
||||
|
||||
if (!empty($result)) {
|
||||
$result = $result[0]["{$type}0"];
|
||||
}
|
||||
} elseif (is_object($ref) && is_a($ref, 'Model')) {
|
||||
$ref = array('model' => $ref->name, 'foreign_key' => $ref->id);
|
||||
}
|
||||
|
||||
if (is_array($ref)) {
|
||||
list($result) = array_values($axo->find($ref, null, null, -1));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
function afterSave(&$model, $created) {
|
||||
if ($created) {
|
||||
$type = $this->__typeMaps[low($this->settings[$model->name]['type'])];
|
||||
$model->{$type}->save(array(
|
||||
'parent_id' => $model->parentNode(),
|
||||
'model' => $model->name,
|
||||
'foreign_key' => $model->id
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function afterDelete(&$model) {
|
||||
$node = $this->node($model);
|
||||
$type = $this->__typeMaps[low($this->settings[$model->name]['type'])];
|
||||
$model->{$type}->delete($node['id']);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
183
cake/libs/model/behaviors/tree.php
Normal file
183
cake/libs/model/behaviors/tree.php
Normal file
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
/* SVN FILE: $Id$ */
|
||||
|
||||
/**
|
||||
* Tree behavior class.
|
||||
*
|
||||
* Enables a model object to act as a node-based tree.
|
||||
*
|
||||
* PHP versions 4 and 5
|
||||
*
|
||||
* CakePHP : Rapid Development Framework <http://www.cakephp.org/>
|
||||
* Copyright (c) 2006, Cake Software Foundation, Inc.
|
||||
* 1785 E. Sahara Avenue, Suite 490-204
|
||||
* Las Vegas, Nevada 89104
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @filesource
|
||||
* @copyright Copyright (c) 2006, Cake Software Foundation, Inc.
|
||||
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
|
||||
* @package cake
|
||||
* @subpackage cake.cake.libs.model
|
||||
* @since CakePHP v 1.2.0.0
|
||||
* @version $Revision$
|
||||
* @modifiedby $LastChangedBy$
|
||||
* @lastmodified $Date$
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
class TreeBehavior extends ModelBehavior {
|
||||
|
||||
function setup(&$model, $config = array()) {
|
||||
$settings = am(array(
|
||||
'parent' => 'parent_id',
|
||||
'left' => 'lft',
|
||||
'right' => 'rght',
|
||||
'scope' => '1 = 1',
|
||||
'type' => 'nested'
|
||||
), $config);
|
||||
|
||||
/*if (in_array($settings['scope'], $model->getAssociated('belongsTo'))) {
|
||||
$data = $model->getAssociated($settings['scope']);
|
||||
$parent =& $model->{$data['className']};
|
||||
$settings['scope'] = $model->escapeField($data['foreignKey']) . ' = ' . $parent->escapeField($parent->primaryKey, $settings['scope']);
|
||||
}*/
|
||||
$this->settings[$model->name] = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent of the given node
|
||||
*
|
||||
* @param mixed $parent_id
|
||||
* @return boolean True on success, false on failure
|
||||
*/
|
||||
function setparent(&$model, $parent_id = null) {
|
||||
extract($this->settings[$model->name]);
|
||||
list($node) = array_values($model->find(array($model->escapeField() => $model->id), array($model->primaryKey, $parent, $left, $right), null, -1));
|
||||
list($edge) = array_values($model->find(null, "MAX({$right}) AS {$right}", null, -1));
|
||||
$edge = ife(empty($edge[$right]), 0, $edge[$right]); // Is the tree empty?
|
||||
|
||||
if (!empty($parent_id)) {
|
||||
@list($parentNode) = array_values($model->find(array($model->escapeField() => $parent_id), array($model->primaryKey, $left, $right), null, -1));
|
||||
if (empty($parentNode)) {
|
||||
trigger_error(__("Null parent in Tree::afterSave()", true), E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
$offset = $parentNode[$left];
|
||||
|
||||
if (!empty($node[$left]) && !empty($node[$right])) {
|
||||
$shift = ($edge - $node[$left]) + 1;
|
||||
$diff = $node[$right] - $node[$left] + 1;
|
||||
$start = $edge + 1;
|
||||
|
||||
// First, move the node (and subnodes) outside the tree
|
||||
$model->updateAll(array($right => "{$right} + {$shift}"), array($right => "BETWEEN {$node[$left]} AND {$node[$right]}"));
|
||||
$model->updateAll(array($left => "{$left} + {$shift}"), array($left => "BETWEEN {$node[$left]} AND {$node[$right]}"));
|
||||
// Close the gap to the right of where the node was
|
||||
$model->updateAll(array($right => "{$right} - {$diff}"), array($right => "BETWEEN {$node[$right]} AND {$edge}"));
|
||||
$model->updateAll(array($left => "{$left} - {$diff}"), array($left => "BETWEEN {$node[$right]} AND {$edge}"));
|
||||
// Open a new gap to insert the node
|
||||
$model->updateAll(array($right => "{$right} + {$diff}"), array($right => "> {$offset}"));
|
||||
$model->updateAll(array($left => "{$left} + {$diff}"), array($left => "> {$offset}"));
|
||||
// Shift the node(s) into position
|
||||
$model->updateAll(array($right => "{$right} - {$shift}"), array($right => ">= {$start}"));
|
||||
$model->updateAll(array($left => "{$left} - {$shift}"), array($left => ">= {$start}"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$offset = $edge;
|
||||
}
|
||||
$model->updateAll(array($right => "{$right} + 2"), array($right => "> {$offset}"));
|
||||
$model->updateAll(array($left => "{$left} + 2"), array($left => "> {$offset}"));
|
||||
return $model->save(array($left => $offset + 1, $right => $offset + 2, $parent => $parent_id), false);
|
||||
}
|
||||
|
||||
function afterSave(&$model, $created) {
|
||||
extract($this->settings[$model->name]);
|
||||
if ($created) {
|
||||
if (!isset($model->data[$model->name][$parent])) {
|
||||
$model->data[$model->name][$parent] = null;
|
||||
}
|
||||
return $this->setparent($model, $model->data[$model->name][$parent]);
|
||||
}
|
||||
}
|
||||
|
||||
function beforeDelete(&$model) {
|
||||
extract($this->settings[$model->name]);
|
||||
list($name, $data) = array($model->name, $model->read());
|
||||
$data = $data[$name];
|
||||
$diff = $data[$right] - $data[$left] + 1;
|
||||
|
||||
$model->deleteAll(array($scope, $left => '> ' . $data[$left], $right => '< ' . $data[$right]));
|
||||
$model->updateAll(array($left => "{$left} - {$diff}"), array($scope, $left => '>= ' . $data[$right]));
|
||||
$model->updateAll(array($right => "{$right} - {$diff}"), array($scope, $right => '>= ' . $data[$right]));
|
||||
}
|
||||
/**
|
||||
* Get the child nodes of the current model
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function children(&$model) {
|
||||
$name = $model->name;
|
||||
extract($this->settings[$name]);
|
||||
@list($item) = array_values($model->find(array($model->escapeField() => $model->id), array($model->primaryKey, $left, $right), null, -1));
|
||||
return $model->findAll(array($right => '< ' . $item[$right], $left => '> ' . $item[$left]));
|
||||
}
|
||||
/**
|
||||
* Get the number of child nodes contained by the current model
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function childcount(&$model) {
|
||||
extract($this->settings[$model->name]);
|
||||
if (!empty($model->data)) {
|
||||
$data = $model->data[$model->name];
|
||||
} else {
|
||||
list($data) = array_values($model->find(array($model->escapeField() => $model->id), null, null, -1));
|
||||
}
|
||||
return ($data[$right] - $data[$left] - 1) / 2;
|
||||
}
|
||||
/**
|
||||
* Get the parent node of the given Aro or Aco
|
||||
*
|
||||
* @param mixed $id
|
||||
* @return array
|
||||
*/
|
||||
function getparentnode(&$model, $id = null) {
|
||||
if (empty($id)) {
|
||||
$id = $model->id;
|
||||
}
|
||||
$path = $this->getPath($model, $id);
|
||||
if ($path == null || count($path) < 2) {
|
||||
return null;
|
||||
} else {
|
||||
return $path[count($path) - 2];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Gets the path to the given Aro or Aco
|
||||
*
|
||||
* @param mixed $id
|
||||
* @return array
|
||||
*/
|
||||
function getpath(&$model, $id) {
|
||||
extract($this->settings[$model->name]);
|
||||
$rec = $model->recursive;
|
||||
$model->recursive = -1;
|
||||
@list($item) = array_values($model->read(null, $id));
|
||||
|
||||
if (empty($item)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$results = $model->findAll(
|
||||
array($model->escapeField($left) => '<= ' . $item[$left], $model->escapeField($right) => '>= ' . $item[$right]),
|
||||
null, array($model->escapeField($left) => 'asc'), null, null, 0
|
||||
);
|
||||
$model->recursive = $rec;
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -1365,8 +1365,10 @@ class DboSource extends DataSource {
|
|||
return $clause . $conditions;
|
||||
} else {
|
||||
$clause = ' WHERE ';
|
||||
$out = $this->conditionKeysToString($conditions);
|
||||
if (empty($out)) {
|
||||
if (!empty($conditions)) {
|
||||
$out = $this->conditionKeysToString($conditions);
|
||||
}
|
||||
if (empty($out) || empty($conditions)) {
|
||||
return $clause . ' 1 = 1';
|
||||
}
|
||||
return $clause . ' (' . join(') AND (', $out) . ')';
|
||||
|
|
Loading…
Add table
Reference in a new issue