server_id = $server_id; } /** * Create an instance of the tree - this is used when we call this class directly * Tree::getInstance($index) * * @param $server_id * @return object Tree */ public static function getInstance($server_id) { if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) { debug_log('Entered (%%)', 33, 0, __FILE__, __LINE__, __METHOD__, $fargs); } $tree = get_cached_item($server_id,'tree'); if (! $tree) { $server = $_SESSION[APPCONFIG]->getServer($server_id); if (! $server) { return NULL; } $treeclass = $_SESSION[APPCONFIG]->getValue('appearance','tree'); $tree = new $treeclass($server_id); # If we are not logged in, just return the empty tree. if (is_null($server->getLogin(null))) { return $tree; } foreach ($server->getBaseDN(null) as $base) { if ($base) { $tree->addEntry($base); if ($server->getValue('appearance','open_tree')) { $baseEntry = $tree->getEntry($base); $baseEntry->open(); } } } set_cached_item($server_id,'tree','null',$tree); } return $tree; } /** * Get the Server ID for this tree * * @return int Server ID that this tree is for */ protected function getServerID() { if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) { debug_log('Entered (%%)', 33, 1, __FILE__, __LINE__, __METHOD__, $fargs, $this->server_id); } return $this->server_id; } /** * Get the server Object for this tree * * @return object Server Object for this tree */ protected function getServer() { if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) { debug_log('Entered (%%)', 33, 0, __FILE__, __LINE__, __METHOD__, $fargs); } return $_SESSION[APPCONFIG]->getServer($this->server_id); } /** * Get the entries that are BaseDN entries. * * @return array Base DN entries */ public function getBaseEntries() { if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) { debug_log('Entered (%%)', 33, 0, __FILE__, __LINE__, __METHOD__, $fargs); } $return = array(); foreach ($this->entries as $details) { if ($details->isBaseDN() AND (( ! $this->getServer()->getValue('server', 'hide_noaccess_base')) OR $details->isInLdap())) { $return[] = $details; } } return $return; } /** * This function will take the DN, convert it to lowercase and strip unnessary * commas. This result will be used as the index for the tree object. * Any display of a DN should use the object->dn entry, not the index. * The reason we need to do this is because: * uid=User A,ou=People,c=AU and * uid=User B, ou=PeOpLe, c=au * are infact in the same branch, but PLA will show them inconsistently. * * @param dn DN to clean * @return dn Lowercase clean DN */ private function indexDN($dn) { if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) { debug_log('Entered (%%)', 33, 0, __FILE__, __LINE__, __METHOD__, $fargs); } $index = strtolower(implode(',',pla_explode_dn($dn))); if (DEBUG_ENABLED) { debug_log('Result (%s)', 1, 0, __FILE__, __LINE__, __METHOD__, $index); } return $index; } /** * Get a tree entry * * @param dn DN to retrieve * @return object Tree DN object */ public function getEntry($dn) { if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) { debug_log('Entered (%%)', 33, 0, __FILE__, __LINE__, __METHOD__, $fargs); } $dnlower = $this->indexDN($dn); if (isset($this->entries[$dnlower])) { return $this->entries[$dnlower]; } else { return NULL; } } /** * Add an entry in the tree view ; the entry is added in the * children array of its parent * * @param string $dn the dn of the entry to create */ public function addEntry($dn) { if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) { debug_log('Entered (%%)', 33, 0, __FILE__, __LINE__, __METHOD__, $fargs); } $server = $this->getServer(); $dnlower = $this->indexDN($dn); # @todo Temporarily removed, some non-ascii char DNs that do exist, fail here for some reason? #if (! ($server->dnExists($dn))) # return; if (isset($this->entries[$dnlower])) { debug_dump_backtrace('Calling add entry to an entry that ALREADY exists?', 1); } if (DEBUG_ENABLED) { debug_log('New ENTRY (%s).', 64, 0, __FILE__, __LINE__, __METHOD__, $dn); } $tree_factory = new TreeItem($server->getIndex(),$dn); $tree_factory->setObjectClasses($server->getDNAttrValue($dn,'objectClass')); if ((($isleaf = $server->getDNAttrValue($dn,'hassubordinates')) && ! strcasecmp($isleaf[0],'false'))) { $tree_factory->setLeaf(); } $this->entries[$dnlower] = $tree_factory; # Is this entry in a base entry? if (in_array_ignore_case($dn,$server->getBaseDN(null))) { $this->entries[$dnlower]->setBase(); # If the parent entry is not in the tree, we add it. This routine will in itself # recall this method until we get to the top of the tree (the base). } else { $parent_dn = $server->getContainer($dn); if (DEBUG_ENABLED) { debug_log('Parent DNs (%s)', 64, 0, __FILE__, __LINE__, __METHOD__, $parent_dn); } if ($parent_dn) { $parent_entry = $this->getEntry($parent_dn); if (! $parent_entry) { $this->addEntry($parent_dn); $parent_entry = $this->getEntry($parent_dn); } # Update this DN's parent's children list as well. $parent_entry->addChild($dn); } } } /** * Delete an entry from the tree view ; the entry is deleted from the * children array of its parent * * @param dn DN to remote */ public function delEntry($dn) { if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) { debug_log('Entered (%%)', 33, 0, __FILE__, __LINE__, __METHOD__, $fargs); } $server = $this->getServer(); $dnlower = $this->indexDN($dn); if (isset($this->entries[$dnlower])) { unset($this->entries[$dnlower]); } # Delete entry from parent's children as well. $parent_dn = $server->getContainer($dn); $parent_entry = $this->getEntry($parent_dn); if ($parent_entry) { $parent_entry->delChild($dn); } } /** * Rename an entry in the tree * * @param dn Old DN * @param dn New DN */ public function renameEntry($dnOLD,$dnNEW) { if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) { debug_log('Entered (%%)', 33, 0, __FILE__, __LINE__, __METHOD__, $fargs); } $server = $this->getServer(); $dnlowerOLD = $this->indexDN($dnOLD); $dnlowerNEW = $this->indexDN($dnNEW); $this->entries[$dnlowerNEW] = $this->entries[$dnlowerOLD]; if ($dnlowerOLD != $dnlowerNEW) { unset($this->entries[$dnlowerOLD]); } $this->entries[$dnlowerNEW]->rename($dnNEW); # Update the parent's children $parentNEW = $server->getContainer($dnNEW); $parentOLD = $server->getContainer($dnOLD); $parent_entry = $this->getEntry($parentNEW); if ($parent_entry) { $parent_entry->addChild($dnNEW); } $parent_entry = $this->getEntry($parentOLD); if ($parent_entry) { $parent_entry->delChild($dnOLD); } } /** * Read the children of a tree entry * * @param dn DN of the entry * @param boolean LDAP Size Limit */ public function readChildren($dn,$nolimit=false) { if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) { debug_log('Entered (%%)', 33, 0, __FILE__, __LINE__, __METHOD__, $fargs); } $server = $this->getServer(); $dnlower = $this->indexDN($dn); if (! isset($this->entries[$dnlower])) { debug_dump_backtrace('Reading children on an entry that isnt set? ' . $dnlower, TRUE); } $ldap['child_limit'] = $nolimit ? 0 : $_SESSION[APPCONFIG]->getValue('search','size_limit'); $ldap['filter'] = $_SESSION[APPCONFIG]->getValue('appearance','tree_filter'); $ldap['deref'] = $_SESSION[APPCONFIG]->getValue('deref','tree'); # Perform the query to get the children. $ldap['children'] = $server->getContainerContents($dn,null,$ldap['child_limit'],$ldap['filter'],$ldap['deref']); if (! count($ldap['children'])) { $this->entries[$dnlower]->unsetSizeLimited(); return; } if (DEBUG_ENABLED) { debug_log('Children of (%s) are (%s)', 64, 0, __FILE__, __LINE__, __METHOD__, $dn, $ldap['children']); } # Relax our execution time, it might take some time to load this if ($nolimit) { @set_time_limit($_SESSION[APPCONFIG]->getValue('search', 'time_limit')); } $this->entries[$dnlower]->readingChildren(true); foreach ($ldap['children'] as $child) { if (DEBUG_ENABLED) { debug_log('Adding (%s)', 64, 0, __FILE__, __LINE__, __METHOD__, $child); } if (! in_array($child,$this->entries[$dnlower]->getChildren())) { $this->entries[$dnlower]->addChild($child); } } $this->entries[$dnlower]->readingChildren(false); if (count($this->entries[$dnlower]->getChildren()) == $ldap['child_limit']) { $this->entries[$dnlower]->setSizeLimited(); } else { $this->entries[$dnlower]->unsetSizeLimited(); } } /** * Return the number of children an entry has. Optionally autoread the child entry. * * @param dn DN of the entry * @param boolean LDAP Size Limit */ protected function readChildrenNumber($dn,$nolimit=false) { if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) { debug_log('Entered (%%)', 33, 0, __FILE__, __LINE__, __METHOD__, $fargs); } $dnlower = $this->indexDN($dn); if (! isset($this->entries[$dnlower])) { debug_dump_backtrace('Reading children on an entry that isnt set?', TRUE); } # Read the entry if we havent got it yet. if (! $this->entries[$dnlower]->isLeaf() && ! $this->entries[$dnlower]->getChildren()) { $this->readChildren($dn, $nolimit); } return count($this->entries[$dnlower]->getChildren()); } }