Commit 42390e15 authored by Johan Cwiklinski's avatar Johan Cwiklinski

Merge branch '9.4/bugfixes'

parents d2418462 43362f3f
![GLPI Logo](https://raw.githubusercontent.com/glpi-project/glpi/master/pics/logos/logo-GLPI-250-black.png)
[![CircleCI](https://img.shields.io/circleci/project/github/glpi-project/glpi/9.3/bugfixes.svg)](https://circleci.com/gh/glpi-project)
[![CircleCI](https://img.shields.io/circleci/project/github/glpi-project/glpi/9.4/bugfixes.svg)](https://circleci.com/gh/glpi-project)
[![Github All Releases](https://img.shields.io/github/downloads/glpi-project/glpi/total.svg)](#download)
[![Twitter Follow](https://img.shields.io/twitter/follow/GLPI_PROJECT.svg?style=social&label=Follow)](https://twitter.com/GLPI_PROJECT)
......
......@@ -3805,6 +3805,7 @@ a.copyright {
.solimg {
position: absolute;
color: rgba(66, 91, 100, 0.1);
pointer-events: none;
}
}
......@@ -4038,7 +4039,7 @@ a.copyright {
margin-bottom: 55px;
}
.item_content img {
.item_content img, table {
max-width: 100%;
}
}
......
......@@ -508,9 +508,9 @@ class Auth extends CommonGLPI {
$user = new User();
$user->getFromDB($cookie_id);
$token = $user->getAuthToken();
$hash = $user->getAuthToken('cookie_token');
if ($token !== false && Auth::checkPassword($token, $cookie_token)) {
if (Auth::checkPassword($cookie_token, $hash)) {
$this->user->fields['name'] = $user->fields['name'];
return true;
} else {
......@@ -862,7 +862,7 @@ class Auth extends CommonGLPI {
}
if ($this->auth_succeded && $CFG_GLPI['login_remember_time'] > 0 && $remember_me) {
$token = $this->user->getAuthToken();
$token = $this->user->getAuthToken('cookie_token', true);
if ($token) {
//Cookie name (Allow multiple GLPI)
......@@ -870,11 +870,9 @@ class Auth extends CommonGLPI {
//Cookie session path
$cookie_path = ini_get('session.cookie_path');
$hash = Auth::getPasswordHash($token);
$data = json_encode([
$this->user->fields['id'],
$hash,
$token,
]);
//Send cookie to browser
......
......@@ -121,7 +121,7 @@ class Change_Ticket extends CommonDBRelation{
case 'add_task' :
$tasktype = 'TicketTask';
if ($ttype = getItemForItemtype($tasktype)) {
$ttype->showFormMassiveAction();
$ttype->showMassiveActionAddTaskForm();
return true;
}
return false;
......
......@@ -2686,7 +2686,7 @@ abstract class CommonITILObject extends CommonDBTM {
$itemtype = $ma->getItemtype(true);
$tasktype = $itemtype.'Task';
if ($ttype = getItemForItemtype($tasktype)) {
$ttype->showFormMassiveAction();
$ttype->showMassiveActionAddTaskForm();
return true;
}
return false;
......@@ -4554,6 +4554,9 @@ abstract class CommonITILObject extends CommonDBTM {
//Types of the plugins (keep the plugin hook for right check)
if (isset($PLUGIN_HOOKS['assign_to_ticket'])) {
foreach (array_keys($PLUGIN_HOOKS['assign_to_ticket']) as $plugin) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
$ptypes = Plugin::doOneHook($plugin, 'AssignToTicket', $ptypes);
}
}
......
......@@ -1687,6 +1687,52 @@ abstract class CommonITILTask extends CommonDBTM {
}
/**
* Form for Ticket or Problem Task on Massive action
*/
function showMassiveActionAddTaskForm() {
echo "<table class='tab_cadre_fixe'>";
echo '<tr><th colspan=4>'.__('Add a new task').'</th></tr>';
echo "<tr class='tab_bg_2'>";
echo "<td>".__('Category')."</td>";
echo "<td>";
TaskCategory::dropdown(['condition' => ['is_active' => 1]]);
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_2'>";
echo "<td>".__('Description')."</td>";
echo "<td><textarea name='content' cols='50' rows='6'></textarea></td>";
echo "</tr>";
echo "<tr class='tab_bg_2'>";
echo "<td>".__('Duration')."</td>";
echo "<td>";
$toadd = [];
for ($i=9; $i<=100; $i++) {
$toadd[] = $i*HOUR_TIMESTAMP;
}
Dropdown::showTimeStamp("actiontime", ['min' => 0,
'max' => 8*HOUR_TIMESTAMP,
'addfirstminutes' => true,
'inhours' => true,
'toadd' => $toadd]);
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_2'>";
echo "<td class='center' colspan='2'>";
if ($this->maybePrivate()) {
echo "<input type='hidden' name='is_private' value='".$_SESSION['glpitask_private']."'>";
}
echo "<input type='submit' name='add' value=\""._sx('button', 'Add')."\" class='submit'>";
echo "</td>";
echo "</tr>";
echo "</table>";
}
/**
* Get tasks list
*
......
......@@ -119,6 +119,10 @@ class Config extends CommonDBTM {
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;
......
......@@ -1288,6 +1288,10 @@ class Html {
if (isset($PLUGIN_HOOKS['add_css']) && count($PLUGIN_HOOKS['add_css'])) {
foreach ($PLUGIN_HOOKS["add_css"] as $plugin => $files) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
$version = Plugin::getInfo($plugin, 'version');
if (!is_array($files)) {
......@@ -1403,6 +1407,9 @@ class Html {
if (isset($PLUGIN_HOOKS["menu_toadd"]) && count($PLUGIN_HOOKS["menu_toadd"])) {
foreach ($PLUGIN_HOOKS["menu_toadd"] as $plugin => $items) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
if (count($items)) {
foreach ($items as $key => $val) {
if (is_array($val)) {
......@@ -1489,7 +1496,7 @@ class Html {
* @param $option option corresponding to the page displayed (default '')
**/
static function header($title, $url = '', $sector = "none", $item = "none", $option = "") {
global $CFG_GLPI, $PLUGIN_HOOKS, $HEADER_LOADED, $DB;
global $CFG_GLPI, $HEADER_LOADED, $DB;
// If in modal : display popHeader
if (isset($_REQUEST['_in_modal']) && $_REQUEST['_in_modal']) {
......@@ -1731,7 +1738,7 @@ class Html {
* @param $url not used anymore (default '')
**/
static function helpHeader($title, $url = '') {
global $CFG_GLPI, $HEADER_LOADED, $PLUGIN_HOOKS;
global $CFG_GLPI, $HEADER_LOADED;
// Print a nice HTML-head for help page
if ($HEADER_LOADED) {
......@@ -1884,7 +1891,7 @@ class Html {
* @param $iframed indicate if page loaded in iframe - css target (default false)
**/
static function popHeader($title, $url = '', $iframed = false) {
global $CFG_GLPI, $PLUGIN_HOOKS, $HEADER_LOADED;
global $CFG_GLPI, $HEADER_LOADED;
// Print a nice HTML-head for every page
if ($HEADER_LOADED) {
......@@ -5945,6 +5952,9 @@ class Html {
if (isset($PLUGIN_HOOKS['add_javascript']) && count($PLUGIN_HOOKS['add_javascript'])) {
foreach ($PLUGIN_HOOKS["add_javascript"] as $plugin => $files) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
$version = Plugin::getInfo($plugin, 'version');
if (!is_array($files)) {
$files = [$files];
......@@ -6361,6 +6371,9 @@ class Html {
&& count($PLUGIN_HOOKS["helpdesk_menu_entry"])) {
foreach ($PLUGIN_HOOKS["helpdesk_menu_entry"] as $plugin => $active) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
if ($active) {
$infos = Plugin::getInfo($plugin);
$link = "";
......
......@@ -994,4 +994,81 @@ class ITILFollowup extends CommonDBChild {
return $values;
}
static function showMassiveActionAddFollowupForm() {
echo "<table class='tab_cadre_fixe'>";
echo '<tr><th colspan=4>'.__('Add a new followup').'</th></tr>';
echo "<tr class='tab_bg_2'>";
echo "<td>".__('Source of followup')."</td>";
echo "<td>";
RequestType::dropdown(
[
'value' => RequestType::getDefault('followup'),
'condition' => ['is_active' => 1, 'is_itilfollowup' => 1]
]
);
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_2'>";
echo "<td>".__('Description')."</td>";
echo "<td><textarea name='content' cols='50' rows='6'></textarea></td>";
echo "</tr>";
echo "<tr class='tab_bg_2'>";
echo "<td class='center' colspan='2'>";
echo "<input type='hidden' name='is_private' value='".$_SESSION['glpifollowup_private']."'>";
echo "<input type='submit' name='add' value=\""._sx('button', 'Add')."\" class='submit'>";
echo "</td>";
echo "</tr>";
echo "</table>";
}
static function showMassiveActionsSubForm(MassiveAction $ma) {
switch ($ma->getAction()) {
case 'add_followup' :
static::showMassiveActionAddFollowupForm();
return true;
}
return parent::showMassiveActionsSubForm($ma);
}
static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item,
array $ids) {
switch ($ma->getAction()) {
case 'add_followup' :
$input = $ma->getInput();
$fup = new self();
foreach ($ids as $id) {
if ($item->getFromDB($id)) {
$input2 = [
'items_id' => $id,
'itemtype' => $item->getType(),
'is_private' => $input['is_private'],
'requesttypes_id' => $input['requesttypes_id'],
'content' => $input['content']
];
if ($fup->can(-1, CREATE, $input2)) {
if ($fup->add($input2)) {
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
} else {
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
$ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
}
} else {
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_NORIGHT);
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
}
} else {
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
$ma->addMessage($item->getErrorMessage(ERROR_NOT_FOUND));
}
}
}
parent::processMassiveActionsForOneItemtype($ma, $item, $ids);
}
}
......@@ -782,9 +782,14 @@ class MailCollector extends CommonDBTM {
$tkt['content'] = $body;
}
// prepare match to find ticket id in headers
// pattern: GLPI-{itemtype}-{items_id}
// ex: GLPI-Ticket-26739
$ref_match = "GLPI-[A-Z]\w+-([0-9]+)";
// See In-Reply-To field
if (isset($head['in_reply_to'])) {
if (preg_match($glpi_message_match, $head['in_reply_to'], $match)) {
if (preg_match($ref_match, $head['in_reply_to'], $match)) {
$tkt['tickets_id'] = intval($match[1]);
}
}
......@@ -792,7 +797,7 @@ class MailCollector extends CommonDBTM {
// See in References
if (!isset($tkt['tickets_id'])
&& isset($head['references'])) {
if (preg_match($glpi_message_match, $head['references'], $match)) {
if (preg_match($ref_match, $head['references'], $match)) {
$tkt['tickets_id'] = intval($match[1]);
}
}
......
......@@ -574,6 +574,9 @@ class MassiveAction {
// Plugin Specific actions
if (isset($PLUGIN_HOOKS['use_massive_action'])) {
foreach ($PLUGIN_HOOKS['use_massive_action'] as $plugin => $val) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
$plug_actions = Plugin::doOneHook($plugin, 'MassiveActions', $itemtype);
if (is_array($plug_actions) && count($plug_actions)) {
......
......@@ -99,7 +99,7 @@ class Plugin extends CommonDBTM {
$appCache = Toolbox::getAppCache();
$appCache->set('plugins', []);
if (!$DB->isConnected() || !$DB->tableExists(self::getTable())) {
if (!isset($DB) || !$DB->isConnected() || !$DB->tableExists(self::getTable())) {
return;
}
......@@ -773,6 +773,17 @@ class Plugin extends CommonDBTM {
43 => "TicketFollowup",
44 => "Budget"];
// Filter tables that does not exists or does not contains an itemtype field.
// This kind of case exist when current method is called from plugins that based their
// logic on an old GLPI datamodel that may have changed upon time.
// see https://github.com/pluginsGLPI/order/issues/111
$glpitables = array_filter(
$glpitables,
function ($table) use ($DB) {
return $DB->tableExists($table) && $DB->fieldExists($table, 'itemtype');
}
);
//Add plugins types
$typetoname = self::doHookFunction("migratetypes", $typetoname);
......
......@@ -154,7 +154,7 @@ class Problem_Ticket extends CommonDBRelation{
case 'add_task' :
$tasktype = 'TicketTask';
if ($ttype = getItemForItemtype($tasktype)) {
$ttype->showFormMassiveAction();
$ttype->showMassiveActionAddTaskForm();
return true;
}
return false;
......
......@@ -133,6 +133,9 @@ class Report extends CommonGLPI{
$optgroup = [];
if (isset($PLUGIN_HOOKS["reports"]) && is_array($PLUGIN_HOOKS["reports"])) {
foreach ($PLUGIN_HOOKS["reports"] as $plug => $pages) {
if (!Plugin::isPluginLoaded($plug)) {
continue;
}
if (is_array($pages) && count($pages)) {
foreach ($pages as $page => $name) {
$names[$plug.'/'.$page] = ["name" => $name,
......
......@@ -1704,6 +1704,9 @@ class Rule extends CommonDBTM {
$input = $this->prepareInputDataForProcess($input, $params);
if (isset($PLUGIN_HOOKS['use_rules'])) {
foreach ($PLUGIN_HOOKS['use_rules'] as $plugin => $val) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
if (is_array($val) && in_array($this->getType(), $val)) {
$results = Plugin::doOneHook($plugin, "rulePrepareInputDataForProcess",
['input' => $input,
......@@ -1740,6 +1743,9 @@ class Rule extends CommonDBTM {
$params['criterias_results'] = $this->criterias_results;
$params['rule_itemtype'] = $this->getType();
foreach ($PLUGIN_HOOKS['use_rules'] as $plugin => $val) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
if (is_array($val) && in_array($this->getType(), $val)) {
$results = Plugin::doOneHook($plugin, "executeActions", ['output' => $output,
'params' => $params,
......@@ -2573,6 +2579,9 @@ class Rule extends CommonDBTM {
$params['criterias_results'] = $this->criterias_results;
$params['rule_itemtype'] = $this->getType();
foreach ($PLUGIN_HOOKS['use_rules'] as $plugin => $val) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
if (is_array($val) && in_array($this->getType(), $val)) {
$results = Plugin::doOneHook($plugin, "preProcessRulePreviewResults",
['output' => $output,
......@@ -2676,6 +2685,9 @@ class Rule extends CommonDBTM {
$toreturn = $params;
if (isset($PLUGIN_HOOKS['use_rules'])) {
foreach ($PLUGIN_HOOKS['use_rules'] as $plugin => $val) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
if (is_array($val) && in_array($itemtype, $val)) {
$results = Plugin::doOneHook($plugin, $hook, ['rule_itemtype' => $itemtype,
'values' => $params]);
......
......@@ -1654,6 +1654,9 @@ class RuleCollection extends CommonDBTM {
$input = $this->prepareInputDataForProcess($input, $params);
if (isset($PLUGIN_HOOKS['use_rules'])) {
foreach ($PLUGIN_HOOKS['use_rules'] as $plugin => $val) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
if (is_array($val) && in_array($this->getRuleClassName(), $val)) {
$results = Plugin::doOneHook($plugin, 'ruleCollectionPrepareInputDataForProcess',
['rule_itemtype' => $this->getRuleClassName(),
......@@ -1834,6 +1837,9 @@ class RuleCollection extends CommonDBTM {
if (isset($PLUGIN_HOOKS['use_rules'])) {
$params['rule_itemtype'] = $this->getType();
foreach ($PLUGIN_HOOKS['use_rules'] as $plugin => $val) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
if (is_array($val) && in_array($this->getType(), $val)) {
$results = Plugin::doOneHook($plugin, "preProcessRuleCollectionPreviewResults",
['output' => $output,
......
......@@ -256,6 +256,9 @@ class RuleImportComputer extends Rule {
//Add plugin global criteria
if (isset($PLUGIN_HOOKS['use_rules'])) {
foreach ($PLUGIN_HOOKS['use_rules'] as $plugin => $val) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
if (is_array($val) && in_array($this->getType(), $val)) {
$global_criteria = Plugin::doOneHook($plugin, "ruleImportComputer_addGlobalCriteria",
$global_criteria);
......@@ -350,6 +353,9 @@ class RuleImportComputer extends Rule {
if (isset($PLUGIN_HOOKS['use_rules'])) {
foreach ($PLUGIN_HOOKS['use_rules'] as $plugin => $val) {
if (!Plugin::isPluginLoaded($plugin)) {
continue;
}
if (is_array($val) && in_array($this->getType(), $val)) {
$params = ['where_entity' => $where_entity,
'input' => $input,
......
......@@ -49,8 +49,6 @@ class RuleImportComputerCollection extends RuleCollection {
* @return boolean
**/
function canList() {
global $PLUGIN_HOOKS;
if (Plugin::haveImport()) {
return static::canView();
}
......
......@@ -121,6 +121,9 @@ class RuleImportEntity extends Rule {
if ($criteria['field'] == '_source') {
$tab = [];
foreach ($PLUGIN_HOOKS['import_item'] as $plug => $types) {
if (!Plugin::isPluginLoaded($plug)) {
continue;
}
$tab[$plug] = Plugin::getInfo($plug, 'name');
}
Dropdown::showFromArray($name, $tab);
......
......@@ -47,8 +47,6 @@ class RuleImportEntityCollection extends RuleCollection {
* @see RuleCollection::canList()
**/
function canList() {
global $PLUGIN_HOOKS;
if (Plugin::haveImport()) {
return static::canView();
}
......
......@@ -1043,9 +1043,19 @@ class Search {
$tmpquery.= $GROUPBY.
$HAVING;
$tmpquery = str_replace($CFG_GLPI["union_search_type"][$data['itemtype']],
$ctable, $tmpquery);
// Replace 'asset_types' by itemtype table name
$tmpquery = str_replace(
$CFG_GLPI["union_search_type"][$data['itemtype']],
$ctable,
$tmpquery
);
// Replace 'AllAssets' by itemtype
// Use quoted value to prevent replacement of AllAssets in column identifiers
$tmpquery = str_replace(
$DB->quoteValue('AllAssets'),
$DB->quoteValue($ctype),
$tmpquery
);
} else {// Ref table case
$reftable = getTableForItemType($data['itemtype']);
......
......@@ -624,8 +624,11 @@ class Session {
$_SESSION['glpipluralnumber'] = $CFG_GLPI["languages"][$trytoload][5];
}
$TRANSLATE = new Zend\I18n\Translator\Translator;
$TRANSLATE->setLocale($trytoload);
$cache_storage = $CONTAINER->get('translation_cache')->getStorage();
$TRANSLATE->setCache($cache_storage);
$TRANSLATE->addTranslationFile('gettext', GLPI_ROOT.$newfile, 'glpi', $trytoload);
// Load plugin dicts
......@@ -633,8 +636,6 @@ class Session {
Plugin::loadLang($plug, $forcelang, $trytoload);
}
$TRANSLATE->setLocale($trytoload);
return $trytoload;
}
......
......@@ -1592,6 +1592,9 @@ class Stat extends CommonGLPI {
$optgroup = [];
if (isset($PLUGIN_HOOKS["stats"]) && is_array($PLUGIN_HOOKS["stats"])) {
foreach ($PLUGIN_HOOKS["stats"] as $plug => $pages) {
if (!Plugin::isPluginLoaded($plug)) {
continue;
}
if (is_array($pages) && count($pages)) {
foreach ($pages as $page => $name) {
$names[$plug.'/'.$page] = ["name" => $name,
......
......@@ -150,6 +150,8 @@ class Ticket extends CommonITILObject {
$menu['create_ticket']['title'] = __('Create ticket');
$menu['create_ticket']['page'] = static::getFormURL(false);
return $menu;
} else {
return self::getAdditionalMenuOptions();
}
}
......
......@@ -4769,24 +4769,33 @@ class User extends CommonDBTM {
* @since 9.4
*
* @param string $field the field storing the token
* @param boolean $force_new force generation of a new token
*
* @return string|false token or false in case of error
*/
public function getAuthToken($field = 'personal_token') {
public function getAuthToken($field = 'personal_token', $force_new = false) {
global $DB;
if ($this->isNewItem()) {
return false;
}
if (!empty($this->fields[$field])) {
if (!empty($this->fields[$field]) && !$force_new) {
return $this->fields[$field];
}
$token = self::getUniqueToken($field);
// for cookie token, we need to store it hashed
$hash = $token;
if ($field === 'cookie_token') {
$hash = Auth::getPasswordHash($token);
}
$this->update(['id' => $this->getID(),
$field => $token,
$field => $hash,
$field . "_date" => $_SESSION['glpi_currenttime']]);
return $this->fields[$field];
return $token;
}
......
......@@ -44,6 +44,10 @@ Config::detectRootDoc();
$GLPI = new GLPI();
$GLPI->initLogger();
// $GLPI_CACHE is indirectly used in Session::loadLanguage() to retrieve list of plugins.
global $GLPI_CACHE;
$GLPI_CACHE = Config::getCache('cache_db');
//Print a correct Html header for application
function header_html($etape) {
global $CFG_GLPI;
......
......@@ -8945,6 +8945,8 @@ CREATE TABLE `glpi_users` (
`personal_token_date` datetime DEFAULT NULL,
`api_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`api_token_date` datetime DEFAULT NULL,
`cookie_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`cookie_token_date` datetime DEFAULT NULL,
`display_count_on_home` int(11) DEFAULT NULL,
`notification_to_myself` tinyint(1) DEFAULT NULL,
`duedateok_color` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
......
......@@ -134,6 +134,12 @@ function update940to941() {
}
/** /Fix URL of images inside ITIL objects contents */
// Create a dedicated token for rememberme process
if (!$DB->fieldExists('glpi_users', 'cookie_token')) {
$migration->addField('glpi_users', 'cookie_token', 'string', ['after' => 'api_token_date']);
$migration->addField('glpi_users', 'cookie_token_date', 'datetime', ['after' => 'cookie_token']);
}
// ************ Keep it at the end **************
$migration->executeMigration();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment