aclnode.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: aclnode_8php-source.html 675 2008-12-26 00:27:14Z gwoo $ */
00003 /**
00004  * Short description for file.
00005  *
00006  * Long description for file
00007  *
00008  * PHP versions 4 and 5
00009  *
00010  * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
00011  * Copyright 2005-2008, Cake Software Foundation, Inc.
00012  *                              1785 E. Sahara Avenue, Suite 490-204
00013  *                              Las Vegas, Nevada 89104
00014  *
00015  * Licensed under The MIT License
00016  * Redistributions of files must retain the above copyright notice.
00017  *
00018  * @filesource
00019  * @copyright       Copyright 2005-2008, Cake Software Foundation, Inc.
00020  * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00021  * @package         cake
00022  * @subpackage      cake.cake.libs.controller.components.dbacl.models
00023  * @since           CakePHP(tm) v 0.10.0.1232
00024  * @version         $Revision: 675 $
00025  * @modifiedby      $LastChangedBy: gwoo $
00026  * @lastmodified    $Date: 2008-12-25 16:27:14 -0800 (Thu, 25 Dec 2008) $
00027  * @license         http://www.opensource.org/licenses/mit-license.php The MIT License
00028  */
00029 /**
00030  * Load AppModel class
00031  */
00032 loadModel();
00033 /**
00034  * Short description for file.
00035  *
00036  * Long description for file
00037  *
00038  * @package     cake
00039  * @subpackage  cake.cake.libs.controller.components.dbacl.models
00040  *
00041  */
00042 class AclNode extends AppModel {
00043 /**
00044  * Database configuration to use
00045  *
00046  * @var string
00047  */
00048     var $useDbConfig = ACL_DATABASE;
00049 /**
00050  * Cache Queries
00051  *
00052  * @var boolean
00053  */
00054     var $cacheQueries = false;
00055 /**
00056  * Creates a new ARO/ACO node
00057  *
00058  * @param int $linkId
00059  * @param mixed $parentId
00060  * @param string $alias
00061  * @return boolean True on success, false on fail
00062  * @access public
00063  */
00064     function create($linkId = 0, $parentId = null, $alias = '') {
00065         parent::create();
00066         if (strtolower(get_class($this)) == "aclnode") {
00067             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);
00068             return null;
00069         }
00070         extract ($this->__dataVars());
00071 
00072         if ($parentId == null || $parentId === 0) {
00073             $parent = $this->find(null, 'MAX(rght) as rght', null, -1);
00074             $parent['lft'] = $parent[0]['rght'];
00075 
00076             if ($parent[0]['rght'] == null || !$parent[0]['rght']) {
00077                 $parent['lft'] = 0;
00078             }
00079         } else {
00080             $parent = $this->find($this->_resolveID($parentId), null, null, 0);
00081             if ($parent == null || count($parent) == 0) {
00082                 trigger_error("Null parent in {$class}::create()", E_USER_WARNING);
00083                 return null;
00084             }
00085             $parent = $parent[$class];
00086             $this->_syncTable(1, $parent['lft'], $parent['lft']);
00087         }
00088         $return = $this->save(array($class => array($secondary_id => $linkId,
00089                                                                     'alias' => $alias,
00090                                                                     'lft' => $parent['lft'] + 1,
00091                                                                     'rght' => $parent['lft'] + 2)));
00092         $this->id  = $this->getLastInsertID();
00093         return $return;
00094     }
00095 /**
00096  * Get the ARO/ACO ID with the given alias
00097  *
00098  * @param mixed $alias  The alias of an ARO/ACO node
00099  * @return mixed    The ID of an ARO/ACO node, or false if not found.
00100  * @access public
00101  */
00102     function id($alias) {
00103         extract($this->__dataVars());
00104         return $this->find(array($this->name . '.alias' => $alias), array($secondary_id), null, -1);
00105     }
00106 /**
00107  * Deletes the ARO/ACO node with the given ID
00108  *
00109  * @param mixed $id The id or alias of an ARO/ACO node
00110  * @return boolean True on success, false on fail
00111  * @access public
00112  */
00113     function delete($id) {
00114         extract ($this->__dataVars());
00115         $db =& ConnectionManager::getDataSource($this->useDbConfig);
00116 
00117         $result = $this->find($this->_resolveID($id));
00118         $object = $result[$class];
00119         if ($object == null || count($object) == 0) {
00120             return false;
00121         }
00122 
00123         $children = $this->findAll(array("{$class}.rght" => "< {$result[$class]['rght']}", "{$class}.lft" => "> {$result[$class]['lft']}"), 'id', null, null, null, -1);
00124         $idList = Set::extract($children, '{n}.' . $class . '.id');
00125         $idList[] = $result[$class]['id'];
00126 
00127         $this->ArosAco->query('DELETE FROM ' . $db->fullTableName($this->ArosAco) . " WHERE {$class}_id in (" . implode(', ', $idList) . ')');
00128 
00129         $table = $db->fullTableName($this);
00130         $this->query("DELETE FROM {$table} WHERE {$table}.lft >= {$result[$class]['lft']} AND {$table}.rght <= {$result[$class]['rght']}");
00131 
00132         $shift = 1 + $result[$class]['rght'] - $result[$class]['lft'];
00133         $this->query('UPDATE ' . $table . ' SET ' . $db->name('rght') . ' = ' . $db->name('rght') . ' - ' . $shift . ' WHERE ' . $db->name('rght') . ' > ' . $result[$class]['rght']);
00134         $this->query('UPDATE ' . $table . ' SET ' . $db->name('lft') . ' = ' . $db->name('lft') . ' - ' . $shift . ' WHERE ' . $db->name('lft') . ' > ' . $result[$class]['lft']);
00135         return true;
00136     }
00137 /**
00138  * Sets the parent of the given node
00139  *
00140  * @param mixed $parentId
00141  * @param mixed $id
00142  * @return boolean True on success, false on failure
00143  * @access public
00144  */
00145     function setParent($parentId = null, $id = null) {
00146         if (strtolower(get_class($this)) == "aclnode") {
00147             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);
00148             return null;
00149         }
00150         extract ($this->__dataVars());
00151 
00152         if ($id == null && $this->id == false) {
00153             return false;
00154         } elseif ($id == null) {
00155             $id = $this->id;
00156         }
00157         $object = $this->find($this->_resolveID($id), null, null, 0);
00158 
00159         if ($object == null || count($object) == 0) {
00160             return false;
00161         }
00162         $object = $object[$class];
00163         $parent = $this->getParent($id);
00164 
00165         if (($parent == null && $parentId == null) || ($parentId == $parent[$class][$secondary_id] && $parentId != null) || ($parentId == $parent[$class]['alias'] && $parentId != null)) {
00166             return false;
00167         }
00168 
00169         if ($parentId == null) {
00170             $newParent = $this->find(null, 'MAX(rght) as lft', null, -1);
00171             $newParent = $newParent[0];
00172             $newParent['rght'] = $newParent['lft'];
00173         } else {
00174             $newParent = $this->find($this->_resolveID($parentId), null, null, 0);
00175             $newParent = $newParent[$class];
00176         }
00177 
00178         if ($parentId != null && $newParent['lft'] <= $object['lft'] && $newParent['rght'] >= $object['rght']) {
00179             return false;
00180         }
00181         $this->_syncTable(0, $object['lft'], $object['lft']);
00182 
00183         if ($object['lft'] < $newParent['lft']) {
00184             $newParent['lft'] = $newParent['lft'] - 2;
00185             $newParent['rght'] = $newParent['rght'] - 2;
00186         }
00187 
00188         if ($parentId != null) {
00189             $this->_syncTable(1, $newParent['lft'], $newParent['lft']);
00190         }
00191         $object['lft'] = $newParent['lft'] + 1;
00192         $object['rght'] = $newParent['lft'] + 2;
00193         $this->save(array($class => $object));
00194 
00195         if ($newParent['lft'] == 0) {
00196             $this->_syncTable(2, $newParent['lft'], $newParent['lft']);
00197         }
00198         return true;
00199     }
00200 /**
00201  * Get the parent node of the given Aro or Aco
00202  *
00203  * @param moxed $id
00204  * @return array
00205  * @access public
00206  */
00207     function getParent($id) {
00208         $path = $this->getPath($id);
00209         if ($path == null || count($path) < 2) {
00210             return null;
00211         } else {
00212             return $path[count($path) - 2];
00213         }
00214     }
00215 /**
00216  * Gets the path to the given Aro or Aco
00217  *
00218  * @param mixed $id
00219  * @return array
00220  * @access public
00221  */
00222     function getPath($id) {
00223         if (strtolower(get_class($this)) == "aclnode") {
00224             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);
00225             return null;
00226         }
00227         extract ($this->__dataVars());
00228         $item = $this->find($this->_resolveID($id), null, null, 0);
00229 
00230         if ($item == null || count($item) == 0) {
00231             return null;
00232         }
00233         return $this->findAll(array($class . '.lft' => '<= ' . $item[$class]['lft'], $class . '.rght' => '>= ' . $item[$class]['rght']), null, array($class . '.lft' => 'ASC'), null, null, 0);
00234     }
00235 /**
00236  * Get the child nodes of the given Aro or Aco
00237  *
00238  * @param mixed $id
00239  * @return array
00240  * @access public
00241  */
00242     function getChildren($id) {
00243         if (strtolower(get_class($this)) == "aclnode") {
00244             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);
00245             return null;
00246         }
00247 
00248         extract ($this->__dataVars());
00249         $item = $this->find($this->_resolveID($id), null, null, 0);
00250         return $this->findAll(array($class . '.lft' => '> ' . $item[$class]['lft'], $class . '.rght' => '< ' . $item[$class]['rght']), null, null, null, null, null, 0);
00251     }
00252 /**
00253  * Gets a conditions array to find an Aro or Aco, based on the given id or alias
00254  *
00255  * @param mixed $id
00256  * @return array Conditions array for a find/findAll call
00257  * @access public
00258  */
00259     function _resolveID($id) {
00260         extract($this->__dataVars());
00261         $key = (is_numeric($id) ? $secondary_id : 'alias');
00262         return array($this->name . '.' . $key => $id);
00263     }
00264 /**
00265  * Shifts the left and right values of the aro/aco tables
00266  * when a node is added or removed
00267  *
00268  * @param unknown_type $dir
00269  * @param unknown_type $lft
00270  * @param unknown_type $rght
00271  * @access protected
00272  */
00273     function _syncTable($dir, $lft, $rght) {
00274         $db =& ConnectionManager::getDataSource($this->useDbConfig);
00275 
00276         if ($dir == 2) {
00277             $shift = 1;
00278             $dir = '+';
00279         } else {
00280             $shift = 2;
00281 
00282             if ($dir > 0) {
00283                 $dir = '+';
00284             } else {
00285                 $dir = '-';
00286             }
00287         }
00288         $db->query('UPDATE ' . $db->fullTableName($this) . ' SET ' . $db->name('rght') . ' = ' . $db->name('rght') . ' ' . $dir . ' ' . $shift . ' WHERE ' . $db->name('rght') . ' > ' . $rght);
00289         $db->query('UPDATE ' . $db->fullTableName($this) . ' SET ' . $db->name('lft') . ' = ' . $db->name('lft') . '  ' . $dir . ' ' . $shift . ' WHERE ' . $db->name('lft') . '  > ' . $lft);
00290     }
00291 /**
00292  * Infers data based on the currently-intantiated subclass.
00293  *
00294  * @return array
00295  * @access private
00296  */
00297     function __dataVars() {
00298         $vars = array();
00299         $class = strtolower(get_class($this));
00300         if ($class == 'aro') {
00301             $vars['secondary_id'] = 'foreign_key';
00302         } else {
00303             $vars['secondary_id'] = 'object_id';
00304         }
00305         $vars['class'] = ucwords($class);
00306         return $vars;
00307     }
00308 }
00309 ?>