1<?php 2/** 3 * Classes and functions for the template engine. 4 * 5 * Templates are either: 6 * + Creating or Editing, (a Container or DN passed to the object) 7 * + A predefined template, or a default template (template ID passed to the object) 8 * 9 * The template object will know which attributes are mandatory (MUST 10 * attributes) and which attributes are optional (MAY attributes). It will also 11 * contain a list of optional attributes. These are attributes that the schema 12 * will allow data for (they are MAY attributes), but the template has not 13 * included a definition for them. 14 * 15 * The template object will be invalidated if it does contain the necessary 16 * items (objectClass, MUST attributes, etc) to make a successful LDAP update. 17 * 18 * @author The phpLDAPadmin development team 19 * @package phpLDAPadmin 20 */ 21 22/** 23 * Template Class 24 * 25 * @package phpLDAPadmin 26 * @subpackage Templates 27 * @todo RDN attributes should be treated as MUST attributes even though the schema marks them as MAY 28 * @todo RDN attributes need to be checked that are included in the schema, otherwise mark it is invalid 29 * @todo askcontainer is no longer used? 30 */ 31class Template extends xmlTemplate { 32 # If this template visible on the template choice list 33 private $visible = true; 34 # Is this template valid after parsing the XML file 35 private $invalid = false; 36 private $invalid_admin = false; 37 private $invalid_reason; 38 # The TEMPLATE structural objectclasses 39 protected $structural_oclass = array(); 40 protected $description = ''; 41 # Is this a read-only template (only valid in modification templates) 42 private $readonly = false; 43 44 # If this is set, it means we are editing an entry. 45 private $dn; 46 # Where this template will store its data 47 protected $container; 48 # Does this template prohibit children being created 49 private $noleaf = false; 50 # A regexp that determines if this template is valid in the container. 51 private $regexp; 52 # Template Title 53 public $title; 54 # Icon for the template 55 private $icon; 56 # Template RDN attributes 57 private $rdn; 58 59 public function __construct($server_id,$name=null,$filename=null,$type=null,$id=null) { 60 parent::__construct($server_id,$name,$filename,$type,$id); 61 62 # If this is the default template, we might disable leafs by default. 63 if (is_null($filename)) 64 $this->noleaf = $_SESSION[APPCONFIG]->getValue('appearance','disable_default_leaf'); 65 } 66 67 public function __clone() { 68 # We need to clone our attributes, when passing back a template with getTemplate 69 foreach ($this->attributes as $key => $value) 70 $this->attributes[$key] = clone $value; 71 } 72 73 /** 74 * Main processing to store the template. 75 * 76 * @param xmldata Parsed xmldata from xml2array object 77 */ 78 protected function storeTemplate($xmldata) { 79 $server = $this->getServer(); 80 $objectclasses = array(); 81 82 foreach ($xmldata['template'] as $xml_key => $xml_value) { 83 switch ($xml_key) { 84 # Build our object Classes from the DN and Template. 85 case ('objectclasses'): 86 if (isset($xmldata['template'][$xml_key]['objectclass'])) 87 if (is_array($xmldata['template'][$xml_key]['objectclass'])) { 88 foreach ($xmldata['template'][$xml_key]['objectclass'] as $index => $details) { 89 90 # XML files with only 1 objectClass dont have a numeric index. 91 $soc = $server->getSchemaObjectClass(strtolower($details)); 92 93 # If we havent recorded this objectclass already, do so now. 94 if (is_object($soc) && ! in_array($soc->getName(),$objectclasses)) 95 array_push($objectclasses,$soc->getName(false)); 96 97 elseif (! is_object($soc) && ! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning')) 98 system_message(array( 99 'title'=>('Automatically removed objectClass from template'), 100 'body'=>sprintf('%s: <b>%s</b> %s',$this->getTitle(),$details,('removed from template as it is not defined in the schema')), 101 'type'=>'warn')); 102 } 103 104 } else { 105 # XML files with only 1 objectClass dont have a numeric index. 106 $soc = $server->getSchemaObjectClass(strtolower($xmldata['template'][$xml_key]['objectclass'])); 107 108 # If we havent recorded this objectclass already, do so now. 109 if (is_object($soc) && ! in_array($soc->getName(),$objectclasses)) 110 array_push($objectclasses,$soc->getName(false)); 111 } 112 113 break; 114 115 # Build our attribute list from the DN and Template. 116 case ('attributes'): 117 if (is_array($xmldata['template'][$xml_key])) { 118 foreach ($xmldata['template'][$xml_key] as $tattrs) 119 foreach ($tattrs as $index => $details) { 120 # If there is no schema definition for the attribute, it will be ignored. 121 if ($sattr = $server->getSchemaAttribute($index)) 122 if (is_null($this->getAttribute($sattr->getName()))) 123 $this->addAttribute($sattr->getName(),$details,'XML'); 124 } 125 126 masort($this->attributes,'order'); 127 } 128 129 break; 130 131 default: 132 # Some key definitions need to be an array, some must not be: 133 $allowed_arrays = array('rdn'); 134 $storelower = array('rdn'); 135 $storearray = array('rdn'); 136 137 # Items that must be stored lowercase 138 if (in_array($xml_key,$storelower)) 139 if (is_array($xml_value)) 140 foreach ($xml_value as $index => $value) 141 $xml_value[$index] = strtolower($value); 142 else 143 $xml_value = strtolower($xml_value); 144 145 # Items that must be stored as arrays 146 if (in_array($xml_key,$storearray) && ! is_array($xml_value)) 147 $xml_value = array($xml_value); 148 149 # Items that should not be an array 150 if (! in_array($xml_key,$allowed_arrays) && is_array($xml_value)) { 151 debug_dump(array(__METHOD__,'key'=>$xml_key,'value'=>$xml_value)); 152 error(sprintf(('In the XML file (%s), [%s] is an array, it must be a string.'), 153 $this->filename,$xml_key),'error'); 154 } 155 156 $this->$xml_key = $xml_value; 157 158 if ($xml_key == 'invalid' && $xml_value) 159 $this->setInvalid(('Disabled by XML configuration'),true); 160 } 161 } 162 163 if (! count($objectclasses)) { 164 $this->setInvalid(('ObjectClasses in XML dont exist in LDAP server.')); 165 return; 166 167 } else { 168 $attribute = $this->addAttribute('objectClass',array('values'=>$objectclasses),'XML'); 169 $attribute->justModified(); 170 $attribute->setRequired(); 171 $attribute->hide(); 172 } 173 174 $this->rebuildTemplateAttrs(); 175 176 # Check we have some manditory items. 177 foreach (array('rdn','structural_oclass','visible') as $key) { 178 if (! isset($this->$key) 179 || (! is_array($this->$key) && ! trim($this->$key))) { 180 181 $this->setInvalid(sprintf(('Missing %s in the XML file.'),$key)); 182 break; 183 } 184 } 185 186 # Mark our RDN attributes as RDN 187 $counter = 1; 188 foreach ($this->rdn as $key) { 189 if ((is_null($attribute = $this->getAttribute($key))) && (in_array_ignore_case('extensibleobject',$this->getObjectClasses()))) { 190 $attribute = $this->addAttribute($key,array('values'=>array())); 191 $attribute->show(); 192 } 193 194 if (! is_null($attribute)) 195 $attribute->setRDN($counter++); 196 elseif ($this->isType('creation')) 197 $this->setInvalid(sprintf(('Missing RDN attribute %s in the XML file.'),$key)); 198 } 199 } 200 201 /** 202 * Is default templates enabled? 203 * This will disable the default template from the engine. 204 * 205 * @return boolean 206 */ 207 protected function hasDefaultTemplate() { 208 if ($_SESSION[APPCONFIG]->getValue('appearance','disable_default_template')) 209 return false; 210 else 211 return true; 212 } 213 214 /** 215 * Return the templates of type (creation/modification) 216 * 217 * @param $string type - creation/modification 218 * @return array - Array of templates of that type 219 */ 220 protected function readTemplates($type) { 221 $template_xml = new Templates($this->server_id); 222 return $template_xml->getTemplates($type); 223 } 224 225 /** 226 * This function will perform the following intialisation steps: 227 * + If a DN is set, query the ldap and load the object 228 * + Read our $_REQUEST variable and set the values 229 * After this action, the template should self describe as to whether it is an update, create 230 * or delete. 231 * (OLD values are IGNORED, we will have got them when we build this object from the LDAP server DN.) 232 */ 233 public function accept($makeVisible=false) { 234 $server = $this->getServer(); 235 236 # If a DN is set, then query the LDAP server for the details. 237 if ($this->dn) { 238 if (! $server->dnExists($this->dn)) 239 system_message(array( 240 'title'=>__METHOD__, 241 'body'=>sprintf('DN (%s) didnt exist in LDAP?',$this->dn), 242 'type'=>'info')); 243 244 $rdnarray = rdn_explode(strtolower(get_rdn(dn_escape($this->dn)))); 245 246 $counter = 1; 247 foreach ($server->getDNAttrValues($this->dn,null,LDAP_DEREF_NEVER,array_merge(array('*'),$server->getValue('server','custom_attrs'))) as $attr => $values) { 248 # We ignore DNs. 249 if ($attr == 'dn') 250 continue; 251 252 $attribute = $this->getAttribute($attr); 253 254 if (is_null($attribute)) 255 $attribute = $this->addAttribute($attr,array('values'=>$values)); 256 else 257 if ($attribute->getValues()) { 258 # Override values to those that are defined in the XML file. 259 if ($attribute->getSource() != 'XML') 260 $attribute->setValue(array_values($values)); 261 else 262 $attribute->setOldValue(array_values($values)); 263 264 } else 265 $attribute->initValue(array_values($values)); 266 267 # Work out the RDN attributes 268 foreach ($attribute->getValues() as $index => $value) 269 if (in_array(sprintf('%s=%s', 270 $attribute->getName(),strtolower($attribute->getValue($index))),$rdnarray)) 271 $attribute->setRDN($counter++); 272 273 if ($makeVisible) 274 $attribute->show(); 275 } 276 277 # Get the Internal Attributes 278 foreach ($server->getDNAttrValues($this->dn,null,LDAP_DEREF_NEVER,array_merge(array('+'),$server->getValue('server','custom_sys_attrs'))) as $attr => $values) { 279 $attribute = $this->getAttribute($attr); 280 281 if (is_null($attribute)) 282 $attribute = $this->addAttribute($attr,array('values'=>$values)); 283 else 284 if ($attribute->getValues()) 285 $attribute->setValue(array_values($values)); 286 else 287 $attribute->initValue(array_values($values)); 288 289 if (! in_array_ignore_case($attribute->getName(),$server->getValue('server','custom_attrs'))) 290 $attribute->setInternal(); 291 } 292 293 # If this is the default template, and our $_REQUEST has defined our objectclass, then query the schema to get the attributes 294 } elseif ($this->container) { 295 if ($this->isType('default') && ! count($this->getAttributes(true)) && isset($_REQUEST['new_values']['objectclass'])) { 296 $attribute = $this->addAttribute('objectclass',array('values'=>$_REQUEST['new_values']['objectclass'])); 297 $attribute->justModified(); 298 $this->rebuildTemplateAttrs(); 299 unset($_REQUEST['new_values']['objectclass']); 300 } 301 302 } elseif (get_request('create_base')) { 303 if (get_request('rdn')) { 304 $rdn = explode('=',get_request('rdn')); 305 $attribute = $this->addAttribute($rdn[0],array('values'=>array($rdn[1]))); 306 $attribute->setRDN(1); 307 } 308 309 } else { 310 debug_dump_backtrace('No DN or CONTAINER?',1); 311 } 312 313 # Read in our new values. 314 foreach (array('new_values') as $key) { 315 if (isset($_REQUEST[$key])) 316 foreach ($_REQUEST[$key] as $attr => $values) { 317 # If it isnt an array, silently ignore it. 318 if (! is_array($values)) 319 continue; 320 321 # If _REQUEST['skip_array'] with this attr set, we'll ignore this new_value 322 if (isset($_REQUEST['skip_array'][$attr]) && $_REQUEST['skip_array'][$attr] == 'on') 323 continue; 324 325 # Prune out entries with a blank value. 326 foreach ($values as $index => $value) 327 if (! strlen(trim($value))) 328 unset($values[$index]); 329 330 $attribute = $this->getAttribute($attr); 331 # If the attribute is null, then no attribute exists, silently ignore it (unless this is the default template) 332 if (is_null($attribute) && (! $this->isType('default') && ! $this->isType(null))) 333 continue; 334 335 # If it is a binary attribute, the post should have base64 encoded the value, we'll need to reverse that 336 if ($server->isAttrBinary($attr)) 337 foreach ($values as $index => $value) 338 $values[$index] = base64_decode($value); 339 340 if (is_null($attribute)) { 341 $attribute = $this->addAttribute($attr,array('values'=>$values)); 342 343 if (count($values)) 344 $attribute->justModified(); 345 346 } else 347 $attribute->setValue(array_values($values)); 348 } 349 350 # Read in our new binary values 351 if (isset($_FILES[$key]['name'])) 352 foreach ($_FILES[$key]['name'] as $attr => $values) { 353 $new_values = array(); 354 355 foreach ($values as $index => $details) { 356 # Ignore empty files 357 if (! $_FILES[$key]['size'][$attr][$index]) 358 continue; 359 360 if (! is_uploaded_file($_FILES[$key]['tmp_name'][$attr][$index])) { 361 if (isset($_FILES[$key]['error'][$attr][$index])) 362 switch($_FILES[$key]['error'][$attr][$index]) { 363 364 # No error; possible file attack! 365 case 0: 366 $msg = _('Security error: The file being uploaded may be malicious.'); 367 break; 368 369 # Uploaded file exceeds the upload_max_filesize directive in php.ini 370 case 1: 371 $msg = _('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); 372 break; 373 374 # Uploaded file exceeds the MAX_FILE_SIZE directive specified in the html form 375 case 2: 376 $msg = _('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); 377 break; 378 379 # Uploaded file was only partially uploaded 380 case 3: 381 $msg = _('The file you selected was only partially uploaded, likley due to a network error.'); 382 break; 383 384 # No file was uploaded 385 case 4: 386 $msg = _('You left the attribute value blank. Please go back and try again.'); 387 break; 388 389 # A default error, just in case! :) 390 default: 391 $msg = _('Security error: The file being uploaded may be malicious.'); 392 break; 393 } 394 395 else 396 $msg = _('Security error: The file being uploaded may be malicious.'); 397 398 system_message(array( 399 'title'=>_('Error'),'body'=>$msg,'type'=>'warn')); 400 401 } else { 402 $binaryfile = array(); 403 $binaryfile['name'] = $_FILES[$key]['tmp_name'][$attr][$index]; 404 $binaryfile['handle'] = fopen($binaryfile['name'],'r'); 405 $binaryfile['data'] = fread($binaryfile['handle'],filesize($binaryfile['name'])); 406 fclose($binaryfile['handle']); 407 408 $new_values[$index] = $binaryfile['data']; 409 } 410 } 411 412 if (count($new_values)) { 413 $attribute = $this->getAttribute($attr); 414 415 if (is_null($attribute)) 416 $attribute = $this->addAttribute($attr,array('values'=>$new_values)); 417 else 418 foreach ($new_values as $value) 419 $attribute->addValue($value); 420 421 $attribute->justModified(); 422 } 423 } 424 } 425 426 # If there are any single item additions (from the add_attr form for example) 427 if (isset($_REQUEST['single_item_attr'])) { 428 if (isset($_REQUEST['single_item_value'])) { 429 if (! is_array($_REQUEST['single_item_value'])) 430 $values = array($_REQUEST['single_item_value']); 431 else 432 $values = $_REQUEST['single_item_value']; 433 434 } elseif (isset($_REQUEST['binary'])) { 435 /* Special case for binary attributes (like jpegPhoto and userCertificate): 436 * we must go read the data from the file and override $_REQUEST['single_item_value'] with the 437 * binary data. Secondly, we must check if the ";binary" option has to be appended to the name 438 * of the attribute. */ 439 440 if ($_FILES['single_item_value']['size'] === 0) 441 system_message(array( 442 'title'=>_('Error'), 443 'body'=>sprintf('%s %s',_('The file you chose is either empty or does not exist.'),_('Please go back and try again.')), 444 'type'=>'warn')); 445 446 else { 447 if (! is_uploaded_file($_FILES['single_item_value']['tmp_name'])) { 448 if (isset($_FILES['single_item_value']['error'])) 449 switch($_FILES['single_item_value']['error']) { 450 451 # No error; possible file attack! 452 case 0: 453 $msg = _('Security error: The file being uploaded may be malicious.'); 454 break; 455 456 # Uploaded file exceeds the upload_max_filesize directive in php.ini 457 case 1: 458 $msg = _('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); 459 break; 460 461 # Uploaded file exceeds the MAX_FILE_SIZE directive specified in the html form 462 case 2: 463 $msg = _('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); 464 break; 465 466 # Uploaded file was only partially uploaded 467 case 3: 468 $msg = _('The file you selected was only partially uploaded, likley due to a network error.'); 469 break; 470 471 # No file was uploaded 472 case 4: 473 $msg = _('You left the attribute value blank. Please go back and try again.'); 474 break; 475 476 # A default error, just in case! :) 477 default: 478 $msg = _('Security error: The file being uploaded may be malicious.'); 479 break; 480 } 481 482 else 483 $msg = _('Security error: The file being uploaded may be malicious.'); 484 485 system_message(array( 486 'title'=>_('Error'),'body'=>$msg,'type'=>'warn'),'index.php'); 487 } 488 489 $binaryfile = array(); 490 $binaryfile['name'] = $_FILES['single_item_value']['tmp_name']; 491 $binaryfile['handle'] = fopen($binaryfile['name'],'r'); 492 $binaryfile['data'] = fread($binaryfile['handle'],filesize($binaryfile['name'])); 493 fclose($binaryfile['handle']); 494 495 $values = array($binaryfile['data']); 496 } 497 } 498 499 if (count($values)) { 500 $attribute = $this->getAttribute($_REQUEST['single_item_attr']); 501 502 if (is_null($attribute)) 503 $attribute = $this->addAttribute($_REQUEST['single_item_attr'],array('values'=>$values)); 504 else 505 $attribute->setValue(array_values($values)); 506 507 $attribute->justModified(); 508 } 509 } 510 511 # If this is the default creation template, we need to set some additional values 512 if ($this->isType('default') && $this->getContext() == 'create') { 513 # Load our schema, based on the objectclasses that may have already been defined. 514 if (! get_request('create_base')) 515 $this->rebuildTemplateAttrs(); 516 517 # Set the RDN attribute 518 $counter = 1; 519 foreach (get_request('rdn_attribute','REQUEST',false,array()) as $key => $value) { 520 $attribute = $this->getAttribute($value); 521 522 if (! is_null($attribute)) 523 $attribute->setRDN($counter++); 524 525 else { 526 system_message(array( 527 'title'=>_('No RDN attribute was selected.'), 528 'body'=>_('No RDN attribute was selected.'), 529 'type'=>'warn'),'index.php'); 530 531 die(); 532 } 533 } 534 } 535 } 536 537 /** 538 * Set the DN for this template, if we are editing entries 539 * 540 * @param dn The DN of the entry 541 */ 542 public function setDN($dn) { 543 if (isset($this->container)) 544 system_message(array( 545 'title'=>__METHOD__, 546 'body'=>'CONTAINER set while setting DN', 547 'type'=>'info')); 548 549 $this->dn = $dn; 550 } 551 552 /** 553 * Set the RDN attributes 554 * Given an RDN, mark the attributes as RDN attributes. If there is no defined attribute, 555 * then the remaining RDNs will be returned. 556 * 557 * @param RDN 558 * @return RDN attributes not processed 559 */ 560 public function setRDNAttributes($rdn) { 561 # Setup to work out our RDN. 562 $rdnarray = rdn_explode($rdn); 563 564 $counter = 1; 565 foreach ($this->getAttributes(true) as $attribute) 566 foreach ($rdnarray as $index => $rdnattr) { 567 list($attr,$value) = explode('=',$rdnattr); 568 569 if (strtolower($attr) == $attribute->getName()) { 570 $attribute->setRDN($counter++); 571 unset($rdnarray[$index]); 572 } 573 } 574 575 return $rdnarray; 576 } 577 578 /** 579 * Display the DN for this template entry. If the DN is not set (creating a new entry), then 580 * a generated DN will be produced, taken from the RDN and the CONTAINER details. 581 * 582 * @return dn 583 */ 584 public function getDN() { 585 if ($this->dn) 586 return $this->dn; 587 588 # If DN is not set, our DN will be made from our RDN and Container. 589 elseif ($this->getRDN() && $this->getContainer()) 590 return sprintf('%s,%s',$this->getRDN(),$this->GetContainer()); 591 592 # If container is not set, we're probably creating the base 593 elseif ($this->getRDN() && get_request('create_base')) 594 return $this->getRDN(); 595 } 596 597 public function getDNEncode($url=true) { 598 // @todo Be nice to do all this in 1 location 599 if ($url) 600 return urlencode(preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->getDN())); 601 else 602 return preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->getDN()); 603 } 604 605 /** 606 * Set the container for this template, if we are creating entries 607 * 608 * @param dn The DN of the container 609 * @todo Trigger a query to the LDAP server and generate an error if the container doesnt exist 610 */ 611 public function setContainer($container) { 612 if (isset($this->dn)) 613 system_message(array( 614 'title'=>__METHOD__, 615 'body'=>'DN set while setting CONTAINER', 616 'type'=>'info')); 617 618 $this->container = $container; 619 } 620 621 /** 622 * Get the DN of the container for this entry 623 * 624 * @return dn DN of the container 625 */ 626 public function getContainer() { 627 return $this->container; 628 } 629 630 public function getContainerEncode($url=true) { 631 // @todo Be nice to do all this in 1 location 632 if ($url) 633 return urlencode(preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->container)); 634 else 635 return preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->container); 636 } 637 638 /** 639 * Copy a DN 640 */ 641 public function copy($template,$rdn,$asnew=false) { 642 $rdnarray = rdn_explode($rdn); 643 644 $counter = 1; 645 foreach ($template->getAttributes(true) as $sattribute) { 646 $attribute = $this->addAttribute($sattribute->getName(false),array('values'=>$sattribute->getValues())); 647 648 # Set our new RDN, and its values 649 if (is_null($attribute)) { 650 debug_dump_backtrace('Attribute is null, it probably doesnt exist in the destination server?'); 651 652 } else { 653 654 # Mark our internal attributes. 655 if ($sattribute->isInternal()) 656 $attribute->setInternal(); 657 658 $modified = false; 659 foreach ($rdnarray as $index => $rdnattr) { 660 list($attr,$value) = explode('=',$rdnattr); 661 if (strtolower($attr) == $attribute->getName()) { 662 663 # If this is already marked as an RDN, then this multivalue RDN was updated on a previous loop 664 if (! $modified) { 665 $attribute->setValue(array($value)); 666 $attribute->setRDN($counter++); 667 $modified = true; 668 669 } else { 670 $attribute->addValue($value); 671 } 672 673 # This attribute has been taken care of, we'll drop it from our list. 674 unset($rdnarray[$index]); 675 } 676 } 677 } 678 679 // @todo If this is a Jpeg Attribute, we need to mark it read only, since it cant be deleted like text attributes can 680 if (strcasecmp(get_class($attribute),'jpegAttribute') == 0) 681 $attribute->setReadOnly(); 682 } 683 684 # If we have any RDN values left over, there werent in the original entry and need to be added. 685 foreach ($rdnarray as $rdnattr) { 686 list($attr,$value) = explode('=',$rdnattr); 687 688 $attribute = $this->addAttribute($attr,array('values'=>array($value))); 689 690 if (is_null($attribute)) 691 debug_dump_backtrace('Attribute is null, it probably doesnt exist in the destination server?'); 692 else 693 $attribute->setRDN($counter++); 694 } 695 696 # If we are copying into a new entry, we need to discard all the "old values" 697 if ($asnew) 698 foreach ($this->getAttributes(true) as $sattribute) 699 $sattribute->setOldValue(array()); 700 } 701 702 /** 703 * Get Attributes by LDAP type 704 * This function will return a list of attributes by LDAP type (MUST,MAY). 705 * 706 * @return array Array of attributes. 707 */ 708 function getAttrbyLdapType($type) { 709 $result = array(); 710 711 foreach ($this->attributes as $index => $attribute) { 712 if ($attribute->getLDAPtype() == strtolower($type)) 713 array_push($result,$attribute->getName()); 714 } 715 716 return $result; 717 } 718 719 /** 720 * Return true if this is a MUST,MAY attribute 721 */ 722 function isAttrType($attr,$type) { 723 if (in_array(strtolower($attr),$this->getAttrbyLdapType($type))) 724 return true; 725 else 726 return false; 727 } 728 729 /** 730 * Return the attributes that comprise the RDN. 731 * 732 * @return array Array of RDN objects 733 */ 734 private function getRDNObjects() { 735 $return = array(); 736 737 foreach ($this->attributes as $attribute) 738 if ($attribute->isRDN()) 739 array_push($return,$attribute); 740 741 masort($return,'rdn'); 742 return $return; 743 } 744 745 /** 746 * Get all the RDNs for this template, in RDN order. 747 * 748 * @return array RDNs in order. 749 */ 750 public function getRDNAttrs() { 751 $return = array(); 752 753 foreach ($this->getRDNObjects() as $attribute) { 754 # We'll test if two RDN's have the same number (we cant test anywhere else) 755 if (isset($return[$attribute->isRDN()]) && $this->getType() == 'creation') 756 system_message(array( 757 'title'=>('RDN attribute sequence already defined'), 758 'body'=>sprintf('%s %s', 759 sprintf(('There is a problem in template [%s].'),$this->getName()), 760 sprintf(('RDN attribute sequence [%s] is already used by attribute [%s] and cant be used by attribute [%s] also.'), 761 $attribute->isRDN(),$return[$attribute->isRDN()],$attribute->getName())), 762 'type'=>'error'),'index.php'); 763 764 $return[$attribute->isRDN()] = $attribute->getName(); 765 } 766 767 return $return; 768 } 769 770 /** 771 * Return the RDN for this template. If the DN is already defined, then the RDN will be calculated from it. 772 * If the DN is not set, then the RDN will be calcuated from the template attribute definitions 773 * 774 * @return rdn RDN for this template 775 */ 776 public function getRDN() { 777 # If the DN is set, then the RDN will be calculated from it. 778 if ($this->dn) 779 return get_rdn($this->dn); 780 781 $rdn = ''; 782 783 foreach ($this->getRDNObjects() as $attribute) { 784 $vals = $attribute->getValues(); 785 786 # If an RDN attribute has no values, return with an empty string. The calling script should handle this. 787 if (! count($vals)) 788 return ''; 789 790 foreach ($vals as $val) 791 $rdn .= sprintf('%s=%s+',$attribute->getName(false),$val); 792 } 793 794 # Chop the last plus sign off when returning 795 return preg_replace('/\+$/','',$rdn); 796 } 797 798 /** 799 * Return the attribute name part of the RDN 800 */ 801 public function getRDNAttributeName() { 802 $attr = array(); 803 804 if ($this->getDN()) { 805 $i = strpos($this->getDN(),','); 806 if ($i !== false) { 807 $attrs = explode('\+',substr($this->getDN(),0,$i)); 808 foreach ($attrs as $id => $attr) { 809 list ($name,$value) = explode('=',$attr); 810 $attrs[$id] = $name; 811 } 812 813 $attr = array_unique($attrs); 814 } 815 } 816 817 return $attr; 818 } 819 820 /** 821 * Determine the type of template this is 822 */ 823 public function getContext() { 824 if ($this->getContainer() && get_request('cmd','REQUEST') == 'copy') 825 return 'copyasnew'; 826 elseif ($this->getContainer() || get_request('create_base')) 827 return 'create'; 828 elseif ($this->getDN()) 829 return 'edit'; 830 else 831 return 'unknown'; 832 } 833 834 /** 835 * Test if the template is visible 836 * 837 * @return boolean 838 */ 839 public function isVisible() { 840 return $this->visible; 841 } 842 843 public function setVisible() { 844 $this->visible = true; 845 } 846 847 public function setInvisible() { 848 $this->visible = false; 849 } 850 851 public function getRegExp() { 852 return $this->regexp; 853 } 854 855 /** 856 * Test if this template has been marked as a read-only template 857 */ 858 public function isReadOnly() { 859 if ((($this->getContext() == 'edit') && $this->readonly) || $this->getServer()->isReadOnly()) 860 return true; 861 else 862 return false; 863 } 864 865 /** 866 * Get the attribute entries 867 * 868 * @param boolean Include the optional attributes 869 * @return array Array of attributes 870 */ 871 public function getAttributes($optional=false) { 872 if ($optional) 873 return $this->attributes; 874 875 $result = array(); 876 foreach ($this->attributes as $attribute) { 877 if (! $attribute->isRequired()) 878 continue; 879 880 array_push($result,$attribute); 881 } 882 883 return $result; 884 } 885 886 /** 887 * Return a list of attributes that should be shown 888 */ 889 public function getAttributesShown() { 890 $result = array(); 891 892 foreach ($this->attributes as $attribute) 893 if ($attribute->isVisible()) 894 array_push($result,$attribute); 895 896 return $result; 897 } 898 899 /** 900 * Return a list of the internal attributes 901 */ 902 public function getAttributesInternal() { 903 $result = array(); 904 905 foreach ($this->attributes as $attribute) 906 if ($attribute->isInternal()) 907 array_push($result,$attribute); 908 909 return $result; 910 } 911 912 /** 913 * Return the objectclasses defined in this template 914 * 915 * @return array Array of Objects 916 */ 917 public function getObjectClasses() { 918 $attribute = $this->getAttribute('objectclass'); 919 if ($attribute) 920 return $attribute->getValues(); 921 else 922 return array(); 923 } 924 925 /** 926 * Get template icon 927 */ 928 public function getIcon() { 929 return isset($this->icon) ? sprintf('%s/%s',IMGDIR,$this->icon) : ''; 930 } 931 932 /** 933 * Return the template description 934 * 935 * @return string Description 936 */ 937 public function getDescription() { 938 return $this->description; 939 } 940 941 /** 942 * Set a template as invalid 943 * 944 * @param string Message indicating the reason the template has been invalidated 945 */ 946 public function setInvalid($msg,$admin=false) { 947 $this->invalid = true; 948 $this->invalid_reason = $msg; 949 $this->invalid_admin = $admin; 950 } 951 952 /** 953 * Get the template validity or the reason it is invalid 954 * 955 * @return string Invalid reason, or false if not invalid 956 */ 957 public function isInValid() { 958 if ($this->invalid) 959 return $this->invalid_reason; 960 else 961 return false; 962 } 963 964 public function isAdminDisabled() { 965 return $this->invalid_admin; 966 } 967 968 /** 969 * Set the minimum number of values for an attribute 970 * 971 * @param object Attribute 972 * @param int 973 */ 974 private function setMinValueCount($attr,$value) { 975 $attribute = $this->getAttribute($attr); 976 977 if (! is_null($attribute)) 978 $attribute->setMinValueCount($value); 979 } 980 981 /** 982 * Set the LDAP type property for an attribute 983 * 984 * @param object Attribute 985 * @param string (MUST,MAY,OPTIONAL) 986 */ 987 private function setAttrLDAPtype($attr,$value) { 988 $attribute = $this->getAttribute($attr); 989 990 if (is_null($attribute)) 991 $attribute = $this->addAttribute($attr,array('values'=>array())); 992 993 $attribute->setLDAPtype($value); 994 } 995 996 /** 997 * OnChangeAdd javascript processing 998 */ 999 public function OnChangeAdd($origin,$value) { 1000 $attribute = $this->getAttribute($origin); 1001 1002 if (preg_match('/^=(\w+)\((.*)\)$/',$value,$matches)) { 1003 $command = $matches[1]; 1004 $arg = $matches[2]; 1005 } else 1006 return; 1007 1008 switch ($command) { 1009 /* 1010 autoFill:string 1011 string is a literal string, and may contain many fields like %attr|start-end/flags% 1012 to substitute values read from other fields. 1013 |start-end is optional, but must be present if the k flag is used. 1014 /flags is optional. 1015 1016 flags may be: 1017 T: Read display text from selection item (drop-down list), otherwise, read the value of the field 1018 For fields that aren't selection items, /T shouldn't be used, and the field value will always be read. 1019 k: Tokenize: 1020 If the "k" flag is not given: 1021 A |start-end instruction will perform a sub-string operation upon 1022 the value of the attr, passing character positions start-end through. 1023 start can be 0 for first character, or any other integer. 1024 end can be 0 for last character, or any other integer for a specific position. 1025 If the "k" flag is given: 1026 The string read will be split into fields, using : as a delimiter 1027 "start" indicates which field number to pass through. 1028 K: The string read will be split into fields, using ' ' as a delimiter "start" indicates which field number to pass through. 1029 l: Make the result lower case. 1030 U: Make the result upper case. 1031 */ 1032 case 'autoFill': 1033 if (! preg_match('/;/',$arg)) { 1034 system_message(array( 1035 'title'=>('Problem with autoFill() in template'), 1036 'body'=>sprintf('%s (<b>%s</b>)',('There is only 1 argument, when there should be two'),$attribute->getName(false)), 1037 'type'=>'warn')); 1038 1039 return; 1040 } 1041 1042 list($attr,$string) = preg_split('(([^,]+);(.*))',$arg,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 1043 preg_match_all('/%(\w+)(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?%/U',$string,$matchall); 1044 //print"<PRE>";print_r($matchall); //0 = highlevel match, 1 = attr, 2 = subst, 3 = mod 1045 1046 if (! isset($attribute->js['autoFill'])) 1047 $attribute->js['autoFill'] = ''; 1048 1049 $formula = $string; 1050 $formula = preg_replace('/^([^%])/','\'$1',$formula); 1051 $formula = preg_replace('/([^%])$/','$1\'',$formula); 1052 1053 # Check that our attributes match our schema attributes. 1054 foreach ($matchall[1] as $index => $checkattr) { 1055 $sattr = $this->getServer()->getSchemaAttribute($checkattr); 1056 1057 # If the attribute is the same as in the XML file, then dont need to do anything. 1058 if (! $sattr || ! strcasecmp($sattr->getName(),$checkattr)) 1059 continue; 1060 1061 $formula = preg_replace("/$checkattr/",$sattr->getName(),$formula); 1062 $matchall[1][$index] = $sattr->getName(); 1063 } 1064 1065 $elem_id = 0; 1066 1067 foreach ($matchall[0] as $index => $null) { 1068 $match_attr = strtolower($matchall[1][$index]); 1069 $match_subst = $matchall[2][$index]; 1070 $match_mod = $matchall[3][$index]; 1071 1072 $substrarray = array(); 1073 1074 if (! isset($varcount[$match_attr])) 1075 $varcount[$match_attr] = 0; 1076 else 1077 $varcount[$match_attr]++; 1078 1079 $js_match_attr = $match_attr; 1080 $match_attr = $js_match_attr.'xx'.$varcount[$match_attr]; 1081 1082 $formula = preg_replace('/%'.$js_match_attr.'([|\/%])/i','%'.$match_attr.'$1',$formula,1); 1083 1084 $attribute->js['autoFill'] .= sprintf(" var %s;\n",$match_attr); 1085 $attribute->js['autoFill'] .= sprintf( 1086 " var elem$elem_id = document.getElementById(pre+'%s'+suf);\n". 1087 " if (!elem$elem_id) return;\n", $js_match_attr); 1088 1089 if (strstr($match_mod,'T')) { 1090 $attribute->js['autoFill'] .= sprintf(" %s = elem$elem_id.options[elem$elem_id.selectedIndex].text;\n", 1091 $match_attr); 1092 } else { 1093 $attribute->js['autoFill'] .= sprintf(" %s = elem$elem_id.value;\n",$match_attr); 1094 } 1095 1096 $elem_id++; 1097 1098 if (strstr($match_mod,'k')) { 1099 preg_match_all('/([0-9]+)/',trim($match_subst),$substrarray); 1100 if (isset($substrarray[1][0])) { 1101 $tok_idx = $substrarray[1][0]; 1102 } else { 1103 $tok_idx = '0'; 1104 } 1105 $attribute->js['autoFill'] .= sprintf(" %s = %s.split(':')[%s];\n",$match_attr,$match_attr,$tok_idx); 1106 1107 } elseif (strstr($match_mod,'K')) { 1108 preg_match_all('/([0-9]+)/',trim($match_subst),$substrarray); 1109 if (isset($substrarray[1][0])) { 1110 $tok_idx = $substrarray[1][0]; 1111 } else { 1112 $tok_idx = '0'; 1113 } 1114 $attribute->js['autoFill'] .= sprintf(" %s = %s.split(' ')[%s];\n",$match_attr,$match_attr,$tok_idx); 1115 1116 } else { 1117 preg_match_all('/([0-9]*)-([0-9]*)/',trim($match_subst),$substrarray); 1118 if ((isset($substrarray[1][0]) && $substrarray[1][0]) || (isset($substrarray[2][0]) && $substrarray[2][0])) { 1119 $attribute->js['autoFill'] .= sprintf(" %s = %s.substr(%s,%s);\n", 1120 $match_attr,$match_attr, 1121 $substrarray[1][0] ? $substrarray[1][0] : '0', 1122 $substrarray[2][0] ? $substrarray[2][0] : sprintf('%s.length',$match_attr)); 1123 } 1124 } 1125 1126 if (strstr($match_mod,'l')) { 1127 $attribute->js['autoFill'] .= sprintf(" %s = %s.toLowerCase();\n",$match_attr,$match_attr); 1128 } 1129 if (strstr($match_mod,'U')) { 1130 $attribute->js['autoFill'] .= sprintf(" %s = %s.toUpperCase();\n",$match_attr,$match_attr); 1131 } 1132 if (strstr($match_mod,'A')) { 1133 $attribute->js['autoFill'] .= sprintf(" %s = toAscii(%s);\n",$match_attr,$match_attr); 1134 } 1135 1136 # Matchfor only entry without modifiers. 1137 $formula = preg_replace('/^%('.$match_attr.')%$/U','$1 + \'\'',$formula); 1138 # Matchfor only entry with modifiers. 1139 $formula = preg_replace('/^%('.$match_attr.')(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?%$/U','$1 + \'\'',$formula); 1140 # Matchfor begining entry. 1141 $formula = preg_replace('/^%('.$match_attr.')(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?%/U','$1 + \'',$formula); 1142 # Matchfor ending entry. 1143 $formula = preg_replace('/%('.$match_attr.')(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?%$/U','\' + $1 ',$formula); 1144 # Match for entries not at begin/end. 1145 $formula = preg_replace('/%('.$match_attr.')(\|[0-9]*-[0-9]*)?(\/[:lTUA]+)?%/U','\' + $1 + \'',$formula); 1146 $attribute->js['autoFill'] .= "\n"; 1147 } 1148 1149 $attribute->js['autoFill'] .= sprintf(" fillRec(pre+'%s'+suf, %s); // %s\n",strtolower($attr),$formula,$string); 1150 $attribute->js['autoFill'] .= "\n"; 1151 break; 1152 1153 default: $return = ''; 1154 } 1155 } 1156 1157 /** 1158 * This functions main purpose is to discover our MUST attributes based on objectclass 1159 * definitions in the template file and to discover which of the objectclasses are 1160 * STRUCTURAL - without one, creating an entry will just product an LDAP error. 1161 */ 1162 private function rebuildTemplateAttrs() { 1163 $server = $this->getServer(); 1164 1165 # Collect our structural, MUST & MAY attributes. 1166 $oclass_processed = array(); 1167 $superclasslist = array(); 1168 $allattrs = array('objectclass'); 1169 1170 foreach ($this->getObjectClasses() as $oclass) { 1171 # If we get some superclasses - then we'll need to go through them too. 1172 $supclass = true; 1173 $inherited = false; 1174 1175 while ($supclass) { 1176 $soc = $server->getSchemaObjectClass($oclass); 1177 1178 if ($soc->getType() == 'structural' && (! $inherited)) 1179 array_push($this->structural_oclass,$oclass); 1180 1181 # Make sure our MUST attributes are marked as such for this template. 1182 if ($soc->getMustAttrs()) 1183 foreach ($soc->getMustAttrs() as $index => $details) { 1184 $objectclassattr = $details->getName(); 1185 1186 # We add the 'objectClass' attribute, only if it's explicitly in the template attribute list 1187 if ((strcasecmp('objectClass',$objectclassattr) != 0) || 1188 ((strcasecmp('objectClass',$objectclassattr) == 0) && (! is_null($this->getAttribute($objectclassattr))))) { 1189 1190 # Go through the aliases, and ignore any that are already defined. 1191 $ignore = false; 1192 $sattr = $server->getSchemaAttribute($objectclassattr); 1193 foreach ($sattr->getAliases() as $alias) { 1194 if ($this->isAttrType($alias,'must')) { 1195 $ignore = true; 1196 break; 1197 } 1198 } 1199 1200 if ($ignore) 1201 continue; 1202 1203 $this->setAttrLDAPtype($sattr->getName(),'must'); 1204 $this->setMinValueCount($sattr->getName(),1); 1205 1206 # We need to mark the attributes as show, except for the objectclass attribute. 1207 if (strcasecmp('objectClass',$objectclassattr) != 0) { 1208 $attribute = $this->getAttribute($sattr->getName()); 1209 $attribute->show(); 1210 } 1211 } 1212 1213 if (! in_array($objectclassattr,$allattrs)) 1214 array_push($allattrs,$objectclassattr); 1215 } 1216 1217 if ($soc->getMayAttrs()) 1218 foreach ($soc->getMayAttrs() as $index => $details) { 1219 $objectclassattr = $details->getName(); 1220 $sattr = $server->getSchemaAttribute($objectclassattr); 1221 1222 # If it is a MUST attribute, skip to the next one. 1223 if ($this->isAttrType($objectclassattr,'must')) 1224 continue; 1225 1226 if (! $this->isAttrType($objectclassattr,'may')) 1227 $this->setAttrLDAPtype($sattr->getName(false),'optional'); 1228 1229 if (! in_array($objectclassattr,$allattrs)) 1230 array_push($allattrs,$objectclassattr); 1231 } 1232 1233 # Keep a list to objectclasses we have processed, so we dont get into a loop. 1234 array_push($oclass_processed,$oclass); 1235 $supoclasses = $soc->getSupClasses(); 1236 1237 if (count($supoclasses) || count($superclasslist)) { 1238 foreach ($supoclasses as $supoclass) { 1239 if (! in_array($supoclass,$oclass_processed)) 1240 $superclasslist[] = $supoclass; 1241 } 1242 1243 $oclass = array_shift($superclasslist); 1244 if ($oclass) 1245 $inherited = true; 1246 else 1247 $supclass = false; 1248 1249 } else { 1250 $supclass = false; 1251 } 1252 } 1253 } 1254 1255 # Check that attributes are defined by an ObjectClass 1256 foreach ($this->getAttributes(true) as $index => $attribute) 1257 if (! in_array($attribute->getName(),$allattrs) && (! array_intersect($attribute->getAliases(),$allattrs)) 1258 && (! in_array_ignore_case('extensibleobject',$this->getObjectClasses())) 1259 && (! in_array_ignore_case($attribute->getName(),$server->getValue('server','custom_attrs')))) { 1260 unset($this->attributes[$index]); 1261 1262 if (! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning')) 1263 system_message(array( 1264 'title'=>('Automatically removed attribute from template'), 1265 'body'=>sprintf('%s: <b>%s</b> %s',$this->getTitle(),$attribute->getName(false),('removed from template as it is not defined by an ObjectClass')), 1266 'type'=>'warn')); 1267 } 1268 } 1269 1270 /** 1271 * Return an array, that can be passed to ldap_add(). 1272 * Attributes with empty values will be excluded. 1273 */ 1274 public function getLDAPadd($attrsOnly=false) { 1275 $return = array(); 1276 $returnattrs = array(); 1277 1278 if ($attrsOnly && count($returnattrs)) 1279 return $returnattrs; 1280 1281 foreach ($this->getAttributes(true) as $attribute) 1282 if (! $attribute->isInternal() && count($attribute->getValues())) { 1283 $return[$attribute->getName()] = $attribute->getValues(); 1284 $returnattrs[$attribute->getName()] = $attribute; 1285 } 1286 1287 # Ensure that our objectclasses has "top". 1288 if (isset($return['objectclass']) && ! in_array('top',$return['objectclass'])) 1289 array_push($return['objectclass'],'top'); 1290 1291 if ($attrsOnly) 1292 return $returnattrs; 1293 1294 return $return; 1295 } 1296 1297 /** 1298 * Return an array, that can be passed to ldap_mod_replace(). 1299 * Only attributes that have changed their value will be returned. 1300 * 1301 * This function will cache its results, so that it can be called with count() to see 1302 * if there are changes, and if they are, the 2nd call will just return the results 1303 * 1304 * @param boolean Return the attribute objects (useful for a confirmation process), or the modification array for ldap_modify() 1305 */ 1306 public function getLDAPmodify($attrsOnly=false,$index=0) { 1307 static $return = array(); 1308 static $returnattrs = array(); 1309 1310 if ($attrsOnly && isset($returnattrs[$index]) && count($returnattrs[$index])) 1311 return $returnattrs[$index]; 1312 1313 $returnattrs[$index] = array(); 1314 $return[$index] = array(); 1315 1316 # If an objectclass is being modified, we need to remove all the orphan attributes that would result. 1317 if ($this->getAttribute('objectclass')->hasBeenModified()) { 1318 $attr_to_keep = array(); 1319 $server = $this->getServer(); 1320 1321 # Make sure that there will be a structural object class remaining. 1322 $haveStructural = false; 1323 foreach ($this->getAttribute('objectclass')->getValues() as $value) { 1324 $soc = $server->getSchemaObjectClass($value); 1325 1326 if ($soc) { 1327 if ($soc->isStructural()) 1328 $haveStructural = true; 1329 1330 # While we are looping, workout which attributes these objectclasses define. 1331 foreach ($soc->getMustAttrs(true) as $value) 1332 if (! in_array($value->getName(),$attr_to_keep)) 1333 array_push($attr_to_keep,$value->getName()); 1334 1335 foreach ($soc->getMayAttrs(true) as $value) 1336 if (! in_array($value->getName(),$attr_to_keep)) 1337 array_push($attr_to_keep,$value->getName()); 1338 } 1339 } 1340 1341 if (! $haveStructural) 1342 error(('An entry should have one structural objectClass.'),'error','index.php'); 1343 1344 # Work out the attributes to delete. 1345 foreach ($this->getAttribute('objectclass')->getRemovedValues() as $value) { 1346 $soc = $server->getSchemaObjectClass($value); 1347 1348 foreach ($soc->getMustAttrs() as $value) { 1349 $attribute = $this->getAttribute($value->getName()); 1350 1351 if ($attribute && (! in_array($value->getName(),$attr_to_keep)) && ($value->getName() != 'objectclass')) 1352 #array_push($attr_to_delete,$value->getName(false)); 1353 $attribute->setForceDelete(); 1354 } 1355 1356 foreach ($soc->getMayAttrs() as $value) { 1357 $attribute = $this->getAttribute($value->getName()); 1358 1359 if ($attribute && (! in_array($value->getName(),$attr_to_keep)) && ($value->getName() != 'objectclass')) 1360 $attribute->setForceDelete(); 1361 } 1362 } 1363 } 1364 1365 foreach ($this->getAttributes(true) as $attribute) 1366 if ($attribute->hasBeenModified() 1367 && (count(array_diff($attribute->getValues(),$attribute->getOldValues())) || ! count($attribute->getValues()) 1368 || $attribute->isForceDelete() || (count($attribute->getValues()) != count($attribute->getOldValues())))) 1369 $returnattrs[$index][$attribute->getName()] = $attribute; 1370 1371 if ($attrsOnly) 1372 return $returnattrs[$index]; 1373 1374 foreach ($returnattrs[$index] as $attribute) 1375 $return[$index][$attribute->getName()] = $attribute->getValues(); 1376 1377 return $return[$index]; 1378 } 1379 1380 /** 1381 * Get the attributes that are marked as force delete 1382 * We'll cache this result in the event of multiple calls. 1383 */ 1384 public function getForceDeleteAttrs() { 1385 static $result = array(); 1386 1387 if (count($result)) 1388 return $result; 1389 1390 foreach ($this->attributes as $attribute) 1391 if ($attribute->isForceDelete()) 1392 array_push($result,$attribute); 1393 1394 return $result; 1395 } 1396 1397 /** 1398 * Get available attributes 1399 */ 1400 public function getAvailAttrs() { 1401 $attributes = array(); 1402 $server = $this->getServer(); 1403 1404 # Initialise the Attribute Factory. 1405 $attribute_factory = new AttributeFactory(); 1406 1407 if (in_array_ignore_case('extensibleobject',$this->getObjectClasses())) { 1408 foreach ($server->SchemaAttributes() as $sattr) { 1409 $attribute = $attribute_factory->newAttribute($sattr->getName(),array('values'=>array()),$server->getIndex(),null); 1410 array_push($attributes,$attribute); 1411 } 1412 1413 } else { 1414 $attrs = array(); 1415 1416 foreach ($this->getObjectClasses() as $oc) { 1417 $soc = $server->getSchemaObjectClass($oc); 1418 $attrs = array_merge($attrs,$soc->getMustAttrNames(true),$soc->getMayAttrNames(true)); 1419 $attrs = array_unique($attrs); 1420 } 1421 1422 foreach ($attrs as $attr) 1423 if (is_null($this->getAttribute($attr))) { 1424 $attribute = $attribute_factory->newAttribute($attr,array('values'=>array()),$server->getIndex(),null); 1425 array_push($attributes,$attribute); 1426 } 1427 } 1428 1429 masort($attributes,'name'); 1430 return $attributes; 1431 } 1432 1433 public function isNoLeaf() { 1434 return $this->noleaf; 1435 } 1436} 1437?> 1438