Commit b0e2c1ed authored by Johan Cwiklinski's avatar Johan Cwiklinski

Merge branch '9.5/bugfixes'

parents 1ad1ee74 23c39509
......@@ -6,7 +6,7 @@ env:
- DB=mysql
before_script:
- phpenv config-rm xdebug.ini
- phpenv config-rm xdebug.ini || true
- ./tests/LDAP/ldap_run.sh
- composer self-update
- sed -e '/"php":/d' -i composer.json
......@@ -52,6 +52,12 @@ matrix:
packages:
- ldap-utils
- slapd
- php: 7.4snapshot
addons:
apt:
packages:
- ldap-utils
- slapd
- php: nightly
addons:
apt:
......@@ -60,6 +66,7 @@ matrix:
- slapd
allow_failures:
- php: nightly
- php: 7.4snapshot
cache:
directories:
......
......@@ -52,6 +52,7 @@ The present file will list all changes made to the project; according to the
- Operating system links on Monitors, Peripherals, Phones and Printers.
- Add datacenter items to global search
- Project task search options for Projects
- Automatic action to purge closed tickets
### Changed
......@@ -59,6 +60,10 @@ The present file will list all changes made to the project; according to the
### API changes
#### Changes
- `DBmysqlIterator::handleOrderClause()` supports QueryExpressions
#### Deprecated
- `DBMysql::fetch_array()`
......@@ -77,6 +82,7 @@ The present file will list all changes made to the project; according to the
- Usage of `$order` parameter in `getAllDataFromTable()` (`DbUtils::getAllDataFromTable()`)
- All `TicketTemplate` classes has been renamed to `ITILTemplate`
- `Ticket::getTicketTemplateToUse()` renamed to `Ticket::getITILTemplateToUse()`
- `TicketTemplate::getFromDBWithDatas()` renamed to `Ticket::getFromDBWithData()` (inherited from `ITILTemplate`)
#### Removed
......
This diff is collapsed.
......@@ -168,14 +168,15 @@ if (isset($_POST["add"])) {
Html::redirect(Ticket::getFormURLWithID($_POST['tickets_id']));
} else if (isset($_POST['addme_assign'])) {
$ticket_user = new Ticket_User();
$track->check($_POST['tickets_id'], READ);
$input = ['tickets_id' => $_POST['tickets_id'],
'users_id' => Session::getLoginUserID(),
'use_notification' => 1,
'type' => CommonITILActor::ASSIGN];
$ticket_user->add($input);
$track->update([
'id' => $_POST['tickets_id'],
'_itil_assign' => [
'_type' => "user",
'users_id' => Session::getLoginUserID(),
'use_notification' => 1,
]
]);
Event::log($_POST['tickets_id'], "ticket", 4, "tracking",
//TRANS: %s is the user login
sprintf(__('%s adds an actor'), $_SESSION["glpiname"]));
......
......@@ -1242,7 +1242,7 @@ abstract class API extends CommonGLPI {
$itemtype::getTable(),
'',
$_SESSION['glpiactiveentities'],
false,
$item->maybeRecursive(),
true);
if ($item instanceof SavedSearch) {
......
......@@ -437,7 +437,7 @@ class Change extends CommonITILObject {
'id' => '60',
'table' => $this->getTable(),
'field' => 'impactcontent',
'name' => __('Impact'),
'name' => __('Analysis impact'),
'massiveaction' => false,
'datatype' => 'text'
];
......@@ -748,8 +748,17 @@ class Change extends CommonITILObject {
if (!$options['template_preview']) {
$this->showFormHeader($options);
if (isset($this->fields['_tasktemplates_id'])) {
foreach ($this->fields['_tasktemplates_id'] as $tasktemplates_id) {
echo "<input type='hidden' name='_tasktemplates_id[]' value='$tasktemplates_id'>";
}
}
}
echo "<div class='spaced' id='tabsbody'>";
echo "<table class='tab_cadre_fixe' id='mainformtable'>";
echo "<tr class='tab_bg_1'>";
echo "<th class='left' width='$colsize1%'>";
echo $tt->getBeginHiddenFieldText('date');
......@@ -1039,7 +1048,7 @@ class Change extends CommonITILObject {
} else {
echo $tt->getBeginHiddenFieldValue('global_validation');
if (Session::haveRightsOr('changevalidation', ChangeeValidation::getCreateRights())
if (Session::haveRightsOr('changevalidation', ChangeValidation::getCreateRights())
&& $canupdate) {
ChangeValidation::dropdownStatus('global_validation',
['global' => true,
......@@ -1054,11 +1063,13 @@ class Change extends CommonITILObject {
echo "<th></th>";
echo "<td></td>";
echo "</tr>";
echo "</table>";
$this->showActorsPartForm($ID, $options);
if (!$ID) {
echo "</table>";
$this->showActorsPartForm($ID, $options);
echo "<table class='tab_cadre_fixe' id='mainformtable3'>";
}
echo "<table class='tab_cadre_fixe' id='mainformtable3'>";
echo "<tr class='tab_bg_1'>";
echo "<th style='width:$colsize1%'>".$tt->getBeginHiddenFieldText('name');
printf(__('%1$s%2$s'), __('Title'), $tt->getMandatoryMark('name'));
......@@ -1075,7 +1086,8 @@ class Change extends CommonITILObject {
echo "<tr class='tab_bg_1'>";
echo "<th style='width:$colsize1%'>".$tt->getBeginHiddenFieldText('content');
printf(__('%1$s%2$s'), __('Description'), $tt->getMandatoryMark('content'));
echo "<td colspan='3'>";
echo $tt->getEndHiddenFieldText('content', $this);
echo "</th><td colspan='3'>";
$rand = mt_rand();
echo $tt->getBeginHiddenFieldValue('content');
......@@ -1100,20 +1112,37 @@ class Change extends CommonITILObject {
echo "<textarea id='$content_id' name='content' style='width:100%' rows='$rows'".
($tt->isMandatoryField('content') ? " required='required'" : '') . ">" .
$content."</textarea></div>";
$content."</textarea>";
echo $tt->getEndHiddenFieldValue('content', $this);
echo "</td></tr>";
$options['colspan'] = 2;
if (!$options['template_preview']) {
if ($tt->isField('id') && ($tt->fields['id'] > 0)) {
echo "<input type='hidden' name='$tpl_key' value='".$tt->fields['id']."'>";
echo "<input type='hidden' name='_predefined_fields'
value=\"".Toolbox::prepareArrayForInput($predefined_fields)."\">";
}
if (!$ID) {
$fields = [
'controlistcontent',
'impactcontent',
'rolloutplancontent',
'backoutplancontent',
'checklistcontent'
];
foreach ($fields as $field) {
if (isset($tt->predefined[$field])) {
echo Html::hidden($field, ['value' => $tt->predefined[$field]]);
}
}
}
$this->showFormButtons($options);
}
echo "</table>";
echo "</div>";
return true;
......@@ -1462,7 +1491,13 @@ class Change extends CommonITILObject {
'itilcategories_id' => 0,
'actiontime' => 0,
'_add_validation' => 0,
'users_id_validate' => []
'users_id_validate' => [],
'_tasktemplates_id' => [],
'controlistcontent' => '',
'impactcontent' => '',
'rolloutplancontent' => '',
'backoutplancontent' => '',
'checklistcontent' => ''
];
}
}
......@@ -47,4 +47,14 @@ class ChangeTemplate extends ITILTemplate {
return _n('Change template', 'change templates', $nb);
}
public static function getExtraAllowedFields($withtypeandcategory = 0, $withitemtype = 0) {
$change = new Change();
return [
$change->getSearchOptionIDByField('field', 'impactcontent', 'glpi_changes') => 'impactcontent',
$change->getSearchOptionIDByField('field', 'controlistcontent', 'glpi_changes') => 'controlistcontent',
$change->getSearchOptionIDByField('field', 'rolloutplancontent', 'glpi_changes') => 'rolloutplancontent',
$change->getSearchOptionIDByField('field', 'backoutplancontent', 'glpi_changes') => 'backoutplancontent',
$change->getSearchOptionIDByField('field', 'checklistcontent', 'glpi_changes') => 'checklistcontent'
];
}
}
......@@ -1562,7 +1562,7 @@ abstract class CommonITILObject extends CommonDBTM {
if (isset($input[$key]) && $input[$key]) {
$tt_class = $this->getType() . 'Template';
$tt = new $tt_class;
if ($tt->getFromDBWithDatas($input[$key])) {
if ($tt->getFromDBWithData($input[$key])) {
if (count($tt->mandatory)) {
$mandatory_missing = [];
$fieldsname = $tt->getAllowedFieldsNames(true);
......@@ -1701,6 +1701,31 @@ abstract class CommonITILObject extends CommonDBTM {
function post_addItem() {
// Add tasks in tasktemplates if defined in itiltemplate
if (isset($this->input['_tasktemplates_id'])
&& is_array($this->input['_tasktemplates_id'])
&& count($this->input['_tasktemplates_id'])) {
$tasktemplate = new TaskTemplate;
$itiltask_class = $this->getType().'Task';
$itiltask = new $itiltask_class;
foreach ($this->input['_tasktemplates_id'] as $tasktemplates_id) {
$tasktemplate->getFromDB($tasktemplates_id);
$tasktemplate_content = Toolbox::addslashes_deep($tasktemplate->fields["content"]);
$itiltask->add([
'tasktemplates_id' => $tasktemplates_id,
'content' => $tasktemplate_content,
'taskcategories_id' => $tasktemplate->fields['taskcategories_id'],
'actiontime' => $tasktemplate->fields['actiontime'],
'state' => $tasktemplate->fields['state'],
$this->getForeignKeyField() => $this->fields['id'],
'is_private' => $tasktemplate->fields['is_private'],
'users_id_tech' => $tasktemplate->fields['users_id_tech'],
'groups_id_tech' => $tasktemplate->fields['groups_id_tech'],
'_disablenotif' => true
]);
}
}
// Add document if needed, without notification
$this->input = $this->addFiles($this->input, ['force_update' => true]);
......@@ -3338,6 +3363,29 @@ abstract class CommonITILObject extends CommonDBTM {
// add objectlock search options
$tab = array_merge($tab, ObjectLock::rawSearchOptionsToAdd(get_class($this)));
// For ITIL template
$tab[] = [
'id' => '142',
'table' => 'glpi_documents',
'field' => 'name',
'name' => _n('Document', 'Documents', Session::getPluralNumber()),
'forcegroupby' => true,
'usehaving' => true,
'nosearch' => true,
'nodisplay' => true,
'datatype' => 'dropdown',
'massiveaction' => false,
'joinparams' => [
'jointype' => 'items_id',
'beforejoin' => [
'table' => 'glpi_documents_items',
'joinparams' => [
'jointype' => 'itemtype_item'
]
]
]
];
return $tab;
}
......@@ -7466,7 +7514,7 @@ abstract class CommonITILObject extends CommonDBTM {
if ($force_template) {
// with type and categ
if ($tt->getFromDBWithDatas($force_template, true)) {
if ($tt->getFromDBWithData($force_template, true)) {
$template_loaded = true;
}
}
......@@ -7481,7 +7529,7 @@ abstract class CommonITILObject extends CommonDBTM {
if (!empty($categ->fields[$field]) && $categ->fields[$field]) {
// without type and categ
if ($tt->getFromDBWithDatas($categ->fields[$field], false)) {
if ($tt->getFromDBWithData($categ->fields[$field], false)) {
$template_loaded = true;
}
}
......@@ -7500,7 +7548,7 @@ abstract class CommonITILObject extends CommonDBTM {
if (isset($_SESSION['glpiactiveprofile']['itiltemplates_id'])
&& $_SESSION['glpiactiveprofile']['itiltemplates_id']) {
// with type and categ
if ($tt->getFromDBWithDatas($_SESSION['glpiactiveprofile']['itiltemplates_id'],
if ($tt->getFromDBWithData($_SESSION['glpiactiveprofile']['itiltemplates_id'],
true)) {
$template_loaded = true;
}
......@@ -7513,7 +7561,7 @@ abstract class CommonITILObject extends CommonDBTM {
// load default entity one if not already loaded
if ($template_id = Entity::getUsedConfig('itiltemplates_id', $entities_id)) {
// with type and categ
if ($tt->getFromDBWithDatas($template_id, true)) {
if ($tt->getFromDBWithData($template_id, true)) {
$template_loaded = true;
}
}
......@@ -7540,7 +7588,7 @@ abstract class CommonITILObject extends CommonDBTM {
if (isset($categ->fields[$field]) && $categ->fields[$field]) {
// without type and categ
if ($tt->getFromDBWithDatas($categ->fields[$field], false)) {
if ($tt->getFromDBWithData($categ->fields[$field], false)) {
$template_loaded = true;
}
}
......
......@@ -298,6 +298,11 @@ abstract class CommonITILTask extends CommonDBTM {
$this->fields["begin"]);
}
if (isset($this->input['_planningrecall'])) {
$this->input['_planningrecall']['items_id'] = $this->fields['id'];
PlanningRecall::manageDatas($this->input['_planningrecall']);
}
$update_done = false;
$itemtype = $this->getItilObjectItemType();
$item = new $itemtype();
......@@ -1422,6 +1427,11 @@ abstract class CommonITILTask extends CommonDBTM {
$this->check(-1, CREATE, $options);
}
//prevent null fields due to getFromDB
if (is_null($this->fields['begin'])) {
$this->fields['begin'] = "";
}
$rand = mt_rand();
$this->showFormHeader($options);
......
......@@ -1648,4 +1648,66 @@ class Document extends CommonDBTM {
);
return ($result ? $context_path : $path);
}
/**
* Give cron information
*
* @param string $name task's name
*
* @return arrray of information
**/
static function cronInfo($name) {
switch ($name) {
case 'cleanorphans' :
return ['description' => __('Clean orphaned documents')];
}
return [];
}
/**
* Cron for clean orphan documents (without Document_Item)
*
* @param Crontask $task Crontask object
*
* @return integer (0 : nothing done - 1 : done)
**/
static function cronCleanOrphans(Crontask $task) {
global $DB;
$dtable = static::getTable();
$ditable = Document_Item::getTable();
//documents tht are nt present in Document_Item are oprhan
$iterator = $DB->request([
'SELECT' => ["$dtable.id"],
'FROM' => $dtable,
'LEFT JOIN' => [
$ditable => [
'ON' => [
$dtable => 'id',
$ditable => 'documents_id'
]
]
],
'WHERE' => [
"$ditable.documents_id" => null
]
]);
$nb = 0;
if (count($iterator)) {
while ($row = $iterator->next()) {
$doc = new Document();
$doc->delete(['id' => $row['id']], true);
++$nb;
}
}
if ($nb) {
$task->addVolume($nb);
$task->log("Documents : $nb");
}
return ($nb > 0 ? 1 : 0);
}
}
......@@ -104,7 +104,7 @@ class Entity extends CommonTreeDropdown {
'inquest_rate', 'inquest_delay',
'inquest_duration','inquest_URL',
'max_closedate', 'tickettemplates_id',
'suppliers_as_private'],
'suppliers_as_private', 'autopurge_delay'],
// Configuration
'config'
=> ['enable_custom_css', 'custom_css_code']];
......@@ -937,6 +937,25 @@ class Entity extends CommonTreeDropdown {
]
];
$tab[] = [
'id' => '59',
'table' => $this->getTable(),
'field' => 'autopurge_delay',
'name' => __('Automatic purge of closed tickets after'),
'massiveaction' => false,
'nosearch' => true,
'datatype' => 'number',
'min' => 1,
'max' => 3650,
'step' => 1,
'unit' => 'day',
'toadd' => [
self::CONFIG_PARENT => __('Inheritance of the parent entity'),
self::CONFIG_NEVER => __('Never'),
0 => __('Immediatly')
]
];
$tab[] = [
'id' => '34',
'table' => $this->getTable(),
......@@ -2384,8 +2403,21 @@ class Entity extends CommonTreeDropdown {
echo "<tr><th colspan='4'>".__('Automatic closing configuration')."</th></tr>";
echo "<tr class='tab_bg_1'>".
"<td colspan='2'>".__('Automatic closing of solved tickets after')."</td>";
echo "<td colspan='2'>";
"<td>".__('Automatic closing of solved tickets after');
//Check if crontask is disabled
$crontask = new CronTask();
$criteria = [
'itemtype' => 'Ticket',
'name' => 'closeticket',
'state' => CronTask::STATE_DISABLE
];
if ($crontask->getFromDBByCrit($criteria)) {
echo "<br/><strong>".__('Close ticket action is disabled.')."</strong>";
}
echo "</td>";
echo "<td>";
$autoclose = [self::CONFIG_PARENT => __('Inheritance of the parent entity'),
self::CONFIG_NEVER => __('Never'),
0 => __('Immediatly')];
......@@ -2414,6 +2446,55 @@ class Entity extends CommonTreeDropdown {
}
echo "</font>";
}
echo "<td>".__('Automatic purge of closed tickets after');
//Check if crontask is disabled
$crontask = new CronTask();
$criteria = [
'itemtype' => 'Ticket',
'name' => 'purgeticket',
'state' => CronTask::STATE_DISABLE
];
if ($crontask->getFromDBByCrit($criteria)) {
echo "<br/><strong>".__('Purge ticket action is disabled.')."</strong>";
}
echo "</td>";
echo "<td>";
$autopurge = [
self::CONFIG_PARENT => __('Inheritance of the parent entity'),
self::CONFIG_NEVER => __('Never')
];
if ($ID == 0) {
unset($autopurge[self::CONFIG_PARENT]);
}
Dropdown::showNumber(
'autopurge_delay',
[
'value' => $entity->fields['autopurge_delay'],
'min' => 1,
'max' => 3650,
'step' => 1,
'toadd' => $autopurge,
'unit' => 'day']);
if (($entity->fields['autopurge_delay'] == self::CONFIG_PARENT)
&& ($ID != 0)) {
$autopurge_mode = self::getUsedConfig(
'autopurge_delay',
$entity->fields['entities_id'],
'',
self::CONFIG_NEVER
);
echo "<br><font class='green'>&nbsp;&nbsp;";
if ($autopurge_mode >= 0) {
printf(_n('%d day', '%d days', $autopurge_mode), $autopurge_mode);
} else {
echo $autopurge[$autopurge_mode];
}
echo "</font>";
}
echo "</td></tr>";
echo "<tr><th colspan='4'>".__('Configuring the satisfaction survey')."</th></tr>";
......
......@@ -233,7 +233,7 @@ class Item_Ticket extends CommonDBRelation{
$opt['templates_id'] = $tt->fields['id'];
}
} else if (isset($options['templates_id'])) {
$tt->getFromDBWithDatas($options['templates_id']);
$tt->getFromDBWithData($options['templates_id']);
if (isset($tt->fields['id'])) {
$opt['templates_id'] = $tt->fields['id'];
}
......
......@@ -1141,9 +1141,9 @@ JAVASCRIPT;
*/
public static function buildParentCondition(
$itemtype,
$target,
$user_table,
$group_table
$target = "",
$user_table = "",
$group_table = ""
) {
// An ITILFollowup parent can only by a CommonItilObject
if (!is_a($itemtype, "CommonITILObject", true)) {
......@@ -1157,10 +1157,6 @@ JAVASCRIPT;
return "(`itemtype` = '$itemtype') ";
}
$requester = CommonITILActor::REQUESTER;
$assign = CommonITILActor::ASSIGN;
$obs = CommonITILActor::OBSERVER;
$user = Session::getLoginUserID();
$groups = "'" . implode("','", $_SESSION['glpigroups']) . "'";
$table = getTableNameForForeignKeyField(
......@@ -1176,73 +1172,7 @@ JAVASCRIPT;
if ($itemtype == "Ticket") {
// Default condition
$condition = "(`itemtype` = '$itemtype' AND (0 = 1 ";
if (Session::haveRight($itemtype, $itemtype::READMY)) {
// Add tickets where the users is requester, observer or recipient
// Subquery for requester/observer user
$user_query = "SELECT `$target`
FROM `$user_table`
WHERE `users_id` = '$user' AND type IN ($requester, $obs)";
$condition .= "OR `items_id` IN ($user_query) ";
// Subquery for recipient
$recipient_query = "SELECT `id`
FROM `$table`
WHERE `users_id_recipient` = '$user'";
$condition .= "OR `items_id` IN ($recipient_query) ";
}
if (Session::haveRight($itemtype, $itemtype::READGROUP)) {
// Add tickets where the users is in a requester or observer group
// Subquery for requester/observer group
$group_query = "SELECT `$target`
FROM `$group_table`
WHERE `groups_id` IN ($groups) AND type IN ($requester, $obs)";
$condition .= "OR `items_id` IN ($group_query) ";
}
if (Session::haveRightsOr($itemtype, [
$itemtype::OWN,
$itemtype::READASSIGN
])) {
// Add tickets where the users is assigned
// Subquery for assigned user
$user_query = "SELECT `$target`
FROM `$user_table`
WHERE `users_id` = '$user' AND type = $assign";
$condition .= "OR `items_id` IN ($user_query) ";
}
if (Session::haveRight($itemtype, $itemtype::READASSIGN)) {
// Add tickets where the users is part of an assigned group
// Subquery for assigned group
$group_query = "SELECT `$target`
FROM `$group_table`
WHERE `groups_id` IN ($groups) AND type = $assign";
$condition .= "OR `items_id` IN ($group_query) ";
if (Session::haveRight('ticket', Ticket::ASSIGN)) {
// Add new tickets
$tickets_query = "SELECT `id`
FROM `$table`
WHERE `status` = '" . CommonITILObject::INCOMING . "'";
$condition .= "OR `items_id` IN ($tickets_query) ";
}
}
if (Session::haveRightsOr('ticketvalidation', [
TicketValidation::VALIDATEINCIDENT,
TicketValidation::VALIDATEREQUEST
])) {
// Add tickets where the users is the validator
// Subquery for validator
$validation_query = "SELECT `$target`
FROM `glpi_ticketvalidations`
WHERE `users_id_validate` = '$user'";
$condition .= "OR `items_id` IN ($validation_query) ";
}
return $condition .= ")) ";
return $condition . Ticket::buildCanViewCondition("items_id") . ")) ";
} else {
if (Session::haveRight($itemtype, $itemtype::READMY)) {
// Subquery for affected/assigned/observer user
......
This diff is collapsed.
......@@ -193,10 +193,14 @@ abstract class ITILTemplateMandatoryField extends ITILTemplateField {
$select_fields = $fields;
foreach ($select_fields as $key => $val) {
if (in_array($key, $simplified_fields)) {
$select_fields[$key] = sprintf(__('%1$s (%2$s)'), $val, $both_interfaces);
if (static::$itiltype == Ticket::getType()) {
if (in_array($key, $simplified_fields)) {
$select_fields[$key] = sprintf(__('%1$s (%2$s)'), $val, $both_interfaces);
} else {
$select_fields[$key] = sprintf(__('%1$s (%2$s)'), $val, __('Standard interface'));
}
} else {
$select_fields[$key] = sprintf(__('%1$s (%2$s)'), $val, __('Standard interface'));
$select_fields[$key] = $val;
}
}
......
......@@ -197,13 +197,10 @@ class ITILTemplatePredefinedField extends ITILTemplateField {
$fields = [
$itil_object->getSearchOptionIDByField('field', 'name', 'glpi_documents'),
$itil_object->getSearchOptionIDByField('field', 'items_id', $itemstable)
$itil_object->getSearchOptionIDByField('field', 'items_id', $itemstable),
$itil_object->getSearchOptionIDByField('field', 'name', 'glpi_tasktemplates')
];
if ($itil_class === Ticket::getType()) {
$fields[] = $itil_object->getSearchOptionIDByField('field', 'name', 'glpi_tasktemplates');
}
return $fields;
}
......
......@@ -295,6 +295,46 @@ class Location extends CommonTreeDropdown {
'datatype' => 'string'
];
$tab[] = [
'id' => '101',
'table' => 'glpi_locations',
'field' => 'address',
'name' => __('Address'),
'datatype' => 'string'
];
$tab[] = [
'id' => '102',
'table' => 'glpi_locations',
'field' => 'postcode',
'name' => __('Postal code'),
'datatype' => 'string'
];
$tab[] = [
'id' => '103',
'table' => 'glpi_locations',
'field' => 'town',
'name' => __('Town'),