Skip to content

Commit 2ff6f60

Browse files
committed
Merge branch '10.0/bugfixes'
2 parents 7ed139f + 944443d commit 2ff6f60

25 files changed

+870
-37
lines changed

.phpstan-baseline.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2878,7 +2878,7 @@
28782878
$ignoreErrors[] = [
28792879
'message' => '#^Function preg_match is unsafe to use\\. It can return FALSE instead of throwing an exception\\. Please add \'use function Safe\\\\preg_match;\' at the beginning of the file to use the variant provided by the \'thecodingmachine/safe\' library\\.$#',
28802880
'identifier' => 'theCodingMachineSafe.function',
2881-
'count' => 1,
2881+
'count' => 3,
28822882
'path' => __DIR__ . '/src/Dropdown.php',
28832883
];
28842884
$ignoreErrors[] = [

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,7 @@ The present file will list all changes made to the project; according to the
633633

634634
### Changed
635635
- Only unsolved/unclosed tickets will be shown in the dropdown when performing the "Merge as Followup" action.
636+
- Domain records must be attached to a domain. Existing unattached records will remain but will require a domain if edited.
636637

637638
### Deprecated
638639

ajax/comments.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,36 @@
105105
}
106106
break;
107107

108+
case Supplier::getType():
109+
$tmpname = [
110+
'comment' => "",
111+
];
112+
if ($_POST['value'] != 0) {
113+
$supplier = new \Supplier();
114+
if (!is_array($_POST["value"]) && $supplier->getFromDB($_POST['value']) && $supplier->canView()) {
115+
$supplier_params = [
116+
'id' => $supplier->getID(),
117+
'supplier_name' => $supplier->fields['name'],
118+
'comment' => $supplier->fields['comment'],
119+
'address' => $supplier->fields['address'],
120+
'postcode' => $supplier->fields['postcode'],
121+
'town' => $supplier->fields['town'],
122+
'email' => $supplier->fields['email'],
123+
'fax' => $supplier->fields['fax'],
124+
'registration_number' => $supplier->fields['registration_number'],
125+
'phonenumber' => $supplier->fields['phonenumber'],
126+
];
127+
$comment = TemplateRenderer::getInstance()->render('components/supplier/info_card.html.twig', [
128+
'supplier' => $supplier_params,
129+
]);
130+
$tmpname = [
131+
'comment' => $comment,
132+
];
133+
}
134+
}
135+
echo($tmpname["comment"]);
136+
break;
137+
108138
default:
109139
if ($_POST["value"] > 0) {
110140
if (
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
/**
4+
* ---------------------------------------------------------------------
5+
*
6+
* GLPI - Gestionnaire Libre de Parc Informatique
7+
*
8+
* http://glpi-project.org
9+
*
10+
* @copyright 2015-2025 Teclib' and contributors.
11+
* @licence https://www.gnu.org/licenses/gpl-3.0.html
12+
*
13+
* ---------------------------------------------------------------------
14+
*
15+
* LICENSE
16+
*
17+
* This file is part of GLPI.
18+
*
19+
* This program is free software: you can redistribute it and/or modify
20+
* it under the terms of the GNU General Public License as published by
21+
* the Free Software Foundation, either version 3 of the License, or
22+
* (at your option) any later version.
23+
*
24+
* This program is distributed in the hope that it will be useful,
25+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
26+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27+
* GNU General Public License for more details.
28+
*
29+
* You should have received a copy of the GNU General Public License
30+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
31+
*
32+
* ---------------------------------------------------------------------
33+
*/
34+
35+
namespace tests\units;
36+
37+
use ChangeTemplatePredefinedField;
38+
use Glpi\Tests\AbstractITILTemplatePredefinedFieldTest;
39+
use ITILTemplatePredefinedField;
40+
41+
final class ChangeTemplatePredefinedFieldTest extends AbstractITILTemplatePredefinedFieldTest
42+
{
43+
public function getConcreteClass(): ITILTemplatePredefinedField
44+
{
45+
return new ChangeTemplatePredefinedField();
46+
}
47+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
/**
4+
* ---------------------------------------------------------------------
5+
*
6+
* GLPI - Gestionnaire Libre de Parc Informatique
7+
*
8+
* http://glpi-project.org
9+
*
10+
* @copyright 2015-2025 Teclib' and contributors.
11+
* @licence https://www.gnu.org/licenses/gpl-3.0.html
12+
*
13+
* ---------------------------------------------------------------------
14+
*
15+
* LICENSE
16+
*
17+
* This file is part of GLPI.
18+
*
19+
* This program is free software: you can redistribute it and/or modify
20+
* it under the terms of the GNU General Public License as published by
21+
* the Free Software Foundation, either version 3 of the License, or
22+
* (at your option) any later version.
23+
*
24+
* This program is distributed in the hope that it will be useful,
25+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
26+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27+
* GNU General Public License for more details.
28+
*
29+
* You should have received a copy of the GNU General Public License
30+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
31+
*
32+
* ---------------------------------------------------------------------
33+
*/
34+
35+
namespace tests\units;
36+
37+
use DbTestCase;
38+
use DomainRecord;
39+
40+
/* Test for inc/software.class.php */
41+
42+
class DomainRecordTest extends DbTestCase
43+
{
44+
public function testCanViewUnattached()
45+
{
46+
/** @var \DBmysql $DB */
47+
global $DB;
48+
$this->login();
49+
$record = new DomainRecord();
50+
// Unattached records are not allowed to be created anymore but may still exist in the DB. For the test, we need to directly add one to the DB.
51+
$DB->insert(
52+
'glpi_domainrecords',
53+
[
54+
'name' => __FUNCTION__,
55+
'entities_id' => $this->getTestRootEntity(true),
56+
'ttl' => 3600,
57+
]
58+
);
59+
$this->assertTrue($record->getFromDB($DB->insertId()));
60+
61+
$this->assertTrue($record->canViewItem());
62+
}
63+
64+
public function testPrepareInput()
65+
{
66+
$this->login();
67+
$record = new DomainRecord();
68+
$this->assertFalse($record->prepareInputForAdd([]));
69+
$this->hasSessionMessages(ERROR, ['A domain is required']);
70+
71+
$created_record = $this->createItem('DomainRecord', ['domains_id' => 1]);
72+
$this->assertEmpty($record->prepareInputForUpdate([]));
73+
$this->assertFalse($record->prepareInputForUpdate(['domains_id' => 0]));
74+
$this->hasSessionMessages(ERROR, ['A domain is required']);
75+
$this->assertFalse($record->prepareInputForUpdate(['domains_id' => '']));
76+
$this->hasSessionMessages(ERROR, ['A domain is required']);
77+
}
78+
}

phpunit/functional/DropdownTest.php

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,124 @@ public static function getDropdownValueProvider()
10331033
'count' => 1,
10341034
],
10351035
],
1036+
// This test verifies the behavior of searches by ID when $_SESSION['glpiis_ids_visible'] is true.
1037+
// Specifically, it checks that a WHERE clause is applied on the index name ("id")
1038+
// when 'searchText' contains only numeric characters (one or more), with no other characters.
1039+
// This condition is evaluated in two contexts: when the related object is either a CommonDropdown or a CommonTreeDropdown.
1040+
// Therefore, we cover both cases by testing with TaskCategory (CommonTreeDropdown) and ComputerModel (CommonDropdown).
1041+
// Additionally, we use both string and integer values for 'searchText' to ensure proper handling and to validate the regex used in preg_match.
1042+
[
1043+
'params' => [
1044+
'display_emptychoice' => 0,
1045+
'itemtype' => 'TaskCategory',
1046+
'searchText' => (int) getItemByTypeName(\TaskCategory::class, '_cat_1', true), // search commonTreeDropdown by id as int
1047+
],
1048+
'expected' => [
1049+
'results' => [
1050+
[
1051+
'text' => 'Root entity',
1052+
'children' => [
1053+
0 => [
1054+
'id' => getItemByTypeName('TaskCategory', '_cat_1', true),
1055+
'text' => '_cat_1',
1056+
'level' => 1,
1057+
'title' => '_cat_1 - Comment for category _cat_1',
1058+
'selection_text' => '_cat_1',
1059+
],
1060+
1 => [
1061+
'id' => getItemByTypeName('TaskCategory', '_subcat_1', true),
1062+
'text' => '_subcat_1',
1063+
'level' => 2,
1064+
'title' => '_cat_1 > _subcat_1 - Comment for sub-category _subcat_1',
1065+
'selection_text' => '_cat_1 > _subcat_1',
1066+
],
1067+
2 => [
1068+
'id' => getItemByTypeName('TaskCategory', 'R&D', true),
1069+
'text' => 'R&D',
1070+
'level' => 2,
1071+
'title' => '_cat_1 > R&D - Comment for sub-category _subcat_2',
1072+
'selection_text' => '_cat_1 > R&D',
1073+
],
1074+
],
1075+
'itemtype' => 'Entity',
1076+
],
1077+
],
1078+
'count' => 3,
1079+
],
1080+
],
1081+
[
1082+
'params' => [
1083+
'display_emptychoice' => 0,
1084+
'itemtype' => 'TaskCategory',
1085+
'searchText' => (string) getItemByTypeName(\TaskCategory::class, '_cat_1', true), // search commonTreeDropdown by id as string
1086+
],
1087+
'expected' => [
1088+
'results' => [
1089+
[
1090+
'text' => 'Root entity',
1091+
'children' => [
1092+
0 => [
1093+
'id' => getItemByTypeName('TaskCategory', '_cat_1', true),
1094+
'text' => '_cat_1',
1095+
'level' => 1,
1096+
'title' => '_cat_1 - Comment for category _cat_1',
1097+
'selection_text' => '_cat_1',
1098+
],
1099+
1 => [
1100+
'id' => getItemByTypeName('TaskCategory', '_subcat_1', true),
1101+
'text' => '_subcat_1',
1102+
'level' => 2,
1103+
'title' => '_cat_1 > _subcat_1 - Comment for sub-category _subcat_1',
1104+
'selection_text' => '_cat_1 > _subcat_1',
1105+
],
1106+
2 => [
1107+
'id' => getItemByTypeName('TaskCategory', 'R&D', true),
1108+
'text' => 'R&D',
1109+
'level' => 2,
1110+
'title' => '_cat_1 > R&D - Comment for sub-category _subcat_2',
1111+
'selection_text' => '_cat_1 > R&D',
1112+
],
1113+
],
1114+
'itemtype' => 'Entity',
1115+
],
1116+
],
1117+
'count' => 3,
1118+
],
1119+
],
1120+
[
1121+
'params' => [
1122+
'display_emptychoice' => 0,
1123+
'itemtype' => 'ComputerModel',
1124+
'searchText' => (int) getItemByTypeName('ComputerModel', '_test_computermodel_1', true), // search CommonDropdown by id as int
1125+
],
1126+
'expected' => [
1127+
'results' => [
1128+
[
1129+
'id' => getItemByTypeName('ComputerModel', '_test_computermodel_1', true),
1130+
'text' => '_test_computermodel_1 - CMP_ADEAF5E1',
1131+
'title' => '_test_computermodel_1 - CMP_ADEAF5E1',
1132+
],
1133+
],
1134+
'count' => 1,
1135+
],
1136+
],
1137+
[
1138+
'params' => [
1139+
'display_emptychoice' => 0,
1140+
'itemtype' => 'ComputerModel',
1141+
'searchText' => (string) getItemByTypeName('ComputerModel', '_test_computermodel_1', true), // search CommonDropdown by id as string
1142+
],
1143+
'expected' => [
1144+
'results' => [
1145+
[
1146+
'id' => getItemByTypeName('ComputerModel', '_test_computermodel_1', true),
1147+
'text' => '_test_computermodel_1 - CMP_ADEAF5E1',
1148+
'title' => '_test_computermodel_1 - CMP_ADEAF5E1',
1149+
],
1150+
],
1151+
'count' => 1,
1152+
],
1153+
],
10361154
];
10371155
}
10381156

phpunit/functional/LockedfieldTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,4 +924,50 @@ public function testReplaceLockedField()
924924
$this->assertTrue($computer->getFromDB($cid));
925925
$this->assertEquals($computer->fields['locations_id'], 0);
926926
}
927+
928+
public function testCheckAllInventoryLockableObjects()
929+
{
930+
$this->login('glpi', 'glpi');
931+
932+
/** @var \DBmysql $DB */
933+
global $DB;
934+
935+
/** @var array $CFG_GLPI */
936+
global $CFG_GLPI;
937+
foreach ($CFG_GLPI['inventory_lockable_objects'] as $itemtype) {
938+
$this->assertTrue($DB->fieldExists($itemtype::getTable(), 'is_dynamic'), "$itemtype does not have is_dynamic field");
939+
}
940+
941+
942+
// Excluded type with is_dynamic field but not in inventory_lockable_objects
943+
$excluded = [
944+
'UserEmail',
945+
'Glpi\\Asset\\Asset', // only concrete classes are registered
946+
'Group_User',
947+
'Profile_User',
948+
];
949+
950+
$global_inventory_type = array_merge(
951+
$CFG_GLPI['inventory_lockable_objects'],
952+
$CFG_GLPI['inventory_types']
953+
);
954+
955+
$tables = $DB->listTables();
956+
foreach ($tables as $table_data) {
957+
$table = $table_data['TABLE_NAME'];
958+
959+
if ($DB->fieldExists($table, 'is_dynamic')) {
960+
$itemtype = getItemTypeForTable($table);
961+
if (in_array($itemtype, $excluded)) {
962+
continue;
963+
}
964+
965+
$this->assertContains(
966+
$itemtype,
967+
$global_inventory_type,
968+
"$itemtype is not in inventory_lockable_objects or inventory_types"
969+
);
970+
}
971+
}
972+
}
927973
}

0 commit comments

Comments
 (0)