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