Adjustments for design and dom elements / attributes
This commit is contained in:
parent
8ce6e6a44e
commit
406cc52279
|
|
@ -55,8 +55,8 @@ class EpcQrGenerator
|
||||||
|
|
||||||
$qr = $writer->writeString($this->encodeMessage(), 'utf-8');
|
$qr = $writer->writeString($this->encodeMessage(), 'utf-8');
|
||||||
|
|
||||||
return "<svg viewBox='0 0 200 200' width='200' height='200' x='0' y='0' xmlns='http://www.w3.org/2000/svg'>
|
return htmlspecialchars("<svg viewBox='0 0 200 200' width='200' height='200' x='0' y='0' xmlns='http://www.w3.org/2000/svg'>
|
||||||
<rect x='0' y='0' width='100%'' height='100%' />{$qr}</svg>";
|
<rect x='0' y='0' width='100%'' height='100%' />{$qr}</svg>");
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
nlog("EPC QR failure => ".$e->getMessage());
|
nlog("EPC QR failure => ".$e->getMessage());
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ class SwissQrGenerator
|
||||||
->setPrintable(false)
|
->setPrintable(false)
|
||||||
->getPaymentPart();
|
->getPaymentPart();
|
||||||
|
|
||||||
return $html;
|
return htmlspecialchars($html);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
||||||
// if (is_iterable($qrBill->getViolations())) {
|
// if (is_iterable($qrBill->getViolations())) {
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,7 @@ class PreviewController extends BaseController
|
||||||
->build();
|
->build();
|
||||||
|
|
||||||
if (request()->query('html') == 'true') {
|
if (request()->query('html') == 'true') {
|
||||||
|
|
||||||
return $maker->getCompiledHTML();
|
return $maker->getCompiledHTML();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ class PdfBuilder
|
||||||
|
|
||||||
private function removeEmptyElements(): self
|
private function removeEmptyElements(): self
|
||||||
{
|
{
|
||||||
|
|
||||||
$elements =[
|
$elements =[
|
||||||
'product-table', 'task-table', 'delivery-note-table',
|
'product-table', 'task-table', 'delivery-note-table',
|
||||||
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
|
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
|
||||||
|
|
@ -96,45 +97,59 @@ class PdfBuilder
|
||||||
|
|
||||||
if ($el && $el->childElementCount === 0) {
|
if ($el && $el->childElementCount === 0) {
|
||||||
$el->parentNode->removeChild($el); // This removes the element completely
|
$el->parentNode->removeChild($el); // This removes the element completely
|
||||||
// $el->setAttribute('style', 'display: none !important;');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// $xpath = new \DOMXPath($this->document);
|
|
||||||
// $elements = $xpath->query('//*[@data-state="encoded-html"]');
|
|
||||||
|
|
||||||
// foreach ($elements as $element) {
|
$xpath = new \DOMXPath($this->document);
|
||||||
|
$elements = $xpath->query('//*[@data-state="encoded-html"]');
|
||||||
|
|
||||||
// // Decode the HTML content
|
foreach ($elements as $element) {
|
||||||
// $html = htmlspecialchars_decode($element->textContent, ENT_QUOTES | ENT_HTML5);
|
|
||||||
// $html = str_ireplace(['<br>'], '<br/>', $html);
|
|
||||||
|
|
||||||
// // Create a temporary document to properly parse the HTML
|
// Decode the HTML content
|
||||||
// $temp = new \DOMDocument();
|
$html = htmlspecialchars_decode($element->textContent, ENT_QUOTES | ENT_HTML5);
|
||||||
|
$html = str_ireplace(['<br>','<?xml encoding="UTF-8">'], ['<br/>',''], $html);
|
||||||
|
|
||||||
// // Add UTF-8 wrapper and div container
|
// Create a temporary document to properly parse the HTML
|
||||||
// $wrappedHtml = '<?xml encoding="UTF-8"><div>' . $html . '</div>';
|
$temp = new \DOMDocument();
|
||||||
|
|
||||||
// // Load the HTML, suppressing any parsing warnings
|
// Add UTF-8 wrapper and div container
|
||||||
// @$temp->loadHTML($wrappedHtml, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
$wrappedHtml = '<?xml encoding="UTF-8"><div>' . $html . '</div>';
|
||||||
|
|
||||||
// // Import the div's contents
|
// Load the HTML, suppressing any parsing warnings
|
||||||
// $imported = $this->document->importNode($temp->getElementsByTagName('div')->item(0), true);
|
@$temp->loadHTML($wrappedHtml, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||||
|
|
||||||
// // Clear existing content of the element
|
// Import the div's contents
|
||||||
// while ($element->firstChild) {
|
$imported = $this->document->importNode($temp->getElementsByTagName('div')->item(0), true);
|
||||||
// $element->removeChild($element->firstChild);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Append the new content to the element
|
// Clear existing content - more efficient
|
||||||
// $element->appendChild($imported);
|
$element->textContent = '';
|
||||||
|
// Get the first div's content
|
||||||
|
$divContent = $temp->getElementsByTagName('div')->item(0);
|
||||||
|
|
||||||
|
if ($divContent) {
|
||||||
|
// Import all nodes from the temporary div
|
||||||
|
foreach ($divContent->childNodes as $child) {
|
||||||
|
$imported = $this->document->importNode($child, true);
|
||||||
|
$element->appendChild($imported);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback - import the entire content if no div found
|
||||||
|
$imported = $this->document->importNode($temp->documentElement, true);
|
||||||
|
$element->appendChild($imported);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Final method to get compiled HTML.
|
* Final method to get compiled HTML.
|
||||||
*
|
*
|
||||||
|
|
@ -1131,7 +1146,7 @@ class PdfBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
if(array_key_exists($column, $column_visibility)){
|
if(array_key_exists($column, $column_visibility)){
|
||||||
return $column_visibility[$column] ? false: true;
|
return !$column_visibility[$column];
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1386,9 +1401,11 @@ class PdfBuilder
|
||||||
|
|
||||||
$elements = [
|
$elements = [
|
||||||
['element' => 'div', 'properties' => ['style' => 'display: flex; flex-direction: column;'], 'elements' => [
|
['element' => 'div', 'properties' => ['style' => 'display: flex; flex-direction: column;'], 'elements' => [
|
||||||
['element' => 'p', 'content' => strtr(str_replace(["labels", "values"], ["",""], $_variables['values']['$entity.public_notes']), $_variables), 'properties' => ['data-ref' => 'total_table-public_notes', 'style' => 'text-align: left;']],
|
['element' => 'div', 'properties' => ['data-ref' => 'total_table-public_notes', 'style' => 'text-align: left;'], 'elements' => [
|
||||||
['element' => 'p', 'content' => '', 'properties' => ['style' => 'text-align: left; display: flex; flex-direction: column; page-break-inside: auto;'], 'elements' => [
|
['element' => 'span', 'content' => strtr(str_replace(["labels", "values"], ["",""], $_variables['values']['$entity.public_notes']), $_variables)]
|
||||||
['element' => 'span', 'content' => '$entity.terms_label: ', 'properties' => ['data-ref' => 'total_table-terms-label', 'style' => "font-weight: bold; text-align: left; margin-top: 1rem; {$show_terms_label}"]],
|
]],
|
||||||
|
['element' => 'div', 'content' => '', 'properties' => ['style' => 'text-align: left; display: flex; flex-direction: column; page-break-inside: auto;'], 'elements' => [
|
||||||
|
['element' => 'span', 'content' => '$entity.terms_label: ', 'properties' => ['data-ref' => 'total_table-terms-label', 'style' => "text-align: left; margin-top: 1rem; {$show_terms_label}"]],
|
||||||
['element' => 'span', 'content' => strtr(str_replace("labels", "", $_variables['values']['$entity.terms']), $_variables['labels']), 'properties' => ['data-ref' => 'total_table-terms', 'style' => 'text-align: left;']],
|
['element' => 'span', 'content' => strtr(str_replace("labels", "", $_variables['values']['$entity.terms']), $_variables['labels']), 'properties' => ['data-ref' => 'total_table-terms', 'style' => 'text-align: left;']],
|
||||||
]],
|
]],
|
||||||
['element' => 'img', 'properties' => ['style' => 'max-width: 50%; height: auto;', 'src' => '$contact.signature', 'id' => 'contact-signature']],
|
['element' => 'img', 'properties' => ['style' => 'max-width: 50%; height: auto;', 'src' => '$contact.signature', 'id' => 'contact-signature']],
|
||||||
|
|
@ -1495,6 +1512,7 @@ class PdfBuilder
|
||||||
['element' => 'span', 'content' => ''],
|
['element' => 'span', 'content' => ''],
|
||||||
]];
|
]];
|
||||||
|
|
||||||
|
|
||||||
return $elements;
|
return $elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2037,11 +2055,15 @@ class PdfBuilder
|
||||||
$html = strtr($this->getCompiledHTML(), $this->service->html_variables['labels']);
|
$html = strtr($this->getCompiledHTML(), $this->service->html_variables['labels']);
|
||||||
$html = strtr($html, $this->service->html_variables['values']);
|
$html = strtr($html, $this->service->html_variables['values']);
|
||||||
|
|
||||||
$html = htmlspecialchars_decode($html, ENT_QUOTES | ENT_HTML5);
|
//old block
|
||||||
$html = str_ireplace(['<br>'], '<br/>', $html);
|
@$this->document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
|
||||||
|
|
||||||
@$this->document->loadHTML('<?xml encoding="UTF-8">'.$html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
//new block
|
||||||
|
// $html = htmlspecialchars_decode($html, ENT_QUOTES | ENT_HTML5);
|
||||||
|
// $html = str_ireplace(['<br>','<?xml encoding="UTF-8">'], ['<br/>',''], $html);
|
||||||
|
// @$this->document->loadHTML('<?xml encoding="UTF-8">'.$html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||||
|
|
||||||
|
//continues
|
||||||
$this->document->saveHTML();
|
$this->document->saveHTML();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,10 @@ class PdfService
|
||||||
public function getPdf()
|
public function getPdf()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$pdf = $this->resolvePdfEngine($this->getHtml());
|
|
||||||
|
$html = $this->getHtml();
|
||||||
|
// nlog($html);
|
||||||
|
$pdf = $this->resolvePdfEngine($html);
|
||||||
|
|
||||||
$numbered_pdf = $this->pageNumbering($pdf, $this->company);
|
$numbered_pdf = $this->pageNumbering($pdf, $this->company);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ class Purify
|
||||||
'polygon', 'g', 'text', 'tspan', 'defs', 'use', 'title',
|
'polygon', 'g', 'text', 'tspan', 'defs', 'use', 'title',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
private static array $allowed_attributes = [
|
private static array $allowed_attributes = [
|
||||||
// Global Attributes
|
// Global Attributes
|
||||||
'class' => ['*'],
|
'class' => ['*'],
|
||||||
|
|
@ -55,7 +56,7 @@ class Purify
|
||||||
'data-state' => ['*'],
|
'data-state' => ['*'],
|
||||||
|
|
||||||
//SVG
|
//SVG
|
||||||
'd' => ['*'],
|
'd' => ['*'],
|
||||||
'viewBox' => ['*'],
|
'viewBox' => ['*'],
|
||||||
'xmlns' => ['http://www.w3.org/2000/svg'],
|
'xmlns' => ['http://www.w3.org/2000/svg'],
|
||||||
'fill' => ['*'],
|
'fill' => ['*'],
|
||||||
|
|
@ -71,7 +72,7 @@ class Purify
|
||||||
'preserveAspectRatio' => ['*'],
|
'preserveAspectRatio' => ['*'],
|
||||||
'version' => ['*'],
|
'version' => ['*'],
|
||||||
'xlink:href' => ['#*'], // Only allow internal references
|
'xlink:href' => ['#*'], // Only allow internal references
|
||||||
|
'fill-rule' => ['nonzero', 'evenodd'],
|
||||||
// Layout & Presentation
|
// Layout & Presentation
|
||||||
'align' => ['left', 'center', 'right', 'justify'],
|
'align' => ['left', 'center', 'right', 'justify'],
|
||||||
'valign' => ['top', 'middle', 'bottom', 'baseline'],
|
'valign' => ['top', 'middle', 'bottom', 'baseline'],
|
||||||
|
|
@ -115,6 +116,8 @@ class Purify
|
||||||
'content' => ['*'],
|
'content' => ['*'],
|
||||||
'http-equiv' => ['*'],
|
'http-equiv' => ['*'],
|
||||||
'viewport' => ['*'],
|
'viewport' => ['*'],
|
||||||
|
'xmlns' => ['http://www.w3.org/2000/svg'],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
private static array $dangerous_css_patterns = [
|
private static array $dangerous_css_patterns = [
|
||||||
|
|
@ -214,6 +217,24 @@ class Purify
|
||||||
return implode('; ', $safe_declarations);
|
return implode('; ', $safe_declarations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static array $dangerous_svg_elements = [
|
||||||
|
'script',
|
||||||
|
'handler',
|
||||||
|
'foreignObject',
|
||||||
|
'annotation-xml',
|
||||||
|
'color-profile',
|
||||||
|
'style', // or carefully sanitize if needed
|
||||||
|
'onload',
|
||||||
|
'onerror',
|
||||||
|
'onunload',
|
||||||
|
'onabort'
|
||||||
|
];
|
||||||
|
|
||||||
|
private static function isDangerousSvgElement(string $tagName): bool
|
||||||
|
{
|
||||||
|
return in_array(strtolower($tagName), self::$dangerous_svg_elements);
|
||||||
|
}
|
||||||
|
|
||||||
public static function clean(string $html): string
|
public static function clean(string $html): string
|
||||||
{
|
{
|
||||||
if(config('ninja.disable_purify_html')){
|
if(config('ninja.disable_purify_html')){
|
||||||
|
|
@ -221,9 +242,9 @@ class Purify
|
||||||
}
|
}
|
||||||
|
|
||||||
$html = str_replace('%24', '$', $html);
|
$html = str_replace('%24', '$', $html);
|
||||||
|
|
||||||
libxml_use_internal_errors(true);
|
libxml_use_internal_errors(true);
|
||||||
libxml_disable_entity_loader(true);
|
libxml_disable_entity_loader(true);
|
||||||
|
// nlog("pre purify => {$html}");
|
||||||
|
|
||||||
$document = new \DOMDocument();
|
$document = new \DOMDocument();
|
||||||
@$document->loadHTML(htmlspecialchars_decode(htmlspecialchars($html, ENT_QUOTES, 'UTF-8')));
|
@$document->loadHTML(htmlspecialchars_decode(htmlspecialchars($html, ENT_QUOTES, 'UTF-8')));
|
||||||
|
|
@ -267,10 +288,24 @@ class Purify
|
||||||
$current_attributes[$attr->name] = $attr->value;
|
$current_attributes[$attr->name] = $attr->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle SVG node separately
|
||||||
|
if ($node->tagName === 'svg') {
|
||||||
|
// Keep only allowed SVG attributes
|
||||||
|
$current_attributes = [];
|
||||||
|
foreach ($node->attributes as $attr) {
|
||||||
|
|
||||||
|
if (in_array($attr->name, self::$dangerous_svg_elements)) {
|
||||||
|
$node->removeAttribute($attr->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
// First, remove ALL attributes from the node
|
// First, remove ALL attributes from the node
|
||||||
while ($node->attributes->length > 0) {
|
while ($node->attributes->length > 0) {
|
||||||
$attr = $node->attributes->item(0);
|
$attr = $node->attributes->item(0);
|
||||||
$node->removeAttribute($attr->nodeName);
|
$node->removeAttribute($attr->nodeName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then add back only the allowed attributes
|
// Then add back only the allowed attributes
|
||||||
|
|
@ -361,15 +396,18 @@ class Purify
|
||||||
|
|
||||||
$html = str_replace('%24', '$', $document->saveHTML());
|
$html = str_replace('%24', '$', $document->saveHTML());
|
||||||
|
|
||||||
|
// nlog("post purify => {$html}");
|
||||||
return $html;
|
return $html;
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
||||||
nlog('Error cleaning HTML: ' . $e->getMessage());
|
nlog('Error cleaning HTML: ' . $e->getMessage());
|
||||||
|
|
||||||
|
libxml_disable_entity_loader(false);
|
||||||
|
libxml_clear_errors();
|
||||||
|
|
||||||
throw new \RuntimeException('HTML sanitization failed');
|
throw new \RuntimeException('HTML sanitization failed');
|
||||||
}finally {
|
} finally {
|
||||||
// Restore original setting
|
|
||||||
libxml_disable_entity_loader(false);
|
libxml_disable_entity_loader(false);
|
||||||
libxml_clear_errors();
|
libxml_clear_errors();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -839,7 +839,6 @@ class Design extends BaseDesign
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$visible_elements = array_filter($elements, function ($element) {
|
$visible_elements = array_filter($elements, function ($element) {
|
||||||
return $element['properties']['visi'] ?? true;
|
return $element['properties']['visi'] ?? true;
|
||||||
});
|
});
|
||||||
|
|
@ -883,7 +882,7 @@ class Design extends BaseDesign
|
||||||
}
|
}
|
||||||
|
|
||||||
if(array_key_exists($column, $column_visibility)){
|
if(array_key_exists($column, $column_visibility)){
|
||||||
return $column_visibility[$column] ? false: true;
|
return !$column_visibility[$column];
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -957,20 +956,6 @@ class Design extends BaseDesign
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Then, filter the elements array
|
|
||||||
$element['elements'] = array_map(function ($el) {
|
|
||||||
if (isset($el['properties']['visi'])) {
|
|
||||||
if ($el['properties']['visi'] === false) {
|
|
||||||
$el['properties']['style'] = 'display: none;';
|
|
||||||
}
|
|
||||||
unset($el['properties']['visi']);
|
|
||||||
}
|
|
||||||
return $el;
|
|
||||||
}, $element['elements']);
|
|
||||||
|
|
||||||
|
|
||||||
$elements[] = $element;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $elements;
|
return $elements;
|
||||||
|
|
@ -1030,7 +1015,15 @@ class Design extends BaseDesign
|
||||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax2-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
|
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax2-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
|
||||||
} elseif ($cell == '$product.tax_rate3') {
|
} elseif ($cell == '$product.tax_rate3') {
|
||||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax3-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
|
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax3-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
|
||||||
} elseif ($cell == '$product.unit_cost' || $cell == '$task.rate') {
|
} elseif ($cell == '$task.discount' && !$this->company->enable_product_discount) {
|
||||||
|
$element['elements'][] = ['element' => 'td', 'content' => $row['$task.discount'], 'properties' => ['data-ref' => 'task_table-task.discount-td', 'style' => 'display: none;']];
|
||||||
|
} elseif ($cell == '$task.tax_rate1') {
|
||||||
|
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'task_table-task.tax1-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
|
||||||
|
} elseif ($cell == '$task.tax_rate2') {
|
||||||
|
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'task_table-task.tax2-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
|
||||||
|
} elseif ($cell == '$task.tax_rate3') {
|
||||||
|
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'task_table-task.tax3-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
|
||||||
|
}elseif ($cell == '$product.unit_cost' || $cell == '$task.rate') {
|
||||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['style' => 'white-space: nowrap;', 'data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
|
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['style' => 'white-space: nowrap;', 'data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
|
||||||
} else {
|
} else {
|
||||||
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
|
$element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td', 'visi' => $this->visibilityCheck($column_visibility, $cell)]];
|
||||||
|
|
@ -1038,7 +1031,19 @@ class Design extends BaseDesign
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Then, filter the elements array
|
||||||
|
$element['elements'] = array_map(function ($el) {
|
||||||
|
if (isset($el['properties']['visi'])) {
|
||||||
|
if ($el['properties']['visi'] === false) {
|
||||||
|
$el['properties']['style'] = 'display: none;';
|
||||||
|
}
|
||||||
|
unset($el['properties']['visi']);
|
||||||
|
}
|
||||||
|
return $el;
|
||||||
|
}, $element['elements']);
|
||||||
|
|
||||||
$elements[] = $element;
|
$elements[] = $element;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$document = null;
|
$document = null;
|
||||||
|
|
@ -1058,10 +1063,11 @@ class Design extends BaseDesign
|
||||||
});
|
});
|
||||||
|
|
||||||
// Transform the items first
|
// Transform the items first
|
||||||
$transformed_items = $this->transformLineItems($filtered_items,
|
$transformed_items = $this->transformLineItems(
|
||||||
|
$filtered_items->toArray(),
|
||||||
$type_id === '1' ? '$product' : '$task'
|
$type_id === '1' ? '$product' : '$task'
|
||||||
);
|
);
|
||||||
|
nlog($transformed_items);
|
||||||
$columns = [];
|
$columns = [];
|
||||||
|
|
||||||
// Initialize all columns as empty
|
// Initialize all columns as empty
|
||||||
|
|
@ -1105,9 +1111,12 @@ class Design extends BaseDesign
|
||||||
$variables = $this->context['pdf_variables']['total_columns'];
|
$variables = $this->context['pdf_variables']['total_columns'];
|
||||||
$show_terms_label = $this->entityVariableCheck('$entity.terms') ? 'display: none;' : '';
|
$show_terms_label = $this->entityVariableCheck('$entity.terms') ? 'display: none;' : '';
|
||||||
|
|
||||||
|
|
||||||
$elements = [
|
$elements = [
|
||||||
['element' => 'div', 'properties' => ['style' => 'display: flex; flex-direction: column;'], 'elements' => [
|
['element' => 'div', 'properties' => ['style' => 'display: flex; flex-direction: column;'], 'elements' => [
|
||||||
['element' => 'p', 'content' => strtr(str_replace(["labels","values"], ["",""], $_variables['values']['$entity.public_notes']), $_variables), 'properties' => ['data-ref' => 'total_table-public_notes', 'style' => 'text-align: left;']],
|
['element' => 'p', 'properties' => ['data-ref' => 'total_table-public_notes', 'style' => 'text-align: left;'], 'elements' => [
|
||||||
|
['element' => 'span', 'content' => strtr(str_replace(["labels", "values"], ["",""], $_variables['values']['$entity.public_notes']), $_variables)]
|
||||||
|
]],
|
||||||
['element' => 'p', 'content' => '', 'properties' => ['style' => 'text-align: left; display: flex; flex-direction: column; page-break-inside: auto;'], 'elements' => [
|
['element' => 'p', 'content' => '', 'properties' => ['style' => 'text-align: left; display: flex; flex-direction: column; page-break-inside: auto;'], 'elements' => [
|
||||||
['element' => 'span', 'content' => '$entity.terms_label: ', 'properties' => ['data-ref' => 'total_table-terms-label', 'style' => "font-weight: bold; text-align: left; margin-top: 1rem; {$show_terms_label}"]],
|
['element' => 'span', 'content' => '$entity.terms_label: ', 'properties' => ['data-ref' => 'total_table-terms-label', 'style' => "font-weight: bold; text-align: left; margin-top: 1rem; {$show_terms_label}"]],
|
||||||
['element' => 'span', 'content' => strtr(str_replace("labels", "", $_variables['values']['$entity.terms']), $_variables['labels']), 'properties' => ['data-ref' => 'total_table-terms', 'style' => 'text-align: left;']],
|
['element' => 'span', 'content' => strtr(str_replace("labels", "", $_variables['values']['$entity.terms']), $_variables['labels']), 'properties' => ['data-ref' => 'total_table-terms', 'style' => 'text-align: left;']],
|
||||||
|
|
@ -1192,9 +1201,6 @@ class Design extends BaseDesign
|
||||||
}
|
}
|
||||||
} elseif (Str::startsWith($variable, '$custom_surcharge')) {
|
} elseif (Str::startsWith($variable, '$custom_surcharge')) {
|
||||||
$_variable = ltrim($variable, '$'); // $custom_surcharge1 -> custom_surcharge1
|
$_variable = ltrim($variable, '$'); // $custom_surcharge1 -> custom_surcharge1
|
||||||
|
|
||||||
//07/09/2023 don't show custom values if they are empty
|
|
||||||
// $visible = intval($this->entity->{$_variable}) != 0;
|
|
||||||
$visible = intval(str_replace(['0','.'], '', $this->entity->{$_variable})) != 0;
|
$visible = intval(str_replace(['0','.'], '', $this->entity->{$_variable})) != 0;
|
||||||
|
|
||||||
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [
|
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ class PdfMaker
|
||||||
|
|
||||||
$ts = new TemplateService();
|
$ts = new TemplateService();
|
||||||
|
|
||||||
if (isset($this->options['client'])) {
|
if (isset($this->options['client']) && !empty($this->options['client'])) {
|
||||||
$client = $this->options['client'];
|
$client = $this->options['client'];
|
||||||
try {
|
try {
|
||||||
$ts->setCompany($client->company);
|
$ts->setCompany($client->company);
|
||||||
|
|
@ -85,7 +85,7 @@ class PdfMaker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->options['vendor'])) {
|
if (isset($this->options['vendor']) && !empty($this->options['vendor'])) {
|
||||||
$vendor = $this->options['vendor'];
|
$vendor = $this->options['vendor'];
|
||||||
try {
|
try {
|
||||||
$ts->setCompany($vendor->company);
|
$ts->setCompany($vendor->company);
|
||||||
|
|
@ -139,40 +139,48 @@ class PdfMaker
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// $xpath = new \DOMXPath($this->document);
|
$xpath = new \DOMXPath($this->document);
|
||||||
// $elements = $xpath->query('//*[@data-state="encoded-html"]');
|
$elements = $xpath->query('//*[@data-state="encoded-html"]');
|
||||||
|
|
||||||
// foreach ($elements as $element) {
|
foreach ($elements as $element) {
|
||||||
|
|
||||||
|
|
||||||
// // Decode the HTML content
|
// Decode the HTML content
|
||||||
// $html = htmlspecialchars_decode($element->textContent, ENT_QUOTES | ENT_HTML5);
|
$html = htmlspecialchars_decode($element->textContent, ENT_QUOTES | ENT_HTML5);
|
||||||
// $html = str_ireplace(['<br>'], '<br/>', $html);
|
$html = str_ireplace(['<br>','<?xml encoding="UTF-8">'], ['<br/>',''], $html);
|
||||||
|
|
||||||
// // Create a temporary document to properly parse the HTML
|
// Create a temporary document to properly parse the HTML
|
||||||
// $temp = new \DOMDocument();
|
$temp = new \DOMDocument();
|
||||||
|
|
||||||
// // Add UTF-8 wrapper and div container
|
// Add UTF-8 wrapper and div container
|
||||||
// $wrappedHtml = '<?xml encoding="UTF-8"><div>' . $html . '</div>';
|
$wrappedHtml = '<?xml encoding="UTF-8"><div>' . $html . '</div>';
|
||||||
|
|
||||||
// // Load the HTML, suppressing any parsing warnings
|
// Load the HTML, suppressing any parsing warnings
|
||||||
// @$temp->loadHTML($wrappedHtml, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
@$temp->loadHTML($wrappedHtml, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||||
|
|
||||||
// // Import the div's contents
|
// Import the div's contents
|
||||||
// $imported = $this->document->importNode($temp->getElementsByTagName('div')->item(0), true);
|
$imported = $this->document->importNode($temp->getElementsByTagName('div')->item(0), true);
|
||||||
|
|
||||||
// // Clear existing content of the element
|
// Clear existing content - more efficient
|
||||||
// while ($element->firstChild) {
|
$element->textContent = '';
|
||||||
// $element->removeChild($element->firstChild);
|
// Get the first div's content
|
||||||
// }
|
$divContent = $temp->getElementsByTagName('div')->item(0);
|
||||||
|
|
||||||
// // Append the new content to the element
|
if ($divContent) {
|
||||||
// $element->appendChild($imported);
|
// Import all nodes from the temporary div
|
||||||
|
foreach ($divContent->childNodes as $child) {
|
||||||
// }
|
$imported = $this->document->importNode($child, true);
|
||||||
|
$element->appendChild($imported);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback - import the entire content if no div found
|
||||||
|
$imported = $this->document->importNode($temp->documentElement, true);
|
||||||
|
$element->appendChild($imported);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -143,13 +143,15 @@ trait PdfMakerUtilities
|
||||||
|
|
||||||
$html = strtr($html, $variables['values']);
|
$html = strtr($html, $variables['values']);
|
||||||
|
|
||||||
// @$this->document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
|
//old block
|
||||||
|
@$this->document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
|
||||||
|
|
||||||
$html = htmlspecialchars_decode($html, ENT_QUOTES | ENT_HTML5);
|
//new block
|
||||||
$html = str_ireplace(['<br>'], '<br/>', $html);
|
// $html = htmlspecialchars_decode($html, ENT_QUOTES | ENT_HTML5);
|
||||||
|
// $html = str_ireplace(['<br>'], '<br/>', $html);
|
||||||
@$this->document->loadHTML('<?xml encoding="UTF-8">'.$html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
// @$this->document->loadHTML('<?xml encoding="UTF-8">'.$html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||||
|
|
||||||
|
//continues
|
||||||
$this->document->saveHTML();
|
$this->document->saveHTML();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,6 @@ trait PdfMaker
|
||||||
|
|
||||||
$html = str_ireplace(['file:/', 'iframe', '<embed', '<embed', '<object', '<object', '127.0.0.1', 'localhost', '<?xml encoding="UTF-8">'], '', $html);
|
$html = str_ireplace(['file:/', 'iframe', '<embed', '<embed', '<object', '<object', '127.0.0.1', 'localhost', '<?xml encoding="UTF-8">'], '', $html);
|
||||||
|
|
||||||
// nlog($html);
|
|
||||||
$generated = $pdf
|
$generated = $pdf
|
||||||
->setHtml($html)
|
->setHtml($html)
|
||||||
->generate();
|
->generate();
|
||||||
|
|
|
||||||
|
|
@ -414,7 +414,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#qr-bill{
|
#qr-bill{
|
||||||
width:100%;
|
width:100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Useful snippets, uncomment to enable. **/
|
/** Useful snippets, uncomment to enable. **/
|
||||||
|
|
|
||||||
|
|
@ -348,7 +348,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#qr-bill{
|
#qr-bill{
|
||||||
width:100%;
|
width:100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -348,6 +348,10 @@
|
||||||
|
|
||||||
.pqrcode {}
|
.pqrcode {}
|
||||||
|
|
||||||
|
#qr-bill{
|
||||||
|
width:100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
/** Useful snippets, uncomment to enable. **/
|
/** Useful snippets, uncomment to enable. **/
|
||||||
|
|
||||||
/** Hide company logo **/
|
/** Hide company logo **/
|
||||||
|
|
|
||||||
|
|
@ -37,17 +37,6 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#qr-bill {
|
|
||||||
width:100% !important;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-collapse: collapse;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#qr-bill td {
|
|
||||||
max-width: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-container {
|
.header-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
|
@ -360,7 +349,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#qr-bill{
|
#qr-bill{
|
||||||
width:100%;
|
width:100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Useful snippets, uncomment to enable. **/
|
/** Useful snippets, uncomment to enable. **/
|
||||||
|
|
|
||||||
|
|
@ -311,7 +311,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#qr-bill{
|
#qr-bill{
|
||||||
width:100%;
|
width:100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Useful snippets, uncomment to enable. **/
|
/** Useful snippets, uncomment to enable. **/
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#qr-bill{
|
#qr-bill{
|
||||||
width:100%;
|
width:100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Useful snippets, uncomment to enable. **/
|
/** Useful snippets, uncomment to enable. **/
|
||||||
|
|
|
||||||
|
|
@ -392,7 +392,7 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
#qr-bill{
|
#qr-bill{
|
||||||
width:100%;
|
width:100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Useful snippets, uncomment to enable. **/
|
/** Useful snippets, uncomment to enable. **/
|
||||||
|
|
|
||||||
|
|
@ -395,7 +395,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#qr-bill{
|
#qr-bill{
|
||||||
width:100%;
|
width:100% !important;
|
||||||
}
|
}
|
||||||
/** Useful snippets, uncomment to enable. **/
|
/** Useful snippets, uncomment to enable. **/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -354,7 +354,7 @@
|
||||||
|
|
||||||
|
|
||||||
#qr-bill{
|
#qr-bill{
|
||||||
width:100%;
|
width:100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -383,6 +383,10 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#qr-bill{
|
||||||
|
width:100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
/** Useful snippets, uncomment to enable. **/
|
/** Useful snippets, uncomment to enable. **/
|
||||||
|
|
||||||
/** Hide company logo **/
|
/** Hide company logo **/
|
||||||
|
|
|
||||||
|
|
@ -387,8 +387,8 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#qr-bill{
|
#qr-bill{
|
||||||
width:100%;
|
width:100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Useful snippets, uncomment to enable. **/
|
/** Useful snippets, uncomment to enable. **/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue