diff --git a/CHANGELOG.md b/CHANGELOG.md index ad7b5ef..8859b81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] + +### 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 + ## [4.1.3] - 2026-06-24 ### Fixed diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ef1bed5 --- /dev/null +++ b/Makefile @@ -0,0 +1 @@ +include ../../PluginsMakefile.mk diff --git a/inc/change.class.php b/inc/change.class.php index f66e41a..1a6d8ac 100644 --- a/inc/change.class.php +++ b/inc/change.class.php @@ -298,7 +298,7 @@ public static function pdfMain(PluginPdfSimplePDF $pdf, Change $job) $pdf->displayText( '' . sprintf(__s('%1$s: %2$s') . '', __s('Description'), ''), - Toolbox::stripTags($job->fields['content']), + $job->fields['content'], 1, ); @@ -310,19 +310,15 @@ public static function pdfAnalysis(PluginPdfSimplePDF $pdf, Change $job) $pdf->setColumnsSize(100); $pdf->displayTitle('' . __s('Analysis') . ''); - $pdf->setColumnsSize(10, 90); - - $pdf->displayText(sprintf( - __s('%1$s: %2$s'), + $pdf->displayText( '' . __s('Impacts') . '', $job->fields['impactcontent'], - )); + ); - $pdf->displayText(sprintf( - __s('%1$s: %2$s'), + $pdf->displayText( '' . __s('Control list') . '', $job->fields['controlistcontent'], - )); + ); } public static function pdfPlan(PluginPdfSimplePDF $pdf, Change $job) @@ -330,25 +326,20 @@ public static function pdfPlan(PluginPdfSimplePDF $pdf, Change $job) $pdf->setColumnsSize(100); $pdf->displayTitle('' . __s('Plans') . ''); - $pdf->setColumnsSize(10, 90); - - $pdf->displayText(sprintf( - __s('%1$s: %2$s'), + $pdf->displayText( '' . __s('Deployment plan') . '', $job->fields['rolloutplancontent'], - )); + ); - $pdf->displayText(sprintf( - __s('%1$s: %2$s'), + $pdf->displayText( '' . __s('Backup plan') . '', $job->fields['backoutplancontent'], - )); + ); - $pdf->displayText(sprintf( - __s('%1$s: %2$s'), + $pdf->displayText( '' . __s('Checklist') . '', $job->fields['checklistcontent'], - )); + ); } public static function pdfStat(PluginPdfSimplePDF $pdf, Change $job) diff --git a/inc/change_item.class.php b/inc/change_item.class.php index e6790b7..dca4a69 100644 --- a/inc/change_item.class.php +++ b/inc/change_item.class.php @@ -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]]]; diff --git a/inc/changevalidation.class.php b/inc/changevalidation.class.php index 6b20670..2a8e713 100644 --- a/inc/changevalidation.class.php +++ b/inc/changevalidation.class.php @@ -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')), ); } } diff --git a/inc/common.class.php b/inc/common.class.php index a883614..528f1ab 100644 --- a/inc/common.class.php +++ b/inc/common.class.php @@ -59,6 +59,7 @@ public function addStandardTab($itemtype, array &$ong, array $options) $withtemplate = $options['withtemplate']; } + // @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)) { diff --git a/inc/item_problem.class.php b/inc/item_problem.class.php index 0a1bdc8..0ceb1fb 100644 --- a/inc/item_problem.class.php +++ b/inc/item_problem.class.php @@ -182,8 +182,8 @@ 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 { @@ -191,8 +191,8 @@ public static function pdfForProblem(PluginPdfSimplePDF $pdf, Problem $problem) '', 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, ); } diff --git a/inc/problem.class.php b/inc/problem.class.php index f2ce73b..2484950 100644 --- a/inc/problem.class.php +++ b/inc/problem.class.php @@ -298,7 +298,7 @@ public static function pdfMain(PluginPdfSimplePDF $pdf, Problem $job) $pdf->displayText( '' . sprintf(__s('%1$s: %2$s'), __s('Description') . '', ''), - Toolbox::stripTags($job->fields['content']), + $job->fields['content'], 1, ); diff --git a/inc/simplepdf.class.php b/inc/simplepdf.class.php index 6b9d322..eb46c07 100644 --- a/inc/simplepdf.class.php +++ b/inc/simplepdf.class.php @@ -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 (<) un-decoded and visible as text in TCPDF. + $decoded = RichText::getEnhancedHtml($content ?? '', ['text_maxsize' => 0]); + $content = preg_replace('/]*>.*?<\/script>/is', '', $name . ' ' . $decoded); // Decode HTML entities BEFORE searching for images $content = html_entity_decode($content, ENT_QUOTES, 'UTF-8'); diff --git a/inc/ticketvalidation.class.php b/inc/ticketvalidation.class.php index 366ba99..848934c 100644 --- a/inc/ticketvalidation.class.php +++ b/inc/ticketvalidation.class.php @@ -80,7 +80,7 @@ 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('' . sprintf( __s('%1$s: %2$s'), __s('Request comments') . '', @@ -88,7 +88,7 @@ public static function pdfForTicket(PluginPdfSimplePDF $pdf, Ticket $ticket) ), (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( '' . sprintf( __s('%1$s: %2$s'),