1<?php
2/**
3 * Classes and functions for the template engine.
4 *
5 * @author The phpLDAPadmin development team
6 * @package phpLDAPadmin
7 */
8
9/**
10 * Represents an attribute of a template.
11 *
12 * @package phpLDAPadmin
13 * @subpackage Templates
14 */
15class Attribute {
16	# Attribute Name
17	public $name;
18	# Source of this attribute definition
19	protected $source;
20
21	# Current and Old Values
22	protected $oldvalues = array();
23	protected $values = array();
24
25	# MIN/MAX number of values
26	protected $min_value_count = -1;
27	protected $max_value_count = -1;
28
29	# Is the attribute internal
30	protected $internal = false;
31	# Has the attribute been modified
32	protected $modified = false;
33	# Is the attribute being deleted because of an object class removal
34	protected $forcedelete = false;
35	# Is the attribute visible
36	protected $visible = false;
37	protected $forcehide = false;
38	# Is the attribute modifiable
39	protected $readonly = false;
40	# LDAP attribute type MUST/MAY
41	protected $ldaptype = null;
42	# Attribute property type (eg password, select, multiselect)
43	protected $type = '';
44	# Attribute value to keep unique
45	protected $unique = false;
46
47	# Display parameters
48	protected $display = '';
49	protected $icon = '';
50	protected $hint = '';
51	# Helper details
52	protected $helper = array();
53	protected $helpervalue = array();
54	# Onchange details
55	protected $onchange = array();
56	# Show spacer after this attribute is rendered
57	protected $spacer = false;
58	protected $verify = false;
59
60	# Component size
61	protected $size = 0;
62	# Value max length
63	protected $maxlength = 0;
64	# Text Area sizings
65	protected $cols = 0;
66	protected $rows = 0;
67
68	# Public for sorting
69	public $page = 1;
70	public $order = 255;
71	public $ordersort = 255;
72	public $rdn = false;
73
74	# Schema Aliases for this attribute (stored in lowercase)
75	protected $aliases = array();
76
77	# Configuration for automatically generated values
78	protected $autovalue = array();
79	protected $postvalue = array();
80
81	public function __construct($name,$values,$server_id,$source=null) {
82		$server = $_SESSION[APPCONFIG]->getServer($server_id);
83
84		$sattr = $server->getSchemaAttribute($name);
85		if ($sattr) {
86			$this->name = $sattr->getName(false);
87			$this->setLDAPdetails($sattr);
88
89		} else
90			$this->name = $name;
91
92		$this->source = $source;
93
94		# XML attributes are shown by default
95		switch ($source) {
96			case 'XML': $this->show();
97				$this->setXML($values);
98
99				break;
100
101			default:
102				if (! isset($values['values']))
103					debug_dump_backtrace('no index "values"',1);
104
105				$this->initValue($values['values']);
106		}
107
108		# Should this attribute be hidden
109		if ($server->isAttrHidden($this->name))
110			$this->forcehide = true;
111
112		# Should this attribute value be read only
113		if ($server->isAttrReadOnly($this->name))
114			$this->readonly = true;
115
116		# Should this attribute value be unique
117		if ($server->isAttrUnique($this->name))
118			$this->unique = true;
119	}
120
121	/**
122	 * Return the name of the attribute.
123	 *
124	 * @param boolean $lower - Return the attribute in normal or lower case (default lower)
125	 * @param boolean $real - Return the real attribute name (with ;binary, or just the name)
126	 * @return string Attribute name
127	 */
128	public function getName($lower=true,$real=false) {
129		if ($real)
130			return $lower ? strtolower($this->name) : $this->name;
131		else
132			return $lower ? strtolower($this->real_attr_name()) : $this->real_attr_name();
133	}
134
135	public function getValues() {
136		return $this->values;
137	}
138
139	public function getOldValues() {
140		return $this->oldvalues;
141	}
142
143	public function getValueCount() {
144		return count($this->values);
145	}
146
147	public function getSource() {
148		return $this->source;
149	}
150
151	/**
152	 * Autovalue is called after the attribute is initialised, and thus the values from the ldap server will be set.
153	 */
154	public function autoValue($new_val) {
155		if ($this->values)
156			return;
157
158		$this->values = $new_val;
159	}
160
161	public function initValue($new_val) {
162		if ($this->values || $this->oldvalues) {
163			debug_dump(array('new_val'=>$new_val,'this'=>$this));
164			debug_dump_backtrace('new and/or old values are set',1);
165		}
166
167		$this->values = $new_val;
168	}
169
170	public function clearValue() {
171		$this->values = array();
172	}
173
174	public function setOldValue($val) {
175		$this->oldvalues = $val;
176	}
177
178	public function setValue($new_val) {
179		if ($this->values) {
180			if ($this->values == $new_val)
181				return;
182
183			if ($this->oldvalues) {
184				debug_dump($this);
185				debug_dump_backtrace('old values are set',1);
186			} else
187				$this->oldvalues = $this->values;
188		}
189
190		if ($new_val == $this->values)
191			return;
192
193		$this->values = $new_val;
194		$this->justModified();
195	}
196
197	public function addValue($new_val,$i=-1) {
198		if ($i < 0)
199			$i = $this->getValueCount();
200
201		$old_val = $this->getValue($i);
202		if (is_null($old_val) || ($old_val != $new_val))
203			$this->justModified();
204
205		$this->values[$i] = $new_val;
206	}
207
208	public function delValue($i=-1) {
209		if ($i < 0)
210			$this->setValue(array());
211
212		if (! $this->hasBeenModified())
213			$this->oldvalues = $this->values;
214
215		if (isset($this->values[$i])) {
216			unset($this->values[$i]);
217			$this->values = array_values($this->values);
218			$this->justModified();
219		}
220	}
221
222	public function getValue($i) {
223		if (isset($this->values[$i]))
224			return $this->values[$i];
225		else
226			return null;
227	}
228
229	public function getOldValue($i) {
230		if (isset($this->oldvalues[$i]))
231			return $this->oldvalues[$i];
232		else
233			return null;
234	}
235
236	public function getMinValueCount() {
237		return $this->min_value_count;
238	}
239
240	public function setMinValueCount($min) {
241		$this->min_value_count = $min;
242	}
243
244	public function getMaxValueCount() {
245		return $this->max_value_count;
246	}
247
248	public function setMaxValueCount($max) {
249		$this->max_value_count = $max;
250	}
251
252	public function haveMoreValues() {
253		if ($this->getMaxValueCount() < 0 || ($this->getValueCount() < $this->getMaxValueCount()))
254			return true;
255		else
256			return false;
257	}
258
259	public function justModified() {
260		$this->modified = true;
261	}
262
263	public function hasBeenModified() {
264		return $this->modified;
265	}
266
267	public function isForceDelete() {
268		return $this->forcedelete;
269	}
270
271	public function setForceDelete() {
272		$this->forcedelete = true;
273		$this->oldvalues = $this->values;
274		$this->values = array();
275		$this->justModified();
276	}
277
278	public function isInternal() {
279		return $this->internal;
280	}
281
282	public function setInternal() {
283		$this->internal = true;
284	}
285
286	public function isRequired() {
287		if ($this->getMinValueCount() > 0)
288			return true;
289		elseif ($this->ldaptype == 'must')
290			return true;
291		elseif ($this->isRDN())
292			return true;
293		else
294			return false;
295	}
296
297	public function isMay() {
298		if (($this->ldaptype == 'may') && ! $this->isRequired())
299			return true;
300		else
301			return false;
302	}
303
304	public function setType($type) {
305		$this->type = strtolower($type);
306	}
307
308	public function getType() {
309		return $this->type;
310	}
311
312	public function setLDAPtype($type) {
313		$this->ldaptype = strtolower($type);
314	}
315
316	public function getLDAPtype() {
317		return $this->ldaptype;
318	}
319
320	public function setProperties($properties) {
321		foreach ($properties as $index => $value) {
322			if ($index == 'maxvalnb') {
323				$this->setMaxValueCount($value);
324				continue;
325
326			} elseif ($index == 'minvalnb') {
327				$this->setMinValueCount($value);
328				continue;
329
330			} elseif ($index == 'maxlength') {
331				$this->setMinValueCount($value);
332				continue;
333
334			} elseif ($index == 'hidden') {
335				$this->visible = $value;
336				continue;
337
338			} elseif (in_array($index,array('cols','rows'))) {
339				# @todo To be implemented
340				continue;
341			}
342
343			if (isset($this->$index))
344				$this->$index = $value;
345			else {
346				debug_dump($this);
347				debug_dump_backtrace(sprintf('Unknown property (%s) with value (%s) for (%s)',$index,$value,$this->getName()),1);
348			}
349		}
350	}
351
352	public function setRequired() {
353		if ($this->getMinValueCount() <= 0)
354			$this->setMinValueCount(1);
355	}
356
357	public function setOptional() {
358		$this->setMinValueCount(0);
359	}
360
361	public function isReadOnly() {
362		return $this->readonly;
363	}
364
365	public function setReadOnly() {
366		$this->readonly = true;
367	}
368
369	public function isMultiple() {
370		return false;
371	}
372
373	public function isVisible() {
374		return $this->visible && (! $this->forcehide);
375	}
376
377	public function hide() {
378		$this->visible = false;
379	}
380
381	public function show() {
382		$this->visible = true;
383	}
384
385	public function haveFriendlyName() {
386		return $_SESSION[APPCONFIG]->haveFriendlyName($this);
387	}
388
389	public function getFriendlyName() {
390		if ($this->display)
391			return $this->display;
392		else
393			return $_SESSION[APPCONFIG]->getFriendlyName($this);
394	}
395
396	public function setDescription($description) {
397		$this->description = $description;
398	}
399
400	public function getDescription() {
401		return $this->description;
402	}
403
404	public function setIcon($icon) {
405		$this->icon = $icon;
406	}
407
408	public function getIcon() {
409		return $this->icon ? sprintf('%s/%s',IMGDIR,$this->icon) : '';
410	}
411
412	public function getHint() {
413		return $this->hint;
414	}
415
416	public function setHint($hint) {
417		$this->hint = $hint;
418	}
419
420	public function getMaxLength() {
421		return $this->maxlength;
422	}
423
424	public function setMaxLength($maxlength) {
425		$this->maxlength = $maxlength;
426	}
427
428	public function getSize() {
429		return $this->size;
430	}
431
432	public function setSize($size) {
433		$this->size = $size;
434	}
435
436	public function getSpacer() {
437		return $this->spacer;
438	}
439
440	public function getPage() {
441		return $this->page;
442	}
443	public function setPage($page) {
444		$this->page = $page;
445	}
446
447	public function getOnChange() {
448		return $this->onchange;
449	}
450
451	public function getHelper() {
452		return $this->helper;
453	}
454
455	public function getHelperValue() {
456		return $this->helpervalue;
457	}
458
459	public function getVerify() {
460		return $this->verify;
461	}
462
463	public function setRDN($rdn) {
464		$this->rdn = $rdn;
465	}
466
467	/**
468	 * Return if this attribute is an RDN attribute
469	 *
470	 * @return boolean
471	 */
472	public function isRDN() {
473		return $this->rdn;
474	}
475
476	/**
477	 * Capture all the LDAP details we are interested in
478	 *
479	 * @param sattr Schema Attribute
480	 */
481	private function setLDAPdetails($sattr) {
482		# By default, set this as a MAY attribute, later processing should make it a MUST attribute if it is.
483		if (! $this->ldaptype)
484			$this->ldaptype = 'may';
485
486		# Store our Aliases
487		foreach ($sattr->getAliases() as $alias)
488			array_push($this->aliases,strtolower($alias));
489
490		if ($sattr->getIsSingleValue())
491			$this->setMaxValueCount(1);
492	}
493
494	/**
495	 * Return a list of aliases for this Attribute (as defined by the schema)
496	 * This list will be lowercase.
497	 */
498	public function getAliases() {
499		return $this->aliases;
500	}
501
502	public function getAutoValue() {
503		return $this->autovalue;
504	}
505
506	public function getPostValue() {
507		return $this->postvalue;
508	}
509
510	public function setPostValue($postvalue) {
511		$this->postvalue = $postvalue;
512	}
513
514	public function setXML($values) {
515		# Mostly all the time, this should be an array
516		if (is_array($values))
517			foreach ($values as $index => $value)
518				switch ($index) {
519					# Helpers should be accompanied with a <post> attribute.
520					case 'helper':
521						if (! isset($values['post']) && ! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning'))
522							system_message(array(
523								'title'=>sprintf('%s [<i>%s</i>]',('Missing [post] setting in XML file'),$index),
524								'body'=>('[helper] needs an accompanying [post] action.'),
525								'type'=>'warn'));
526
527						if (isset($value['value']) && ! is_array($value['value']) && preg_match('/^=php\.(\w+)\((.*)\)$/',$value['value'],$matches)) {
528							$this->helpervalue['function'] = $matches[1];
529							$this->helpervalue['args'] = $matches[2];
530
531							unset ($value['value']);
532						}
533
534						foreach ($value as $i => $detail) {
535							if (! in_array($i,array('default','display','id','value'))) {
536								if (! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning'))
537									system_message(array(
538										'title'=>sprintf('%s [<i>%s</i>]',('Unknown XML setting'),$i),
539										'body'=>sprintf('%s <small>[%s]</small>',('Unknown XML type setting for helper will be ignored.'),$detail),
540										'type'=>'warn'));
541
542								unset($value[$i]);
543							}
544						}
545
546						$this->$index = $value;
547
548						break;
549
550					case 'hidden': $value ? $this->visible = false : $this->visible = true;
551						break;
552
553					case 'spacer': $value ? $this->$index = true : $this->$index = false;
554						break;
555
556					# Essentially, we ignore type, it is used to select an Attribute type in the Factory. But we'll generated a warning if there is an unknown type.
557					case 'type':
558						if (! in_array($value,array('password','multiselect','select','textarea')) && ! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning'))
559							system_message(array(
560								'title'=>sprintf('%s [<i>%s</i>]',('Unknown XML setting'),$index),
561								'body'=>sprintf('%s <small>[%s]</small>',('Unknown XML type setting will be ignored.'),$value),
562								'type'=>'warn'));
563
564						break;
565
566					case 'post':
567						if (preg_match('/^=php\.(\w+)\((.*)\)$/',$value,$matches)) {
568							$this->postvalue['function'] = $matches[1];
569							$this->postvalue['args'] = $matches[2];
570
571						} else
572							if (! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning'))
573								system_message(array(
574									'title'=>sprintf('%s [<i>%s</i>]',('Unknown XML setting'),$index),
575									'body'=>sprintf('%s <small>[%s]</small>',('Unknown XML type setting will be ignored.'),$value),
576									'type'=>'warn'));
577
578					case 'value':
579						if (is_array($value))
580							foreach ($value as $y) {
581								if (! $this->haveMoreValues()) {
582									system_message(array(
583									'title'=>('Automatically removed attribute values from template'),
584										'body'=>sprintf('%s <small>[%s]</small>',('Template defines more values than can be accepted by attribute.'),$this->getName(true)),
585										'type'=>'warn'));
586
587									$this->clearValue();
588
589									break;
590
591								} else
592									$this->addValue($y);
593							}
594
595						else
596							# Check to see if the value is auto generated.
597							if (preg_match('/^=php\.(\w+)\((.*)\)$/',$value,$matches)) {
598								$this->autovalue['function'] = $matches[1];
599								$this->autovalue['args'] = $matches[2];
600
601								# We'll add a hint too
602								if (! $this->hint)
603									$this->hint = ('Automatically determined');
604
605							} else
606								$this->addValue($value);
607
608						break;
609
610					# Queries
611					case 'ordersort':
612
613					# Creation/Editing Templates
614					case 'cols':
615					case 'default':
616					case 'display':
617					case 'hint':
618					case 'icon':
619					case 'maxlength':
620					case 'onchange':
621					case 'order':
622					case 'page':
623					case 'readonly':
624					case 'rows':
625					case 'size':
626					case 'values':
627					case 'verify': $this->$index = $value;
628						break;
629
630					case 'max':
631						if ($this->getMaxValueCount() == -1)
632							$this->setMaxValueCount($value);
633
634					default:
635						if (! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning'))
636							system_message(array(
637								'title'=>sprintf('%s [<i>%s</i>]',('Unknown XML setting'),$index),
638								'body'=>sprintf('%s <small>[%s]</small>',('Unknown attribute setting will be ignored.'),serialize($value)),
639								'type'=>'warn'));
640				}
641
642		elseif (is_string($values) && (strlen($values) > 0))
643			$this->values = array($values);
644	}
645
646	/**
647	 * Display the values removed in an attribute.
648	 */
649	public function getRemovedValues() {
650		return array_diff($this->getOldValues(),$this->getValues());
651	}
652
653	/**
654	 * Display the values removed in an attribute.
655	 */
656	public function getAddedValues() {
657		return array_diff($this->getValues(),$this->getOldValues());
658	}
659
660	/**
661	 * Prunes off anything after the ";" in an attr name. This is useful for
662	 * attributes that may have ";binary" appended to their names. With
663	 * real_attr_name(), you can more easily fetch these attributes' schema
664	 * with their "real" attribute name.
665	 *
666	 * @param string $attr_name The name of the attribute to examine.
667	 * @return string
668	 */
669	private function real_attr_name() {
670		return preg_replace('/;.*$/U','',$this->name);
671	}
672
673	/**
674	 * Does this attribute need supporting JS
675	 */
676	public function needJS($type=null) {
677		if (is_null($type)) {
678			foreach (array('focus','blur','validate') as $type)
679				if ($this->needJS($type))
680					return true;
681
682			return false;
683
684		} elseif ($type == 'focus') {
685			# We dont have any focus javascript routines.
686			return false;
687
688		} elseif ($type == 'blur') {
689			if ($this->onchange || $this->isRequired())
690				return true;
691			else
692				return false;
693
694		} elseif ($type == 'validate') {
695			if ($this->isRequired())
696				return true;
697			else
698				return false;
699
700		} else
701			debug_dump_backtrace(sprintf('Unknown JS request %s',$type),1);
702	}
703}
704?>
705