FusionDirectory
 All Data Structures Files Functions Variables
class_simplePlugin.inc
Go to the documentation of this file.
1 <?php
2 /*
3  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
4  Copyright (C) 2012-2016 FusionDirectory
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20 
30 {
33 
38  public $attributesAccess = array();
39 
51  public $is_account = FALSE;
52  public $initially_was_account = FALSE;
53  public $ignore_account = FALSE;
54 
55  public $acl_base = '';
56  public $acl_category = '';
57 
59  public $dn = '';
60 
62  public $orig_dn = '';
63 
74  public $parent = NULL;
75 
83  public $is_template = FALSE;
84 
90  public $attrs = array();
91 
93  protected $objectclasses = array();
94 
96  protected $saved_attributes = array();
97 
99  protected $displayHeader = FALSE;
100 
102  protected $mainTab = FALSE;
103 
104  protected $header = "";
105 
106  protected $templatePath;
107 
108  protected $dialog = FALSE;
109 
112  protected $needEditMode = FALSE;
113 
115  protected $preInitAttributes = array();
116 
119  protected $inheritance = FALSE;
120  protected $member_of_group = FALSE;
121  protected $editing_group = NULL;
122  protected $group_attrs = array();
123 
125  protected $read_only = FALSE;
126 
128  protected $ldap_error;
129 
137  protected $entryCSN = '';
138 
148  function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE, $attributesInfo = NULL)
149  {
150  global $config;
151 
152  $this->dn = $dn;
153  $this->parent = $parent;
154  $this->mainTab = $mainTab;
155 
156  if ($attributesInfo === NULL) {
157  $attributesInfo = $this->getAttributesInfo();
158  }
159  if (!$this->displayHeader) {
160  // If we don't display the header to activate/deactive the plugin, that means it's always activated
161  $this->ignore_account = TRUE;
162  }
163 
164  $this->attributesInfo = array();
165  foreach ($attributesInfo as $section => $sectionInfo) {
166  $attrs = array();
167  foreach ($sectionInfo['attrs'] as $attr) {
168  $name = $attr->getLdapName();
169  if (isset($attrs[$name])) {
170  // We check that there is no duplicated attribute name
171  trigger_error("Duplicated attribute LDAP name '$name' in a simplePlugin subclass");
172  }
173  // We make so that attribute have their LDAP name as key
174  // That allow the plugin to use $this->attributesInfo[$sectionName]['attrs'][$myLdapName] to retreive the attribute info.
175  $attrs[$name] = $attr;
176  }
177  $sectionInfo['attrs'] = $attrs;
178  $this->attributesInfo[$section] = $sectionInfo;
179  foreach ($this->attributesInfo[$section]['attrs'] as $name => $attr) {
180  if (isset($this->attributesAccess[$name])) {
181  // We check that there is no duplicated attribute name
182  trigger_error("Duplicated attribute LDAP name '$name' in a simplePlugin subclass");
183  }
184  $this->attributesAccess[$name] =& $this->attributesInfo[$section]['attrs'][$name];
185  unset($this->$name);
186  }
187  }
188 
189  /* Ensure that we've a valid acl_category set */
190  if (empty($this->acl_category)) {
191  $tmp = pluglist::pluginInfos(get_class($this));
192  if (isset($tmp['plCategory'])) {
193  $c = key($tmp['plCategory']);
194  if (is_numeric($c)) {
195  $c = $tmp['plCategory'][0];
196  }
197  $this->acl_category = $c.'/';
198  }
199  }
200 
201  /* Handle read only */
202  if ($this->dn != 'new') {
203  /* Check if this entry was opened in read only mode */
204  if (isset($_POST['open_readonly'])) {
205  if (session::global_is_set('LOCK_CACHE')) {
206  $cache = session::get('LOCK_CACHE');
207  if (isset($cache['READ_ONLY'][$this->dn])) {
208  $this->read_only = TRUE;
209  }
210  }
211  }
212 
213  /* Save current dn as acl_base */
214  $this->acl_base = $this->dn;
215  }
216 
217  /* Load LDAP data */
218  if (($this->dn != 'new' && $this->dn !== NULL) || ($object !== NULL)) {
219  /* Load data to 'attrs' */
220  if ($object !== NULL) {
221  /* From object */
222  $this->attrs = $object->attrs;
223  if (isset($object->is_template)) {
224  $this->setTemplate($object->is_template);
225  }
226  } else {
227  /* From LDAP */
228  $ldap = $config->get_ldap_link();
229  $ldap->cat($this->dn);
230  $this->attrs = $ldap->fetch();
231  if (empty($this->attrs)) {
232  throw new NonExistingLdapNodeException('Could not open dn '.$this->dn);
233  }
234  if ($this->mainTab) {
235  /* Make sure that initially_was_account is TRUE if we loaded an LDAP node,
236  * even if it’s missing an objectClass */
237  $this->is_account = TRUE;
238  }
239  }
240 
241  /* Set the template flag according to the existence of objectClass fdTemplate */
242  if (isset($this->attrs['objectClass'])) {
243  if (in_array_ics ('fdTemplate', $this->attrs['objectClass'])) {
244  @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, 'found', 'Template check');
245  $this->setTemplate(TRUE);
246  $this->templateLoadAttrs($this->attrs);
247  }
248  }
249 
250  /* Is Account? */
251  if ($this->is_this_account($this->attrs)) {
252  $this->is_account = TRUE;
253  @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, 'found', 'Object check');
254  }
255  }
256 
257  /* Save initial account state */
258  $this->initially_was_account = $this->is_account;
259 
260  $this->loadAttributes();
261 
262  $this->prepareSavedAttributes();
263 
264  $this->orig_dn = $dn;
265 
266  if ($this->mainTab) {
267  $this->is_account = TRUE;
268  $this->entryCSN = getEntryCSN($this->dn);
269  }
270 
271  if (!isset($this->templatePath)) {
272  $this->templatePath = get_template_path('simpleplugin.tpl');
273  }
274 
275  if (is_array($this->inheritance)) {
276  /* Check group membership */
277  $ldap = $config->get_ldap_link();
278  $ldap->cd($config->current['BASE']);
279  foreach ($this->inheritance as $oc => $at) {
280  if ($this->mainTab) {
281  $filter = '(&(objectClass='.$oc.')('.$at.'='.ldap_escape_f($this->dn).'))';
282  } else {
283  $filter = '(&(objectClass='.$oc.')'.$this->getObjectClassFilter().'('.$at.'='.ldap_escape_f($this->dn).'))';
284  }
285  $ldap->search($filter, $this->attributes);
286  if ($ldap->count() == 1) {
287  $this->member_of_group = TRUE;
288  $attrs = $ldap->fetch();
289  $this->group_attrs = $attrs;
290  break;
291  }
292  }
293  }
294  }
295 
296  protected function loadAttributes()
297  {
298  // We load attributes values
299  // First the one flagged as preInit
300  foreach ($this->preInitAttributes as $attr) {
301  $this->attributesAccess[$attr]->setParent($this);
302  $this->attributesAccess[$attr]->loadValue($this->attrs);
303  }
304  // Then the others
305  foreach ($this->attributesInfo as &$sectionInfo) {
306  foreach ($sectionInfo['attrs'] as $name => &$attr) {
307  if (in_array($name, $this->preInitAttributes)) {
308  /* skip the preInit ones */
309  continue;
310  }
311  $attr->setParent($this);
312  $attr->loadValue($this->attrs);
313  }
314  unset($attr);
315  }
316  unset($sectionInfo);
317  }
318 
319  function is_this_account($attrs)
320  {
321  $found = TRUE;
322  foreach ($this->objectclasses as $obj) {
323  if (preg_match('/^top$/i', $obj)) {
324  continue;
325  }
326  if (!isset($attrs['objectClass']) || !in_array_ics ($obj, $attrs['objectClass'])) {
327  $found = FALSE;
328  break;
329  }
330  }
331  return $found;
332  }
333 
334  function setTemplate ($bool)
335  {
336  $this->is_template = $bool;
337  if ($this->is_template && $this->mainTab) {
338  /* Unshift special section for template infos */
339  $this->attributesInfo = array_merge(
340  array(
341  '_template' => array(
342  'class' => array('fullwidth'),
343  'name' => _('Template settings'),
344  'attrs' => array(
345  '_template_cn' => new StringAttribute(
346  _('Template name'), _('This is the name of the template'),
347  '_template_cn', TRUE,
348  '', 'template_cn'
349  )
350  )
351  ),
352  '_template_dummy' => array(
353  'class' => array('invisible'),
354  'name' => '_template_dummy',
355  'attrs' => array()
356  )
357  ),
358  $this->attributesInfo
359  );
360  $this->attributesAccess['_template_cn'] =& $this->attributesInfo['_template']['attrs']['_template_cn'];
361  $this->attributesAccess['_template_cn']->setInLdap(FALSE);
362  $this->attributesAccess['_template_cn']->setValue($this->_template_cn);
363  $this->attributesAccess['_template_cn']->setParent($this);
364  unset($this->_template_cn);
365  }
366  }
367 
368  protected function templateLoadAttrs(array $template_attrs)
369  {
370  if ($this->mainTab) {
371  $this->_template_cn = $template_attrs['cn'][0];
372  }
373  $this->attrs = templateHandling::fieldsFromLDAP($template_attrs);
374  }
375 
376  protected function templateSaveAttrs()
377  {
378  global $config;
379  $ldap = $config->get_ldap_link();
380  $ldap->cat($this->dn);
381  $template_attrs = $ldap->fetch();
382  if (!$template_attrs) {
383  if (!$this->mainTab) {
384  trigger_error('It seems main tab has not been saved.');
385  }
386  $template_attrs = array(
387  'objectClass' => array('fdTemplate'),
388  'fdTemplateField' => array()
389  );
390  }
391  $template_attrs = templateHandling::fieldsToLDAP($template_attrs, $this->attrs);
392  if ($this->mainTab) {
393  $template_attrs['cn'] = $this->_template_cn;
394  }
395  return $template_attrs;
396  }
397 
401  {
402  if (!empty($this->objectclasses)) {
403  return '(&(objectClass='.implode(')(objectClass=', $this->objectclasses).'))';
404  } else {
405  return '';
406  }
407  }
408 
414  public function __get($name)
415  {
416  if ($name == 'attributes') {
417  $plugin = $this;
418  return array_filter(array_keys($this->attributesAccess),
419  function ($a) use ($plugin)
420  {
421  return $plugin->attributesAccess[$a]->isInLdap();
422  }
423  );
424  } elseif (isset($this->attributesAccess[$name])) {
425  return $this->attributesAccess[$name]->getValue();
426  } else {
427  /* Calling default behaviour */
428  return $this->$name;
429  }
430  }
431 
436  public function __set($name, $value)
437  {
438  if ($name == 'attributes') {
439  trigger_error('Tried to set obsolete attribute "attributes" (it is now dynamic)');
440  } elseif (isset($this->attributesAccess[$name])) {
441  $this->attributesAccess[$name]->setValue($value);
442  } else {
443  /* Calling default behaviour */
444  $this->$name = $value;
445  }
446  }
447 
452  public function __isset($name)
453  {
454  if ($name == 'attributes') {
455  return TRUE;
456  }
457  return isset($this->attributesAccess[$name]);
458  }
459 
462  public function compute_dn()
463  {
464  global $config;
465  if (!$this->mainTab) {
466  msg_dialog::display(_('Fatal error'), _('Only main tab can compute dn'), FATAL_ERROR_DIALOG);
467  exit;
468  }
469  if (!isset($this->parent) || !($this->parent instanceof simpleTabs)) {
471  _('Fatal error'),
472  sprintf(
473  _('Could not compute dn: no parent tab class for "%s"'),
474  get_class($this)
475  ),
476  FATAL_ERROR_DIALOG
477  );
478  exit;
479  }
480  $infos = $this->parent->objectInfos();
481  if ($infos === FALSE) {
483  _('Fatal error'),
484  sprintf(
485  _('Could not compute dn: could not find objectType infos from tab class "%s"'),
486  get_class($this->parent)
487  ),
488  FATAL_ERROR_DIALOG
489  );
490  exit;
491  }
492  $attr = $infos['mainAttr'];
493  $ou = $infos['ou'];
494  if (isset($this->base)) {
495  $base = $this->base;
496  } else {
497  $base = $config->current['BASE'];
498  }
499  if ($this->is_template) {
500  $dn = 'cn='.ldap_escape_dn($this->_template_cn).',ou=templates,'.$ou.$base;
501  return $dn;
502  }
503  return $attr.'='.ldap_escape_dn($this->attributesAccess[$attr]->computeLdapValue()).','.$ou.$base;
504  }
505 
506  protected function addAttribute($section, $attr)
507  {
508  $name = $attr->getLdapName();
509  $this->attributesInfo[$section]['attrs'][$name] = $attr;
510  $this->attributesAccess[$name] =& $this->attributesInfo[$section]['attrs'][$name];
511  $this->attributesAccess[$name]->setParent($this);
512  unset($this->$name);
513  }
514 
515  protected function removeAttribute($section, $id)
516  {
517  unset($this->attributesInfo[$section]['attrs'][$id]);
518  unset($this->attributesAccess[$id]);
519  }
520 
530  function get_allowed_bases()
531  {
532  global $config;
533  $deps = array();
534 
535  /* Is this a new object ? Or just an edited existing object */
536  foreach ($config->idepartments as $dn => $name) {
537  if (!$this->initially_was_account && $this->acl_is_createable($dn)) {
538  $deps[$dn] = $name;
539  } elseif ($this->initially_was_account && $this->acl_is_moveable($dn)) {
540  $deps[$dn] = $name;
541  }
542  }
543 
544  /* Add current base */
545  if (isset($this->base) && isset($config->idepartments[$this->base])) {
546  $deps[$this->base] = $config->idepartments[$this->base];
547  } elseif (strtolower($this->dn) != strtolower($config->current['BASE'])) {
548  trigger_error('Cannot return list of departments, no default base found in class '.get_class($this).'. (base is "'.$this->base.'")');
549  }
550  return $deps;
551  }
552 
558  function set_acl_base($base)
559  {
560  $this->acl_base = $base;
561  }
562 
568  function set_acl_category($category)
569  {
570  $this->acl_category = "$category/";
571  }
572 
582  function move($src_dn, $dst_dn)
583  {
584  global $config, $ui;
585 
586  /* Do not move if only case has changed */
587  if (strtolower($src_dn) == strtolower($dst_dn)) {
588  return TRUE;
589  }
590 
591  /* Try to move with ldap routines */
592  $ldap = $config->get_ldap_link();
593  $ldap->cd($config->current['BASE']);
594  $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
595  if (!$ldap->rename_dn($src_dn, $dst_dn)) {
596  logging::log('debug', 'Ldap Protocol v3 implementation error, ldap_rename failed.',
597  "FROM: $src_dn -- TO: $dst_dn", array(), $ldap->get_error());
598  @DEBUG(DEBUG_LDAP, __LINE__, __FUNCTION__, __FILE__, "Rename failed FROM: $src_dn -- TO: $dst_dn",
599  'Ldap Protocol v3 implementation error. Error:'.$ldap->get_error());
600  return $ldap->get_error();
601  }
602 
603  /* Update userinfo if necessary */
604  if (preg_match('/'.preg_quote($src_dn, '/').'$/i', $ui->dn)) {
605  $ui_dn = preg_replace('/'.preg_quote($src_dn, '/').'$/i', $dst_dn, $ui->dn);
606  logging::log('view', 'acl/'.get_class($this), $this->dn, array(), 'Updated userinfo dn from "'.$ui->dn.'" to "'.$ui_dn.'"');
607  $ui->dn = $ui_dn;
608  }
609 
610  /* Check if departments were moved. If so, force the reload of config->departments */
611  $ldap->cd($dst_dn);
612  $ldap->search('(objectClass=gosaDepartment)', array('dn'));
613  if ($ldap->count()) {
614  $config->get_departments();
615  $config->make_idepartments();
616  $ui->reset_acl_cache();
617  }
618 
619  $this->handleForeignKeys($src_dn, $dst_dn);
620  return TRUE;
621  }
622 
623  function getRequiredAttributes()
624  {
625  $tmp = array();
626  foreach ($this->attributesAccess as $attr) {
627  if ($attr->isRequired()) {
628  $tmp[] = $attr->getLdapName();
629  }
630  }
631  return $tmp;
632  }
633 
634  function editing_group ()
635  {
636  if ($this->editing_group == NULL) {
637  if (isset($this->parent)) {
638  $this->editing_group = (get_class($this->parent->getBaseObject()) == 'ogroup');
639  } else {
640  return NULL;
641  }
642  }
643  return $this->editing_group;
644  }
645 
647  function readOnly()
648  {
649  return $this->read_only;
650  }
651 
654  function execute ()
655  {
656  @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->dn, "execute");
657 
658  /* Reset Lock message POST/GET check array, to prevent preg_match errors */
659  session::set('LOCK_VARS_TO_USE', array());
660  session::set('LOCK_VARS_USED_GET', array());
661  session::set('LOCK_VARS_USED_POST', array());
662  session::set('LOCK_VARS_USED_REQUEST', array());
663 
664  $this->displayPlugin = TRUE;
665  $this->header = "";
666 
667  if (is_object($this->dialog)) {
668  $dialogResult = $this->dialog->execute();
669  if ($dialogResult === FALSE) {
670  $this->closeDialog();
671  } else {
672  $this->header = $dialogResult;
673  $this->displayPlugin = FALSE;
674  return $this->header;
675  }
676  }
677 
678  if ($this->displayHeader) {
679  /* Show tab dialog headers */
680  if ($this->parent !== NULL) {
681  list($disabled, $buttonText, $text) = $this->getDisplayHeaderInfos();
682  $this->header = $this->show_header(
683  $buttonText,
684  $text,
685  $this->is_account,
686  $disabled,
687  get_class($this).'_modify_state'
688  );
689  if (!$this->is_account) {
690  $this->displayPlugin = FALSE;
691  return $this->header.$this->inheritanceDisplay();
692  }
693  } elseif (!$this->is_account) {
694  $plInfo = pluglist::pluginInfos(get_class($this));
695  $this->header = '<img alt="" src="geticon.php?context=status&amp;icon=dialog-error&amp;size=16" align="middle"/>&nbsp;<b>'.
696  msgPool::noValidExtension($plInfo['plShortName'])."</b>";
697  $this->header .= back_to_main();
698  $this->displayPlugin = FALSE;
699  return $this->header.$this->inheritanceDisplay();
700  }
701  }
702 
703  $smarty = get_smarty();
704 
705  $this->renderAttributes(FALSE);
706  $smarty->assign("hiddenPostedInput", get_class($this)."_posted");
707  if (isset($this->focusedField)) {
708  $smarty->assign("focusedField", $this->focusedField);
709  unset($this->focusedField);
710  } else {
711  $smarty->assign("focusedField", key($this->attributesAccess));
712  }
713 
714  return $this->header.$smarty->fetch($this->templatePath);
715  }
716 
717  public function getDisplayHeaderInfos()
718  {
719  $plInfo = pluglist::pluginInfos(get_class($this));
720  $disabled = $this->acl_skip_write();
721  if ($this->is_account) {
722  $depends = array();
723  if (isset($plInfo['plDepending'])) {
724  foreach ($plInfo['plDepending'] as $plugin) {
725  if (isset($this->parent->by_object[$plugin]) &&
726  $this->parent->by_object[$plugin]->is_account) {
727  $disabled = TRUE;
728  $dependPlInfos = pluglist::pluginInfos($plugin);
729  $depends[] = $dependPlInfos['plShortName'];
730  }
731  }
732  }
733  $buttonText = msgPool::removeFeaturesButton($plInfo['plShortName']);
734  $text = msgPool::featuresEnabled($plInfo['plShortName'], $depends);
735  } else {
736  $depends = array();
737  $conflicts = array();
738  if (isset($plInfo['plDepends'])) {
739  foreach ($plInfo['plDepends'] as $plugin) {
740  if (isset($this->parent->by_object[$plugin]) &&
741  !$this->parent->by_object[$plugin]->is_account) {
742  $disabled = TRUE;
743  $dependPlInfos = pluglist::pluginInfos($plugin);
744  $depends[] = $dependPlInfos['plShortName'];
745  }
746  }
747  }
748  if (isset($plInfo['plConflicts'])) {
749  foreach ($plInfo['plConflicts'] as $plugin) {
750  if (isset($this->parent->by_object[$plugin]) &&
751  $this->parent->by_object[$plugin]->is_account) {
752  $disabled = TRUE;
753  $conflictPlInfos = pluglist::pluginInfos($plugin);
754  $conflicts[] = $conflictPlInfos['plShortName'];
755  }
756  }
757  }
758  $buttonText = msgPool::addFeaturesButton($plInfo['plShortName']);
759  $text = msgPool::featuresDisabled($plInfo['plShortName'], $depends, $conflicts);
760  }
761  return array($disabled,$buttonText,$text);
762  }
763 
775  function show_header($button_text, $text, $plugin_enabled, $button_disabled = FALSE, $name = 'modify_state')
776  {
777  if ($button_disabled || ((!$this->acl_is_createable() && !$plugin_enabled) || (!$this->acl_is_removeable() && $plugin_enabled))) {
778  $state = 'disabled="disabled"';
779  } else {
780  $state = '';
781  }
782  $display = '<div width="100%"><p><b>'.$text.'</b><br/>'."\n";
783  $display .= '<input type="submit" value="'.$button_text.'" name="'.$name.'" '.$state.'></p></div><hr class="separator"/>';
784 
785  return $display;
786  }
787 
792  function attrIsWriteable($attr)
793  {
794  if (!is_object($attr)) {
795  $attr = $this->attributesAccess[$attr];
796  }
797  if ($attr->getLdapName() == 'base') {
798  if (!$this->acl_skip_write() && (!$this->initially_was_account || $this->acl_is_moveable() || $this->acl_is_removeable())) {
799  return TRUE;
800  } else {
801  return FALSE;
802  }
803  }
804  return $this->acl_is_writeable($attr->getAcl(), $this->acl_skip_write());
805  }
806 
807  function renderAttributes($readOnly = FALSE)
808  {
809  global $ui;
810  $smarty = get_smarty();
811 
812  if ($this->is_template) {
813  $smarty->assign('template_cnACL', $ui->get_permissions($this->acl_base, $this->acl_category.'template', 'template_cn', $this->acl_skip_write()));
814  }
815 
816  /* Handle rights to modify the base */
817  if (isset($this->attributesAccess['base'])) {
818  if ($this->attrIsWriteable('base')) {
819  $smarty->assign('baseACL', 'rw');
820  } else {
821  $smarty->assign('baseACL', 'r');
822  }
823  }
824 
825  $sections = array();
826  foreach ($this->attributesInfo as $section => $sectionInfo) {
827  $legend = $sectionInfo['name'];
828  if (isset($sectionInfo['icon'])) {
829  $legend = '<img '.
830  'src="'.htmlentities($sectionInfo['icon'], ENT_COMPAT, 'UTF-8').'" '.
831  'alt="section '.$sectionInfo['name'].' icon" '.
832  'title="section '.$sectionInfo['name'].' icon" '.
833  '/>'.$legend;
834  }
835  $smarty->assign("section", $legend);
836  $smarty->assign("sectionId", $section);
837  if (isset($sectionInfo['class'])) {
838  $smarty->assign("sectionClasses", ' '.join(' ', $sectionInfo['class']));
839  } else {
840  $smarty->assign("sectionClasses", '');
841  }
842  $attributes = array();
843  foreach ($sectionInfo['attrs'] as $attr) {
844  if ($attr->getAclInfo() !== FALSE) {
845  // We assign ACLs so that attributes can use them in their template code
846  $smarty->assign($attr->getAcl()."ACL", $this->aclGetPermissions($attr->getAcl(), NULL, $this->acl_skip_write()));
847  }
848  $attr->renderAttribute($attributes, $readOnly);
849  }
850  $smarty->assign("attributes", $attributes);
851  // We fetch each section with the section template
852  if (isset($sectionInfo['template'])) {
853  $displaySection = $smarty->fetch($sectionInfo['template']);
854  } else {
855  $displaySection = $smarty->fetch(get_template_path('simpleplugin_section.tpl'));
856  }
857  $sections[$section] = $displaySection;
858  }
859  $smarty->assign("sections", $sections);
860  }
861 
862  function inheritanceDisplay()
863  {
864  if (!$this->member_of_group) {
865  return "";
866  }
867  $class = get_class($this);
868  $attrsWrapper = new stdClass();
869  $attrsWrapper->attrs = $this->group_attrs;
870  $group = new $class($this->group_attrs['dn'], $attrsWrapper, $this->parent, $this->mainTab);
871  $smarty = get_smarty();
872 
873  $group->renderAttributes(TRUE);
874  $smarty->assign("hiddenPostedInput", get_class($this)."_posted");
875 
876  return "<h1>Inherited information:</h1><div></div>\n".$smarty->fetch($this->templatePath);
877  }
878 
883  function openDialog ($dialog)
884  {
885  $this->dialog = $dialog;
886  }
887 
890  function closeDialog ()
891  {
892  $this->dialog = NULL;
893  }
894 
895  public function setNeedEditMode ($bool)
896  {
897  $this->needEditMode = $bool;
898  }
899 
900  protected function acl_skip_write ()
901  {
902  return ($this->needEditMode && !session::is_set('edit'));
903  }
904 
906  function acl_is_writeable($attribute, $skipWrite = FALSE)
907  {
908  return preg_match('/w/', $this->aclGetPermissions($attribute, NULL, $skipWrite));
909  }
910 
916  function acl_is_readable($attribute)
917  {
918  return preg_match('/r/', $this->aclGetPermissions($attribute));
919  }
920 
926  function acl_is_createable($base = NULL)
927  {
928  return preg_match('/c/', $this->aclGetPermissions('0', $base));
929  }
930 
936  function acl_is_removeable($base = NULL)
937  {
938  return preg_match('/d/', $this->aclGetPermissions('0', $base));
939  }
940 
946  function acl_is_moveable($base = NULL)
947  {
948  return preg_match('/m/', $this->aclGetPermissions('0', $base));
949  }
950 
952  function aclGetPermissions($attribute = '0', $base = NULL, $skipWrite = FALSE)
953  {
954  if (isset($this->parent) && isset($this->parent->ignoreAcls) && $this->parent->ignoreAcls) {
955  return 'cdmr'.($skipWrite ? '' : 'w');
956  }
957  $ui = get_userinfo();
958  $skipWrite |= $this->readOnly();
959  if ($base === NULL) {
960  $base = $this->acl_base;
961  }
962  return $ui->get_permissions($base, $this->acl_category.get_class($this), $attribute, $skipWrite);
963  }
964 
968  {
969  if (!$this->initially_was_account || !$this->acl_is_removeable()) {
970  return;
971  }
972  $this->prepare_remove();
973  if ($this->is_template && (!defined('_OLD_TEMPLATES_') || !_OLD_TEMPLATES_)) {
974  $this->attrs = $this->templateSaveAttrs();
975  $this->saved_attributes = array();
976  }
977  /* Pre hooks */
978  $errors = $this->pre_remove();
979  if (!empty($errors)) {
980  return $errors;
981  }
982  $errors = $this->ldap_remove();
983  if (!empty($errors)) {
984  return $errors;
985  }
986  $this->post_remove();
987  return;
988  }
989 
990  /* Remove FusionDirectory attributes */
991  protected function prepare_remove ()
992  {
993  global $config;
994  $this->attrs = array();
995 
996  if (!$this->mainTab) {
997  /* include global link_info */
998  $ldap = $config->get_ldap_link();
999 
1000  /* Get current objectClasses in order to add the required ones */
1001  $ldap->cat($this->dn);
1002  $tmp = $ldap->fetch ();
1003  $oc = array();
1004  if ($this->is_template) {
1005  if (isset($tmp['fdTemplateField'])) {
1006  foreach ($tmp['fdTemplateField'] as $tpl_field) {
1007  if (preg_match('/^objectClass:(.+)$/', $tpl_field, $m)) {
1008  $oc[] = $m[1];
1009  }
1010  }
1011  }
1012  } else {
1013  if (isset($tmp['objectClass'])) {
1014  $oc = $tmp['objectClass'];
1015  unset($oc['count']);
1016  }
1017  }
1018 
1019  /* Remove objectClasses from entry */
1020  $this->attrs['objectClass'] = array_remove_entries_ics($this->objectclasses, $oc);
1021 
1022  /* Unset attributes from entry */
1023  foreach ($this->attributes as $val) {
1024  $this->attrs["$val"] = array();
1025  }
1026  }
1027  }
1028 
1029  protected function pre_remove ()
1030  {
1031  if ($this->initially_was_account) {
1032  return $this->handle_pre_events('remove');
1033  }
1034  }
1035 
1036  protected function ldap_remove ()
1037  {
1038  global $config;
1039  $ldap = $config->get_ldap_link();
1040  if ($this->mainTab) {
1041  $ldap->rmdir_recursive($this->dn);
1042  } else {
1043  $this->cleanup();
1044  $ldap->cd($this->dn);
1045  $ldap->modify($this->attrs);
1046  }
1047  $this->ldap_error = $ldap->get_error();
1048 
1049  if ($ldap->success()) {
1050  return array();
1051  } else {
1052  return array(msgPool::ldaperror($this->ldap_error, $this->dn, LDAP_MOD, get_class()));
1053  }
1054  }
1055 
1056  protected function post_remove ()
1057  {
1058  logging::log('remove', 'plugin/'.get_class($this), $this->dn, array_keys($this->attrs), $this->ldap_error);
1059 
1060  /* Optionally execute a command after we're done */
1061  $errors = $this->handle_post_events('remove');
1062  if (!empty($errors)) {
1063  msg_dialog::displayChecks($errors);
1064  }
1065  }
1066 
1069  function save_object ()
1070  {
1071  @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->dn, 'save_object');
1072  if ($this->displayHeader && isset($_POST[get_class($this).'_modify_state'])) {
1073  if ($this->is_account && $this->acl_is_removeable()) {
1074  $this->is_account = FALSE;
1075  } elseif (!$this->is_account && $this->acl_is_createable()) {
1076  $this->is_account = TRUE;
1077  }
1078  }
1079  if (isset($_POST[get_class($this).'_posted'])) {
1080  // If our form has been posted
1081  // A first pass that loads the post values
1082  foreach ($this->attributesInfo as $section => &$sectionInfo) {
1083  foreach ($sectionInfo['attrs'] as &$attr) {
1084  if ($this->attrIsWriteable($attr)) {
1085  // Each attribute know how to read its value from POST
1086  $attr->loadPostValue();
1087  }
1088  }
1089  unset ($attrs);
1090  }
1091  unset($sectionInfo);
1092  // A second one that applies them. That allow complex stuff such as attribute disabling
1093  foreach ($this->attributesInfo as $section => &$sectionInfo) {
1094  foreach ($sectionInfo['attrs'] as &$attr) {
1095  if ($this->attrIsWriteable($attr)) {
1096  // Each attribute know how to read its value from POST
1097  $attr->applyPostValue();
1098  }
1099  }
1100  unset ($attrs);
1101  }
1102  unset($sectionInfo);
1103  }
1104  }
1105 
1106  protected function prepareSavedAttributes()
1107  {
1108  /* Prepare saved attributes */
1109  $this->saved_attributes = $this->attrs;
1110  foreach (array_keys($this->saved_attributes) as $index) {
1111  if (is_numeric($index)) {
1112  unset($this->saved_attributes[$index]);
1113  continue;
1114  }
1115 
1116  if (!in_array_ics($index, $this->attributes) && strcasecmp('objectClass', $index)) {
1117  unset($this->saved_attributes[$index]);
1118  continue;
1119  }
1120 
1121  if (isset($this->saved_attributes[$index][0])) {
1122  if (!isset($this->saved_attributes[$index]['count'])) {
1123  $this->saved_attributes[$index]['count'] = count($this->saved_attributes[$index]);
1124  }
1125  if ($this->saved_attributes[$index]['count'] == 1) {
1126  $tmp = $this->saved_attributes[$index][0];
1127  unset($this->saved_attributes[$index]);
1128  $this->saved_attributes[$index] = $tmp;
1129  continue;
1130  }
1131  }
1132  unset($this->saved_attributes[$index]['count']);
1133  }
1134  }
1135 
1140  function cleanup()
1141  {
1142  foreach ($this->attrs as $index => $value) {
1143 
1144  /* Convert arrays with one element to non arrays, if the saved
1145  attributes are no array, too */
1146  if (is_array($this->attrs[$index]) &&
1147  count ($this->attrs[$index]) == 1 &&
1148  isset($this->saved_attributes[$index]) &&
1149  !is_array($this->saved_attributes[$index])) {
1150  $this->attrs[$index] = $this->attrs[$index][0];
1151  }
1152 
1153  /* Remove emtpy arrays if they do not differ */
1154  if (is_array($this->attrs[$index]) &&
1155  count($this->attrs[$index]) == 0 &&
1156  !isset($this->saved_attributes[$index])) {
1157  unset ($this->attrs[$index]);
1158  continue;
1159  }
1160 
1161  /* Remove single attributes that do not differ */
1162  if (!is_array($this->attrs[$index]) &&
1163  isset($this->saved_attributes[$index]) &&
1164  !is_array($this->saved_attributes[$index]) &&
1165  $this->attrs[$index] == $this->saved_attributes[$index]) {
1166  unset ($this->attrs[$index]);
1167  continue;
1168  }
1169 
1170  /* Remove arrays that do not differ */
1171  if (is_array($this->attrs[$index]) &&
1172  isset($this->saved_attributes[$index]) &&
1173  is_array($this->saved_attributes[$index])) {
1174  if (!array_differs($this->attrs[$index], $this->saved_attributes[$index])) {
1175  unset ($this->attrs[$index]);
1176  continue;
1177  }
1178  }
1179  }
1180  }
1181 
1182  function prepareNextCleanup()
1183  {
1184  /* Update saved attributes and ensure that next cleanups will be successful too */
1185  foreach ($this->attrs as $name => $value) {
1186  $this->saved_attributes[$name] = $value;
1187  }
1188  }
1189 
1192  function save ()
1193  {
1194  @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->dn, "save");
1195  $this->prepare_save();
1196  if ($this->is_template && (!defined('_OLD_TEMPLATES_') || !_OLD_TEMPLATES_)) {
1197  $errors = templateHandling::checkFields($this->attrs);
1198  if (!empty($errors)) {
1199  return $errors;
1200  }
1201  $this->attrs = $this->templateSaveAttrs();
1202  $this->saved_attributes = array();
1203  }
1204  $this->cleanup();
1205  if (!$this->shouldSave()) {
1206  return; /* Nothing to do here */
1207  }
1208  /* Pre hooks */
1209  $errors = $this->pre_save();
1210  if (!empty($errors)) {
1211  return $errors;
1212  }
1213  /* LDAP save itself */
1214  $errors = $this->ldap_save();
1215  if (!empty($errors)) {
1216  return $errors;
1217  }
1218  $this->prepareNextCleanup();
1219  /* Post hooks and logging */
1220  $this->post_save();
1221  return;
1222  }
1223 
1224  protected function shouldSave()
1225  {
1226  if ($this->mainTab && !$this->initially_was_account) {
1227  return TRUE;
1228  }
1229  return !empty($this->attrs);
1230  }
1231 
1232  /* Used by prepare_save and template::apply */
1233  public function mergeObjectClasses(array $oc)
1234  {
1235  return array_merge_unique($oc, $this->objectclasses);
1236  }
1237 
1238  protected function prepare_save ()
1239  {
1240  global $config;
1241  /* prepare $this->attrs */
1242  $ldap = $config->get_ldap_link();
1243 
1244  $this->entryCSN = '';
1245 
1246  /* Start with empty array */
1247  $this->attrs = array();
1248 
1249  /* Get current objectClasses in order to add the required ones */
1250  $ldap->cat($this->dn, array('fdTemplateField', 'objectClass'));
1251 
1252  $tmp = $ldap->fetch();
1253  $oc = array();
1254 
1255  if ($this->is_template) {
1256  if (isset($tmp['fdTemplateField'])) {
1257  foreach ($tmp['fdTemplateField'] as $tpl_field) {
1258  if (preg_match('/^objectClass:(.+)$/', $tpl_field, $m)) {
1259  $oc[] = $m[1];
1260  }
1261  }
1262  }
1263  } else {
1264  if (isset($tmp['objectClass'])) {
1265  $oc = $tmp['objectClass'];
1266  unset($oc['count']);
1267  }
1268  }
1269 
1270  $this->attrs['objectClass'] = $this->mergeObjectClasses($oc);
1271 
1272  /* Fill attributes LDAP values into the attrs array */
1273  foreach ($this->attributesInfo as $section => $sectionInfo) {
1274  foreach ($sectionInfo['attrs'] as $attr) {
1275  $attr->fillLdapValue($this->attrs);
1276  }
1277  }
1278  /* Some of them have post-filling hook */
1279  foreach ($this->attributesInfo as $section => $sectionInfo) {
1280  foreach ($sectionInfo['attrs'] as $attr) {
1281  $attr->fillLdapValueHook($this->attrs);
1282  }
1283  }
1284  }
1285 
1286  protected function pre_save ()
1287  {
1288  if ($this->initially_was_account) {
1289  return $this->handle_pre_events('modify');
1290  } else {
1291  return $this->handle_pre_events('add');
1292  }
1293  }
1294 
1295  /* Returns an array with the errors or an empty array */
1296  protected function ldap_save ()
1297  {
1298  global $config;
1299 
1300  /* Check if this is a new entry ... add/modify */
1301  $ldap = $config->get_ldap_link();
1302  $ldap->cat($this->dn, array("objectClass"));
1303  if ($this->mainTab && !$this->initially_was_account) {
1304  if ($ldap->count()) {
1305  return array(sprintf(_('There is already an entry with the same dn : %s'), $this->dn));
1306  }
1307  $ldap->cd($config->current['BASE']);
1308  $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $this->dn));
1309  $action = "add";
1310  } else {
1311  if (!$ldap->count()) {
1312  return array(sprintf(_('The entry %s is not existing'), $this->dn));
1313  }
1314  $action = "modify";
1315  }
1316 
1317  $ldap->cd($this->dn);
1318  $ldap->$action($this->attrs);
1319  $this->ldap_error = $ldap->get_error();
1320 
1321  /* Check for errors */
1322  if (!$ldap->success()) {
1323  return array(msgPool::ldaperror($this->ldap_error, $this->dn, 0, get_class()));
1324  }
1325  return array();
1326  }
1327 
1332  protected function post_save()
1333  {
1334  /* Propagate and log the event */
1335  if ($this->initially_was_account) {
1336  $errors = $this->handle_post_events('modify');
1337  logging::log('modify', 'plugin/'.get_class($this), $this->dn, array_keys($this->attrs), $this->ldap_error);
1338  } else {
1339  $errors = $this->handle_post_events('add');
1340  logging::log('create', 'plugin/'.get_class($this), $this->dn, array_keys($this->attrs), $this->ldap_error);
1341  }
1342  if (!empty($errors)) {
1343  msg_dialog::displayChecks($errors);
1344  }
1345  }
1346 
1356  protected function handle_hooks($when, $mode, array $addAttrs = array())
1357  {
1358  switch ($mode) {
1359  case 'add':
1360  return $this->callHook($when.'CREATE', $addAttrs);
1361 
1362  case 'modify':
1363  return $this->callHook($when.'MODIFY', $addAttrs);
1364 
1365  case 'remove':
1366  return $this->callHook($when.'REMOVE', $addAttrs);
1367 
1368  default:
1369  trigger_error(sprintf('Invalid %s event type given: "%s"! Valid types are: add, modify, remove.', strtolower($when), $mode));
1370  break;
1371  }
1372  }
1373 
1377  function handle_post_events($mode, array $addAttrs = array())
1378  {
1379  /* Update foreign keys */
1380  if ($mode == 'remove') {
1381  $this->handleForeignKeys($this->dn, NULL);
1382  } elseif ($mode == 'modify') {
1383  $this->handleForeignKeys();
1384  }
1385  return $this->handle_hooks('POST', $mode, $addAttrs);
1386  }
1387 
1392  function handle_pre_events($mode, array $addAttrs = array())
1393  {
1394  $this->ldap_error = '';
1395  return $this->handle_hooks('PRE', $mode, $addAttrs);
1396  }
1397 
1403  function callHook($cmd, array $addAttrs = array(), &$returnOutput = array(), &$returnCode = NULL)
1404  {
1405  if ($this->is_template) {
1406  return array();
1407  }
1408  global $config;
1409 
1410  $commands = $config->searchHooks(get_class($this), $cmd);
1411  $messages = array();
1412 
1413  foreach ($commands as $command) {
1414  // Walk trough attributes list and add the plugins attributes.
1415  foreach ($this->attributes as $attr) {
1416  $addAttrs[$attr] = $this->$attr;
1417  }
1418 
1419  $ui = get_userinfo();
1420 
1421  $addAttrs['callerDN'] = $ui->dn;
1422  $addAttrs['callerCN'] = $ui->cn;
1423  $addAttrs['callerUID'] = $ui->uid;
1424  $addAttrs['callerSN'] = $ui->sn;
1425  $addAttrs['callerGIVENNAME'] = $ui->givenName;
1426 
1427  $addAttrs['dn'] = $this->dn;
1428  $addAttrs['location'] = $config->current['NAME'];
1429 
1430  if (isset($this->parent->by_object)) {
1431  foreach ($this->parent->by_object as $object) {
1432  foreach ($object->attributes as $attr) {
1433  if (!isset($addAttrs[$attr])) {
1434  $addAttrs[$attr] = $object->$attr;
1435  }
1436  }
1437  }
1438  }
1439 
1440  if (!isset($addAttrs['base']) && isset($this->base)) {
1441  $addAttrs['base'] = $this->base;
1442  }
1443 
1444  $command = templateHandling::parseString($command, $addAttrs, 'escapeshellarg');
1445 
1446  @DEBUG(DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execute");
1447  exec($command, $arr, $returnCode);
1448  $returnOutput = $arr;
1449 
1450  if ($returnCode != 0) {
1451  $str = implode("\n", $arr);
1452  @DEBUG(DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execution failed code: ".$returnCode);
1453  $message = msgPool::cmdexecfailed($cmd, $command, get_class($this));
1454  if (!empty($str)) {
1455  $message .= "Result: ".$str;
1456  }
1457  $messages[] = $message;
1458  } elseif (is_array($arr)) {
1459  $str = implode("\n", $arr);
1460  @DEBUG(DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$str);
1461  if (!empty($str) && $config->get_cfg_value("displayHookOutput", "FALSE") == "TRUE") {
1462  msg_dialog::display('['.get_class($this).' '.strtolower($cmd)."hook] $command", $str, INFO_DIALOG);
1463  }
1464  }
1465  }
1466  return $messages;
1467  }
1468 
1471  function check ()
1472  {
1473  @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->dn, 'check');
1474  $messages = array();
1475 
1476  foreach ($this->attributesInfo as $sectionInfo) {
1477  foreach ($sectionInfo['attrs'] as $attr) {
1478  $error = $attr->check();
1479  if (!empty($error)) {
1480  if (is_array($error)) {
1481  $messages = array_merge($messages, $error);
1482  } else {
1483  $messages[] = $error;
1484  }
1485  }
1486  }
1487  }
1488 
1489  $error = $this->callHook('CHECK', array('nbCheckErrors' => count($messages)), $returnOutput);
1490  if (!empty($error)) {
1491  $messages = array_merge($messages, $error);
1492  }
1493  if (!empty($returnOutput)) {
1494  $messages[] = join("\n", $returnOutput);
1495  }
1496 
1497  /* Check entryCSN */
1498  if (!empty($this->entryCSN)) {
1499  $current_csn = getEntryCSN($this->dn);
1500  if (($current_csn != $this->entryCSN) && !empty($current_csn)) {
1501  $this->entryCSN = $current_csn;
1502  $messages[] = _('The object has changed since opened in FusionDirectory. All changes that may be done by others will get lost if you save this entry!');
1503  }
1504  }
1505 
1506  return $messages;
1507  }
1508 
1509  function handleForeignKeys ($olddn = NULL, $newdn = NULL, $mode = 'move')
1510  {
1511  if (($olddn !== NULL) && ($olddn == $newdn)) {
1512  return;
1513  }
1514  if ($this->is_template) {
1515  return;
1516  }
1517  $this->browseForeignKeys(
1518  'handle_'.$mode,
1519  $olddn,
1520  $newdn
1521  );
1522  }
1523 
1524  function browseForeignKeys($mode, $param1 = NULL, $param2 = NULL)
1525  {
1526  if (preg_match('/^handle_/', $mode)) {
1527  $olddn = $param1;
1528  $newdn = $param2;
1529  $classes = array(get_class($this));
1530  } elseif ($mode == 'references') {
1531  $classes = array_keys($this->parent->by_object);
1532  }
1533  // We group by objetType concerned
1534  $foreignRefs = array();
1535  foreach ($classes as $tabclass) {
1536  $infos = pluglist::pluginInfos($tabclass);
1537  foreach ($infos['plForeignRefs'] as $field => $refs) {
1538  if (preg_match('/^handle_/', $mode)) {
1539  if (($newdn !== NULL) && ($field != 'dn') && ($mode == 'handle_move')) {
1540  // Move action, ignore other fields than dn
1541  continue;
1542  } elseif (($newdn === NULL) && ($olddn === NULL) && (($field == 'dn') || (!$this->attributeHaveChanged($field)))) {
1543  // Edit action, ignore dn changes or attributes which did not change
1544  continue;
1545  }
1546  // else = delete action, all fields are concerned, nothing to do here
1547  }
1548  foreach ($refs as $ref) {
1549  $class = $ref[0];
1550  $ofield = $ref[1];
1551  $filter = $ref[2];
1552  $cinfos = pluglist::pluginInfos($class);
1553  if (is_subclass_of($class, 'simpleService')) {
1554  $objectTypes = array('server');
1555  } else {
1556  $objectTypes = array();
1557  foreach ($cinfos['plObjectType'] as $key => $objectType) {
1558  if (!is_numeric($key)) {
1559  $objectType = $key;
1560  }
1561  if (preg_match('/^ogroup-/i', $objectType)) {
1562  $objectType = 'ogroup';
1563  }
1564  $objectTypes[] = $objectType;
1565  }
1566  $objectTypes = array_unique($objectTypes);
1567  }
1568  foreach ($objectTypes as $objectType) {
1569  if (preg_match('/^handle_/', $mode)) {
1570  if ($field == 'dn') {
1571  $oldvalue = $olddn;
1572  $newvalue = $newdn;
1573  } elseif (($olddn !== NULL) && ($newdn === NULL)) {
1574  $oldvalue = $this->attributeInitialValue($field);
1575  $newvalue = NULL;
1576  } else {
1577  $oldvalue = $this->attributeInitialValue($field);
1578  $newvalue = $this->attributeValue($field);
1579  }
1580  $foreignRefs[$objectType]['refs'][$class][$ofield][$field] =
1581  array(
1582  'tab' => $tabclass,
1583  'field' => $field,
1584  'oldvalue' => $oldvalue,
1585  'newvalue' => $newvalue,
1586  );
1587  $filter = templateHandling::parseString($filter, array('oldvalue' => $oldvalue, 'newvalue' => $newvalue), 'ldap_escape_f');
1588  } elseif ($mode == 'references') {
1589  $foreignRefs[$objectType]['refs'][$class]['name'] = $cinfos['plShortName'];
1590  $foreignRefs[$objectType]['refs'][$class]['fields'][$ofield][$field] =
1591  array(
1592  'tab' => $tabclass,
1593  'field' => $field,
1594  'tabname' => $this->parent->by_name[$tabclass],
1595  'value' => $this->parent->by_object[$tabclass]->$field,
1596  );
1597  $filter = templateHandling::parseString($filter, array('oldvalue' => $this->parent->by_object[$tabclass]->$field), 'ldap_escape_f');
1598  }
1599  if (!preg_match('/^\(.*\)$/', $filter)) {
1600  $filter = '('.$filter.')';
1601  }
1602  $foreignRefs[$objectType]['filters'][$filter] = $filter;
1603  }
1604  }
1605  }
1606  }
1607 
1608  /* Back up POST content */
1609  $SAVED_POST = $_POST;
1610  $refs = array();
1611  // For each concerned objectType
1612  foreach ($foreignRefs as $objectType => $tabRefs) {
1613  // Compute filter
1614  $filters = array_values($tabRefs['filters']);
1615  $filter = '(|'.join($filters).')';
1616  // Search objects
1617  try {
1618  $objects = objects::ls($objectType, array('dn' => 'raw'), NULL, $filter);
1619  } catch (NonExistingObjectTypeException $e) {
1620  continue;
1621  } catch (EmptyFilterException $e) {
1622  continue;
1623  }
1624  // For each object of this type
1625  foreach (array_keys($objects) as $dn) {
1626  /* Avoid sending POST to opened objects */
1627  $_POST = array();
1628  // Build the object
1629  $tabobject = objects::open($dn, $objectType);
1630  if (preg_match('/^handle_/', $mode)) {
1631  // For each tab concerned
1632  foreach ($tabRefs['refs'] as $tab => $fieldRefs) {
1633  // If the tab is activated on this object
1634  $pluginobject = $tabobject->getTabOrServiceObject($tab);
1635  if ($pluginobject !== FALSE) {
1636  // For each field
1637  foreach ($fieldRefs as $ofield => $fields) {
1638  foreach ($fields as $field) {
1639  // call plugin::foreignKeyUpdate(ldapname, oldvalue, newvalue, source) on the object
1640  $pluginobject->foreignKeyUpdate(
1641  $ofield,
1642  $field['oldvalue'],
1643  $field['newvalue'],
1644  array(
1645  'CLASS' => $field['tab'],
1646  'FIELD' => $field['field'],
1647  'MODE' => preg_replace('/^handle_/', '', $mode),
1648  'DN' => $this->dn,
1649  )
1650  );
1651  }
1652  }
1653  $pluginobject->save_object();
1654  }
1655  }
1656  $tabobject->save();
1657  } elseif ($mode == 'references') {
1658  // For each tab concerned
1659  foreach ($tabRefs['refs'] as $tab => $tab_infos) {
1660  // If the tab is activated on this object
1661  $pluginobject = $tabobject->getTabOrServiceObject($tab);
1662  if ($pluginobject !== FALSE) {
1663  // For each field
1664  foreach ($tab_infos['fields'] as $ofield => $fields) {
1665  foreach ($fields as $field) {
1666  if ($pluginobject->foreignKeyCheck(
1667  $ofield,
1668  $field['value'],
1669  array(
1670  'CLASS' => $field['tab'],
1671  'FIELD' => $field['field'],
1672  'DN' => $this->dn,
1673  )
1674  )) {
1675  if (!isset($refs[$dn])) {
1676  $refs[$dn] = array(
1677  'link' => '',
1678  'tabs' => array(),
1679  );
1680  try {
1681  $refs[$dn]['link'] = objects::link($dn, $objectType);
1682  } catch (FusionDirectoryException $e) {
1683  trigger_error("Could not create link to $dn: ".$e->getMessage());
1684  $refs[$dn]['link'] = $dn;
1685  }
1686  }
1687  if (!isset($refs[$dn]['tabs'][$tab])) {
1688  $refs[$dn]['tabs'][$tab] = array(
1689  'link' => '',
1690  'fields' => array(),
1691  );
1692  try {
1693  if (is_subclass_of($tab, 'simpleService')) {
1694  $refs[$dn]['tabs'][$tab]['link'] = objects::link($dn, $objectType, "service_$tab", sprintf(_('Service "%s"'), $tab_infos['name']));
1695  } else {
1696  $refs[$dn]['tabs'][$tab]['link'] = objects::link($dn, $objectType, "tab_$tab", sprintf(_('Tab "%s"'), $tab_infos['name']));
1697  }
1698  } catch (FusionDirectoryException $e) {
1699  trigger_error("Could not create link to $dn $tab: ".$e->getMessage());
1700  $refs[$dn]['tabs'][$tab]['link'] = $tab;
1701  }
1702  }
1703  $refs[$dn]['tabs'][$tab]['fields'][$ofield] = $field;
1704  }
1705  }
1706  }
1707  }
1708  }
1709  }
1710  }
1711  }
1712  /* Restore POST */
1713  $_POST = $SAVED_POST;
1714  if ($mode == 'references') {
1715  return $refs;
1716  }
1717  }
1718 
1726  function create_unique_dn($attribute, $base)
1727  {
1728  global $config;
1729  $ldap = $config->get_ldap_link();
1730  $base = preg_replace('/^,*/', '', $base);
1731 
1732  /* Try to use plain entry first */
1733  $dn = $attribute.'='.ldap_escape_dn($this->$attribute).','.$base;
1734  if (($dn == $this->orig_dn) || !$ldap->dn_exists($dn)) {
1735  return $dn;
1736  }
1737 
1738  /* Build DN with multiple attributes */
1739  $usableAttributes = array();
1740  foreach ($this->attributes as $attr) {
1741  if (($attr != $attribute) && is_string($this->$attr) && ($this->$attr != '')) {
1742  $usableAttributes[] = $attr;
1743  }
1744  }
1745  for ($i = 1; $i < count($usableAttributes); $i++) {
1746  foreach (new Combinations($usableAttributes, $i) as $attrs) {
1747  $dn = $attribute.'='.ldap_escape_dn($this->$attribute);
1748  foreach ($attrs as $attr) {
1749  $dn .= '+'.$attr.'='.ldap_escape_dn($this->$attr);
1750  }
1751  $dn .= ','.$base;
1752  if (($dn == $this->orig_dn) || !$ldap->dn_exists($dn)) {
1753  return $dn;
1754  }
1755  }
1756  }
1757 
1758  /* None found */
1759  throw new FusionDirectoryException(_('Failed to create a unique DN'));
1760  }
1761 
1762  /*
1763  * \brief Adapt from template, using 'dn'
1764  *
1765  * \param string $dn The DN
1766  *
1767  * \param array $skip A new array
1768  */
1769  function adapt_from_template($attrs, $skip = array())
1770  {
1771  $this->attrs = $attrs;
1772 
1773  /* Walk through attributes */
1774  foreach ($this->attributesAccess as $ldapName => &$attr) {
1775  /* Skip the ones in skip list */
1776  if (in_array($ldapName, $skip)) {
1777  continue;
1778  }
1779  /* Load values */
1780  $attr->loadValue($this->attrs);
1781  }
1782  unset($attr);
1783 
1784  /* Is Account? */
1785  $this->is_account = $this->is_this_account($this->attrs);
1786  }
1787 
1791  function resetCopyInfos()
1792  {
1793  $this->dn = 'new';
1794  $this->orig_dn = $this->dn;
1795 
1796  $this->saved_attributes = array();
1797  $this->initially_was_account = FALSE;
1798  }
1799 
1800  protected function attributeHaveChanged($field)
1801  {
1802  return $this->attributesAccess[$field]->hasChanged();
1803  }
1804 
1805  protected function attributeValue($field)
1806  {
1807  return $this->attributesAccess[$field]->getValue();
1808  }
1809 
1810  protected function attributeInitialValue($field)
1811  {
1812  return $this->attributesAccess[$field]->getInitialValue();
1813  }
1814 
1815  function foreignKeyUpdate ($field, $oldvalue, $newvalue, $source)
1816  {
1817  if (!isset($source['MODE'])) {
1818  $source['MODE'] = 'move';
1819  }
1820  $this->attributesAccess[$field]->foreignKeyUpdate($oldvalue, $newvalue, $source);
1821  }
1822 
1823  /*
1824  * Source is an array like this:
1825  * array(
1826  * 'CLASS' => class,
1827  * 'FIELD' => field,
1828  * 'DN' => dn,
1829  * 'MODE' => mode
1830  * )
1831  * mode being either 'copy' or 'move', defaults to 'move'
1832  */
1833  function foreignKeyCheck ($field, $value, $source)
1834  {
1835  return $this->attributesAccess[$field]->foreignKeyCheck($value, $source);
1836  }
1837 
1838  function deserializeValues($values, $checkAcl = TRUE)
1839  {
1840  foreach ($values as $name => $value) {
1841  if (isset($this->attributesAccess[$name])) {
1842  if (!$checkAcl || $this->attrIsWriteable($name)) {
1843  $this->attributesAccess[$name]->setValue($value);
1844  } else {
1845  return msgPool::permModify($this->dn, $name);
1846  }
1847  } else {
1848  return sprintf(_('Unknown field "%s"'), $name);
1849  }
1850  }
1851  return TRUE;
1852  }
1853 
1854  /* Returns TRUE if this attribute should be asked in the creation by template dialog */
1855  function showInTemplate($attr, $templateAttrs)
1856  {
1857  if (isset($templateAttrs[$attr])) {
1858  return FALSE;
1859  }
1860  return TRUE;
1861  }
1862 
1863  function is_modal_dialog()
1864  {
1865  return (isset($this->dialog) && $this->dialog);
1866  }
1867 
1873  static function plInfo()
1874  {
1875  return array();
1876  }
1877 
1883  {
1884  $plProvidedAcls = array();
1885  foreach ($attributesInfo as $sectionInfo) {
1886  foreach ($sectionInfo['attrs'] as $attr) {
1887  $aclInfo = $attr->getAclInfo();
1888  if ($aclInfo !== FALSE) {
1889  $plProvidedAcls[$aclInfo['name']] = $aclInfo['desc'];
1890  }
1891  }
1892  }
1893 
1894  return $plProvidedAcls;
1895  }
1896 
1910  static function mainInc ($classname, $entry_dn, $tabs = FALSE, $edit_mode = TRUE, $objectType = FALSE)
1911  {
1912  global $remove_lock, $cleanup, $display, $config, $plug, $ui;
1913 
1914  $plInfo = pluglist::pluginInfos($classname);
1915  $plIcon = (isset($plInfo['plIcon']) ? $plInfo['plIcon'] : 'plugin.png');
1916  $plHeadline = $plInfo['plTitle'];
1917  if ($objectType === FALSE) {
1918  $key = key($plInfo['plObjectType']);
1919  if (is_numeric($key)) {
1920  $key = $plInfo['plObjectType'][$key];
1921  }
1922  $objectType = $key;
1923  }
1924  $plCategory = (isset($plInfo['plCategory']) ? $plInfo['plCategory'] : array('user'));
1925  $key = key($plCategory);
1926  if (is_numeric($key)) {
1927  $plCategory = $plCategory[$key];
1928  } else {
1929  $plCategory = $key;
1930  }
1931 
1932  $lock_msg = "";
1933  if ($edit_mode) {
1934  /* Remove locks created by this plugin */
1935  if ($remove_lock || (isset($_POST['edit_cancel']) && session::is_set('edit'))) {
1936  if (session::is_set($classname)) {
1937  del_lock($entry_dn);
1938  }
1939  }
1940  }
1941 
1942  /* Remove this plugin from session */
1943  if ($cleanup) {
1944  session::un_set($classname);
1945  session::un_set('edit');
1946  } else {
1947  /* Reset requested? */
1948  if ($edit_mode && isset($_POST['edit_cancel'])) {
1949  session::un_set($classname);
1950  session::un_set('edit');
1951  }
1952 
1953  /* Create tab object on demand */
1954  if (!session::is_set($classname) || (isset($_GET['reset']) && $_GET['reset'] == 1)) {
1955  try {
1956  $tabObject = objects::open($entry_dn, $objectType);
1957  } catch (NonExistingLdapNodeException $e) {
1958  $tabObject = objects::open('new', $objectType);
1959  }
1960  if ($edit_mode) {
1961  $tabObject->setNeedEditMode(TRUE);
1962  }
1963  if (!$tabs) {
1964  $tabObject->current = $classname;
1965  }
1966  if (($entry_dn != '') && ($entry_dn != 'new')) {
1967  $tabObject->set_acl_base($entry_dn);
1968  } else {
1969  $tabObject->set_acl_base($config->current['BASE']);
1970  }
1971  session::set($classname, $tabObject);
1972  }
1973  $tabObject = session::get($classname);
1974 
1975  /* save changes back to object */
1976  if (!$edit_mode || session::is_set('edit')) {
1977  $tabObject->save_object();
1978  }
1979 
1980  if ($edit_mode) {
1981  /* Enter edit mode? */
1982  if ((isset($_POST['edit'])) && (!session::is_set('edit'))) {
1983  /* Check locking */
1984  if ($locks = get_locks($entry_dn)) {
1985  session::set('back_plugin', $plug);
1986  session::set('LOCK_VARS_TO_USE', array("/^edit$/", "/^plug$/"));
1987  $lock_msg = gen_locked_message($locks, $entry_dn);
1988  } else {
1989  /* Lock the current entry */
1990  add_lock($entry_dn, $ui->dn);
1991  session::set('edit', TRUE);
1992  }
1993  }
1994 
1995  /* save changes to LDAP and disable edit mode */
1996  $info = "";
1997  if (isset($_POST['edit_finish'])) {
1998  /* Perform checks */
1999  $message = $tabObject->save();
2000 
2001  /* No errors, save object */
2002  if (count($message) == 0) {
2003  del_lock($entry_dn);
2004  session::un_set('edit');
2005 
2006  /* Remove from session */
2007  session::un_set($classname);
2008  } else {
2009  /* Errors found, show message */
2010  msg_dialog::displayChecks($message);
2011  }
2012  }
2013  } else {
2014  $info = "";
2015  }
2016 
2017  /* Execute formular */
2018  if ($edit_mode && $lock_msg) {
2019  $display = $lock_msg;
2020  } else {
2021  if ($tabs) {
2022  $display .= $tabObject->execute();
2023  } else {
2024  $display .= $tabObject->by_object[$classname]->execute();
2025  }
2026  }
2027 
2028  /* Store changes in session */
2029  if (!$edit_mode || session::is_set('edit')) {
2030  session::set($classname, $tabObject);
2031  }
2032 
2033  /* Show page footer depending on the mode */
2034  $info = $entry_dn.'&nbsp;';
2035  if ($edit_mode && (!$tabObject->dialogOpened()) && empty($lock_msg)) {
2036  $display .= '<p class="plugbottom">';
2037 
2038  /* Are we in edit mode? */
2039  if (session::is_set('edit')) {
2040  $display .= '<input type="submit" name="edit_finish" style="width:80px" value="'.msgPool::okButton().'"/>'."\n";
2041  $display .= '&nbsp;';
2042  $display .= '<input type="submit" name="edit_cancel" value="'.msgPool::cancelButton().'"/>'."\n";
2043  } else {
2044  /* Only display edit button if there is at least one attribute editable */
2045  if (preg_match('/r/', $ui->get_permissions($entry_dn, $plCategory.'/'.$tabObject->current))) {
2046  $info .= '<div style="display:inline-block" class="optional"><img class="center" alt="information" '.
2047  'src="geticon.php?context=status&amp;icon=dialog-information&amp;size=16"> '.
2048  msgPool::clickEditToChange().'</div>';
2049 
2050  $display .= '<input type="submit" name="edit" value="'.msgPool::editButton().'"/>'."\n";
2051  }
2052  $display .= '<input type="hidden" name="ignore"/>'."\n";
2053  }
2054  $display .= "</p>\n";
2055  }
2056 
2057  /* Page header */
2058  if (!preg_match('/^geticon/', $plIcon)) {
2059  $plIcon = get_template_path($plIcon);
2060  }
2061  $display = print_header($plIcon, $plHeadline, $info).$display;
2062  }
2063  }
2064 }
2065 ?>
$parent
Reference to parent object.
$saved_attributes
The state of the attributes when we opened the object.
static mainInc($classname, $entry_dn, $tabs=FALSE, $edit_mode=TRUE, $objectType=FALSE)
This function is the needed main.inc for plugins that are not used inside a management class...
This class is made for easy plugin creation for editing LDAP attributes.
in_array_ics($value, array $items)
Check if a value exists in an array (case-insensitive)
Definition: functions.inc:1677
set_acl_category($category)
Set acl category.
$inheritance
FALSE to disable inheritance. Array like array ('objectClass' => 'attribute') to specify oc of the gr...
static featuresEnabled($name, $depends="")
List the features settings enabled.
acl_is_readable($attribute)
Can we read the acl.
$objectclasses
The objectClasses set by this tab.
static generatePlProvidedAcls($attributesInfo)
This function generate the needed ACLs for a given attribtues array.
get_allowed_bases()
Returns a list of all available departments for this object.
This class contains all function to manage tabs classes.
show_header($button_text, $text, $plugin_enabled, $button_disabled=FALSE, $name= 'modify_state')
Show header message for tab dialogs.
remove_from_parent()
This function removes the object from LDAP.
compute_dn()
This function returns the dn this object should have.
__set($name, $value)
This function allows to use the syntax $plugin->attributeName to set attributes values.
$dn
dn of the opened object
const DEBUG_LDAP
Definition: functions.inc:37
save()
This function saves the object in the LDAP.
static ldaperror($error, $dn= '', $type=0, $plugin= '')
Display LDAP error.
$mainTab
Is this plugin the main tab, the one that handle the object itself.
openDialog($dialog)
This function allows you to open a dialog.
acl_is_createable($base=NULL)
Can we create the object.
& get_userinfo()
Return the current userinfo object.
Definition: functions.inc:941
handle_hooks($when, $mode, array $addAttrs=array())
Forward command execution requests to the pre/post hook execution method.
del_lock($object)
Remove a lock for object(s)
Definition: functions.inc:689
get_template_path($filename= '', $plugin=FALSE, $path= '')
Return themed path for specified base file.
Definition: functions.inc:315
handle_post_events($mode, array $addAttrs=array())
Forward command execution requests to the post hook execution method.
handle_pre_events($mode, array $addAttrs=array())
Forward command execution requests to the pre hook execution method.
move($src_dn, $dst_dn)
Move ldap entries from one place to another.
static plInfo()
Return plugin informations for acl handling.
static featuresDisabled($name, array $depends=array(), array $conflicts=array())
List the features settings disabled.
acl_is_removeable($base=NULL)
Can we delete the object.
array_merge_unique($ar1, $ar2)
Merge to array but remove duplicate entries (case-insensitive)
Definition: functions.inc:412
static addFeaturesButton($name)
Display Add features button.
static set($name, $value)
Set a value in a session.
static parseString($string, array $attrs, $escapeMethod=NULL)
Parse template masks in a single string.
aclGetPermissions($attribute= '0', $base=NULL, $skipWrite=FALSE)
Get the acl permissions for an attribute or the plugin itself.
save_object()
This function handle $_POST informations.
cleanup()
Remove attributes, empty arrays, arrays single attributes that do not differ.
$ldap_error
Last LDAP error (used by logging calls from post_* methods)
static & get($name)
Accessor of a session.
acl_is_writeable($attribute, $skipWrite=FALSE)
Can we write the attribute.
execute()
This function display the plugin and return the html code.
& get_smarty()
Get global smarty object.
Definition: functions.inc:953
$attributesAccess
This attribute store references toward attributes.
__get($name)
This function allows to use the syntax $plugin->attributeName to get attributes values.
$preInitAttributes
Attributes that needs to be initialized before the others.
__isset($name)
This function allows to use the syntax isset($plugin->attributeName)
getEntryCSN($dn)
Get the Change Sequence Number of a certain DN.
Definition: functions.inc:2332
const DEBUG_SHELL
Definition: functions.inc:39
Parent class for all exceptions thrown in FusionDirectory.
getObjectClassFilter()
This function returns an LDAP filter for this plugin object classes.
$read_only
Used when the entry is opened as "readonly" due to locks.
$is_account
Mark plugin as account.
static fieldsFromLDAP(array $template_attrs)
Translate template attrs into $attrs as if taken from LDAP.
__construct($dn=NULL, $object=NULL, $parent=NULL, $mainTab=FALSE, $attributesInfo=NULL)
constructor
$needEditMode
Are we executed in a edit-mode environment? (this is FALSE if we're called from management, TRUE if we're called from a main.inc)
$orig_dn
original dn of the opened object
static fieldsToLDAP(array $template_attrs, array $attrs)
Translate $attrs into template attrs.
gen_locked_message($locks, $dn, $allow_readonly=FALSE)
Generate a lock message.
Definition: functions.inc:1189
static log($action, $objecttype, $object, array $changes_array=array(), $result= '')
logging method
static un_set($name)
Unset a session.
closeDialog()
This function closes the dialog.
get_locks($objects, $allow_readonly=FALSE)
Get locks for objects.
Definition: functions.inc:801
DEBUG($level, $line, $function, $file, $data, $info= '')
Debug level action.
Definition: functions.inc:219
static cmdexecfailed($type, $command="", $plugin="")
Display that a command execution failed in this plugin.
array_differs($src, $dst)
Determine if two arrays are different.
Definition: functions.inc:1941
static noValidExtension($name)
Display error about invalid extension from account.
print_header($image, $headline, $info= '')
Print plugin HTML header.
Definition: functions.inc:1368
$is_template
Mark plugin as template.
static ls($types, $attrs=NULL, $ou=NULL, $filter= '', $checkAcl=FALSE, $scope= 'subtree')
Get list of object of objectTypes from $types in $ou.
acl_is_moveable($base=NULL)
Can we move the object.
$attrs
Represent temporary LDAP data.
attrIsWriteable($attr)
Check if logged in user have enough right to write this attribute value.
static permModify($name= '', $field= '')
Display that we have no permission to modify an object.
$attributesInfo
This attribute store all information about attributes.
$displayHeader
Do we want a header allowing to able/disable this plugin.
static display($s_title, $s_message, $i_type=INFO_DIALOG)
Display a message dialog.
create_unique_dn($attribute, $base)
Create unique DN.
callHook($cmd, array $addAttrs=array(), &$returnOutput=array(), &$returnCode=NULL)
Calls external hooks which are defined for this plugin (fusiondirectory.conf) Replaces placeholder by...
check()
This function checks the attributes values and yell if something is wrong.
static open($dn, $type)
Create the tab object for the given dn.
readOnly()
Indicates if this object is opened as read-only (because of locks)
array_remove_entries_ics(array $needles, array $haystack)
Remove multiple entries from an array (case-insensitive)
Definition: functions.inc:394
static global_is_set($name)
Check if a session is defined.
resetCopyInfos()
This function is called on the copied object to set its dn to where it will be saved.
static is_set($name)
Check if the name of the session is set.
static checkFields($attrs)
Check template fields.
This class allow to handle easily a String LDAP attribute.
back_to_main()
Generate HTML for the 'Back' button.
Definition: functions.inc:1389
post_save()
This function is called after LDAP save to do some post operations and logging.
add_lock($object, $user)
Add a lock for object(s)
Definition: functions.inc:619
$entryCSN
Object entry CSN.
static removeFeaturesButton($name)
Display Remove features button.
set_acl_base($base)
Set acl base.
static clickEditToChange()
Display : Click the 'Edit' button below to change information in this dialog.