mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 21:22:58 +01:00
Fixes for stacked fields
This commit is contained in:
parent
8c2f94a1c0
commit
d5ba3bcf46
@ -11,31 +11,32 @@
|
||||
|
||||
namespace App\Services\Template;
|
||||
|
||||
use App\Models\Quote;
|
||||
use App\Utils\Number;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Design;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Project;
|
||||
use App\Models\PurchaseOrder;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Vendor;
|
||||
use App\Transformers\ProjectTransformer;
|
||||
use App\Transformers\PurchaseOrderTransformer;
|
||||
use App\Transformers\QuoteTransformer;
|
||||
use App\Transformers\TaskTransformer;
|
||||
use App\Utils\HostedPDF\NinjaPdf;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Utils\Number;
|
||||
use League\Fractal\Manager;
|
||||
use App\Models\PurchaseOrder;
|
||||
use App\Utils\VendorHtmlEngine;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Utils\PaymentHtmlEngine;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\HostedPDF\NinjaPdf;
|
||||
use App\Utils\Traits\Pdf\PdfMaker;
|
||||
use App\Utils\VendorHtmlEngine;
|
||||
use League\Fractal\Manager;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
use Twig\Extra\Intl\IntlExtension;
|
||||
use App\Transformers\TaskTransformer;
|
||||
use App\Transformers\QuoteTransformer;
|
||||
use App\Transformers\ProjectTransformer;
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
use App\Transformers\PurchaseOrderTransformer;
|
||||
use League\Fractal\Serializer\ArraySerializer;
|
||||
|
||||
class TemplateService
|
||||
{
|
||||
@ -61,6 +62,8 @@ class TemplateService
|
||||
|
||||
private Payment $payment;
|
||||
|
||||
private CommonMarkConverter $commonmark;
|
||||
|
||||
public function __construct(public ?Design $template = null)
|
||||
{
|
||||
$this->template = $template;
|
||||
@ -74,6 +77,12 @@ class TemplateService
|
||||
*/
|
||||
private function init(): self
|
||||
{
|
||||
|
||||
|
||||
$this->commonmark = new CommonMarkConverter([
|
||||
'allow_unsafe_links' => false,
|
||||
]);
|
||||
|
||||
$this->document = new \DOMDocument();
|
||||
$this->document->validateOnParse = true;
|
||||
|
||||
@ -111,6 +120,7 @@ class TemplateService
|
||||
{
|
||||
$this->compose()
|
||||
->processData($data)
|
||||
->parseGlobalStacks()
|
||||
->parseNinjaBlocks()
|
||||
->processVariables($data)
|
||||
->parseVariables();
|
||||
@ -135,6 +145,7 @@ class TemplateService
|
||||
|
||||
|
||||
$this->parseNinjaBlocks()
|
||||
->parseGlobalStacks()
|
||||
->parseVariables();
|
||||
|
||||
return $this;
|
||||
@ -563,8 +574,6 @@ class TemplateService
|
||||
'refund_activity' => $this->getPaymentRefundActivity($payment),
|
||||
];
|
||||
|
||||
nlog($data);
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
@ -806,7 +815,7 @@ class TemplateService
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
private function parseGlobalStacks(): self
|
||||
public function parseGlobalStacks(): self
|
||||
{
|
||||
$stacks = [
|
||||
'entity-details',
|
||||
@ -857,7 +866,7 @@ class TemplateService
|
||||
})
|
||||
->map(function ($variable) {
|
||||
return ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_details-' . substr($variable, 1)]];
|
||||
});
|
||||
})->toArray();
|
||||
|
||||
|
||||
return $this;
|
||||
@ -901,24 +910,130 @@ class TemplateService
|
||||
{
|
||||
|
||||
|
||||
$elements = [];
|
||||
// $elements = [];
|
||||
|
||||
if (!$this->vendor) {
|
||||
return $elements;
|
||||
// if (!$this->vendor) {
|
||||
// return $elements;
|
||||
// }
|
||||
|
||||
// $variables = $this->context['pdf_variables']['vendor_details'];
|
||||
|
||||
// foreach ($variables as $variable) {
|
||||
// $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'vendor_details-' . substr($variable, 1)]];
|
||||
// }
|
||||
|
||||
// return $elements;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// Dom Traversal
|
||||
///////////////////////////////////////
|
||||
|
||||
public function updateElementProperties(string $element_id, array $elements): self
|
||||
{
|
||||
$node = $this->document->getElementById($element_id);
|
||||
|
||||
$this->createElementContent($node, $elements);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function updateElementProperty($element, string $attribute, ?string $value)
|
||||
{
|
||||
|
||||
if ($attribute == 'hidden' && ($value == false || $value == 'false')) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
$variables = $this->context['pdf_variables']['vendor_details'];
|
||||
$element->setAttribute($attribute, $value);
|
||||
|
||||
foreach ($variables as $variable) {
|
||||
$elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'vendor_details-' . substr($variable, 1)]];
|
||||
if ($element->getAttribute($attribute) === $value) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
return $elements;
|
||||
return $element;
|
||||
|
||||
}
|
||||
|
||||
public function createElementContent($element, $children) :self
|
||||
{
|
||||
foreach ($children as $child) {
|
||||
$contains_html = false;
|
||||
|
||||
if ($child['element'] !== 'script') {
|
||||
if ($this->company->markdown_enabled && array_key_exists('content', $child)) {
|
||||
$child['content'] = str_replace('<br>', "\r", $child['content']);
|
||||
$child['content'] = $this->commonmark->convert($child['content'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($child['content'])) {
|
||||
if (isset($child['is_empty']) && $child['is_empty'] === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$contains_html = preg_match('#(?<=<)\w+(?=[^<]*?>)#', $child['content'], $m) != 0;
|
||||
}
|
||||
|
||||
if ($contains_html) {
|
||||
// If the element contains the HTML, we gonna display it as is. Backend is going to
|
||||
// encode it for us, preventing any errors on the processing stage.
|
||||
// Later, we decode this using Javascript so it looks like it's normal HTML being injected.
|
||||
// To get all elements that need frontend decoding, we use 'data-state' property.
|
||||
|
||||
$_child = $this->document->createElement($child['element'], '');
|
||||
$_child->setAttribute('data-state', 'encoded-html');
|
||||
$_child->nodeValue = htmlspecialchars($child['content']);
|
||||
} else {
|
||||
// .. in case string doesn't contain any HTML, we'll just return
|
||||
// raw $content.
|
||||
|
||||
$_child = $this->document->createElement($child['element'], isset($child['content']) ? htmlspecialchars($child['content']) : '');
|
||||
}
|
||||
|
||||
$element->appendChild($_child);
|
||||
|
||||
if (isset($child['properties'])) {
|
||||
foreach ($child['properties'] as $property => $value) {
|
||||
$this->updateElementProperty($_child, $property, $value);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($child['elements'])) {
|
||||
$this->createElementContent($_child, $child['elements']);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -165,6 +165,8 @@ class TemplateTest extends TestCase
|
||||
</ninja>
|
||||
';
|
||||
|
||||
private string $stack = '<html><div id="company-details"></div></html>';
|
||||
|
||||
protected function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
@ -177,6 +179,23 @@ class TemplateTest extends TestCase
|
||||
|
||||
}
|
||||
|
||||
public function testStackResolution()
|
||||
{
|
||||
|
||||
$partials['design']['includes'] = '';
|
||||
$partials['design']['header'] = '';
|
||||
$partials['design']['body'] = $this->stack;
|
||||
$partials['design']['footer'] = '';
|
||||
|
||||
$ts = new TemplateService();
|
||||
$x = $ts->setTemplate($partials)
|
||||
->setCompany($this->company)
|
||||
->parseGlobalStacks()
|
||||
->getHtml();
|
||||
|
||||
nlog($x);
|
||||
}
|
||||
|
||||
public function testDataMaps()
|
||||
{
|
||||
$start = microtime(true);
|
||||
@ -319,19 +338,8 @@ class TemplateTest extends TestCase
|
||||
];
|
||||
});
|
||||
|
||||
$queries = \DB::getQueryLog();
|
||||
$count = count($queries);
|
||||
|
||||
nlog("query count = {$count}");
|
||||
$x = $invoices->toArray();
|
||||
// nlog(json_encode($x));
|
||||
// nlog(json_encode(htmlspecialchars(json_encode($x), ENT_QUOTES, 'UTF-8')));
|
||||
// nlog($invoices->toJson());
|
||||
|
||||
$this->assertIsArray($invoices->toArray());
|
||||
|
||||
nlog("end invoices = " . microtime(true) - $start);
|
||||
|
||||
}
|
||||
|
||||
private function transformPayment(Payment $payment): array
|
||||
|
Loading…
Reference in New Issue
Block a user