PageRenderTime 67ms CodeModel.GetById 2ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 0ms

/phpldapadmin-1.2.2/lib/Template.php

#
PHP | 1571 lines | 952 code | 296 blank | 323 comment | 298 complexity | 9a8b8c4845fcb46d7af00d404d179a20 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file