Linux server.jmdstrack.com 3.10.0-1160.119.1.el7.tuxcare.els10.x86_64 #1 SMP Fri Oct 11 21:40:41 UTC 2024 x86_64
/ home/ jmdstrac/ public_html/ devices/ src/ |
|
<?php /** * --------------------------------------------------------------------- * * GLPI - Gestionnaire Libre de Parc Informatique * * http://glpi-project.org * * @copyright 2015-2023 Teclib' and contributors. * @copyright 2003-2014 by the INDEPNET Development Team. * @licence https://www.gnu.org/licenses/gpl-3.0.html * * --------------------------------------------------------------------- * * LICENSE * * This file is part of GLPI. * * 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 3 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, see <https://www.gnu.org/licenses/>. * * --------------------------------------------------------------------- */ use Glpi\Agent\Communication\AbstractRequest; use Glpi\Application\View\TemplateRenderer; use Glpi\Cache\CacheManager; use Glpi\Dashboard\Grid; use Glpi\Exception\PasswordTooWeakException; use Glpi\Plugin\Hooks; use Glpi\System\RequirementsManager; use Glpi\Toolbox\Sanitizer; use PHPMailer\PHPMailer\PHPMailer; /** * Config class **/ class Config extends CommonDBTM { const DELETE_ALL = -1; const KEEP_ALL = 0; public const UNIT_MANAGEMENT = 0; public const GLOBAL_MANAGEMENT = 1; public const NO_MANAGEMENT = 2; public const TIMELINE_ACTION_BTN_MERGED = 0; public const TIMELINE_ACTION_BTN_SPLITTED = 1; public const TIMELINE_RELATIVE_DATE = 0; public const TIMELINE_ABSOLUTE_DATE = 1; // From CommonGLPI protected $displaylist = false; // From CommonDBTM public $auto_message_on_action = false; public $showdebug = true; public static $rightname = 'config'; public static $undisclosedFields = [ 'proxy_passwd', 'smtp_passwd', 'smtp_oauth_client_id', 'smtp_oauth_client_secret', 'smtp_oauth_options', 'smtp_oauth_refresh_token', 'glpinetwork_registration_key', 'ldap_pass', // this one should not exist anymore, but may be present when admin restored config dump after migration ]; public static $saferUndisclosedFields = ['admin_email', 'replyto_email']; public static function getTypeName($nb = 0) { return __('Setup'); } public static function getMenuContent() { $menu = []; if (static::canView()) { $menu['title'] = _x('setup', 'General'); $menu['page'] = Config::getFormURL(false); $menu['icon'] = Config::getIcon(); $menu['options']['apiclient']['icon'] = APIClient::getIcon(); $menu['options']['apiclient']['title'] = APIClient::getTypeName(Session::getPluralNumber()); $menu['options']['apiclient']['page'] = Config::getFormURL(false) . '?forcetab=Config$8'; $menu['options']['apiclient']['links']['search'] = Config::getFormURL(false) . '?forcetab=Config$8'; $menu['options']['apiclient']['links']['add'] = '/front/apiclient.form.php'; } if (count($menu)) { return $menu; } return false; } public static function canCreate() { return false; } public function canViewItem() { if ( isset($this->fields['context']) && ( in_array($this->fields['context'], ['core', 'inventory'], true) // GLPI config contexts || Plugin::isPluginActive($this->fields['context']) ) ) { return true; } return false; } public function defineTabs($options = []) { $ong = []; $this->addStandardTab(__CLASS__, $ong, $options); $this->addStandardTab('GLPINetwork', $ong, $options); $this->addStandardTab('Log', $ong, $options); return $ong; } public function prepareInputForUpdate($input) { global $CFG_GLPI; // Unset _no_history to not save it as a configuration value unset($input['_no_history']); // Update only an item if (isset($input['context'])) { return $input; } // Process configuration for plugins if (!empty($input['config_context'])) { $config_context = $input['config_context']; unset($input['id']); unset($input['_glpi_csrf_token']); unset($input['_update']); unset($input['config_context']); if ( (!empty($input['config_class'])) && (class_exists($input['config_class'])) && (method_exists($input['config_class'], 'configUpdate')) ) { $config_method = $input['config_class'] . '::configUpdate'; unset($input['config_class']); $input = call_user_func($config_method, $input); } $this->setConfigurationValues($config_context, $input); return false; } // Trim automatically ending slash for url_base config as, for all existing occurrences, // this URL will be prepended to something that starts with a slash. if (isset($input["url_base"]) && !empty($input["url_base"])) { if (Toolbox::isValidWebUrl($input["url_base"])) { $input["url_base"] = rtrim($input["url_base"], '/'); } else { Session::addMessageAfterRedirect(__('Invalid base URL!'), false, ERROR); return false; } } if (isset($input["url_base_api"]) && !empty($input["url_base_api"])) { if (!Toolbox::isValidWebUrl($input["url_base_api"])) { Session::addMessageAfterRedirect(__('Invalid API base URL!'), false, ERROR); return false; } } $input = $this->handleSmtpInput($input); if (isset($input["proxy_passwd"]) && empty($input["proxy_passwd"])) { unset($input["proxy_passwd"]); } if (isset($input["_blank_proxy_passwd"]) && $input["_blank_proxy_passwd"]) { $input['proxy_passwd'] = ''; } // Manage DB Slave process if (isset($input['_dbslave_status'])) { $already_active = DBConnection::isDBSlaveActive(); if ($input['_dbslave_status']) { DBConnection::changeCronTaskStatus(true); if (!$already_active) { // Activate Slave from the "system" tab DBConnection::createDBSlaveConfig(); } else if (isset($input["_dbreplicate_dbhost"])) { // Change parameter from the "replicate" tab DBConnection::saveDBSlaveConf( $input["_dbreplicate_dbhost"], $input["_dbreplicate_dbuser"], $input["_dbreplicate_dbpassword"], $input["_dbreplicate_dbdefault"] ); } } if (!$input['_dbslave_status'] && $already_active) { DBConnection::deleteDBSlaveConfig(); DBConnection::changeCronTaskStatus(false); } } // Matrix for Impact / Urgence / Priority if (isset($input['_matrix'])) { $tab = []; for ($urgency = 1; $urgency <= 5; $urgency++) { for ($impact = 1; $impact <= 5; $impact++) { $priority = $input["_matrix_{$urgency}_{$impact}"]; $tab[$urgency][$impact] = $priority; } } $input['priority_matrix'] = exportArrayToDB($tab); $input['urgency_mask'] = 0; $input['impact_mask'] = 0; for ($i = 1; $i <= 5; $i++) { if ($input["_urgency_{$i}"]) { $input['urgency_mask'] += (1 << $i); } if ($input["_impact_{$i}"]) { $input['impact_mask'] += (1 << $i); } } } if (isset($input['_update_devices_in_menu'])) { $input['devices_in_menu'] = exportArrayToDB( (isset($input['devices_in_menu']) ? $input['devices_in_menu'] : []) ); } // lock mechanism update if (isset($input['lock_use_lock_item'])) { $input['lock_item_list'] = exportArrayToDB((isset($input['lock_item_list']) ? $input['lock_item_list'] : [])); } if (isset($input[Impact::CONF_ENABLED])) { $input[Impact::CONF_ENABLED] = exportArrayToDB($input[Impact::CONF_ENABLED]); } if (isset($input['planning_work_days'])) { $input['planning_work_days'] = exportArrayToDB($input['planning_work_days']); } // Beware : with new management system, we must update each value unset($input['id']); unset($input['_glpi_csrf_token']); unset($input['_update']); // Add skipMaintenance if maintenance mode update if (isset($input['maintenance_mode']) && $input['maintenance_mode']) { $_SESSION['glpiskipMaintenance'] = 1; $url = $CFG_GLPI['root_doc'] . "/index.php?skipMaintenance=1"; Session::addMessageAfterRedirect( sprintf( __('Maintenance mode activated. Backdoor using: %s'), "<a href='$url'>$url</a>" ), false, WARNING ); } // Automatically trim whitespaces around registration key. if (array_key_exists('glpinetwork_registration_key', $input) && !empty($input['glpinetwork_registration_key'])) { $input['glpinetwork_registration_key'] = trim($input['glpinetwork_registration_key']); } $this->setConfigurationValues('core', $input); return false; } /** * Handle SMTP input values. * * @param array $input * * @return array */ private function handleSmtpInput(array $input): array { global $CFG_GLPI; if (array_key_exists('smtp_mode', $input) && (int)$input['smtp_mode'] === MAIL_SMTPOAUTH) { $input['smtp_check_certificate'] = 1; $input['smtp_passwd'] = ''; if (array_key_exists('smtp_oauth_client_secret', $input) && $input['smtp_oauth_client_secret'] === '') { // form does not contains existing password value for security reasons // prevent password to be overriden by an empty value unset($input['smtp_oauth_client_secret']); } if (array_key_exists('smtp_oauth_options', $input)) { if (is_array($input['smtp_oauth_options'])) { $input['smtp_oauth_options'] = json_encode($input['smtp_oauth_options']); } else { $input['smtp_oauth_options'] = ''; } } $has_oauth_settings_changed = (array_key_exists('smtp_oauth_provider', $input) && $input['smtp_oauth_provider'] !== $CFG_GLPI['smtp_oauth_provider']) || (array_key_exists('smtp_oauth_client_id', $input) && $input['smtp_oauth_client_id'] !== $CFG_GLPI['smtp_oauth_client_id']) || (array_key_exists('smtp_oauth_options', $input) && $input['smtp_oauth_options'] !== $CFG_GLPI['smtp_oauth_options']); if ($has_oauth_settings_changed) { // clean credentials, they will have to be replaced by new ones $input['smtp_oauth_refresh_token'] = ''; $input['smtp_username'] = ''; } // remember whether the SMTP Oauth flow has to be triggered $_SESSION['redirect_to_smtp_oauth'] = (bool)($input['_force_redirect_to_smtp_oauth'] ?? false) === true || $has_oauth_settings_changed || (string)$CFG_GLPI['smtp_oauth_refresh_token'] === ''; // ensure value is not saved in DB unset($input['_force_redirect_to_smtp_oauth']); } elseif (array_key_exists('smtp_mode', $input) && (int)$input['smtp_mode'] !== MAIL_SMTPOAUTH) { // clean oauth related information $input['smtp_oauth_provider'] = ''; $input['smtp_oauth_client_id'] = ''; $input['smtp_oauth_client_secret'] = ''; $input['smtp_oauth_options'] = '{}'; $input['smtp_oauth_refresh_token'] = ''; } if (isset($input['smtp_passwd']) && empty($input['smtp_passwd'])) { unset($input['smtp_passwd']); } if (isset($input["_blank_smtp_passwd"]) && $input["_blank_smtp_passwd"]) { $input['smtp_passwd'] = ''; } return $input; } public static function unsetUndisclosedFields(&$fields) { if (isset($fields['context']) && isset($fields['name'])) { if ( $fields['context'] == 'core' && in_array($fields['name'], self::$undisclosedFields) ) { unset($fields['value']); } else { $fields = Plugin::doHookFunction(Hooks::UNDISCLOSED_CONFIG_VALUE, $fields); } } } /** * Print the config form for display * * @return void **/ public function showFormDisplay() { global $CFG_GLPI; if (!self::canView()) { return; } TemplateRenderer::getInstance()->display('pages/setup/general/general_setup.html.twig', [ 'canedit' => Session::haveRight(self::$rightname, UPDATE), 'config' => $CFG_GLPI, ]); } /** * Print the config form for restrictions * * @return void **/ public function showFormInventory() { global $CFG_GLPI; if (!self::canView()) { return; } $rand = mt_rand(); $canedit = Config::canUpdate(); if ($canedit) { echo "<form name='form' action=\"" . Toolbox::getItemTypeFormURL(__CLASS__) . "\" method='post' data-track-changes='true'>"; } echo "<div class='center' id='tabsbody'>"; echo "<table class='tab_cadre_fixe'>"; echo "<tr><th colspan='4'>" . _n('Asset', 'Assets', Session::getPluralNumber()) . "</th></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td width='30%'><label for='dropdown_auto_create_infocoms$rand'>" . __('Enable the financial and administrative information by default') . "</label></td>"; echo "<td width='20%'>"; Dropdown::ShowYesNo('auto_create_infocoms', $CFG_GLPI["auto_create_infocoms"], -1, ['rand' => $rand]); echo "</td><td width='20%'><label for='dropdown_monitors_management_restrict$rand'>" . __('Restrict monitor management') . "</label></td>"; echo "<td width='30%'>"; $this->dropdownGlobalManagement( "monitors_management_restrict", $CFG_GLPI["monitors_management_restrict"], $rand ); echo "</td></tr>"; echo "<tr class='tab_bg_2'><td><label for='dropdown_softwarecategories_id_ondelete$rand'>" . __('Software category deleted by the dictionary rules') . "</label></td><td>"; SoftwareCategory::dropdown(['value' => $CFG_GLPI["softwarecategories_id_ondelete"], 'name' => "softwarecategories_id_ondelete", 'rand' => $rand ]); echo "</td><td><label for='dropdown_peripherals_management_restrict$rand'>" . __('Restrict device management') . "</label></td><td>"; $this->dropdownGlobalManagement( "peripherals_management_restrict", $CFG_GLPI["peripherals_management_restrict"], $rand ); echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='showdate$rand'>" . __('End of fiscal year') . "</label></td><td>"; Html::showDateField("date_tax", ['value' => $CFG_GLPI["date_tax"], 'maybeempty' => false, 'canedit' => true, 'min' => '', 'max' => '', 'showyear' => false, 'rand' => $rand ]); echo "</td><td><label for='dropdown_phones_management_restrict$rand'>" . __('Restrict phone management') . "</label></td><td>"; $this->dropdownGlobalManagement( "phones_management_restrict", $CFG_GLPI["phones_management_restrict"], $rand ); echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_use_autoname_by_entity$rand'>" . __('Automatic fields (marked by *)') . "</label></td><td>"; $tab = [0 => __('Global'), 1 => __('By entity') ]; Dropdown::showFromArray( 'use_autoname_by_entity', $tab, ['value' => $CFG_GLPI["use_autoname_by_entity"], 'rand' => $rand] ); echo "</td><td><label for='dropdown_printers_management_restrict$rand'>" . __('Restrict printer management') . "</label></td><td>"; $this->dropdownGlobalManagement( "printers_management_restrict", $CFG_GLPI["printers_management_restrict"], $rand ); echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='devices_in_menu$rand'>" . __('Devices displayed in menu') . "</label></td>"; echo "<td>"; $dd_params = [ 'name' => 'devices_in_menu', 'values' => $CFG_GLPI['devices_in_menu'], 'display' => true, 'rand' => $rand, 'multiple' => true, 'size' => 3 ]; $item_devices_types = []; foreach ($CFG_GLPI['itemdevices'] as $key => $itemtype) { if ($item = getItemForItemtype($itemtype)) { $item_devices_types[$itemtype] = $item->getTypeName(); } else { unset($CFG_GLPI['itemdevices'][$key]); } } Dropdown::showFromArray($dd_params['name'], $item_devices_types, $dd_params); echo "<input type='hidden' name='_update_devices_in_menu' value='1'>"; echo "</td></tr>\n"; echo "</table>"; echo "<br><table class='tab_cadre_fixe'>"; echo "<tr>"; echo "<th colspan='4'>" . __('Automatically update of the elements related to the computers'); echo "</th><th colspan='2'>" . __('Unit management') . "</th></tr>"; echo "<tr><th> </th>"; echo "<th>" . __('Alternate username') . "</th>"; echo "<th>" . User::getTypeName(1) . "</th>"; echo "<th>" . Group::getTypeName(1) . "</th>"; echo "<th>" . Location::getTypeName(1) . "</th>"; echo "<th>" . __('Status') . "</th>"; echo "</tr>"; $fields = ["contact", "user", "group", "location"]; echo "<tr class='tab_bg_2'>"; echo "<td> " . __('When connecting or updating') . "</td>"; $values = [ __('Do not copy'), __('Copy'), ]; foreach ($fields as $field) { echo "<td>"; $fieldname = "is_" . $field . "_autoupdate"; Dropdown::showFromArray($fieldname, $values, ['value' => $CFG_GLPI[$fieldname]]); echo "</td>"; } echo "<td>"; State::dropdownBehaviour( "state_autoupdate_mode", __('Copy computer status'), $CFG_GLPI["state_autoupdate_mode"] ); echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td> " . __('When disconnecting') . "</td>"; $values = [ __('Do not delete'), __('Clear'), ]; foreach ($fields as $field) { echo "<td>"; $fieldname = "is_" . $field . "_autoclean"; Dropdown::showFromArray($fieldname, $values, ['value' => $CFG_GLPI[$fieldname]]); echo "</td>"; } echo "<td>"; State::dropdownBehaviour( "state_autoclean_mode", __('Clear status'), $CFG_GLPI["state_autoclean_mode"] ); echo "</td></tr>"; if ($canedit) { echo "<tr class='tab_bg_2'>"; echo "<td colspan='6' class='center'>"; echo "<input type='submit' name='update' class='btn btn-primary' value=\"" . _sx('button', 'Save') . "\">"; echo "</td></tr>"; } echo "</table></div>"; Html::closeForm(); } /** * Print the config form for restrictions * * @return void **/ public function showFormAuthentication() { global $CFG_GLPI; if (!Config::canUpdate()) { return; } echo "<form name='form' action=\"" . Toolbox::getItemTypeFormURL(__CLASS__) . "\" method='post' data-track-changes='true'>"; echo "<div class='card' id='tabsbody'>"; echo "<table class='tab_cadre_fixe'>"; echo "<tr><th colspan='4'>" . __('Authentication') . "</th></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td width='30%'>" . __('Automatically add users from an external authentication source') . "</td><td width='20%'>"; Dropdown::showYesNo("is_users_auto_add", $CFG_GLPI["is_users_auto_add"]); echo "</td><td width='30%'>" . __('Add a user without accreditation from a LDAP directory') . "</td><td width='20%'>"; Dropdown::showYesNo("use_noright_users_add", $CFG_GLPI["use_noright_users_add"]); echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td> " . __('Action when a user is deleted from the LDAP directory') . "</td><td>"; AuthLDAP::dropdownUserDeletedActions($CFG_GLPI["user_deleted_ldap"]); echo "</td><td> " . __('Action when a user is restored in the LDAP directory') . "</td><td>"; AuthLDAP::dropdownUserRestoredActions($CFG_GLPI["user_restored_ldap"]); echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td> " . __('GLPI server time zone') . "</td><td>"; Dropdown::showGMT("time_offset", $CFG_GLPI["time_offset"]); echo "</td><td></td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td colspan='4' class='center'>"; echo "<input type='submit' name='update_auth' class='btn btn-primary' value=\"" . _sx('button', 'Save') . "\">"; echo "</td></tr>"; echo "</table></div>"; Html::closeForm(); } /** * Print the config form for slave DB * * @return void **/ public function showFormDBSlave() { global $DB, $CFG_GLPI, $DBslave; if (!Config::canUpdate()) { return; } echo "<form name='form' action=\"" . Toolbox::getItemTypeFormURL(__CLASS__) . "\" method='post' data-track-changes='true'>"; echo "<div class='center' id='tabsbody'>"; echo "<input type='hidden' name='_dbslave_status' value='1'>"; echo "<table class='tab_cadre_fixe'>"; echo "<tr class='tab_bg_2'><th colspan='4'>" . _n('SQL replica', 'SQL replicas', Session::getPluralNumber()) . "</th></tr>"; $DBslave = DBConnection::getDBSlaveConf(); if (is_array($DBslave->dbhost)) { $host = implode(' ', $DBslave->dbhost); } else { $host = $DBslave->dbhost; } echo "<tr class='tab_bg_2'>"; echo "<td>" . __('SQL server (MariaDB or MySQL)') . "</td>"; echo "<td><input type='text' name='_dbreplicate_dbhost' size='40' value='$host'></td>"; echo "<td>" . _n('Database', 'Databases', 1) . "</td>"; echo "<td><input type='text' name='_dbreplicate_dbdefault' value='" . $DBslave->dbdefault . "'>"; echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td>" . __('SQL user') . "</td>"; echo "<td><input type='text' name='_dbreplicate_dbuser' value='" . $DBslave->dbuser . "'></td>"; echo "<td>" . __('SQL password') . "</td>"; echo "<td><input type='password' name='_dbreplicate_dbpassword' value='" . rawurldecode($DBslave->dbpassword) . "'>"; echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td>" . __('Use the replica for the search engine') . "</td><td>"; $values = [0 => __('Never'), 1 => __('If synced (all changes)'), 2 => __('If synced (current user changes)'), 3 => __('If synced or read-only account'), 4 => __('Always') ]; Dropdown::showFromArray( 'use_slave_for_search', $values, ['value' => $CFG_GLPI["use_slave_for_search"]] ); echo "<td colspan='2'> </td>"; echo "</tr>"; if ($DBslave->connected && !$DB->isSlave()) { echo "<tr class='tab_bg_2'><td colspan='4' class='center'>"; DBConnection::showAllReplicateDelay(); echo "</td></tr>"; } echo "<tr class='tab_bg_2'><td colspan='4' class='center'>"; echo "<input type='submit' name='update' class='btn btn-primary' value=\"" . _sx('button', 'Save') . "\">"; echo "</td></tr>"; echo "</table></div>"; Html::closeForm(); } /** * Print the config form for External API * * @since 9.1 * @return void **/ public function showFormAPI() { global $CFG_GLPI; if (!self::canView()) { return; } echo "<div class='center spaced' id='tabsbody'>"; $rand = mt_rand(); $canedit = Config::canUpdate(); if ($canedit) { echo "<form name='form' action=\"" . Toolbox::getItemTypeFormURL(__CLASS__) . "\" method='post' data-track-changes='true'>"; } echo "<table class='tab_cadre_fixe'>"; echo "<tr><th colspan='4'>" . __('API') . "</th></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='url_base_api'>" . __('URL of the API') . "</label></td>"; echo "<td colspan='3'><input type='url' name='url_base_api' id='url_base_api' value='" . $CFG_GLPI["url_base_api"] . "' class='form-control'></td>"; echo "</tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_enable_api$rand'>" . __("Enable Rest API") . "</label></td>"; echo "<td>"; Dropdown::showYesNo("enable_api", $CFG_GLPI["enable_api"], -1, ['rand' => $rand]); echo "</td>"; if ($CFG_GLPI["enable_api"]) { echo "<td colspan='2'>"; $inline_doc_api = trim($CFG_GLPI['url_base_api'], '/') . "/"; echo "<a href='$inline_doc_api'>" . __("API inline Documentation") . "</a>"; echo "</td>"; } echo "</tr>"; echo "<tr><th colspan='4'>" . __('Authentication') . "</th></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_enable_api_login_credentials$rand'>"; echo __("Enable login with credentials") . "</label> "; Html::showToolTip(__("Allow to login to API and get a session token with user credentials")); echo "</td>"; echo "<td>"; Dropdown::showYesNo("enable_api_login_credentials", $CFG_GLPI["enable_api_login_credentials"], -1, ['rand' => $rand]); echo "</td>"; echo "<td><label for='dropdown_enable_api_login_external_token$rand'>"; echo __("Enable login with external token") . "</label> "; Html::showToolTip(__("Allow to login to API and get a session token with user external token. See Remote access key in user Settings tab ")); echo "</td>"; echo "<td>"; Dropdown::showYesNo("enable_api_login_external_token", $CFG_GLPI["enable_api_login_external_token"], -1, ['rand' => $rand]); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_2'><td colspan='4' class='center'>"; echo "<input type='submit' name='update' class='btn btn-primary' value=\"" . _sx('button', 'Save') . "\">"; echo "<br><br><br>"; echo "</td></tr>"; echo "</table>"; Html::closeForm(); echo "<table class='tab_cadre_fixe'>"; echo "<tr><td>"; echo "<hr>"; $buttons = [ 'apiclient.form.php' => __('Add API client'), ]; Html::displayTitle( "", self::getTypeName(Session::getPluralNumber()), "", $buttons ); Search::show("APIClient"); echo "</td></tr>"; echo "</table></div>"; } /** * Print the config form for connections * * @return void **/ public function showFormHelpdesk() { global $CFG_GLPI; if (!self::canView()) { return; } $rand = mt_rand(); $canedit = Config::canUpdate(); if ($canedit) { echo "<form name='form' action=\"" . Toolbox::getItemTypeFormURL(__CLASS__) . "\" method='post' data-track-changes='true'>"; } echo "<div class='center spaced' id='tabsbody'>"; echo "<table class='tab_cadre_fixe'>"; echo "<tr><th colspan='4'>" . __('Assistance') . "</th></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td width='30%'><label for='dropdown_time_step$rand'>" . __('Step for the hours (minutes)') . "</label></td>"; echo "<td width='20%'>"; Dropdown::showNumber('time_step', ['value' => $CFG_GLPI["time_step"], 'min' => 30, 'max' => 60, 'step' => 30, 'toadd' => [1 => 1, 5 => 5, 10 => 10, 15 => 15, 20 => 20 ], 'rand' => $rand ]); echo "</td>"; echo "<td width='30%'><label for='dropdown_planning_begin$rand'>" . __('Limit of the schedules for planning') . "</label></td>"; echo "<td width='20%'>"; Dropdown::showHours('planning_begin', ['value' => $CFG_GLPI["planning_begin"], 'rand' => $rand]); echo " <label for='dropdown_planning_end$rand'>-></label> "; Dropdown::showHours('planning_end', ['value' => $CFG_GLPI["planning_end"], 'rand' => $rand]); echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_default_mailcollector_filesize_max$rand'>" . __('Default file size limit imported by the mails receiver') . "</label></td><td>"; MailCollector::showMaxFilesize( 'default_mailcollector_filesize_max', $CFG_GLPI["default_mailcollector_filesize_max"], $rand ); echo "</td>"; echo "<td><label for='dropdown_documentcategories_id_forticket$rand'>" . __('Default heading when adding a document to a ticket') . "</label></td><td>"; DocumentCategory::dropdown(['value' => $CFG_GLPI["documentcategories_id_forticket"], 'name' => "documentcategories_id_forticket", 'rand' => $rand ]); echo "</td></tr>"; echo "<tr class='tab_bg_2'><td><label for='dropdown_default_software_helpdesk_visible$rand'>" . __('By default, a software may be linked to a ticket') . "</label></td><td>"; Dropdown::showYesNo( "default_software_helpdesk_visible", $CFG_GLPI["default_software_helpdesk_visible"], -1, ['rand' => $rand] ); echo "</td>"; echo "<td><label for='dropdown_keep_tickets_on_delete$rand'>" . __('Keep tickets when purging hardware in the inventory') . "</label></td><td>"; Dropdown::showYesNo("keep_tickets_on_delete", $CFG_GLPI["keep_tickets_on_delete"], -1, ['rand' => $rand]); echo "</td></tr><tr class='tab_bg_2'><td><label for='dropdown_use_check_pref$rand'>" . __('Show personnal information in new ticket form (simplified interface)'); echo "</label></td>"; echo "<td>"; Dropdown::showYesNo('use_check_pref', $CFG_GLPI['use_check_pref'], -1, ['rand' => $rand]); echo "</td>"; echo "<td><label for='dropdown_use_anonymous_helpdesk$rand'>" . __('Allow anonymous ticket creation (helpdesk.receiver)') . "</label></td><td>"; Dropdown::showYesNo("use_anonymous_helpdesk", $CFG_GLPI["use_anonymous_helpdesk"], -1, ['rand' => $rand]); echo "</td></tr><tr class='tab_bg_2'><td><label for='dropdown_use_anonymous_followups$rand'>" . __('Allow anonymous followups (receiver)') . "</label></td><td>"; Dropdown::showYesNo("use_anonymous_followups", $CFG_GLPI["use_anonymous_followups"], -1, ['rand' => $rand]); echo "</td><td colspan='2'></td></tr>"; echo "<tr>"; echo "<td>"; echo "<label for='dropdown_planning_work_days$rand'>" . __('Planning work days') . "</label>"; echo "</td>"; echo "<td colspan='3'>"; Dropdown::showFromArray( "planning_work_days", [ 1 => __("Monday"), 2 => __("Tuesday"), 3 => __("Wednesday"), 4 => __("Thursday"), 5 => __("Friday"), 6 => __("Saturday"), 0 => __("Sunday"), ], [ 'values' => $CFG_GLPI["planning_work_days"], 'multiple' => true, 'rand' => $rand, ] ); echo "</td>"; echo "</tr>"; echo "</table>"; echo "<table class='tab_cadre_fixe'>"; echo "<tr><th colspan='7'>" . __('Matrix of calculus for priority'); echo "<input type='hidden' name='_matrix' value='1'></th></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td class='b right' colspan='2'>" . __('Impact') . "</td>"; $isimpact = []; for ($impact = 5; $impact >= 1; $impact--) { echo "<td class='center'>" . Ticket::getImpactName($impact) . '<br>'; if ($impact == 3) { $isimpact[3] = 1; echo "<input type='hidden' name='_impact_3' value='1'>"; } else { $isimpact[$impact] = (($CFG_GLPI['impact_mask'] & (1 << $impact)) > 0); Dropdown::showYesNo("_impact_{$impact}", $isimpact[$impact]); } echo "</td>"; } echo "</tr>"; echo "<tr class='tab_bg_1'>"; echo "<td class='b' colspan='2'>" . __('Urgency') . "</td>"; for ($impact = 5; $impact >= 1; $impact--) { echo "<td> </td>"; } echo "</tr>"; $isurgency = []; for ($urgency = 5; $urgency >= 1; $urgency--) { echo "<tr class='tab_bg_1'>"; echo "<td>" . Ticket::getUrgencyName($urgency) . " </td>"; echo "<td>"; if ($urgency == 3) { $isurgency[3] = 1; echo "<input type='hidden' name='_urgency_3' value='1'>"; } else { $isurgency[$urgency] = (($CFG_GLPI['urgency_mask'] & (1 << $urgency)) > 0); Dropdown::showYesNo("_urgency_{$urgency}", $isurgency[$urgency]); } echo "</td>"; for ($impact = 5; $impact >= 1; $impact--) { $pri = round(($urgency + $impact) / 2); if (isset($CFG_GLPI['priority_matrix'][$urgency][$impact])) { $pri = $CFG_GLPI['priority_matrix'][$urgency][$impact]; } if ($isurgency[$urgency] && $isimpact[$impact]) { $bgcolor = $_SESSION["glpipriority_$pri"]; echo "<td class='center' bgcolor='$bgcolor'>"; Ticket::dropdownPriority([ 'value' => $pri, 'name' => "_matrix_{$urgency}_{$impact}", 'enable_filtering' => false, ]); echo "</td>"; } else { echo "<td><input type='hidden' name='_matrix_{$urgency}_{$impact}' value='$pri'> </td>"; } } echo "</tr>\n"; } if ($canedit) { echo "<tr class='tab_bg_2'>"; echo "<td colspan='7' class='center'>"; echo "<input type='submit' name='update' class='btn btn-primary' value=\"" . _sx('button', 'Save') . "\">"; echo "</td></tr>"; } echo "</table></div>"; Html::closeForm(); } /** * Print the config form for default user prefs * * @param $data array containing datas * (CFG_GLPI for global config / glpi_users fields for user prefs) * * @return void **/ public function showFormUserPrefs($data = []) { global $CFG_GLPI, $DB; $oncentral = (Session::getCurrentInterface() == "central"); $userpref = false; $url = Toolbox::getItemTypeFormURL(__CLASS__); $rand = mt_rand(); $canedit = Config::canUpdate(); $canedituser = Session::haveRight('personalization', UPDATE); if (array_key_exists('last_login', $data)) { $userpref = true; if ($data["id"] === Session::getLoginUserID()) { $url = $CFG_GLPI['root_doc'] . "/front/preference.php"; } else { $url = User::getFormURL(); } } if ((!$userpref && $canedit) || ($userpref && $canedituser)) { echo "<form name='form' action='$url' method='post' data-track-changes='true'>"; } // Only set id for user prefs if ($userpref) { echo "<input type='hidden' name='id' value='" . $data['id'] . "'>"; } echo "<div class='center' id='tabsbody'>"; echo "<table class='tab_cadre_fixe'>"; echo "<tr><th colspan='4'>" . __('Personalization') . "</th></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td width='30%'><label for='dropdown_language$rand'>" . ($userpref ? __('Language') : __('Default language')) . "</label></td>"; echo "<td width='20%'>"; if ( Config::canUpdate() || !GLPI_DEMO_MODE ) { Dropdown::showLanguages("language", ['value' => $data["language"], 'rand' => $rand]); } else { echo " "; } echo "<td width='30%'><label for='dropdown_date_format$rand'>" . __('Date format') . "</label></td>"; echo "<td width='20%'>"; Dropdown::showFromArray('date_format', Toolbox::phpDateFormats(), ['value' => $data["date_format"], 'rand' => $rand]); echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_names_format$rand'>" . __('Display order of surnames firstnames') . "</label></td><td>"; $values = [User::REALNAME_BEFORE => __('Surname, First name'), User::FIRSTNAME_BEFORE => __('First name, Surname') ]; Dropdown::showFromArray('names_format', $values, ['value' => $data["names_format"], 'rand' => $rand]); echo "</td>"; echo "<td><label for='dropdown_number_format$rand'>" . __('Number format') . "</label></td>"; $values = [0 => '1 234.56', 1 => '1,234.56', 2 => '1 234,56', 3 => '1234.56', 4 => '1234,56' ]; echo "<td>"; Dropdown::showFromArray('number_format', $values, ['value' => $data["number_format"], 'rand' => $rand]); echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_list_limit$rand'>" . __('Results to display by page') . "</label></td><td>"; // Limit using global config $value = (($data['list_limit'] < $CFG_GLPI['list_limit_max']) ? $data['list_limit'] : $CFG_GLPI['list_limit_max']); Dropdown::showNumber('list_limit', ['value' => $value, 'min' => 5, 'max' => $CFG_GLPI['list_limit_max'], 'step' => 5, 'rand' => $rand ]); echo "</td>"; echo "<td><label for='dropdown_backcreated$rand'>" . __('Go to created item after creation') . "</label></td>"; echo "<td>"; Dropdown::showYesNo("backcreated", $data["backcreated"], -1, ['rand' => $rand]); echo "</td>"; echo "</tr>"; if ($oncentral) { echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_use_flat_dropdowntree$rand'>" . __('Display the tree dropdown complete name in dropdown inputs') . "</label></td><td>"; Dropdown::showYesNo('use_flat_dropdowntree', $data["use_flat_dropdowntree"], -1, ['rand' => $rand]); echo "</td>"; echo "<td><label for='dropdown_use_flat_dropdowntree_on_search_result$rand'>" . __('Display the complete name of tree dropdown in search results') . "</label></td><td>"; Dropdown::showYesNo('use_flat_dropdowntree_on_search_result', $data["use_flat_dropdowntree_on_search_result"], -1, ['rand' => $rand]); echo "</td>"; echo "</tr>"; } echo "<tr class='tab_bg_2'>"; if ( !$userpref || ($CFG_GLPI['show_count_on_tabs'] != -1) ) { echo "<td><label for='dropdown_show_count_on_tabs$rand'>" . __('Display counters') . "</label></td><td>"; $values = [0 => __('No'), 1 => __('Yes') ]; if (!$userpref) { $values[-1] = __('Never'); } Dropdown::showFromArray( 'show_count_on_tabs', $values, ['value' => $data["show_count_on_tabs"], 'rand' => $rand] ); echo "</td>"; } else { echo "<td colspan='2'> </td>"; } echo "</tr>"; echo "<tr class='tab_bg_2'>"; if ($oncentral) { echo "<td><label for='dropdown_is_ids_visible$rand'>" . __('Show GLPI ID') . "</label></td><td>"; Dropdown::showYesNo("is_ids_visible", $data["is_ids_visible"], -1, ['rand' => $rand]); echo "</td>"; } else { echo "<td colspan='2'></td>"; } echo "<td><label for='dropdown_keep_devices_when_purging_item$rand'>" . __('Keep devices when purging an item') . "</label></td><td>"; Dropdown::showYesNo( 'keep_devices_when_purging_item', $data['keep_devices_when_purging_item'], -1, ['rand' => $rand] ); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_notification_to_myself$rand'>" . __('Notifications for my changes') . "</label></td><td>"; Dropdown::showYesNo("notification_to_myself", $data["notification_to_myself"], -1, ['rand' => $rand]); echo "</td>"; if ($oncentral) { echo "<td><label for='dropdown_display_count_on_home$rand'>" . __('Results to display on home page') . "</label></td><td>"; Dropdown::showNumber( 'display_count_on_home', ['value' => $data['display_count_on_home'], 'min' => 0, 'max' => 30, 'rand' => $rand ] ); echo "</td>"; } else { echo "<td colspan='2'> </td>"; } echo "</tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_pdffont$rand'>" . __('PDF export font') . "</label></td><td>"; Dropdown::showFromArray( "pdffont", GLPIPDF::getFontList(), ['value' => $data["pdffont"], 'width' => 200, 'rand' => $rand ] ); echo "</td>"; echo "<td><label for='dropdown_csv_delimiter$rand'>" . __('CSV delimiter') . "</label></td><td>"; $values = [';' => ';', ',' => ',' ]; Dropdown::showFromArray('csv_delimiter', $values, ['value' => $data["csv_delimiter"], 'rand' => $rand]); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='theme-selector'>" . __("Color palette") . "</label></td><td>"; echo Html::select( 'palette', $this->getPalettes(), [ 'id' => 'theme-selector', 'selected' => $data['palette'] ] ); echo Html::scriptBlock(" function formatThemes(theme) { if (!theme.id) { return theme.text; } return $('<span></span>').html('<img src=\'../css/palettes/previews/' + theme.text.toLowerCase() + '.png\'/>' + ' ' + theme.text); } $(\"#theme-selector\").select2({ templateResult: formatThemes, templateSelection: formatThemes, width: '100%', escapeMarkup: function(m) { return m; } }); $('label[for=theme-selector]').on('click', function(){ $('#theme-selector').select2('open'); }); "); echo "</td>"; echo "<td>"; echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_page_layout$rand'>" . __('Page layout') . "</label></td>"; echo "<td>"; $global_layout_options = [ 'horizontal' => __('Horizontal (menu in header)'), 'vertical' => __('Vertical (menu in sidebar)'), ]; echo Html::select( 'page_layout', $global_layout_options, [ 'id' => 'global-layout-selector', 'selected' => $data['page_layout'] ] ); echo Html::scriptBlock(" function formatGlobalLayout(layout) { if (!layout.id) { return layout.text; } return $('<span></span>').html('<img src=\'../pics/layout/global_layout_' + layout.id.toLowerCase() + '.png\'/>' + ' ' + layout.text); } $('#global-layout-selector').select2({ dropdownAutoWidth: true, templateResult: formatGlobalLayout, templateSelection: formatGlobalLayout }); $('label[for=global-layout-selector]').on('click', function(){ $('#global-layout-selector').select2('open'); }); "); echo "</td>"; echo "<td><label for='dropdown_richtext_layout$rand'>" . __('Rich text field layout') . "</label></td>"; echo "<td>"; Dropdown::showFromArray( 'richtext_layout', [ 'inline' => __('Inline (no toolbars)'), 'classic' => __('Classic (toolbar on top)'), ], [ 'value' => $data["richtext_layout"], ] ); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_2'><td><label for='dropdown_highcontrast_css$rand'>" . __('Enable high contrast') . "</label></td>"; echo "<td>"; Dropdown::showYesNo('highcontrast_css', $data['highcontrast_css'], -1, ['rand' => $rand]); echo "</td>"; echo "<td><label for='dropdown_timezone$rand'>" . __('Timezone') . "</label></td>"; echo "<td>"; if ($DB->use_timezones) { $timezones = $DB->getTimezones(); Dropdown::showFromArray( 'timezone', $timezones, [ 'value' => $data["timezone"] ?? "", 'display_emptychoice' => true, 'emptylabel' => __('Use server configuration') ] ); } else { echo __('Timezone usage has not been activated.') . ' ' . sprintf(__('Run the "%1$s" command to activate it.'), 'php bin/console database:enable_timezones'); } echo "<tr class='tab_bg_2'><td><label for='dropdown_default_central_tab$rand'>" . __('Default central tab') . "</label></td>"; echo "<td>"; $central = new Central(); Dropdown::showFromArray('default_central_tab', $central->getTabNameForItem($central, 0), ['value' => $data['default_central_tab'], 'rand' => $rand]); echo "</td>"; echo "<td><label for='dropdown_timeline_order$rand'>" . __('Timeline order') . "</label></td>"; echo "<td>"; Dropdown::showFromArray('timeline_order', [ CommonITILObject::TIMELINE_ORDER_NATURAL => __('Natural order (old items on top, recent on bottom)'), CommonITILObject::TIMELINE_ORDER_REVERSE => __('Reverse order (old items on bottom, recent on top)'), ], [ 'value' => $data['timeline_order'], 'rand' => $rand ]); echo "</td>"; echo "</tr>"; if ($oncentral) { echo "<tr class='tab_bg_1'><th colspan='4'>" . __('Assistance') . "</th></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_followup_private$rand'>" . __('Private followups by default') . "</label></td><td>"; Dropdown::showYesNo("followup_private", $data["followup_private"], -1, ['rand' => $rand]); echo "</td><td><label for='dropdown_show_jobs_at_login$rand'>" . __('Show new tickets on the home page') . "</label></td><td>"; if ( Session::haveRightsOr( "ticket", [Ticket::READMY, Ticket::READALL, Ticket::READASSIGN] ) ) { Dropdown::showYesNo("show_jobs_at_login", $data["show_jobs_at_login"], -1, ['rand' => $rand]); } else { echo Dropdown::getYesNo(0); } echo " </td></tr>"; echo "<tr class='tab_bg_2'><td><label for='dropdown_task_private$rand'>" . __('Private tasks by default') . "</label></td><td>"; Dropdown::showYesNo("task_private", $data["task_private"], -1, ['rand' => $rand]); echo "</td><td><label for='dropdown_default_requesttypes_id$rand'>" . __('Request sources by default') . "</label></td><td>"; RequestType::dropdown([ 'value' => $data["default_requesttypes_id"], 'name' => "default_requesttypes_id", 'condition' => ['is_active' => 1, 'is_ticketheader' => 1], 'rand' => $rand ]); echo "</td></tr>"; echo "<tr class='tab_bg_2'><td><label for='dropdown_task_state$rand'>" . __('Tasks state by default') . "</label></td><td>"; Planning::dropdownState("task_state", $data["task_state"], true, ['rand' => $rand]); echo "</td><td><label for='dropdown_refresh_views$rand'>" . __('Automatically refresh data (tickets list, project kanban) in minutes.') . "</label></td><td>"; Dropdown::showNumber('refresh_views', ['value' => $data["refresh_views"], 'min' => 1, 'max' => 30, 'step' => 1, 'toadd' => [0 => __('Never')], 'rand' => $rand ]); echo "</td></tr>"; echo "<tr class='tab_bg_2'><td><label for='dropdown_set_default_tech$rand'>" . __('Pre-select me as a technician when creating a ticket') . "</label></td><td>"; if (!$userpref || Session::haveRight('ticket', Ticket::OWN)) { Dropdown::showYesNo("set_default_tech", $data["set_default_tech"], -1, ['rand' => $rand]); } else { echo Dropdown::getYesNo(0); } echo "</td><td><label for='dropdown_set_default_requester$rand'>" . __('Pre-select me as a requester when creating a ticket') . "</label></td><td>"; if (!$userpref || Session::haveRight('ticket', CREATE)) { Dropdown::showYesNo("set_default_requester", $data["set_default_requester"], -1, ['rand' => $rand]); } else { echo Dropdown::getYesNo(0); } echo "<tr class='tab_bg_2'><td><label for='timeline_action_btn_layout$rand'>" . __('Action button layout') . "</label></td><td>"; Dropdown::showFromArray('timeline_action_btn_layout', [ self::TIMELINE_ACTION_BTN_MERGED => __('Merged'), self::TIMELINE_ACTION_BTN_SPLITTED => __('Splitted'), ], [ 'value' => $data['timeline_action_btn_layout'], 'rand' => $rand ]); echo "</td><td></td></tr>"; echo "</td></tr>"; echo "<tr class='tab_bg_2'><td><label for='timeline_date_format$rand'>" . __('Timeline date display') . "</label></td><td>"; Dropdown::showFromArray('timeline_date_format', [ self::TIMELINE_RELATIVE_DATE => __('Relative'), self::TIMELINE_ABSOLUTE_DATE => __('Precise'), ], [ 'value' => $data['timeline_date_format'], 'rand' => $rand ]); echo "</td><td></td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td>" . __('Priority colors') . "</td>"; echo "<td colspan='3'>"; echo "<table><tr>"; echo "<td><label for='dropdown_priority_1$rand'>1</label> "; Html::showColorField('priority_1', ['value' => $data["priority_1"], 'rand' => $rand]); echo "</td>"; echo "<td><label for='dropdown_priority_2$rand'>2</label> "; Html::showColorField('priority_2', ['value' => $data["priority_2"], 'rand' => $rand]); echo "</td>"; echo "<td><label for='dropdown_priority_3$rand'>3</label> "; Html::showColorField('priority_3', ['value' => $data["priority_3"], 'rand' => $rand]); echo "</td>"; echo "<td><label for='dropdown_priority_4$rand'>4</label> "; Html::showColorField('priority_4', ['value' => $data["priority_4"], 'rand' => $rand]); echo "</td>"; echo "<td><label for='dropdown_priority_5$rand'>5</label> "; Html::showColorField('priority_5', ['value' => $data["priority_5"], 'rand' => $rand]); echo "</td>"; echo "<td><label for='dropdown_priority_6$rand'>6</label> "; Html::showColorField('priority_6', ['value' => $data["priority_6"], 'rand' => $rand]); echo "</td>"; echo "</tr></table>"; echo "</td></tr>"; } echo "<tr><th colspan='4'>" . __('Due date progression') . "</th></tr>"; echo "<tr class='tab_bg_1'>" . "<td>" . __('OK state color') . "</td>"; echo "<td>"; Html::showColorField('duedateok_color', ['value' => $data["duedateok_color"]]); echo "</td><td colspan='2'> </td></tr>"; echo "<tr class='tab_bg_1'>"; echo "<td>" . __('Warning state color') . "</td>"; echo "<td>"; Html::showColorField('duedatewarning_color', ['value' => $data["duedatewarning_color"]]); echo "</td>"; echo "<td>" . __('Warning state threshold') . "</td>"; echo "<td>"; Dropdown::showNumber("duedatewarning_less", ['value' => $data['duedatewarning_less']]); $elements = ['%' => '%', 'hours' => _n('Hour', 'Hours', Session::getPluralNumber()), 'days' => _n('Day', 'Days', Session::getPluralNumber()) ]; echo " "; Dropdown::showFromArray( "duedatewarning_unit", $elements, ['value' => $data['duedatewarning_unit']] ); echo "</td></tr>"; echo "<tr class='tab_bg_1'>" . "<td>" . __('Critical state color') . "</td>"; echo "<td>"; Html::showColorField('duedatecritical_color', ['value' => $data["duedatecritical_color"]]); echo "</td>"; echo "<td>" . __('Critical state threshold') . "</td>"; echo "<td>"; Dropdown::showNumber("duedatecritical_less", ['value' => $data['duedatecritical_less']]); echo " "; $elements = ['%' => '%', 'hours' => _n('Hour', 'Hours', Session::getPluralNumber()), 'days' => _n('Day', 'Days', Session::getPluralNumber()) ]; Dropdown::showFromArray( "duedatecritical_unit", $elements, ['value' => $data['duedatecritical_unit']] ); echo "</td></tr>"; if ($oncentral && $CFG_GLPI["lock_use_lock_item"]) { echo "<tr class='tab_bg_1'><th colspan='4' class='center b'>" . __('Item locks') . "</th></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td>" . __('Auto-lock Mode') . "</td><td>"; Dropdown::showYesNo("lock_autolock_mode", $data["lock_autolock_mode"]); echo "</td><td>" . __('Direct Notification (requester for unlock will be the notification sender)') . "</td><td>"; Dropdown::showYesNo("lock_directunlock_notification", $data["lock_directunlock_notification"]); echo "</td></tr>"; } if (Grid::canViewOneDashboard()) { echo "<tr class='tab_bg_1'><th colspan='4' class='center b'>" . __('Dashboards') . "</th></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td>" . __('Default for central') . "</td><td>"; Grid::dropdownDashboard("default_dashboard_central", [ 'value' => $data['default_dashboard_central'], 'display_emptychoice' => true ]); echo "</td><td>" . __('Default for Assets') . "</td><td>"; Grid::dropdownDashboard("default_dashboard_assets", [ 'value' => $data['default_dashboard_assets'], 'display_emptychoice' => true ]); echo "</td></tr>"; echo "<tr class='tab_bg_1'>"; echo "<td>" . __('Default for Assistance') . "</td><td>"; Grid::dropdownDashboard("default_dashboard_helpdesk", [ 'value' => $data['default_dashboard_helpdesk'], 'display_emptychoice' => true ]); echo "</td><td>" . __('Default for tickets (mini dashboard)') . "</td><td>"; Grid::dropdownDashboard("default_dashboard_mini_ticket", [ 'value' => $data['default_dashboard_mini_ticket'], 'display_emptychoice' => true, 'context' => 'mini_core', ]); echo "</td></tr>"; } if ((!$userpref && $canedit) || ($userpref && $canedituser)) { echo "<tr class='tab_bg_2'>"; echo "<td colspan='4' class='center'>"; echo "<input type='submit' name='update' class='btn btn-primary' value=\"" . _sx('button', 'Save') . "\">"; echo "</td></tr>"; } echo "</table></div>"; Html::closeForm(); } /** * Check if the "use_password_security" parameter is enabled * * @return bool */ public static function arePasswordSecurityChecksEnabled(): bool { global $CFG_GLPI; return $CFG_GLPI["use_password_security"]; } /** * Display security checks on password * * @param $field string id of the field containing password to check (default 'password') * * @since 0.84 **/ public static function displayPasswordSecurityChecks($field = 'password') { global $CFG_GLPI; $needs = []; if ($CFG_GLPI["use_password_security"]) { printf( __('%1$s: %2$s'), __('Password minimum length'), "<span id='password_min_length' class='red'>" . $CFG_GLPI['password_min_length'] . "</span>" ); } echo "<script type='text/javascript' >\n"; echo "function passwordCheck() {\n"; if ($CFG_GLPI["use_password_security"]) { echo "var pwd = " . Html::jsGetElementbyID($field) . ";"; echo "if (pwd.val().length < " . $CFG_GLPI['password_min_length'] . ") { " . Html::jsGetElementByID('password_min_length') . ".addClass('red'); " . Html::jsGetElementByID('password_min_length') . ".removeClass('green'); } else { " . Html::jsGetElementByID('password_min_length') . ".addClass('green'); " . Html::jsGetElementByID('password_min_length') . ".removeClass('red'); }"; if ($CFG_GLPI["password_need_number"]) { $needs[] = "<span id='password_need_number' class='red'>" . __('Digit') . "</span>"; echo "var numberRegex = new RegExp('[0-9]', 'g'); if (false == numberRegex.test(pwd.val())) { " . Html::jsGetElementByID('password_need_number') . ".addClass('red'); " . Html::jsGetElementByID('password_need_number') . ".removeClass('green'); } else { " . Html::jsGetElementByID('password_need_number') . ".addClass('green'); " . Html::jsGetElementByID('password_need_number') . ".removeClass('red'); }"; } if ($CFG_GLPI["password_need_letter"]) { $needs[] = "<span id='password_need_letter' class='red'>" . __('Lowercase') . "</span>"; echo "var letterRegex = new RegExp('[a-z]', 'g'); if (false == letterRegex.test(pwd.val())) { " . Html::jsGetElementByID('password_need_letter') . ".addClass('red'); " . Html::jsGetElementByID('password_need_letter') . ".removeClass('green'); } else { " . Html::jsGetElementByID('password_need_letter') . ".addClass('green'); " . Html::jsGetElementByID('password_need_letter') . ".removeClass('red'); }"; } if ($CFG_GLPI["password_need_caps"]) { $needs[] = "<span id='password_need_caps' class='red'>" . __('Uppercase') . "</span>"; echo "var capsRegex = new RegExp('[A-Z]', 'g'); if (false == capsRegex.test(pwd.val())) { " . Html::jsGetElementByID('password_need_caps') . ".addClass('red'); " . Html::jsGetElementByID('password_need_caps') . ".removeClass('green'); } else { " . Html::jsGetElementByID('password_need_caps') . ".addClass('green'); " . Html::jsGetElementByID('password_need_caps') . ".removeClass('red'); }"; } if ($CFG_GLPI["password_need_symbol"]) { $needs[] = "<span id='password_need_symbol' class='red'>" . __('Symbol') . "</span>"; echo "var capsRegex = new RegExp('[^a-zA-Z0-9_]', 'g'); if (false == capsRegex.test(pwd.val())) { " . Html::jsGetElementByID('password_need_symbol') . ".addClass('red'); " . Html::jsGetElementByID('password_need_symbol') . ".removeClass('green'); } else { " . Html::jsGetElementByID('password_need_symbol') . ".addClass('green'); " . Html::jsGetElementByID('password_need_symbol') . ".removeClass('red'); }"; } } echo "}"; echo '</script>'; if (count($needs)) { echo "<br>"; printf(__('%1$s: %2$s'), __('Password must contains'), implode(', ', $needs)); } } /** * Validate password based on security rules * * @since 0.84 * * @param $password string password to validate * @param $display boolean display errors messages? (true by default) * * @throws PasswordTooWeakException when $display is false and the password does not matches the requirements * * @return boolean is password valid? **/ public static function validatePassword($password, $display = true) { global $CFG_GLPI; $ok = true; $exception = new \Glpi\Exception\PasswordTooWeakException(); if ($CFG_GLPI["use_password_security"]) { if (Toolbox::strlen($password) < $CFG_GLPI['password_min_length']) { $ok = false; if ($display) { Session::addMessageAfterRedirect(__('Password too short!'), false, ERROR); } else { $exception->addMessage(__('Password too short!')); } } if ( $CFG_GLPI["password_need_number"] && !preg_match("/[0-9]+/", $password) ) { $ok = false; if ($display) { Session::addMessageAfterRedirect( __('Password must include at least a digit!'), false, ERROR ); } else { $exception->addMessage(__('Password must include at least a digit!')); } } if ( $CFG_GLPI["password_need_letter"] && !preg_match("/[a-z]+/", $password) ) { $ok = false; if ($display) { Session::addMessageAfterRedirect( __('Password must include at least a lowercase letter!'), false, ERROR ); } else { $exception->addMessage(__('Password must include at least a lowercase letter!')); } } if ( $CFG_GLPI["password_need_caps"] && !preg_match("/[A-Z]+/", $password) ) { $ok = false; if ($display) { Session::addMessageAfterRedirect( __('Password must include at least a uppercase letter!'), false, ERROR ); } else { $exception->addMessage(__('Password must include at least a uppercase letter!')); } } if ( $CFG_GLPI["password_need_symbol"] && !preg_match("/\W+/", $password) ) { $ok = false; if ($display) { Session::addMessageAfterRedirect( __('Password must include at least a symbol!'), false, ERROR ); } else { $exception->addMessage(__('Password must include at least a symbol!')); } } } if (!$ok && !$display) { throw $exception; } return $ok; } /** * Display a report about system performance * - opcode cache (opcache) * - core cache * - translations cache * * @since 9.1 **/ public function showPerformanceInformations() { if (!Config::canUpdate()) { return false; } echo "<div class='center' id='tabsbody'>"; echo "<table class='tab_cadre_fixe'>"; echo "<tr><th colspan='4'>" . __('PHP opcode cache') . "</th></tr>"; $ext = 'Zend OPcache'; if (extension_loaded($ext) && ($info = opcache_get_status(false))) { $msg = sprintf(__s('%s extension is installed'), $ext); echo "<tr><td>" . sprintf(__('The "%s" extension is installed'), $ext) . "</td> <td>" . phpversion($ext) . "</td> <td></td> <td class='icons_block'><i class='fa fa-check-circle ok' title='$msg'><span class='sr-only'>$msg</span></td></tr>"; // Memory $used = $info['memory_usage']['used_memory']; $free = $info['memory_usage']['free_memory']; $rate = round(100.0 * $used / ($used + $free)); $max = Toolbox::getSize($used + $free); $used = Toolbox::getSize($used); echo "<tr><td>" . _n('Memory', 'Memories', 1) . "</td> <td>" . sprintf(__('%1$s / %2$s'), $used, $max) . "</td><td>"; Html::displayProgressBar('100', $rate, ['simple' => true, 'forcepadding' => false ]); $class = 'info-circle missing'; $msg = sprintf(__s('%1$s memory usage is too low or too high'), $ext); if ($rate > 5 && $rate < 75) { $class = 'check-circle ok'; $msg = sprintf(__s('%1$s memory usage is correct'), $ext); } echo "</td><td class='icons_block'><i title='$msg' class='fa fa-$class'></td></tr>"; // Hits $hits = $info['opcache_statistics']['hits']; $miss = $info['opcache_statistics']['misses']; $max = $hits + $miss; $rate = round($info['opcache_statistics']['opcache_hit_rate']); echo "<tr><td>" . __('Hits rate') . "</td> <td>" . sprintf(__('%1$s / %2$s'), $hits, $max) . "</td><td>"; Html::displayProgressBar('100', $rate, ['simple' => true, 'forcepadding' => false ]); $class = 'info-circle missing'; $msg = sprintf(__s('%1$s hits rate is low'), $ext); if ($rate > 90) { $class = 'check-circle ok'; $msg = sprintf(__s('%1$s hits rate is correct'), $ext); } echo "</td><td class='icons_block'><i title='$msg' class='fa fa-$class'></td></tr>"; // Restart (1 seems ok, can happen) $max = $info['opcache_statistics']['oom_restarts']; echo "<tr><td>" . __('Out of memory restart') . "</td> <td>$max</td><td>"; $class = 'info-circle missing'; $msg = sprintf(__s('%1$s restart rate is too high'), $ext); if ($max < 2) { $class = 'check-circle ok'; $msg = sprintf(__s('%1$s restart rate is correct'), $ext); } echo "</td><td class='icons_block'><i title='$msg' class='fa fa-$class'></td></tr>"; if ($_SESSION['glpi_use_mode'] == Session::DEBUG_MODE) { echo "<tr><td></td><td colspan='3'>"; echo '<form method="POST" action="' . static::getFormURL() . '" class="d-inline">'; echo Html::hidden('_glpi_csrf_token', ['value' => Session::getNewCSRFToken()]); echo Html::hidden('reset_opcache', ['value' => 1]); echo '<button type="submit" class="btn btn-primary">'; echo __('Reset'); echo '</button>'; echo '</form>'; echo "</td></tr>"; } } else { $msg = sprintf(__s('%s extension is not present'), $ext); echo "<tr><td colspan='3'>" . sprintf(__('Installing and enabling the "%s" extension may improve GLPI performance'), $ext) . "</td> <td class='icons_block'><i class='fa fa-info-circle missing' title='$msg'></i><span class='sr-only'>$msg</span></td></tr>"; } echo "<tr><th colspan='4'>" . __('User data cache') . "</th></tr>"; echo '<tr><td class="b">' . __('You can use "php bin/console cache:configure" command to configure cache system.') . '</td></tr>'; $cache_manager = new CacheManager(); $ext = strtolower(get_class($cache_manager->getCacheStorageAdapter(CacheManager::CONTEXT_CORE))); $ext = preg_replace('/^.*\\\([a-z]+?)(?:adapter)?$/', '$1', $ext); if (in_array($ext, ['memcached', 'redis'])) { $msg = sprintf(__s('The "%s" cache extension is installed'), $ext); } else { $msg = sprintf(__s('"%s" cache system is used'), $ext); } echo "<tr><td>" . $msg . "</td> <td>" . phpversion($ext) . "</td> <td></td> <td class='icons_block'><i class='fa fa-check-circle ok' title='$msg'></i><span class='sr-only'>$msg</span></td></tr>"; if ($_SESSION['glpi_use_mode'] == Session::DEBUG_MODE) { echo "<tr><td></td><td colspan='3'>"; echo '<form method="POST" action="' . static::getFormURL() . '" class="d-inline">'; echo Html::hidden('_glpi_csrf_token', ['value' => Session::getNewCSRFToken()]); echo Html::hidden('reset_core_cache', ['value' => 1]); echo '<button type="submit" class="btn btn-primary">'; echo __('Reset'); echo '</button>'; echo '</form>'; echo "</td></tr>"; } echo "<tr><th colspan='4'>" . __('Translation cache') . "</th></tr>"; $adapter_class = strtolower(get_class($cache_manager->getCacheStorageAdapter(CacheManager::CONTEXT_TRANSLATIONS))); $adapter = preg_replace('/^.*\\\([a-z]+?)(?:adapter)?$/', '$1', $adapter_class); $msg = sprintf(__s('"%s" cache system is used'), $adapter); echo "<tr><td colspan='3'>" . $msg . "</td> <td class='icons_block'><i class='fa fa-check-circle ok' title='$msg'></i><span class='sr-only'>$msg</span></td></tr>"; if ($_SESSION['glpi_use_mode'] == Session::DEBUG_MODE) { echo "<tr><td></td><td colspan='3'>"; echo '<form method="POST" action="' . static::getFormURL() . '" style="d-inline">'; echo Html::hidden('_glpi_csrf_token', ['value' => Session::getNewCSRFToken()]); echo Html::hidden('reset_translation_cache', ['value' => 1]); echo '<button type="submit" class="btn btn-primary">'; echo __('Reset'); echo '</button>'; echo '</form>'; echo "</td></tr>"; } echo "</table></div>"; } public static function showSystemInfoTable($params = []) { global $CFG_GLPI, $DB; $p = [ 'word_wrap_width' => 128 ]; $p = array_replace($p, $params); echo "<table id='system-info-table' class='tab_cadre_fixe'>"; echo "<tr><th class='section-header'>" . __('Information about system installation and configuration') . "</th></tr>"; echo "<tr class='tab_bg_1'><td></td></tr>"; $oldlang = $_SESSION['glpilanguage']; // Keep this, for some function call which still use translation (ex showAllReplicateDelay) Session::loadLanguage('en_GB'); // No need to translate, this part always display in english (for copy/paste to forum) // Try to compute a better version for .git $ver = GLPI_VERSION; if (is_dir(GLPI_ROOT . "/.git")) { $dir = getcwd(); chdir(GLPI_ROOT); $returnCode = 1; /** @var array $output */ $gitrev = @exec('git show --format="%h" --no-patch 2>&1', $output, $returnCode); $gitbranch = ''; if (!$returnCode) { $gitbranch = @exec('git symbolic-ref --quiet --short HEAD || git rev-parse --short HEAD 2>&1', $output, $returnCode); } chdir($dir); if (!$returnCode) { $ver .= '-git-' . $gitbranch . '-' . $gitrev; } } echo "<tr class='tab_bg_1'><td><pre class='section-content'>"; echo "GLPI $ver (" . $CFG_GLPI['root_doc'] . " => " . GLPI_ROOT . ")\n"; echo "Installation mode: " . GLPI_INSTALL_MODE . "\n"; echo "Current language:" . $oldlang . "\n"; echo "\n</pre></td></tr>"; echo "<tr><th class='section-header'>Server</th></tr>\n"; echo "<tr class='tab_bg_1'><td><pre class='section-content'>\n \n"; echo wordwrap("Operating system: " . php_uname() . "\n", $p['word_wrap_width'], "\n\t"); $exts = get_loaded_extensions(); sort($exts); echo wordwrap( "PHP " . phpversion() . ' ' . php_sapi_name() . " (" . implode(', ', $exts) . ")\n", $p['word_wrap_width'], "\n\t" ); $msg = "Setup: "; foreach ( ['max_execution_time', 'memory_limit', 'post_max_size', 'safe_mode', 'session.save_handler', 'upload_max_filesize' ] as $key ) { $msg .= $key . '="' . ini_get($key) . '" '; } echo wordwrap($msg . "\n", $p['word_wrap_width'], "\n\t"); $msg = 'Software: '; if (isset($_SERVER["SERVER_SOFTWARE"])) { $msg .= $_SERVER["SERVER_SOFTWARE"]; } if (isset($_SERVER["SERVER_SIGNATURE"])) { $msg .= ' (' . Toolbox::stripTags($_SERVER["SERVER_SIGNATURE"]) . ')'; } echo wordwrap($msg . "\n", $p['word_wrap_width'], "\n\t"); if (isset($_SERVER["HTTP_USER_AGENT"])) { echo "\t" . Sanitizer::encodeHtmlSpecialChars($_SERVER["HTTP_USER_AGENT"]) . "\n"; } foreach ($DB->getInfo() as $key => $val) { echo "$key: $val\n\t"; } echo "\n"; $core_requirements = (new RequirementsManager())->getCoreRequirementList($DB); /* @var \Glpi\System\Requirement\RequirementInterface $requirement */ foreach ($core_requirements as $requirement) { if ($requirement->isOutOfContext()) { continue; // skip requirement if not relevant } $img = $requirement->isValidated() ? 'ok' : ($requirement->isOptional() ? 'warning' : 'ko'); $messages = Html::entities_deep($requirement->getValidationMessages()); echo '<img src="' . $CFG_GLPI['root_doc'] . '/pics/' . $img . '_min.png"' . ' alt="' . implode(' ', $messages) . '" title="' . implode(' ', $messages) . '" />'; echo implode("\n", $messages); echo "\n"; } echo "\n</pre></td></tr>"; echo "<tr><th class='section-header'>GLPI constants</th></tr>\n"; echo "<tr class='tab_bg_1'><td><pre class='section-content'>\n \n"; foreach (get_defined_constants() as $constant_name => $constant_value) { if (preg_match('/^GLPI_/', $constant_name)) { echo $constant_name . ': ' . json_encode($constant_value, JSON_UNESCAPED_SLASHES) . "\n"; } } echo "\n</pre></td></tr>"; self::showLibrariesInformation(); foreach ($CFG_GLPI["systeminformations_types"] as $type) { $tmp = new $type(); $tmp->showSystemInformations($p['word_wrap_width']); } Session::loadLanguage($oldlang); $files = array_merge( glob(GLPI_LOCAL_I18N_DIR . "/**/*.php"), glob(GLPI_LOCAL_I18N_DIR . "/**/*.mo") ); sort($files); if (count($files)) { echo "<tr><th class='section-header'>Locales overrides</th></tr>\n"; echo "<tr class='tab_bg_1'><td>\n"; foreach ($files as $file) { echo "$file<br/>\n"; } echo "</td></tr>"; } echo "<tr class='tab_bg_2'><th>" . __('To copy/paste in your support request') . "</th></tr>\n"; echo "</table>"; } /** * Display a HTML report about systeme information / configuration **/ public function showSystemInformations() { global $DB, $CFG_GLPI; if (!Config::canUpdate()) { return false; } $rand = mt_rand(); echo "<div class='center' id='tabsbody'>"; echo "<form name='form' action=\"" . Toolbox::getItemTypeFormURL(__CLASS__) . "\" method='post' data-track-changes='true'>"; echo "<table class='tab_cadre_fixe'>"; echo "<tr><th colspan='4'>" . __('General setup') . "</th></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_event_loglevel$rand'>" . __('Log Level') . "</label></td><td>"; $values = [ 1 => __('1- Critical (login error only)'), 2 => __('2- Severe (not used)'), 3 => __('3- Important (successful logins)'), 4 => __('4- Notices (add, delete, tracking)'), 5 => __('5- Complete (all)'), ]; Dropdown::showFromArray( 'event_loglevel', $values, ['value' => $CFG_GLPI["event_loglevel"], 'rand' => $rand] ); echo "</td><td><label for='dropdown_cron_limit$rand'>" . __('Maximal number of automatic actions (run by CLI)') . "</label></td><td>"; Dropdown::showNumber('cron_limit', ['value' => $CFG_GLPI["cron_limit"], 'min' => 1, 'max' => 30, 'rand' => $rand ]); echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_use_log_in_files$rand'>" . __('Logs in files (SQL, email, automatic action...)') . "</label></td><td>"; Dropdown::showYesNo("use_log_in_files", $CFG_GLPI["use_log_in_files"], -1, ['rand' => $rand]); echo "</td><td><label for='dropdown__dbslave_status$rand'>" . _n('SQL replica', 'SQL replicas', 1) . "</label></td><td>"; $active = DBConnection::isDBSlaveActive(); Dropdown::showYesNo("_dbslave_status", $active, -1, ['rand' => $rand]); echo "</td></tr>"; echo "<tr class='tab_bg_1'>"; echo "<td colspan='4' class='center b'>" . __('Maintenance mode'); echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='dropdown_maintenance_mode$rand'>" . __('Maintenance mode') . "</label></td>"; echo "<td>"; Dropdown::showYesNo("maintenance_mode", $CFG_GLPI["maintenance_mode"], -1, ['rand' => $rand]); echo "</td>"; //TRANS: Proxy port echo "<td><label for='maintenance_text'>" . __('Maintenance text') . "</label></td>"; echo "<td>"; echo "<textarea class='form-control' name='maintenance_text' id='maintenance_text'>" . $CFG_GLPI["maintenance_text"]; echo "</textarea>"; echo "</td></tr>"; echo "<tr class='tab_bg_1'>"; echo "<td colspan='4' class='center b'>" . __('Proxy configuration for upgrade check'); echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='proxy_name'>" . __('Server') . "</label></td>"; echo "<td><input type='text' name='proxy_name' id='proxy_name' value='" . $CFG_GLPI["proxy_name"] . "' class='form-control'></td>"; //TRANS: Proxy port echo "<td><label for='proxy_port'>" . _n('Port', 'Ports', 1) . "</label></td>"; echo "<td><input type='text' name='proxy_port' id='proxy_port' value='" . $CFG_GLPI["proxy_port"] . "' class='form-control'></td>"; echo "</tr>"; echo "<tr class='tab_bg_2'>"; echo "<td><label for='proxy_user'>" . __('Login') . "</label></td>"; echo "<td><input type='text' name='proxy_user' id='proxy_user' value='" . $CFG_GLPI["proxy_user"] . "' class='form-control'></td>"; echo "<td><label for='proxy_passwd'>" . __('Password') . "</label></td>"; echo "<td><input type='password' name='proxy_passwd' id='proxy_passwd' value='' autocomplete='new-password' class='form-control'>"; echo "<br><input type='checkbox' name='_blank_proxy_passwd' id='_blank_proxy_passwd'><label for='_blank_proxy_passwd'>" . __('Clear') . "</label>"; echo "</td></tr>"; echo "<tr class='tab_bg_2'>"; echo "<td colspan='4' class='center'>"; echo "<input type='submit' name='update' class='btn btn-primary' value=\"" . _sx('button', 'Save') . "\">"; echo "</td></tr>"; echo "</table>"; Html::closeForm(); $cleaner_script = <<<JS // Search all .section-content text content and Replace all instances of a '#' followed by a number so that there is a zero-width space between the # and the number $('.section-content').each(function() { $(this).text($(this).text().replace(/#(\d+)/g, '#\u200B$1')); }); JS; echo Html::scriptBlock($cleaner_script); echo "<p>" . Telemetry::getViewLink() . "</p>"; $copy_msg = __('Copy system information'); $copy_onclick = <<<JS copyTextToClipboard(tableToDetails('#system-info-table')); flashIconButton(this, 'btn btn-success', 'fas fa-check', 1500); JS; echo <<<HTML <button type="button" name="copy-sysinfo" class="btn btn-secondary" onclick="{$copy_onclick}"> <i class="far fa-copy me-2"></i>{$copy_msg} </button> HTML; $check_new_version_msg = __('Check if a new version is available'); echo <<<HTML <a class='btn btn-secondary' href='?check_version'> <i class="fas fa-sync me-2"></i>{$check_new_version_msg} </a> HTML; self::showSystemInfoTable(); echo "</div>\n"; } /** * Retrieve full directory of a lib * @param $libstring object, class or function * @return string the path or false * * @since 9.1 */ public static function getLibraryDir($libstring) { if (is_object($libstring)) { return realpath(dirname((new ReflectionObject($libstring))->getFileName())); } else if (class_exists($libstring) || interface_exists($libstring)) { return realpath(dirname((new ReflectionClass($libstring))->getFileName())); } else if (function_exists($libstring)) { // Internal function have no file name $path = (new ReflectionFunction($libstring))->getFileName(); return ($path ? realpath(dirname($path)) : false); } return false; } /** * get libraries list * * @param $all (default false) * @return array dependencies list * * @since 9.4 */ public static function getLibraries($all = false) { $pm = new PHPMailer(); $sp = new SimplePie(); // use same name that in composer.json $deps = [[ 'name' => 'htmlawed/htmlawed', 'version' => hl_version() , 'check' => 'hl_version' ], [ 'name' => 'phpmailer/phpmailer', 'version' => $pm::VERSION, 'check' => 'PHPMailer\\PHPMailer\\PHPMailer' ], [ 'name' => 'simplepie/simplepie', 'version' => SIMPLEPIE_VERSION, 'check' => $sp ], [ 'name' => 'tecnickcom/tcpdf', 'version' => TCPDF_STATIC::getTCPDFVersion(), 'check' => 'TCPDF' ], [ 'name' => 'michelf/php-markdown', 'check' => 'Michelf\\Markdown' ], [ 'name' => 'true/punycode', 'check' => 'TrueBV\\Punycode' ], [ 'name' => 'iamcal/lib_autolink', 'check' => 'autolink' ], [ 'name' => 'sabre/dav', 'check' => 'Sabre\\DAV\\Version' ], [ 'name' => 'sabre/http', 'check' => 'Sabre\\HTTP\\Version' ], [ 'name' => 'sabre/uri', 'check' => 'Sabre\\Uri\\Version' ], [ 'name' => 'sabre/vobject', 'check' => 'Sabre\\VObject\\Component' ], [ 'name' => 'laminas/laminas-i18n', 'check' => 'Laminas\\I18n\\Module' ], [ 'name' => 'laminas/laminas-servicemanager', 'check' => 'Laminas\\ServiceManager\\ServiceManager' ], [ 'name' => 'monolog/monolog', 'check' => 'Monolog\\Logger' ], [ 'name' => 'sebastian/diff', 'check' => 'SebastianBergmann\\Diff\\Diff' ], [ 'name' => 'donatj/phpuseragentparser', 'check' => 'donatj\\UserAgent\\UserAgentParser' ], [ 'name' => 'elvanto/litemoji', 'check' => 'LitEmoji\\LitEmoji' ], [ 'name' => 'symfony/console', 'check' => 'Symfony\\Component\\Console\\Application' ], [ 'name' => 'scssphp/scssphp', 'check' => 'ScssPhp\ScssPhp\Compiler' ], [ 'name' => 'laminas/laminas-mail', 'check' => 'Laminas\\Mail\\Protocol\\Imap' ], [ 'name' => 'laminas/laminas-mime', 'check' => 'Laminas\\Mime\\Mime' ], [ 'name' => 'rlanvin/php-rrule', 'check' => 'RRule\\RRule' ], [ 'name' => 'ramsey/uuid', 'check' => 'Ramsey\\Uuid\\Uuid' ], [ 'name' => 'psr/log', 'check' => 'Psr\\Log\\LoggerInterface' ], [ 'name' => 'psr/simple-cache', 'check' => 'Psr\\SimpleCache\\CacheInterface' ], [ 'name' => 'psr/cache', 'check' => 'Psr\\Cache\\CacheItemPoolInterface' ], [ 'name' => 'league/csv', 'check' => 'League\\Csv\\Writer' ], [ 'name' => 'mexitek/phpcolors', 'check' => 'Mexitek\\PHPColors\\Color' ], [ 'name' => 'guzzlehttp/guzzle', 'check' => 'GuzzleHttp\\Client' ], [ 'name' => 'guzzlehttp/psr7', 'check' => 'GuzzleHttp\\Psr7\\Response' ], [ 'name' => 'glpi-project/inventory_format', 'check' => 'Glpi\Inventory\Converter' ], [ 'name' => 'wapmorgan/unified-archive', 'check' => 'wapmorgan\\UnifiedArchive\\UnifiedArchive' ], [ 'name' => 'paragonie/sodium_compat', 'check' => 'ParagonIE_Sodium_Compat' ], [ 'name' => 'symfony/cache', 'check' => 'Symfony\\Component\\Cache\\Psr16Cache' ], [ 'name' => 'html2text/html2text', 'check' => 'Html2Text\\Html2Text' ], [ 'name' => 'symfony/css-selector', 'check' => 'Symfony\\Component\\CssSelector\\CssSelectorConverter' ], [ 'name' => 'symfony/dom-crawler', 'check' => 'Symfony\\Component\\DomCrawler\\Crawler' ], [ 'name' => 'twig/twig', 'check' => 'Twig\\Environment' ], [ 'name' => 'twig/string-extra', 'check' => 'Twig\\Extra\\String\\StringExtension' ], [ 'name' => 'symfony/polyfill-ctype', 'check' => 'ctype_digit' ], [ 'name' => 'symfony/polyfill-iconv', 'check' => 'iconv' ], [ 'name' => 'symfony/polyfill-mbstring', 'check' => 'mb_list_encodings' ], [ 'name' => 'symfony/polyfill-php80', 'check' => 'str_contains' ], [ 'name' => 'symfony/polyfill-php81', 'check' => 'array_is_list' ], [ 'name' => 'symfony/polyfill-php82', 'check' => 'Symfony\\Polyfill\\Php82\\SensitiveParameterValue' ], [ 'name' => 'league/oauth2-client', 'check' => 'League\\OAuth2\\Client\\Provider\\AbstractProvider' ], [ 'name' => 'league/oauth2-google', 'check' => 'League\\OAuth2\\Client\\Provider\\Google' ], [ 'name' => 'thenetworg/oauth2-azure', 'check' => 'TheNetworg\\OAuth2\\Client\\Provider\\Azure' ], ]; if (Toolbox::canUseCAS()) { $deps[] = [ 'name' => 'phpCas', 'version' => phpCAS::getVersion(), 'check' => 'phpCAS' ]; } return $deps; } /** * show Libraries information in system information * * @since 0.84 **/ public static function showLibrariesInformation() { // No gettext echo "<tr class='tab_bg_2'><th class='section-header'>Libraries</th></tr>\n"; echo "<tr class='tab_bg_1'><td><pre class='section-content'>\n \n"; foreach (self::getLibraries() as $dep) { $path = self::getLibraryDir($dep['check']); if ($path) { echo "{$dep['name']} "; if (isset($dep['version'])) { echo "version {$dep['version']} "; } echo "in ($path)\n"; } else { echo "{$dep['name']} not found\n"; } } echo "\n</pre></td></tr>"; } /** * Dropdown for global management config * * @param string $name select name * @param string $value default value * @param integer|null $rand rand **/ public static function dropdownGlobalManagement($name, $value, $rand = null) { $choices = [ self::UNIT_MANAGEMENT => __('Yes - Restrict to unit management'), self::GLOBAL_MANAGEMENT => __('Yes - Restrict to global management'), self::NO_MANAGEMENT => __('No'), ]; Dropdown::showFromArray($name, $choices, ['value' => $value, 'rand' => $rand]); } /** * Get language in GLPI associated with the value coming from LDAP/SSO * Value can be, for example : English, en_EN, en-EN or en * * @param string $lang the value coming from LDAP/SSO * * @return string locale's php page in GLPI or '' is no language associated with the value **/ public static function getLanguage($lang) { global $CFG_GLPI; // Alternative language code: en-EN --> en_EN $altLang = str_replace("-", "_", $lang); // Search in order : ID or extjs dico or tinymce dico / native lang / english name // / extjs dico / tinymce dico // ID or extjs dico or tinymce dico foreach ($CFG_GLPI["languages"] as $ID => $language) { if ( (strcasecmp($lang, $ID) == 0) || (strcasecmp($altLang, $ID) == 0) || (strcasecmp($lang, $language[2]) == 0) || (strcasecmp($lang, $language[3]) == 0) ) { return $ID; } } // native lang foreach ($CFG_GLPI["languages"] as $ID => $language) { if (strcasecmp($lang, $language[0]) == 0) { return $ID; } } // english lang name foreach ($CFG_GLPI["languages"] as $ID => $language) { if (strcasecmp($lang, $language[4]) == 0) { return $ID; } } return ""; } public static function detectRootDoc() { global $DB, $CFG_GLPI; if (isset($CFG_GLPI['root_doc'])) { return; // already computed } if (isset($_SERVER['REQUEST_URI'])) { // $_SERVER['REQUEST_URI'] is set, meaning that GLPI is accessed from web server. // In this case, `$CFG_GLPI['root_doc']` corresponds to the piece of path // that is common between `GLPI_ROOT` and $_SERVER['SCRIPT_NAME'] // e.g. GLPI_ROOT=/var/www/glpi and $_SERVER['SCRIPT_NAME']=/glpi/front/index.php -> $CFG_GLPI['root_doc']=/glpi // We cannot rely on $_SERVER['REQUEST_URI'] value as it is a value defined by HTTP client. // $_SERVER['SCRIPT_NAME'] is consider safe as it is either set by GLPI router (see `/public/index.php`), // either it contains the path of PHP script executed by the web server. $script_path = $_SERVER['SCRIPT_NAME']; // Extract relative path of entry script directory // e.g. /var/www/mydomain.org/glpi/front/index.php -> /front $current_dir_relative = str_replace( str_replace(DIRECTORY_SEPARATOR, '/', realpath(GLPI_ROOT)), '', str_replace(DIRECTORY_SEPARATOR, '/', realpath(getcwd())) ); // Extract relative path of script directory // e.g. /glpi/front/index.php -> /glpi/front $script_dir_relative = preg_replace( '/\/[0-9a-zA-Z\.\-\_]+\.php/', '', $script_path ); // API exception (handles `RewriteRule api/(.*)$ apirest.php/$1`) if (strpos($script_dir_relative, 'api/') !== false) { $script_dir_relative = preg_replace("/(.*\/)api\/.*/", "$1", $script_dir_relative); } // Remove relative path of entry script directory // e.g. /glpi/front -> /glpi $root_doc = str_replace($current_dir_relative, '', $script_dir_relative); $root_doc = rtrim($root_doc, '/'); $CFG_GLPI['root_doc'] = $root_doc; } else { // $_SERVER['REQUEST_URI'] is not set, meaning that GLPI is probably acces from CLI. // In this case, `$CFG_GLPI['root_doc']` has to be extracted from `$CFG_GLPI['url_base']`. $url_base = $CFG_GLPI['url_base'] ?? null; // $CFG_GLPI may have not been loaded yet, load value form DB if `$CFG_GLPI['url_base']` is not set. if ( $url_base === null && $DB instanceof DBmysql && $DB->connected // table/field may not exists in edge case (e.g. update from GLPI < 0.85) && $DB->tableExists('glpi_configs') && $DB->fieldExists('glpi_configs', 'context') ) { $url_base = Config::getConfigurationValue('core', 'url_base'); } if ($url_base !== null) { $CFG_GLPI['root_doc'] = parse_url($url_base, PHP_URL_PATH) ?? ''; } } // Path for icon of document type (web mode only) if (isset($CFG_GLPI['root_doc'])) { $CFG_GLPI['typedoc_icon_dir'] = $CFG_GLPI['root_doc'] . '/pics/icones'; } } /** * Display debug information for dbslave **/ public function showDebug() { $options = [ 'diff' => 0, 'name' => '', ]; NotificationEvent::debugEvent(new DBConnection(), $options); } /** * Display field unicity criterias form **/ public function showFormFieldUnicity() { $unicity = new FieldUnicity(); $unicity->showForm(1, -1); } public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { switch ($item->getType()) { case 'Preference': return __('Personalization'); case 'User': if ( User::canUpdate() && $item->currentUserHaveMoreRightThan($item->getID()) ) { return __('Settings'); } break; case __CLASS__: $tabs = [ 1 => __('General setup'), // Display 2 => __('Default values'), // Prefs 3 => _n('Asset', 'Assets', Session::getPluralNumber()), 4 => __('Assistance'), 12 => __('Management'), ]; if (Config::canUpdate()) { $tabs[9] = __('Logs purge'); $tabs[5] = __('System'); $tabs[10] = __('Security'); $tabs[7] = __('Performance'); $tabs[8] = __('API'); $tabs[11] = Impact::getTypeName(); } if ( DBConnection::isDBSlaveActive() && Config::canUpdate() ) { $tabs[6] = _n('SQL replica', 'SQL replicas', Session::getPluralNumber()); // Slave } return $tabs; case 'GLPINetwork': return 'GLPI Network'; case Impact::getType(): return Impact::getTypeName(); } return ''; } public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { global $CFG_GLPI; if ($item->getType() == 'Preference') { $config = new self(); $user = new User(); if ($user->getFromDB(Session::getLoginUserID())) { $user->computePreferences(); $config->showFormUserPrefs($user->fields); } } else if ($item->getType() == 'User') { $config = new self(); $item->computePreferences(); $config->showFormUserPrefs($item->fields); } else if ($item->getType() == __CLASS__) { switch ($tabnum) { case 1: $item->showFormDisplay(); break; case 2: $item->showFormUserPrefs($CFG_GLPI); break; case 3: $item->showFormInventory(); break; case 4: $item->showFormHelpdesk(); break; case 5: $item->showSystemInformations(); break; case 6: $item->showFormDBSlave(); break; case 7: $item->showPerformanceInformations(); break; case 8: $item->showFormAPI(); break; case 9: $item->showFormLogs(); break; case 10: $item->showFormSecurity(); break; case 11: Impact::showConfigForm(); break; case 12: $item->showFormManagement(); break; } } return true; } /** * Display database engine checks report * * @since 9.3 * * @param boolean $fordebug display for debug (no html required) (false by default) * @param string $version Version to check (mainly from install), defaults to null * * @return integer 2: missing extension, 1: missing optionnal extension, 0: OK, **/ public static function displayCheckDbEngine($fordebug = false, $version = null) { global $CFG_GLPI; $error = 0; $result = self::checkDbEngine($version); $version = key($result); $db_ver = $result[$version]; $ok_message = sprintf(__s('Database version seems correct (%s) - Perfect!'), $version); $ko_message = sprintf(__s('Your database engine version seems too old: %s.'), $version); if (!$db_ver) { $error = 2; } $message = $error > 0 ? $ko_message : $ok_message; if (isCommandLine()) { echo $message . "\n"; } else { $img = "<img src='" . $CFG_GLPI['root_doc'] . "/pics/"; $img .= ($error > 0 ? "ko_min" : "ok_min") . ".png' alt='$message' title='$message'/>"; if ($fordebug) { echo $img . $message . "\n"; } else { $html = "<td"; if ($error > 0) { $html .= " class='red'"; } $html .= ">"; $html .= $img; $html .= '</td>'; echo $html; } } return $error; } /** * Check for needed extensions * * @since 9.3 * * @param string $raw Raw version to check (mainly from install), defaults to null * * @return array **/ public static function checkDbEngine($raw = null) { if ($raw === null) { global $DB; $raw = $DB->getVersion(); } $server = preg_match('/-MariaDB/', $raw) ? 'MariaDB' : 'MySQL'; $version = preg_replace('/^((\d+\.?)+).*$/', '$1', $raw); // MySQL >= 5.7 || MariaDB >= 10.2 $is_supported = $server === 'MariaDB' ? version_compare($version, '10.2', '>=') : version_compare($version, '5.7', '>='); return [$version => $is_supported]; } /** * Check for needed extensions * * @since 9.2 Method signature and return has changed * * @param null|array $list Extensions list (from plugins) * * @return array [ * 'error' => integer 2: missing extension, 1: missing optionnal extension, 0: OK, * 'good' => [ext => message], * 'missing' => [ext => message], * 'may' => [ext => message] * ] **/ public static function checkExtensions($list = null) { if ($list === null) { $extensions_to_check = [ 'mysqli' => [ 'required' => true ], 'fileinfo' => [ 'required' => true, 'class' => 'finfo' ], 'json' => [ 'required' => true, 'function' => 'json_encode' ], 'zlib' => [ 'required' => true, ], 'curl' => [ 'required' => true, ], 'gd' => [ 'required' => true, ], 'simplexml' => [ 'required' => true, ], //to sync/connect from LDAP 'ldap' => [ 'required' => false, ], //to enhance perfs 'Zend OPcache' => [ 'required' => false ], //for CAS lib 'CAS' => [ 'required' => false, 'class' => 'phpCAS' ], 'exif' => [ 'required' => false ], 'intl' => [ 'required' => true ], 'sodium' => [ 'required' => false ] ]; } else { $extensions_to_check = $list; } $report = [ 'error' => 0, 'good' => [], 'missing' => [], 'may' => [] ]; //check for PHP extensions foreach ($extensions_to_check as $ext => $params) { $success = true; if (isset($params['call'])) { $success = call_user_func($params['call']); } else if (isset($params['function'])) { if (!function_exists($params['function'])) { $success = false; } } else if (isset($params['class'])) { if (!class_exists($params['class'])) { $success = false; } } else { if (!extension_loaded($ext)) { $success = false; } } if ($success) { $msg = sprintf(__('%s extension is installed'), $ext); $report['good'][$ext] = $msg; } else { if (isset($params['required']) && $params['required'] === true) { if ($report['error'] < 2) { $report['error'] = 2; } $msg = sprintf(__('%s extension is missing'), $ext); $report['missing'][$ext] = $msg; } else { if ($report['error'] < 1) { $report['error'] = 1; } $msg = sprintf(__('%s extension is not present'), $ext); $report['may'][$ext] = $msg; } } } return $report; } /** * Get current DB version (compatible with all version of GLPI) * * @since 0.85 * * @return DB version **/ public static function getCurrentDBVersion() { global $DB; //Default current case $select = 'value AS version'; $table = 'glpi_configs'; $where = [ 'context' => 'core', 'name' => 'version' ]; if (!$DB->tableExists('glpi_configs')) { $select = 'version'; $table = 'glpi_config'; $where = ['id' => 1]; } else if ($DB->fieldExists('glpi_configs', 'version')) { $select = 'version'; $where = ['id' => 1]; } $row = $DB->request([ 'SELECT' => [$select], 'FROM' => $table, 'WHERE' => $where ])->current(); return trim($row['version']); } /** * Get config values * * @since 0.85 * * @param $context string context to get values (default for glpi is core) * @param $names array of config names to get * * @return array of config values **/ public static function getConfigurationValues($context, array $names = []) { global $DB; $query = [ 'FROM' => self::getTable(), 'WHERE' => [ 'context' => $context ] ]; if (count($names) > 0) { $query['WHERE']['name'] = $names; } $iterator = $DB->request($query); $result = []; foreach ($iterator as $line) { $result[$line['name']] = $line['value']; } return $result; } /** * Get config value * * @param $context string context to get values (default for glpi is core) * @param $name string config name * * @return mixed * * @since 10.0.0 */ public static function getConfigurationValue(string $context, string $name) { return self::getConfigurationValues($context, [$name])[$name] ?? null; } /** * Load legacy configuration into $CFG_GLPI global variable. * * @return boolean True for success, false if an error occurred * * @since 10.0.0 Parameter $older_to_latest is no longer used. */ public static function loadLegacyConfiguration() { global $CFG_GLPI, $DB; $iterator = $DB->request(['FROM' => 'glpi_configs']); if ($iterator->count() === 0) { return false; } if ($iterator->count() === 1) { // 1 row = 0.78 to 0.84 config table schema $values = $iterator->current(); } else { // multiple rows = 0.85+ config $values = []; $allowed_context = ['core', 'inventory']; foreach ($iterator as $row) { if (!in_array($row['context'], $allowed_context)) { continue; } $values[$row['name']] = $row['value']; } } $CFG_GLPI = array_merge($CFG_GLPI, $values); if (isset($CFG_GLPI['priority_matrix'])) { $CFG_GLPI['priority_matrix'] = importArrayFromDB($CFG_GLPI['priority_matrix']); } if (isset($CFG_GLPI['devices_in_menu'])) { $CFG_GLPI['devices_in_menu'] = importArrayFromDB($CFG_GLPI['devices_in_menu']); } if (isset($CFG_GLPI['lock_item_list'])) { $CFG_GLPI['lock_item_list'] = importArrayFromDB($CFG_GLPI['lock_item_list']); } if ( isset($CFG_GLPI['lock_lockprofile_id']) && $CFG_GLPI['lock_use_lock_item'] && $CFG_GLPI['lock_lockprofile_id'] > 0 && !isset($CFG_GLPI['lock_lockprofile']) ) { $prof = new Profile(); $prof->getFromDB($CFG_GLPI['lock_lockprofile_id']); $prof->cleanProfile(); $CFG_GLPI['lock_lockprofile'] = $prof->fields; } if (isset($CFG_GLPI['planning_work_days'])) { $CFG_GLPI['planning_work_days'] = importArrayFromDB($CFG_GLPI['planning_work_days']); } return true; } /** * Set config values : create or update entry * * @since 0.85 * * @param $context string context to get values (default for glpi is core) * @param $values array of config names to set * * @return void **/ public static function setConfigurationValues($context, array $values = []) { $glpikey = new GLPIKey(); $config = new self(); foreach ($values as $name => $value) { // Encrypt config values according to list declared to GLPIKey service if (!empty($value) && $glpikey->isConfigSecured($context, $name)) { $value = $glpikey->encrypt($value); } if ( $config->getFromDBByCrit([ 'context' => $context, 'name' => $name ]) ) { $input = ['id' => $config->getID(), 'context' => $context, 'value' => $value ]; $config->update($input); } else { $input = ['context' => $context, 'name' => $name, 'value' => $value ]; $config->add($input); } } //reload config for loggedin user if ($_SESSION['glpiID'] ?? false) { $user = new \User(); if ($user->getFromDB($_SESSION['glpiID'])) { $user->loadPreferencesInSession(); } } } /** * Delete config entries * * @since 0.85 * * @param $context string context to get values (default for glpi is core) * @param $values array of config names to delete * * @return void **/ public static function deleteConfigurationValues($context, array $values = []) { $config = new self(); foreach ($values as $value) { if ( $config->getFromDBByCrit([ 'context' => $context, 'name' => $value ]) ) { $config->delete(['id' => $config->getID()]); } } } public function getRights($interface = 'central') { $values = parent::getRights(); unset( $values[CREATE], $values[DELETE], $values[PURGE] ); return $values; } /** * Get message that informs the user he is using an unstable version. * * @param bool $is_dev * * @return void */ public static function agreeUnstableMessage(bool $is_dev) { $msg = $is_dev ? __('You are using a development version, be careful!') : __('You are using a pre-release version, be careful!'); $out = '<div class="alert alert-warning"> <strong>' . $msg . '</strong> <br/>'; $out .= "<div class='form-check'> <input type='checkbox' class='form-check-input' required='required' id='agree_unstable' name='agree_unstable'> <label for='agree_unstable' class='form-check-label'>" . __('I know I am using a unstable version.') . "</label> </div> </div>"; $out .= "<script type=text/javascript> $(function() { $('[name=from_update]').on('click', function(event){ if(!$('#agree_unstable').is(':checked')) { event.preventDefault(); alert('" . __('Please check the unstable version checkbox.') . "'); } }); }); </script>"; return $out; } /** * Get available palettes * * @return array */ public function getPalettes() { $themes_files = scandir(GLPI_ROOT . "/css/palettes/"); $themes = []; foreach ($themes_files as $file) { if (preg_match('/^[^_].*\.scss$/', $file) === 1) { $name = basename($file, '.scss'); $themes[$name] = ucfirst($name); } } return $themes; } /** * Logs purge form * * @since 9.3 * * @return void|boolean (display) Returns false if there is a rights error. */ public function showFormLogs() { global $CFG_GLPI; if (!Config::canUpdate()) { return false; } echo "<form name='form' id='purgelogs_form' method='post' action='" . $this->getFormURL() . "' data-track-changes='true'>"; echo "<div class='center'>"; echo "<table class='tab_cadre_fixe'>"; echo "<tr class='tab_bg_1'><th colspan='4'>" . __("Logs purge configuration") . "</th></tr>"; echo "<tr class='tab_bg_1 center'><td colspan='4'><i>" . __("Change all") . "</i>"; echo Html::scriptBlock("function form_init_all(value) { $('#purgelogs_form .purgelog_interval select').val(value).trigger('change');; }"); self::showLogsInterval( 'init_all', 0, [ 'on_change' => "form_init_all(this.value);", 'class' => '' ] ); echo "</td></tr>"; $config_id = self::getConfigIDForContext('core'); echo "<input type='hidden' name='id' value='{$config_id}'>"; echo "<tr class='tab_bg_1'><th colspan='4'>" . __("General") . "</th></tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Add/update relation between items") . "</td><td>"; self::showLogsInterval('purge_addrelation', $CFG_GLPI["purge_addrelation"]); echo "</td>"; echo "<td>" . __("Delete relation between items") . "</td><td>"; self::showLogsInterval('purge_deleterelation', $CFG_GLPI["purge_deleterelation"]); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Add the item") . "</td><td>"; self::showLogsInterval('purge_createitem', $CFG_GLPI["purge_createitem"]); echo "</td>"; echo "<td>" . __("Delete the item") . "</td><td>"; self::showLogsInterval('purge_deleteitem', $CFG_GLPI["purge_deleteitem"]); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Restore the item") . "</td><td>"; self::showLogsInterval('purge_restoreitem', $CFG_GLPI["purge_restoreitem"]); echo "</td>"; echo "<td>" . __('Update the item') . "</td><td>"; self::showLogsInterval('purge_updateitem', $CFG_GLPI["purge_updateitem"]); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Comments") . "</td><td>"; self::showLogsInterval('purge_comments', $CFG_GLPI["purge_comments"]); echo "</td>"; echo "<td>" . __("Last update") . "</td><td>"; self::showLogsInterval('purge_datemod', $CFG_GLPI["purge_datemod"]); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Plugins") . "</td><td>"; self::showLogsInterval('purge_plugins', $CFG_GLPI["purge_plugins"]); echo "</td>"; echo "<td class='center'>" . RefusedEquipment::getTypeName(Session::getPluralNumber()) . "</td><td>"; self::showLogsInterval('purge_refusedequipment', $CFG_GLPI["purge_refusedequipment"]); echo "</td></tr>"; echo "<tr class='tab_bg_1'><th colspan='4'>" . _n('Software', 'Software', Session::getPluralNumber()) . "</th></tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Installation/uninstallation of software on items") . "</td><td>"; self::showLogsInterval( 'purge_item_software_install', $CFG_GLPI["purge_item_software_install"] ); echo "</td>"; echo "<td>" . __("Installation/uninstallation versions on software") . "</td><td>"; self::showLogsInterval( 'purge_software_version_install', $CFG_GLPI["purge_software_version_install"] ); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Add/Remove items from software versions") . "</td><td>"; self::showLogsInterval( 'purge_software_item_install', $CFG_GLPI["purge_software_item_install"] ); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_1'><th colspan='4'>" . __('Financial and administrative information') . "</th></tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Add financial information to an item") . "</td><td>"; self::showLogsInterval('purge_infocom_creation', $CFG_GLPI["purge_infocom_creation"]); echo "</td>"; echo "<td colspan='2'></td></tr>"; echo "<tr class='tab_bg_1'><th colspan='4'>" . User::getTypeName(Session::getPluralNumber()) . "</th></tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Add/remove profiles to users") . "</td><td>"; self::showLogsInterval('purge_profile_user', $CFG_GLPI["purge_profile_user"]); echo "</td>"; echo "<td>" . __("Add/remove groups to users") . "</td><td>"; self::showLogsInterval('purge_group_user', $CFG_GLPI["purge_group_user"]); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("User authentication method changes") . "</td><td>"; self::showLogsInterval('purge_user_auth_changes', $CFG_GLPI["purge_user_auth_changes"]); echo "</td>"; echo "<td class='center'>" . __("Deleted user in LDAP directory") . "</td><td>"; self::showLogsInterval('purge_userdeletedfromldap', $CFG_GLPI["purge_userdeletedfromldap"]); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_1'><th colspan='4'>" . _n('Component', 'Components', Session::getPluralNumber()) . "</th></tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Add component") . "</td><td>"; self::showLogsInterval('purge_adddevice', $CFG_GLPI["purge_adddevice"]); echo "</td>"; echo "<td>" . __("Update component") . "</td><td>"; self::showLogsInterval('purge_updatedevice', $CFG_GLPI["purge_updatedevice"]); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Disconnect a component") . "</td><td>"; self::showLogsInterval('purge_disconnectdevice', $CFG_GLPI["purge_disconnectdevice"]); echo "</td>"; echo "<td>" . __("Connect a component") . "</td><td>"; self::showLogsInterval('purge_connectdevice', $CFG_GLPI["purge_connectdevice"]); echo "</td>"; echo "</tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Delete component") . "</td><td>"; self::showLogsInterval('purge_deletedevice', $CFG_GLPI["purge_deletedevice"]); echo "</td>"; echo "<td colspan='2'></td></tr>"; echo "<tr class='tab_bg_1'><th colspan='4'>" . __("All sections") . "</th></tr>"; echo "<tr class='tab_bg_1'><td class='center'>" . __("Purge all log entries") . "</td><td>"; self::showLogsInterval('purge_all', $CFG_GLPI["purge_all"]); echo "</td>"; echo "<td colspan='2'></td></tr>"; echo "<tr class='tab_bg_1'>"; echo "<td colspan='4' class='center'>"; echo "<input type='submit' name='update' value=\"" . _sx('button', 'Save') . "\" class='btn btn-primary' >"; echo"</td>"; echo "</tr>"; echo "</table></div>"; Html::closeForm(); } /** * Show intervals for logs purge * * @since 9.3 * * @param string $name Parameter name * @param mixed $value Parameter value * @param array $options Options * * @return void */ public static function showLogsInterval($name, $value, $options = []) { $values = [ self::DELETE_ALL => __("Delete all"), self::KEEP_ALL => __("Keep all"), ]; for ($i = 1; $i < 121; $i++) { $values[$i] = sprintf( _n( "Delete if older than %s month", "Delete if older than %s months", $i ), $i ); } $options = array_merge([ 'value' => $value, 'display' => false, 'class' => 'purgelog_interval' ], $options); $out = "<div class='{$options['class']}'>"; $out .= Dropdown::showFromArray($name, $values, $options); $out .= "</div>"; echo $out; } /** * Security policy form * * @since 9.5.0 * * @return void|boolean (display) Returns false if there is a rights error. */ public function showFormSecurity() { global $CFG_GLPI; if (!Config::canUpdate()) { return false; } $rand = mt_rand(); echo '<div class="center" id="tabsbody">'; echo '<form name="form" action="' . Toolbox::getItemTypeFormURL(__CLASS__) . '" method="post" data-track-changes="true">'; echo '<table class="tab_cadre_fixe">'; echo '<tr><th colspan="4">' . __('Security setup') . '</th></tr>'; echo '<tr class="tab_bg_1">'; echo '<td colspan="4" class="center b">' . __('Password security policy') . '</td>'; echo '</tr>'; echo '<tr class="tab_bg_2">'; echo '<td>'; echo '<label for="dropdown_use_password_security' . $rand . '">'; echo __('Password security policy validation'); echo '</label>'; echo '</td>'; echo '<td>'; Dropdown::showYesNo( 'use_password_security', $CFG_GLPI['use_password_security'], -1, [ 'rand' => $rand, ] ); echo '</td>'; echo '<td>'; echo '<label for="dropdown_password_min_length' . $rand . '">'; echo __('Password minimum length'); echo '</label>'; echo '</td>'; echo '<td>'; Dropdown::showNumber( 'password_min_length', [ 'value' => $CFG_GLPI['password_min_length'], 'min' => 4, 'max' => 30, 'rand' => $rand ] ); echo '</td>'; echo '</tr>'; echo '<tr class="tab_bg_2">'; echo '<td>'; echo '<label for="dropdown_password_need_number' . $rand . '">'; echo __('Password need digit'); echo '</label>'; echo '</td>'; echo '<td>'; Dropdown::showYesNo( 'password_need_number', $CFG_GLPI['password_need_number'], -1, [ 'rand' => $rand, ] ); echo '</td>'; echo '<td>'; echo '<label for="dropdown_password_need_letter' . $rand . '">'; echo __('Password need lowercase character'); echo '</label>'; echo '</td>'; echo '<td>'; Dropdown::showYesNo( 'password_need_letter', $CFG_GLPI['password_need_letter'], -1, [ 'rand' => $rand, ] ); echo '</td>'; echo '</tr>'; echo '<tr class="tab_bg_2">'; echo '<td>'; echo '<label for="dropdown_password_need_caps' . $rand . '">'; echo __('Password need uppercase character'); echo '</label>'; echo '</td>'; echo '<td>'; Dropdown::showYesNo( 'password_need_caps', $CFG_GLPI['password_need_caps'], -1, [ 'rand' => $rand, ] ); echo '</td>'; echo '<td>'; echo '<label for="dropdown_password_need_symbol' . $rand . '">'; echo __('Password need symbol'); echo '</label>'; echo '</td>'; echo '<td>'; Dropdown::showYesNo( 'password_need_symbol', $CFG_GLPI['password_need_symbol'], -1, [ 'rand' => $rand, ] ); echo '</td>'; echo '</tr>'; echo '<tr class="tab_bg_1">'; echo '<td colspan="4" class="center b">' . __('Password expiration policy') . '</td>'; echo '</tr>'; echo '<tr class="tab_bg_2">'; echo '<td>'; echo '<label for="dropdown_password_expiration_delay' . $rand . '">'; echo __('Password expiration delay (in days)'); echo '</label>'; echo '</td>'; echo '<td>'; Dropdown::showNumber( 'password_expiration_delay', [ 'value' => $CFG_GLPI['password_expiration_delay'], 'min' => 30, 'max' => 365, 'step' => 15, 'toadd' => [-1 => __('Never')], 'rand' => $rand ] ); echo '</td>'; echo '<td>'; echo '<label for="dropdown_password_expiration_notice' . $rand . '">'; echo __('Password expiration notice time (in days)'); echo '</label>'; echo '</td>'; echo '<td>'; Dropdown::showNumber( 'password_expiration_notice', [ 'value' => $CFG_GLPI['password_expiration_notice'], 'min' => 0, 'max' => 30, 'step' => 1, 'toadd' => [-1 => __('Notification disabled')], 'rand' => $rand ] ); echo '</td>'; echo '</tr>'; echo '<tr class="tab_bg_2">'; echo '<td>'; echo '<label for="dropdown_password_expiration_lock_delay' . $rand . '">'; echo __('Delay before account deactivation (in days)'); echo '</label>'; echo '</td>'; echo '<td>'; Dropdown::showNumber( 'password_expiration_lock_delay', [ 'value' => $CFG_GLPI['password_expiration_lock_delay'], 'min' => 0, 'max' => 30, 'step' => 1, 'toadd' => [-1 => __('Do not deactivate')], 'rand' => $rand ] ); echo '</td>'; echo '<td colspan="2"></td>'; echo '</tr>'; echo '<tr class="tab_bg_2">'; echo '<td colspan="4" class="center">'; echo '<input type="submit" name="update" class="btn btn-primary" value="' . _sx('button', 'Save') . '">'; echo '</td>'; echo '</tr>'; echo '</table>'; Html::closeForm(); } /** * Security form related to management entries. * * @since 10.0.0 * * @return void|boolean (display) Returns false if there is a rights error. */ public function showFormManagement() { global $CFG_GLPI; if (!self::canView()) { return; } $rand = mt_rand(); $canedit = Session::haveRight(self::$rightname, UPDATE); echo '<div class="center" id="tabsbody">'; if ($canedit) { echo '<form name="form" action="' . Toolbox::getItemTypeFormURL(__CLASS__) . '" method="post" data-track-changes="true">'; } echo '<table class="tab_cadre_fixe">'; echo '<tr><th colspan="4">' . __('Documents setup') . '</th></tr>'; echo '<tr class="tab_bg_2">'; echo '<td>'; echo '<label for="document_max_size' . $rand . '">'; echo __('Document files maximum size (Mio)'); echo '</label>'; echo '</td>'; echo '<td>'; echo Html::input('document_max_size', [ 'type' => 'number', 'min' => 1, 'value' => $CFG_GLPI['document_max_size'], 'id' => 'document_max_size' . $rand, ]); echo '</td>'; echo '<td colspan="2"></td>'; echo '</tr>'; if ($canedit) { echo '<tr class="tab_bg_2">'; echo '<td colspan="4" class="center">'; echo '<input type="submit" name="update" class="btn btn-primary" value="' . _sx('button', 'Save') . '">'; echo '</td>'; echo '</tr>'; } echo '</table>'; if ($canedit) { Html::closeForm(); } echo '</div>'; } public function rawSearchOptions() { $tab = []; $tab[] = [ 'id' => 'common', 'name' => __('Characteristics') ]; $tab[] = [ 'id' => 1, 'table' => $this->getTable(), 'field' => 'value', 'name' => __('Value'), 'massiveaction' => false ]; return $tab; } public function getLogTypeID() { return [$this->getType(), 1]; } public function post_addItem() { $this->logConfigChange($this->fields['context'], $this->fields['name'], (string)$this->fields['value'], ''); } public function post_updateItem($history = 1) { global $DB, $CFG_GLPI; // Check if password expiration mechanism has been activated if ( $this->fields['name'] == 'password_expiration_delay' && array_key_exists('value', $this->oldvalues) && (int)$this->oldvalues['value'] === -1 ) { // As passwords will now expire, consider that "now" is the reference date of expiration delay $DB->update( User::getTable(), ['password_last_update' => $_SESSION['glpi_currenttime']], ['authtype' => Auth::DB_GLPI] ); // Activate passwordexpiration automated task $DB->update( CronTask::getTable(), ['state' => 1,], ['name' => 'passwordexpiration'] ); } if (array_key_exists('value', $this->oldvalues)) { $newvalue = (string)$this->fields['value']; $oldvalue = (string)$this->oldvalues['value']; if ($newvalue === $oldvalue) { return; } // avoid inserting truncated json in logs if (strlen($newvalue) > 255 && Toolbox::isJSON($newvalue)) { $newvalue = "{...}"; } if (strlen($oldvalue) > 255 && Toolbox::isJSON($oldvalue)) { $oldvalue = "{...}"; } $CFG_GLPI[$this->fields['name']] = $newvalue; // Ensure post update actions and hook that are using `$CFG_GLPI` will use the new value $this->logConfigChange( $this->fields['context'], $this->fields['name'], $newvalue, $oldvalue ); } } public function post_purgeItem() { $this->logConfigChange($this->fields['context'], $this->fields['name'], '', (string)$this->fields['value']); } /** * Log config change in history. * * @param string $context * @param string $name * @param string $newvalue * @param string $oldvalue * * @return void */ private function logConfigChange(string $context, string $name, string $newvalue, string $oldvalue): void { $glpi_key = new GLPIKey(); if ($glpi_key->isConfigSecured($context, $name)) { $newvalue = $oldvalue = '********'; } $oldvalue = $name . ($context !== 'core' ? ' (' . $context . ') ' : ' ') . $oldvalue; Log::constructHistory($this, ['value' => $oldvalue], ['value' => $newvalue]); } /** * Get the GLPI Config without unsafe keys like passwords and emails (true on $safer) * * @param boolean $safer do we need to clean more (avoid emails disclosure) * @return array of $CFG_GLPI without unsafe keys * * @since 9.5 */ public static function getSafeConfig($safer = false) { global $CFG_GLPI; $excludedKeys = array_flip(self::$undisclosedFields); $safe_config = array_diff_key($CFG_GLPI, $excludedKeys); if ($safer) { $excludedKeys = array_flip(self::$saferUndisclosedFields); $safe_config = array_diff_key($safe_config, $excludedKeys); } // override with session values foreach ($safe_config as $key => &$value) { $value = $_SESSION['glpi' . $key] ?? $value; } return $safe_config; } public static function getIcon() { return "ti ti-adjustments"; } /** * Get UUID * * @param string $type UUID type (e.g. 'instance' or 'registration') * * @return string */ final public static function getUuid($type) { $conf = self::getConfigurationValues('core', [$type . '_uuid']); $uuid = null; if (!isset($conf[$type . '_uuid']) || empty($conf[$type . '_uuid'])) { $uuid = self::generateUuid($type); } else { $uuid = $conf[$type . '_uuid']; } return $uuid; } /** * Generates an unique identifier and store it * * @param string $type UUID type (e.g. 'instance' or 'registration') * * @return string */ final public static function generateUuid($type) { $uuid = Toolbox::getRandomString(40); self::setConfigurationValues('core', [$type . '_uuid' => $uuid]); return $uuid; } /** * Try to find a valid sender email from the GLPI configuration * * @param int|null $entities_id Entity configuration to be used, default to * global configuration * @param bool $no_reply Should the configured "noreply" address be * used (default: false) * * @return array [email => sender address, name => sender name] */ public static function getEmailSender( ?int $entities_id = null, bool $no_reply = false ): array { // Try to use the configured noreply address if no response is expected // for this notification if ($no_reply) { $sender = Config::getNoReplyEmailSender($entities_id); if ($sender['email'] !== null) { return $sender; } else { trigger_error('No-Reply address is not defined in configuration.', E_USER_WARNING); } } // Try to use the configured "from" email address $sender = Config::getFromEmailSender($entities_id); if ($sender['email'] !== null) { return $sender; } // Try to use the configured "admin" email address $sender = Config::getAdminEmailSender($entities_id); if ($sender['email'] !== null) { return $sender; } // No valid email was found trigger_error( 'No email address is not defined in configuration.', E_USER_WARNING ); // No values found return [ 'email' => null, 'name' => null, ]; } /** * Try to find a valid "from" email from the GLPI configuration * * @param int|null $entities_id Entity configuration to be used, default to * global configuration * * @return array [email => sender address, name => sender name] */ public static function getFromEmailSender(?int $entities_id = null): array { return self::getEmailSenderFromEntityOrConfig('from_email', $entities_id); } /** * Try to find a valid "admin_email" email from the GLPI configuration * * @param int|null $entities_id Entity configuration to be used, default to * global configuration * * @return array [email => sender address, name => sender name] */ public static function getAdminEmailSender(?int $entities_id = null): array { return self::getEmailSenderFromEntityOrConfig('admin_email', $entities_id); } /** * Try to find a valid noreply email from the GLPI configuration * * @param int|null $entities_id Entity configuration to be used, default to * global configuration * * @return array [email => noreply address, name => noreply name] */ public static function getNoReplyEmailSender(?int $entities_id = null): array { return self::getEmailSenderFromEntityOrConfig('noreply_email', $entities_id); } /** * Try to find a valid replyto email from the GLPI configuration * * @param int|null $entities_id Entity configuration to be used, default to * global configuration * * @return array [email => replyto address, name => replyto name] */ public static function getReplyToEmailSender(?int $entities_id = null): array { return self::getEmailSenderFromEntityOrConfig('replyto_email', $entities_id); } /** * Try to find a valid email from the GLPI configuration * * @param string $config_name Configuration name * @param int|null $entities_id Entity configuration to be used, default to * global configuration * * @return array [email => address, name => name] */ private static function getEmailSenderFromEntityOrConfig(string $config_name, ?int $entities_id = null): array { global $CFG_GLPI; $email_config_name = $config_name; $name_config_name = $config_name . '_name'; // Check admin email in specified entity if (!is_null($entities_id)) { $entity_sender_email = trim( Entity::getUsedConfig($email_config_name, $entities_id, '', '') ); $entity_sender_name = trim( Entity::getUsedConfig($name_config_name, $entities_id, '', '') ); if (NotificationMailing::isUserAddressValid($entity_sender_email)) { return [ 'email' => $entity_sender_email, 'name' => $entity_sender_name, ]; } elseif ($entity_sender_email !== '') { trigger_error( sprintf( 'Invalid email address "%s" configured for entity "%s". Default administrator email will be used.', $entity_sender_email, $entities_id ), E_USER_WARNING ); } } // Fallback to global configuration $global_sender_email = $CFG_GLPI[$email_config_name] ?? ""; $global_sender_name = $CFG_GLPI[$name_config_name] ?? ""; if (NotificationMailing::isUserAddressValid($global_sender_email)) { return [ 'email' => $global_sender_email, 'name' => $global_sender_name, ]; } elseif ($global_sender_email !== '') { trigger_error( sprintf('Invalid email address "%s" configured in "%s".', $global_sender_email, $config_name), E_USER_WARNING ); } // No valid values found return [ 'email' => null, 'name' => null, ]; } /** * Override parent: "{itemtype} - {header name}" -> "{itemtype}" * There is only one config, no need to display the item name * * @return string */ public function getBrowserTabName(): string { return self::getTypeName(1); } /** * Gets the ID of a random record from the config table with the specified context. * * Used as a hacky workaround when we require a valid glpi_configs record for rights checks. * We cannot rely on something being in ID 1 for the core context for example, because some clustering solutions may change how autoincrement works. * @return ?int * @internal */ public static function getConfigIDForContext(string $context) { global $DB; $iterator = $DB->request([ 'SELECT' => 'id', 'FROM' => self::getTable(), 'WHERE' => [ 'context' => $context, ], 'LIMIT' => 1, ]); if (count($iterator)) { return $iterator->current()['id']; } return null; } }