Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Fixed

- Fixed approval comments rendering as raw HTML in PDF
- Fixed PDF crash when exporting Problems with linked items lacking serial/inventory fields
- Fixed Change and Problem description exported as a single unstructured text block
- Fixed Change analysis and plan fields rendering as raw HTML in PDF
- Fixed rich text content rendered as visible HTML tags and unformatted text in PDF exports
- Fixed Change approval comments rendering as visible HTML entities in PDF exports
- Fixed Changes linked items showing phantom empty entries for users with broad entity access
- Fix image rendering in Change/Problem descriptions and Problem task PDF exports

## [4.1.3] - 2026-06-24
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../../PluginsMakefile.mk
29 changes: 10 additions & 19 deletions inc/change.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -310,45 +310,36 @@ public static function pdfAnalysis(PluginPdfSimplePDF $pdf, Change $job)
$pdf->setColumnsSize(100);
$pdf->displayTitle('<b>' . __s('Analysis') . '</b>');

$pdf->setColumnsSize(10, 90);

$pdf->displayText(sprintf(
__s('%1$s: %2$s'),
$pdf->displayText(
'<b><i>' . __s('Impacts') . '</i></b>',
$job->fields['impactcontent'],
));
);

$pdf->displayText(sprintf(
__s('%1$s: %2$s'),
$pdf->displayText(
'<b><i>' . __s('Control list') . '</i></b>',
$job->fields['controlistcontent'],
));
);
}

public static function pdfPlan(PluginPdfSimplePDF $pdf, Change $job)
{
$pdf->setColumnsSize(100);
$pdf->displayTitle('<b>' . __s('Plans') . '</b>');

$pdf->setColumnsSize(10, 90);

$pdf->displayText(sprintf(
__s('%1$s: %2$s'),
$pdf->displayText(
'<b><i>' . __s('Deployment plan') . '</i></b>',
$job->fields['rolloutplancontent'],
));
);

$pdf->displayText(sprintf(
__s('%1$s: %2$s'),
$pdf->displayText(
'<b><i>' . __s('Backup plan') . '</i></b>',
$job->fields['backoutplancontent'],
));
);

$pdf->displayText(sprintf(
__s('%1$s: %2$s'),
$pdf->displayText(
'<b><i>' . __s('Checklist') . '</i></b>',
$job->fields['checklistcontent'],
));
);
}

public static function pdfStat(PluginPdfSimplePDF $pdf, Change $job)
Expand Down
4 changes: 2 additions & 2 deletions inc/change_item.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ public static function pdfForChange(PluginPdfSimplePDF $pdf, Change $change)

$query = ['FIELDS' => [$itemtable . '.*', 'glpi_changes_items.id AS IDD',
'glpi_entities.id AS entity'],
'FROM' => 'glpi_changes_items',
'LEFT JOIN' => [$itemtable => ['FKEY' => [$itemtable => 'id',
'FROM' => 'glpi_changes_items',
'INNER JOIN' => [$itemtable => ['FKEY' => [$itemtable => 'id',
'glpi_changes_items' => 'items_id'],
'glpi_changes_items.itemtype' => $itemtype,
'glpi_changes_items.changes_id' => $instID]]];
Expand Down
4 changes: 2 additions & 2 deletions inc/changevalidation.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ public static function pdfForChange(PluginPdfSimplePDF $pdf, Change $change)
TicketValidation::getStatus($row['status']),
Html::convDateTime($row['submission_date']),
$dbu->getUserName($row['users_id']),
trim($row['comment_submission']),
trim(html_entity_decode($row['comment_submission'] ?? '', ENT_QUOTES, 'UTF-8')),
Html::convDateTime($row['validation_date']),
$dbu->getUserName($row['users_id_validate']),
trim($row['comment_validation']),
trim(html_entity_decode($row['comment_validation'] ?? '', ENT_QUOTES, 'UTF-8')),
);
}
}
Expand Down
3 changes: 1 addition & 2 deletions inc/common.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ public function addStandardTab($itemtype, array &$ong, array $options)
$withtemplate = $options['withtemplate'];
}

// $itemtype has no native type hint; PHPDoc-inferred class-string type isn't enforced at runtime
/** @phpstan-ignore-next-line function.impossibleType */
// @phpstan-ignore-next-line function.impossibleType - is_numeric() kept for parity with CommonGLPI::addStandardTab(), whose $itemtype PHPDoc type makes it always false
if (!is_numeric($itemtype) && ($obj = $dbu->getItemForItemtype($itemtype)) && (method_exists($itemtype, 'displayTabContentForPDF'))) {
$titles = $obj->getTabNameForItem($this->obj, $withtemplate);
if (!is_array($titles)) {
Expand Down
8 changes: 4 additions & 4 deletions inc/item_problem.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,17 +182,17 @@ public static function pdfForProblem(PluginPdfSimplePDF $pdf, Problem $problem)
Toolbox::stripTags(sprintf(__s('%1$s: %2$s'), $typename, $nb)),
Toolbox::stripTags($name),
Dropdown::getDropdownName('glpi_entities', $data['entity']),
Toolbox::stripTags($data['serial']),
Toolbox::stripTags($data['otherserial']),
Toolbox::stripTags($data['serial'] ?? ''),
Toolbox::stripTags($data['otherserial'] ?? ''),
$nb,
);
} else {
$pdf->displayLine(
'',
Toolbox::stripTags($name),
Dropdown::getDropdownName('glpi_entities', $data['entity']),
Toolbox::stripTags($data['serial']),
Toolbox::stripTags($data['otherserial']),
Toolbox::stripTags($data['serial'] ?? ''),
Toolbox::stripTags($data['otherserial'] ?? ''),
$nb,
);
}
Expand Down
6 changes: 4 additions & 2 deletions inc/simplepdf.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,10 @@ public function displayText($name, $content = '', $minline = 3, $maxline = 100)
$save = [$this->cols, $this->colsx, $this->colsw, $this->align];

$this->setColumnsSize(100);
$text = $name . ' ' . $content;
$content = RichText::getEnhancedHtml($text, ['text_maxsize' => 0]);

// Decode $content alone: mixing literal HTML from $name breaks isHtmlEncoded(), leaving GLPI entities (&#60;) un-decoded and visible as text in TCPDF.
$decoded = RichText::getEnhancedHtml($content ?? '', ['text_maxsize' => 0]);
$content = preg_replace('/<script\b[^>]*>.*?<\/script>/is', '', $name . ' ' . $decoded);

// Decode HTML entities BEFORE searching for images
$content = html_entity_decode($content, ENT_QUOTES, 'UTF-8');
Expand Down
4 changes: 2 additions & 2 deletions inc/ticketvalidation.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,15 @@ public static function pdfForTicket(PluginPdfSimplePDF $pdf, Ticket $ticket)
Html::convDateTime($row['validation_date']),
$dbu->getUserName($row['users_id_validate']),
);
$tmp = trim($row['comment_submission']);
$tmp = trim(html_entity_decode($row['comment_submission'] ?? '', ENT_QUOTES, 'UTF-8'));
$pdf->displayText('<b><i>' . sprintf(
__s('%1$s: %2$s'),
__s('Request comments') . '</i></b>',
'',
), (empty($tmp) ? __s('None') : $tmp), 1);

if ($row['validation_date']) {
$tmp = trim($row['comment_validation']);
$tmp = trim(html_entity_decode($row['comment_validation'] ?? '', ENT_QUOTES, 'UTF-8'));
$pdf->displayText(
'<b><i>' . sprintf(
__s('%1$s: %2$s'),
Expand Down