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:
nate 2007-02-09 08:13:13 +00:00
parent ce71e04618
commit 30134cbd9e
8 changed files with 315 additions and 330 deletions

View file

@ -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;

View file

@ -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');
}
?>

View file

@ -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'));
}
?>

View file

@ -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'));
}
?>

View file

@ -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';
}
?>

View 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']);
}
}
?>

View 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;
}
}
?>

View file

@ -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) . ')';