<?php /* This code is part of FusionDirectory (http://www.fusiondirectory.org/) Copyright (C) 2003-2010 Cajus Pollmeier Copyright (C) 2011-2018 FusionDirectory This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /*! * \file class_config.inc * Source code for the class config */ /*! * \brief This class is responsible for parsing and querying the * fusiondirectory configuration file. */ class config { /* XML parser */ var $parser; var $config_found = FALSE; var $tags = []; var $level = 0; var $gpc = 0; var $section = ''; var $currentLocation = ''; /*! * \brief Store configuration for current location */ var $current = []; /* Link to LDAP-server */ var $ldap = NULL; var $referrals = []; /* * \brief Configuration data * * - $data['SERVERS'] contains server informations. */ var $data = [ 'LOCATIONS' => [], 'SERVERS' => [], 'MAIN' => [], ]; var $basedir = ''; /* Keep a copy of the current department list */ var $departments = []; var $idepartments = []; var $department_info = []; var $filename = ''; var $last_modified = 0; /*! * \brief Class constructor of the config class * * \param string $filename path to the configuration file * * \param string $basedir base directory */ function __construct ($filename, $basedir = '') { $this->basedir = $basedir; /* Parse config file directly? */ if ($filename != '') { $this->parse($filename); } } /*! * \brief Check and reload the configuration * * This function checks if the configuration has changed, since it was * read the last time and reloads it. It uses the file mtime to check * weither the file changed or not. */ function check_and_reload ($force = FALSE) { /* Check if class_location.inc has changed, this is the case if we have installed or removed plugins. */ $tmp = stat(CACHE_DIR.'/'.CLASS_CACHE); if (session::is_set('class_location.inc:timestamp')) { if ($tmp['mtime'] != session::get('class_location.inc:timestamp')) { session::un_set('plist'); } } session::set('class_location.inc:timestamp', $tmp['mtime']); if (($this->filename != '') && ((filemtime($this->filename) != $this->last_modified) || $force)) { $this->config_found = FALSE; $this->tags = []; $this->level = 0; $this->gpc = 0; $this->section = ''; $this->currentLocation = ''; $this->parse($this->filename); $this->set_current($this->current['NAME']); } } /*! * \brief Parse the given configuration file * * Parses the configuration file and displays errors if there * is something wrong with it. * * \param string $filename The filename of the configuration file. */ function parse ($filename) { $this->last_modified = filemtime($filename); $this->filename = $filename; $fh = fopen($filename, 'r'); $xmldata = fread($fh, 100000); fclose($fh); $this->parse_data($xmldata); } function parse_data ($xmldata) { $this->data = [ 'LOCATIONS' => [], 'SERVERS' => [], 'MAIN' => [], ]; $this->parser = xml_parser_create(); xml_set_object($this->parser, $this); xml_set_element_handler($this->parser, "tag_open", "tag_close"); if (!xml_parse($this->parser, chop($xmldata))) { $msg = sprintf(_("XML error in fusiondirectory.conf: %s at line %d"), xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser)); msg_dialog::display(_("Configuration error"), $msg, FATAL_ERROR_DIALOG); exit; } xml_parser_free($this->parser); } /*! * \brief Open xml tag when parsing the xml config * * \param string $parser * * \param string $tag * * \param string $attrs */ function tag_open ($parser, $tag, $attrs) { /* Save last and current tag for reference */ $this->tags[$this->level] = $tag; $this->level++; /* Trigger on CONF section */ if ($tag == 'CONF') { $this->config_found = TRUE; } /* Return if we're not in config section */ if (!$this->config_found) { return; } /* yes/no to true/false and upper case TRUE to true and so on*/ foreach ($attrs as $name => $value) { if (preg_match("/^(true|yes)$/i", $value)) { $attrs[$name] = "TRUE"; } elseif (preg_match("/^(false|no)$/i", $value)) { $attrs[$name] = "FALSE"; } } /* Look through attributes */ switch($this->tags[$this->level - 1]) { /* Handle location */ case 'LOCATION': if ($this->tags[$this->level - 2] == 'MAIN') { $attrs['NAME'] = preg_replace('/[<>"\']/', '', $attrs['NAME']); $this->currentLocation = $attrs['NAME']; /* Add location elements */ $this->data['LOCATIONS'][$attrs['NAME']] = $attrs; } break; /* Handle referral tags */ case 'REFERRAL': if ($this->tags[$this->level - 2] == 'LOCATION') { if (isset($attrs['BASE'])) { $server = $attrs['URI']; } elseif (isset($this->data['LOCATIONS'][$this->currentLocation]['BASE'])) { /* Fallback on location base */ $server = $attrs['URI']; $attrs['BASE'] = $this->data['LOCATIONS'][$this->currentLocation]['BASE']; } else { /* Format from FD<1.3 */ $server = preg_replace('!^([^:]+://[^/]+)/.*$!', '\\1', $attrs['URI']); $attrs['BASE'] = preg_replace('!^[^:]+://[^/]+/(.*)$!', '\\1', $attrs['URI']); $attrs['URI'] = $server; } /* Add location elements */ if (!isset($this->data['LOCATIONS'][$this->currentLocation]['REFERRAL'])) { $this->data['LOCATIONS'][$this->currentLocation]['REFERRAL'] = []; } $this->data['LOCATIONS'][$this->currentLocation]['REFERRAL'][$server] = $attrs; } break; /* Load main parameters */ case 'MAIN': $this->data['MAIN'] = array_merge($this->data['MAIN'], $attrs); break; /* Ignore other tags */ default: break; } } /*! * \brief Close xml tag when parsing the xml config * * \param string $parser * * \param string $tag */ function tag_close ($parser, $tag) { /* Close config section */ if ($tag == 'CONF') { $this->config_found = FALSE; } $this->level--; } /*! * \brief Get the password when needed from the config file * * This function can be used to get the password associated to * a keyword in the config file * * \param string $creds the keyword associated to the password needed * * \return string the password corresponding to the keyword */ function get_credentials ($creds) { if (isset($_SERVER['HTTP_FDKEY'])) { if (!session::is_set('HTTP_FDKEY_CACHE')) { session::set('HTTP_FDKEY_CACHE', []); } $cache = session::get('HTTP_FDKEY_CACHE'); if (!isset($cache[$creds])) { try { $cache[$creds] = cred_decrypt($creds, $_SERVER['HTTP_FDKEY']); session::set('HTTP_FDKEY_CACHE', $cache); } catch (FusionDirectoryException $e) { $msg = sprintf( _('It seems you are trying to decode something which is not encoded : %s<br/>'."\n". 'Please check you are not using a fusiondirectory.secrets file while your passwords are not encrypted.'), $e->getMessage() ); msg_dialog::display(_('Configuration error'), $msg, FATAL_ERROR_DIALOG); exit; } } return $cache[$creds]; } return $creds; } /*! * \brief Get a LDAP link object * * This function can be used to get an ldap object, which in turn can * be used to query the LDAP. See the LDAP class for more information * on how to use it. * * Example usage: * \code * $ldap = $config->get_ldap_link(); * \endcode * * \param boolean $sizelimit Weither to impose a sizelimit on the LDAP object or not. * Defaults to false. If set to true, the size limit in the configuration * file will be used to set the option LDAP_OPT_SIZELIMIT. * * \return ldapMultiplexer object */ function get_ldap_link ($sizelimit = FALSE) { global $ui; if ($this->ldap === NULL || !is_resource($this->ldap->cid)) { /* Build new connection */ $this->ldap = ldap_init($this->current['SERVER'], $this->current['BASE'], $this->current['ADMINDN'], $this->get_credentials($this->current['ADMINPASSWORD'])); /* Check for connection */ if (is_null($this->ldap) || (is_int($this->ldap) && $this->ldap == 0)) { msg_dialog::display(_("LDAP error"), _("Cannot bind to LDAP. Please contact the system administrator."), FATAL_ERROR_DIALOG); exit(); } /* Move referrals */ if (!isset($this->current['REFERRAL'])) { $this->ldap->referrals = []; } else { $this->ldap->referrals = $this->current['REFERRAL']; } } $obj = new ldapMultiplexer($this->ldap); if ($sizelimit) { $obj->set_size_limit($ui->getSizeLimitHandler()->getSizeLimit()); } else { $obj->set_size_limit(0); } return $obj; } /*! * \brief Set the current location * * \param string $name the name of the location */ function set_current ($name) { if (!isset($this->data['LOCATIONS'][$name])) { msg_dialog::display(_('Error'), sprintf(_('Location "%s" could not be found in the configuration file'), $name), FATAL_ERROR_DIALOG); exit; } $this->current = $this->data['LOCATIONS'][$name]; if (isset($this->current['INITIAL_BASE'])) { session::set('CurrentMainBase', $this->current['INITIAL_BASE']); } /* Sort referrals, if present */ if (isset($this->current['REFERRAL'])) { $servers = []; foreach ($this->current['REFERRAL'] as $server => $ref) { $servers[$server] = strlen($ref['BASE']); } asort($servers); reset($servers); } /* SERVER not defined? Load the one with the shortest base */ if (!isset($this->current['SERVER'])) { $this->current['SERVER'] = key($servers); } /* Parse LDAP referral informations */ if (!isset($this->current['ADMINDN']) || !isset($this->current['ADMINPASSWORD'])) { $this->current['BASE'] = $this->current['REFERRAL'][$this->current['SERVER']]['BASE']; $this->current['ADMINDN'] = $this->current['REFERRAL'][$this->current['SERVER']]['ADMINDN']; $this->current['ADMINPASSWORD'] = $this->current['REFERRAL'][$this->current['SERVER']]['ADMINPASSWORD']; } /* Load in-ldap configuration */ $this->load_inldap_config(); if (class_available('systemManagement')) { /* Load server informations */ $this->load_servers(); } $debugLevel = $this->get_cfg_value('DEBUGLEVEL'); if ($debugLevel & DEBUG_CONFIG) { /* Value from LDAP can't activate DEBUG_CONFIG */ $debugLevel -= DEBUG_CONFIG; } if (isset($this->data['MAIN']['DEBUGLEVEL'])) { $debugLevel |= $this->data['MAIN']['DEBUGLEVEL']; } session::set('DEBUGLEVEL', $debugLevel); IconTheme::loadThemes('themes'); timezone::setDefaultTimezoneFromConfig(); Language::init(); } /*! * \brief Load server information from config/LDAP * * This function searches the LDAP for servers (e.g. goImapServer, goMailServer etc.) * and stores information about them $this->data['SERVERS']. In the case of mailservers * the main section of the configuration file is searched, too. */ function load_servers () { /* Only perform actions if current is set */ if ($this->current === NULL) { return; } $ldap = $this->get_ldap_link(); /* Get samba servers from LDAP */ $this->data['SERVERS']['SAMBA'] = []; if (class_available('sambaAccount')) { $ldap->cd($this->current['BASE']); $ldap->search('(objectClass=sambaDomain)'); while ($attrs = $ldap->fetch()) { $this->data['SERVERS']['SAMBA'][$attrs['sambaDomainName'][0]] = [ 'SID' => '','RIDBASE' => '']; if (isset($attrs['sambaSID'][0])) { $this->data['SERVERS']['SAMBA'][$attrs['sambaDomainName'][0]]['SID'] = $attrs['sambaSID'][0]; } if (isset($attrs['sambaAlgorithmicRidBase'][0])) { $this->data['SERVERS']['SAMBA'][$attrs['sambaDomainName'][0]]['RIDBASE'] = $attrs['sambaAlgorithmicRidBase'][0]; } } /* If no samba servers are found, look for configured sid/ridbase */ if (count($this->data['SERVERS']['SAMBA']) == 0) { if (isset($this->current['SAMBASID']) && isset($this->current['SAMBARIDBASE'])) { $this->data['SERVERS']['SAMBA']['DEFAULT'] = [ 'SID' => $this->get_cfg_value('SAMBASID'), 'RIDBASE' => $this->get_cfg_value('SAMBARIDBASE') ]; } } } } /* Check that configuration is in LDAP, check that no plugin got installed since last configuration update */ function checkLdapConfig ($forceReload = FALSE) { global $ui; $dn = CONFIGRDN.$this->current['BASE']; if (!$forceReload) { $ldap = $this->get_ldap_link(); $ldap->cat($dn, ['fusionConfigMd5']); if ($attrs = $ldap->fetch()) { if (isset($attrs['fusionConfigMd5'][0]) && ($attrs['fusionConfigMd5'][0] == md5_file(CACHE_DIR.'/'.CLASS_CACHE))) { return; } } } add_lock($dn, $ui->dn); $config_plugin = objects::open($dn, 'configuration'); $config_plugin->save_object(); $config_plugin->save(); del_lock($dn); } function load_inldap_config () { $ldap = $this->get_ldap_link(); $ldap->cat(CONFIGRDN.$this->current['BASE']); if ($attrs = $ldap->fetch()) { for ($i = 0; $i < $attrs['count']; $i++) { $key = $attrs[$i]; if (preg_match('/^fdTabHook$/i', $key)) { for ($j = 0; $j < $attrs[$key]['count']; ++$j) { $parts = explode('|', $attrs[$key][$j], 3); $class = strtoupper($parts[0]); $mode = strtoupper($parts[1]); $cmd = $parts[2]; if (!isset($this->data['HOOKS'][$class])) { $this->data['HOOKS'][$class] = ['CLASS' => $parts[0]]; } if (!isset($this->data['HOOKS'][$class][$mode])) { $this->data['HOOKS'][$class][$mode] = []; } $this->data['HOOKS'][$class][$mode][] = $cmd; } } elseif (preg_match('/^fd/', $key)) { if (isset($attrs[$key]['count']) && ($attrs[$key]['count'] > 1)) { $value = $attrs[$key]; unset($value['count']); } else { $value = $attrs[$key][0]; } $key = strtoupper(preg_replace('/^fd/', '', $key)); $this->current[$key] = $value; } } } } /*! * \brief Store the departments from ldap in $this->departments */ function get_departments () { /* Initialize result hash */ $result = []; $result['/'] = $this->current['BASE']; /* Get all department types from department Management, to be able detect the department type. -It is possible that different department types have the same name, in this case we have to mark the department name to be able to differentiate. (e.g l=Name or o=Name) */ $types = departmentManagement::getDepartmentTypes(); /* Create a list of attributes to fetch */ $filter = ''; $ldap_values = ['objectClass', 'description']; foreach ($types as $type) { $i = objects::infos($type); $filter .= $i['filter']; /* Add type main attr to fetched attributes list */ $ldap_values[] = $i['mainAttr']; } $filter = '(|'.$filter.')'; /* Get list of department objects */ $ldap = $this->get_ldap_link(); $ldap->cd($this->current['BASE']); $ldap->search($filter, $ldap_values); while ($attrs = $ldap->fetch()) { /* Detect department type */ $oc = NULL; foreach ($types as $type) { if (objects::isOfType($attrs, $type)) { $oc = $type; break; } } /* Unknown department type -> skip */ if ($oc == NULL) { continue; } $dn = $attrs['dn']; $data = objects::infos($oc); $this->department_info[$dn] = [ 'img' => $data['icon'], 'description' => (isset($attrs['description'][0]) ? $attrs['description'][0] : ''), 'name' => $attrs[$data['mainAttr']][0] ]; /* Only assign non-root departments */ if ($dn != $result['/']) { $c_dn = convert_department_dn($dn).' ('.$data['mainAttr'].')'; $result[$c_dn] = $dn; } } $this->departments = $result; } function make_idepartments ($max_size = 28) { $base = $this->current['BASE']; $qbase = preg_quote($base, '/'); $arr = []; $this->idepartments = []; /* Create multidimensional array, with all departments. */ foreach ($this->departments as $key => $val) { /* Split dn into single department pieces */ $elements = array_reverse(explode(',', preg_replace("/$qbase$/", '', $val))); /* Add last ou element of current dn to our array */ $last = &$arr; foreach ($elements as $key => $ele) { /* skip empty */ if (empty($ele)) { continue; } /* Extract department name */ $elestr = trim(preg_replace('/^[^=]*+=/', '', $ele), ','); $nameA = trim(preg_replace('/=.*$/', '', $ele), ','); if ($nameA != 'ou') { $nameA = " ($nameA)"; } else { $nameA = ''; } /* Add to array */ if ($key == (count($elements) - 1)) { $last[$elestr.$nameA]['ENTRY'] = $val; } /* Set next array appending position */ $last = &$last[$elestr.$nameA]['SUB']; } } /* Add base entry */ $ret['/']['ENTRY'] = $base; $ret['/']['SUB'] = $arr; $this->idepartments = $this->generateDepartmentArray($ret, -1, $max_size); } /* * \brief Creates display friendly output from make_idepartments * * \param $arr arr * * \param int $depth initialized at -1 * * \param int $max_size initialized at 256 */ function generateDepartmentArray ($arr, $depth = -1, $max_size = 256) { $ret = []; $depth++; /* Walk through array */ ksort($arr); foreach ($arr as $name => $entries) { /* If this department is the last in the current tree position * remove it, to avoid generating output for it */ if (count($entries['SUB']) == 0) { unset($entries['SUB']); } /* Fix name, if it contains a replace tag */ $name = preg_replace('/\\\\,/', ',', $name); /* Check if current name is too long, then cut it */ if (mb_strlen($name, 'UTF-8') > $max_size) { $name = mb_substr($name, 0, ($max_size - 3), 'UTF-8')." ..."; } /* Append the name to the list */ if (isset($entries['ENTRY'])) { $a = ""; for ($i = 0; $i < $depth; $i++) { $a .= "."; } $ret[$entries['ENTRY']] = $a." ".$name; } /* recursive add of subdepartments */ if (isset($entries['SUB'])) { $ret = array_merge($ret, $this->generateDepartmentArray($entries['SUB'], $depth, $max_size)); } } return $ret; } /*! * \brief Search for hooks * * Example usage: * \code * $postcmd = $config->search(get_class($this), 'POSTMODIFY'); * \endcode * * \param string $class The class name * * \param string $value Key to search in the hooks * * \return array of hook commands or empty array */ function searchHooks ($class, $value) { $class = strtoupper($class); $value = strtoupper($value); return (isset($this->data['HOOKS'][$class][$value]) ? $this->data['HOOKS'][$class][$value] : []); } /*! * \brief Get a configuration value from the config * * This returns a configuration value from the config. It either * uses the data of the current location ($this->current), * if it contains the value (e.g. current['BASE']) or otherwise * uses the data from the main configuration section. * * If no value is found and an optional default has been specified, * then the default is returned. * * \param string $name The configuration key (case-insensitive) * * \param string $default A default that is returned, if no value is found * * \return string the configuration value if found or the default value */ function get_cfg_value ($name, $default = '') { $name = strtoupper($name); $res = $default; /* Check if we have a current value for $name */ if (isset($this->current[$name])) { $res = $this->current[$name]; } elseif (isset($this->data['MAIN'][$name])) { /* Check if we have a global value for $name */ $res = $this->data['MAIN'][$name]; } if (is_array($default) && !is_array($res)) { $res = [$res]; } return $res; } /*! * \brief Check if session lifetime matches session.gc_maxlifetime * * On debian systems the session files are deleted with * a cronjob, which detects all files older than specified * in php.ini:'session.gc_maxlifetime' and removes them. * This function checks if the fusiondirectory.conf value matches the range * defined by session.gc_maxlifetime. * * \return boolean TRUE or FALSE depending on weither the settings match * or not. If SESSIONLIFETIME is not configured in FusionDirectory it always returns * TRUE. */ function check_session_lifetime () { $cfg_lifetime = $this->get_cfg_value('SESSIONLIFETIME', 0); if ($cfg_lifetime > 0) { $ini_lifetime = ini_get('session.gc_maxlifetime'); $deb_system = file_exists('/etc/debian_version'); return !($deb_system && ($ini_lifetime < $cfg_lifetime)); } else { return TRUE; } } /*! * \brief Check if snapshot are enabled * * \return boolean TRUE if snapshot are enabled, FALSE otherwise */ function snapshotEnabled () { if ($this->get_cfg_value('enableSnapshots') != 'TRUE') { return FALSE; } /* Check if the snapshot_base is defined */ if ($this->get_cfg_value('snapshotBase') == '') { /* Send message if not done already */ if (!session::is_set('snapshotFailMessageSend')) { session::set('snapshotFailMessageSend', TRUE); msg_dialog::display(_('Configuration error'), sprintf(_('The snapshot functionality is enabled, but the required variable "%s" is not set.'), 'snapshotBase'), ERROR_DIALOG); } return FALSE; } /* Check if gzcompress is available */ if (!is_callable('gzcompress')) { /* Send message if not done already */ if (!session::is_set('snapshotFailMessageSend')) { session::set('snapshotFailMessageSend', TRUE); msg_dialog::display(_('Configuration error'), sprintf(_('The snapshot functionality is enabled, but the required compression module is missing. Please install "%s".'), 'php5-zip / php5-gzip'), ERROR_DIALOG); } return FALSE; } return TRUE; } function loadPlist (pluglist $plist) { $this->data['OBJECTS'] = []; $this->data['SECTIONS'] = []; $this->data['CATEGORIES'] = []; $this->data['MENU'] = []; $this->data['TABS'] = []; foreach ($plist->info as $class => &$plInfo) { if (isset($plInfo['plObjectType'])) { $entry = ['CLASS' => $class,'NAME' => $plInfo['plShortName']]; if (isset($plInfo['plSubTabs'])) { $entry['SUBTABS'] = strtoupper($plInfo['plSubTabs']); } foreach ($plInfo['plObjectType'] as $key => $value) { if (is_numeric($key)) { /* This is not the main tab */ $tabclass = strtoupper($value).'TABS'; if (($tabclass == 'GROUPTABS') && class_available('mixedGroup')) { $tabclass = 'OGROUP-USERTABS'; } @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $tabclass, "Adding $class to tab list"); if (!isset($this->data['TABS'][$tabclass])) { $this->data['TABS'][$tabclass] = []; } $this->data['TABS'][$tabclass][] = $entry; } else { /* This is the main tab */ if (isset($this->data['OBJECTS'][strtoupper($key)])) { die("duplicated object type ".strtoupper($key)." in ".$this->data['OBJECTS'][strtoupper($key)]['mainTab']." and $class"); } $tabclass = strtoupper($key)."TABS"; $value['tabGroup'] = $tabclass; $value['mainTab'] = $class; $value['templateActive'] = FALSE; $value['snapshotActive'] = FALSE; foreach (['ou', 'tabClass'] as $i) { if (!isset($value[$i])) { $value[$i] = NULL; } } if (!isset($value['aclCategory'])) { $value['aclCategory'] = $key; } if (isset($value['filter'])) { if (!preg_match('/^\(.*\)$/', $value['filter'])) { $value['filter'] = '('.$value['filter'].')'; } } else { $value['filter'] = NULL; } if (!isset($value['mainAttr'])) { $value['mainAttr'] = 'cn'; } if (!isset($value['nameAttr'])) { $value['nameAttr'] = $value['mainAttr']; } if (!isset($value['tabClass'])) { $value['tabClass'] = 'simpleTabs'; } $this->data['OBJECTS'][strtoupper($key)] = $value; @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $tabclass, "Adding $class as main tab of"); if (!isset($this->data['TABS'][$tabclass])) { $this->data['TABS'][$tabclass] = []; } array_unshift($this->data['TABS'][$tabclass], $entry); } } } elseif (class_available($class) && is_subclass_of($class, 'simpleService')) { @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $class, "Adding service"); if (!isset($this->data['TABS']['SERVERSERVICE'])) { $this->data['TABS']['SERVERSERVICE'] = []; } $this->data['TABS']['SERVERSERVICE'][] = [ 'CLASS' => $class, 'NAME' => $plInfo['plShortName'] ]; } /* Feed categories */ if (isset($plInfo['plCategory'])) { /* Walk through supplied list and feed only translated categories */ $cats = []; foreach ($plInfo['plCategory'] as $idx => $data) { $cat = (is_numeric($idx) ? $data : $idx); $cats[] = $cat; if (!isset($this->data['CATEGORIES'][$cat])) { $this->data['CATEGORIES'][$cat] = [ 'classes' => ['0'] ]; } if (!empty($plInfo['plProvidedAcls'])) { $this->data['CATEGORIES'][$cat]['classes'][] = $class; } if (!is_numeric($idx)) { /* Non numeric index means -> base object containing more informations */ $this->data['CATEGORIES'][$cat]['description'] = $data['description']; if (!is_array($data['objectClass'])) { $data['objectClass'] = [$data['objectClass']]; } $this->data['CATEGORIES'][$cat]['objectClass'] = $data['objectClass']; } } $plInfo['plCategory'] = $cats; } } unset($plInfo); $this->data['CATEGORIES']['all'] = [ 'classes' => ['all'], 'description' => '* '._('All categories'), 'objectClass' => [], ]; /* Extract categories definitions from object types */ foreach ($this->data['OBJECTS'] as $key => $infos) { @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $infos['aclCategory'], "ObjectType $key category"); if (strtoupper($infos['aclCategory']) == $key) { $cat = $infos['aclCategory']; if (!isset($this->data['CATEGORIES'][$cat])) { $this->data['CATEGORIES'][$cat] = ['classes' => ['0']]; } if (!isset($this->data['CATEGORIES'][$cat]['description'])) { $this->data['CATEGORIES'][$cat]['description'] = $infos['name']; preg_match_all('/objectClass=([^= \)\(]+)/', $infos['filter'], $m); $this->data['CATEGORIES'][$cat]['objectClass'] = $m[1]; } } } /* Now that OBJECTS are filled, place tabs in categories */ foreach ($plist->info as $class => &$plInfo) { $acl = []; if (isset($plInfo['plCategory'])) { $acl = $plInfo['plCategory']; if (!is_array($acl)) { $acl = [$acl]; } } if (isset($plInfo['plObjectType'])) { foreach ($plInfo['plObjectType'] as $key => $value) { if (is_numeric($key)) { /* This is not the main tab */ $obj = strtoupper($value); } else { /* This is the main tab */ $obj = strtoupper($key); } if (strpos($obj, 'OGROUP-') === 0) { $obj = 'OGROUP'; } /* if this is an existing objectType, not just a tab group */ if (isset($this->data['OBJECTS'][$obj])) { $cat = $this->data['OBJECTS'][$obj]['aclCategory']; $acl[] = $cat; if (!empty($plInfo['plProvidedAcls'])) { $this->data['CATEGORIES'][$cat]['classes'][] = $class; } if (!in_array($cat, $plInfo['plCategory'])) { $plInfo['plCategory'][] = $cat; } } } } /* Read management info */ if (isset($plInfo['plManages'])) { foreach ($plInfo['plManages'] as $type) { $obj = strtoupper($type); if (!isset($this->data['OBJECTS'][$obj])) { continue; } $cat = $this->data['OBJECTS'][$obj]['aclCategory']; $acl[] = $cat; if (!empty($plInfo['plProvidedAcls'])) { $this->data['CATEGORIES'][$cat]['classes'][] = $class; } if (!in_array($cat, $plInfo['plCategory'])) { $plInfo['plCategory'][] = $cat; } if (isset($this->data['OBJECTS'][$obj])) { $this->data['OBJECTS'][$obj]['management'] = $class; if (isset($class::$skipTemplates) && ($class::$skipTemplates == FALSE)) { $this->data['OBJECTS'][$obj]['templateActive'] = TRUE; $this->data['CATEGORIES'][$cat]['classes'][] = 'template'; } if (isset($class::$skipSnapshots) && ($class::$skipSnapshots == FALSE)) { $this->data['OBJECTS'][$obj]['snapshotActive'] = TRUE; $this->data['CATEGORIES'][$cat]['classes'][] = 'SnapshotHandler'; } } } } @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, join (',', array_unique($acl)), "Class $class categories"); /* Feed menu */ if (isset($plInfo['plSection'])) { $section = $plInfo['plSection']; if (!is_array($acl)) { $acl = [$acl]; } if (!is_numeric(key($acl))) { $acl = array_keys($acl); } if (isset($plInfo['plSelfModify']) && $plInfo['plSelfModify']) { $acl[] = $acl[0].'/'.$class.':self'; } $acl = join(',', array_unique($acl)); if (is_array($section)) { $section = key($section); if (is_numeric($section)) { trigger_error("$class have wrong setting in plInfo/plSection"); continue; } $this->data['SECTIONS'][$section] = array_change_key_case($plInfo['plSection'][$section], CASE_UPPER); } if (!isset($this->data['MENU'][$section])) { $this->data['MENU'][$section] = []; } $attrs = ['CLASS' => $class]; if (!empty($acl)) { $attrs['ACL'] = $acl; } $this->data['MENU'][$section][] = $attrs; } if (isset($plInfo['plMenuProvider']) && $plInfo['plMenuProvider']) { list($sections, $entries) = $class::getMenuEntries(); foreach ($sections as $section => $infos) { if (!isset($this->data['SECTIONS'][$section])) { $this->data['SECTIONS'][$section] = array_change_key_case($infos, CASE_UPPER); } if (!isset($this->data['MENU'][$section])) { $this->data['MENU'][$section] = []; } } foreach ($entries as $section => $section_entries) { foreach ($section_entries as $entry) { $this->data['MENU'][$section][] = $entry; } } } } unset($plInfo); ksort($this->data['CATEGORIES']); foreach ($this->data['CATEGORIES'] as $name => &$infos) { $infos['classes'] = array_unique($infos['classes']); if (!isset($infos['description'])) { $infos['description'] = $name; $infos['objectClass'] = []; } } unset($infos); $this->data['SECTIONS']['personal'] = ['NAME' => _('My account'), 'PRIORITY' => 40]; $personal = []; foreach ($this->data['TABS']['USERTABS'] as $tab) { if ($plist->info[$tab['CLASS']]['plSelfModify']) { $personal[] = ['CLASS' => $tab['CLASS'], 'ACL' => 'user/'.$tab['CLASS'].':self']; } } if (!isset($this->data['MENU']['personal'])) { $this->data['MENU']['personal'] = $personal; } else { $this->data['MENU']['personal'] = array_merge($personal, $this->data['MENU']['personal']); } uasort($this->data['SECTIONS'], function ($a, $b) { if ($a['PRIORITY'] == $b['PRIORITY']) { return 0; } return (($a['PRIORITY'] < $b['PRIORITY']) ? -1 : 1); } ); } } ?>