added export context (#2216)

This commit is contained in:
Kevin Papst
2020-12-22 18:41:00 +01:00
committed by GitHub
parent 74b3917e69
commit 40eda01c22
16 changed files with 266 additions and 64 deletions

View File

@@ -8,6 +8,10 @@ you can upgrade your Kimai installation to the latest stable release.
Check below if there are more version specific steps required, which need to be executed after the normal update process. Check below if there are more version specific steps required, which need to be executed after the normal update process.
Perform EACH version specific task between your version and the new one, otherwise you risk data inconsistency or a broken installation. Perform EACH version specific task between your version and the new one, otherwise you risk data inconsistency or a broken installation.
## [1.13](https://github.com/kevinpapst/kimai2/releases/tag/1.13)
- Deprecated `now` variable in export templates: create it yourself with `{% set now = create_date('now', app.user) %}`
## [1.12](https://github.com/kevinpapst/kimai2/releases/tag/1.12) ## [1.12](https://github.com/kevinpapst/kimai2/releases/tag/1.12)
- Export templates can now include items from plugins (eg. Expenses). - Export templates can now include items from plugins (eg. Expenses).

View File

@@ -9,10 +9,10 @@
namespace App\Export\Base; namespace App\Export\Base;
use App\Export\ExportContext;
use App\Export\ExportItemInterface; use App\Export\ExportItemInterface;
use App\Repository\ProjectRepository; use App\Repository\ProjectRepository;
use App\Repository\Query\TimesheetQuery; use App\Repository\Query\TimesheetQuery;
use App\Timesheet\UserDateTimeFactory;
use App\Utils\HtmlToPdfConverter; use App\Utils\HtmlToPdfConverter;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\HttpFoundation\ResponseHeaderBag;
@@ -26,10 +26,6 @@ class PDFRenderer
* @var Environment * @var Environment
*/ */
private $twig; private $twig;
/**
* @var UserDateTimeFactory
*/
private $dateTime;
/** /**
* @var HtmlToPdfConverter * @var HtmlToPdfConverter
*/ */
@@ -46,11 +42,14 @@ class PDFRenderer
* @var string * @var string
*/ */
private $template = 'default.pdf.twig'; private $template = 'default.pdf.twig';
/**
* @var array
*/
private $pdfOptions = [];
public function __construct(Environment $twig, UserDateTimeFactory $dateTime, HtmlToPdfConverter $converter, ProjectRepository $projectRepository) public function __construct(Environment $twig, HtmlToPdfConverter $converter, ProjectRepository $projectRepository)
{ {
$this->twig = $twig; $this->twig = $twig;
$this->dateTime = $dateTime;
$this->converter = $converter; $this->converter = $converter;
$this->projectRepository = $projectRepository; $this->projectRepository = $projectRepository;
} }
@@ -72,6 +71,18 @@ class PDFRenderer
return ['decimal' => $decimal]; return ['decimal' => $decimal];
} }
public function getPdfOptions(): array
{
return $this->pdfOptions;
}
public function setPdfOption(string $key, string $value): PDFRenderer
{
$this->pdfOptions[$key] = $value;
return $this;
}
/** /**
* @param ExportItemInterface[] $timesheets * @param ExportItemInterface[] $timesheets
* @param TimesheetQuery $query * @param TimesheetQuery $query
@@ -82,21 +93,33 @@ class PDFRenderer
*/ */
public function render(array $timesheets, TimesheetQuery $query): Response public function render(array $timesheets, TimesheetQuery $query): Response
{ {
$context = new ExportContext();
$context->setOption('filename', 'kimai-export.pdf');
$summary = $this->calculateSummary($timesheets); $summary = $this->calculateSummary($timesheets);
$content = $this->twig->render($this->getTemplate(), array_merge([ $content = $this->twig->render($this->getTemplate(), array_merge([
'entries' => $timesheets, 'entries' => $timesheets,
'query' => $query, 'query' => $query,
'now' => $this->dateTime->createDateTime(), // @deprecated since 1.13
'now' => new \DateTime('now', new \DateTimeZone(date_default_timezone_get())),
'summaries' => $summary, 'summaries' => $summary,
'budgets' => $this->calculateProjectBudget($timesheets, $query, $this->projectRepository), 'budgets' => $this->calculateProjectBudget($timesheets, $query, $this->projectRepository),
'decimal' => false, 'decimal' => false,
'pdfContext' => $context
], $this->getOptions($query))); ], $this->getOptions($query)));
$content = $this->converter->convertToPdf($content); $pdfOptions = array_merge($context->getOptions(), $this->getPdfOptions());
$content = $this->converter->convertToPdf($content, $pdfOptions);
$response = new Response($content); $response = new Response($content);
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'kimai-export.pdf'); $filename = $context->getOption('filename');
if (empty($filename)) {
$filename = 'kimai-export.pdf';
}
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename);
$response->headers->set('Content-Type', 'application/pdf'); $response->headers->set('Content-Type', 'application/pdf');
$response->headers->set('Content-Disposition', $disposition); $response->headers->set('Content-Disposition', $disposition);

View File

@@ -0,0 +1,37 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Export;
/**
* A simple class that is available in twig renderer context, which can be used to define global renderer options.
*/
final class ExportContext
{
private $options = [];
public function setOption(string $key, string $value): void
{
$this->options[$key] = $value;
}
public function getOptions(): array
{
return $this->options;
}
public function getOption(string $key): ?string
{
if (\array_key_exists($key, $this->options)) {
return $this->options[$key];
}
return null;
}
}

View File

@@ -10,7 +10,6 @@
namespace App\Export\Renderer; namespace App\Export\Renderer;
use App\Repository\ProjectRepository; use App\Repository\ProjectRepository;
use App\Timesheet\UserDateTimeFactory;
use App\Utils\HtmlToPdfConverter; use App\Utils\HtmlToPdfConverter;
use Twig\Environment; use Twig\Environment;
@@ -20,10 +19,6 @@ final class PdfRendererFactory
* @var Environment * @var Environment
*/ */
private $twig; private $twig;
/**
* @var UserDateTimeFactory
*/
private $dateTime;
/** /**
* @var HtmlToPdfConverter * @var HtmlToPdfConverter
*/ */
@@ -33,17 +28,16 @@ final class PdfRendererFactory
*/ */
private $projectRepository; private $projectRepository;
public function __construct(Environment $twig, UserDateTimeFactory $dateTime, HtmlToPdfConverter $converter, ProjectRepository $projectRepository) public function __construct(Environment $twig, HtmlToPdfConverter $converter, ProjectRepository $projectRepository)
{ {
$this->twig = $twig; $this->twig = $twig;
$this->dateTime = $dateTime;
$this->converter = $converter; $this->converter = $converter;
$this->projectRepository = $projectRepository; $this->projectRepository = $projectRepository;
} }
public function create(string $id, string $template): PDFRenderer public function create(string $id, string $template): PDFRenderer
{ {
$renderer = new PDFRenderer($this->twig, $this->dateTime, $this->converter, $this->projectRepository); $renderer = new PDFRenderer($this->twig, $this->converter, $this->projectRepository);
$renderer->setId($id); $renderer->setId($id);
$renderer->setTemplate($template); $renderer->setTemplate($template);

View File

@@ -11,6 +11,7 @@ namespace App\Twig;
use App\Configuration\LanguageFormattings; use App\Configuration\LanguageFormattings;
use App\Constants; use App\Constants;
use App\Entity\User;
use App\Utils\LocaleFormats; use App\Utils\LocaleFormats;
use App\Utils\LocaleFormatter; use App\Utils\LocaleFormatter;
use DateTime; use DateTime;
@@ -89,6 +90,7 @@ class DateExtensions extends AbstractExtension
{ {
return [ return [
new TwigFunction('get_format_duration', [$this, 'getDurationFormat']), new TwigFunction('get_format_duration', [$this, 'getDurationFormat']),
new TwigFunction('create_date', [$this, 'createDate']),
]; ];
} }
@@ -130,6 +132,13 @@ class DateExtensions extends AbstractExtension
return $this->formatter->dateTimeFull($date); return $this->formatter->dateTimeFull($date);
} }
public function createDate(string $date, ?User $user = null): \DateTime
{
$timezone = $user !== null ? $user->getTimezone() : date_default_timezone_get();
return new DateTime($date, new \DateTimeZone($timezone));
}
/** /**
* @param DateTime|string $date * @param DateTime|string $date
* @param string $format * @param string $format

View File

@@ -10,6 +10,8 @@
namespace App\Utils; namespace App\Utils;
use App\Constants; use App\Constants;
use Mpdf\Config\ConfigVariables;
use Mpdf\Config\FontVariables;
use Mpdf\Mpdf; use Mpdf\Mpdf;
use Mpdf\Output\Destination; use Mpdf\Output\Destination;
@@ -25,6 +27,32 @@ class MPdfConverter implements HtmlToPdfConverter
$this->cacheDirectory = $cacheDirectory; $this->cacheDirectory = $cacheDirectory;
} }
protected function sanitizeOptions(array $options): array
{
$configs = new ConfigVariables();
$fonts = new FontVariables();
$allowed = [
'mode', 'format', 'default_font_size', 'default_font', 'margin_left', 'margin_right', 'margin_top',
'margin_bottom', 'margin_header', 'margin_footer', 'orientation'
];
$filtered = array_filter($options, function ($key) use ($allowed, $configs, $fonts) {
if (!\in_array($key, $allowed)) {
if (!\array_key_exists($key, $configs->getDefaults())) {
return \array_key_exists($key, $fonts->getDefaults());
}
}
return true;
}, ARRAY_FILTER_USE_KEY);
if (\array_key_exists('tempDir', $filtered)) {
unset($filtered['tempDir']);
}
return $filtered;
}
/** /**
* @param string $html * @param string $html
* @param array $options * @param array $options
@@ -33,7 +61,11 @@ class MPdfConverter implements HtmlToPdfConverter
*/ */
public function convertToPdf(string $html, array $options = []) public function convertToPdf(string $html, array $options = [])
{ {
$options = array_merge($options, ['tempDir' => $this->cacheDirectory]); $options = array_merge(
$this->sanitizeOptions($options),
['tempDir' => $this->cacheDirectory, 'exposeVersion' => false]
);
$mpdf = new Mpdf($options); $mpdf = new Mpdf($options);
$mpdf->creator = Constants::SOFTWARE; $mpdf->creator = Constants::SOFTWARE;

View File

@@ -116,7 +116,11 @@
<br> <br>
<small>{{ widgets.label_customer(entry.project.customer) }}</small> <small>{{ widgets.label_customer(entry.project.customer) }}</small>
</td> </td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'activity') }}">{{ widgets.label_activity(entry.activity) }}</td> <td class="{{ tables.data_table_column_class(tableName, columns, 'activity') }}">
{% if entry.activity is not null %}
{{ widgets.label_activity(entry.activity) }}
{% endif %}
</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'description') }}"> <td class="{{ tables.data_table_column_class(tableName, columns, 'description') }}">
{{ entry.description|desc2html }} {{ entry.description|desc2html }}
</td> </td>

View File

@@ -3,6 +3,8 @@
{% set showRateColumn = showRateColumn ?? is_granted('view_rate_other_timesheet') %} {% set showRateColumn = showRateColumn ?? is_granted('view_rate_other_timesheet') %}
{% set showRateBudget = showRateBudget ?? false %} {% set showRateBudget = showRateBudget ?? false %}
{% set showTimeBudget = showTimeBudget ?? false %} {% set showTimeBudget = showTimeBudget ?? false %}
{% set showTotalSummary = showTotalSummary ?? true %}
{% set now = create_date('now', app.user) %}
{% set decimal = decimal ?? false %} {% set decimal = decimal ?? false %}
{# this is only triggered, if a user exports from his personal timesheet screen #} {# this is only triggered, if a user exports from his personal timesheet screen #}
{% if query.user is not null %} {% if query.user is not null %}
@@ -12,7 +14,7 @@
{% endif %} {% endif %}
<html lang="{{ app.request.locale }}"> <html lang="{{ app.request.locale }}">
<head> <head>
{% block styles %} {% block styles %}
<style> <style>
body { body {
font-family: sans-serif; font-family: sans-serif;
@@ -70,7 +72,7 @@
white-space: nowrap; white-space: nowrap;
} }
</style> </style>
{% endblock %} {% endblock %}
</head> </head>
<body> <body>
{% block pdf_footer %} {% block pdf_footer %}
@@ -132,29 +134,39 @@ mpdf-->
{% set customerInternalRate = 0 %} {% set customerInternalRate = 0 %}
{% set customerCurrency = null %} {% set customerCurrency = null %}
{% set customerCount = 0 %} {% set customerCount = 0 %}
{% set multiCurrency = false %}
{% set totalDuration = 0 %}
{% set totalInternalRate = 0 %}
{% set totalRate = 0 %}
{% for id, summary in summaries %} {% for id, summary in summaries %}
{% set totalDuration = totalDuration + summary.duration %}
{% set totalInternalRate = totalInternalRate + summary.rate_internal %}
{% set totalRate = totalRate + summary.rate %}
{% if customerCurrency is not null and customerCurrency is not same as(summary.currency) %}
{% set multiCurrency = true %}
{% endif %}
{% if customer is same as(null) %} {% if customer is same as(null) %}
{% set customer = summary.customer %} {% set customer = summary.customer %}
{% set customerCurrency = summary.currency %} {% set customerCurrency = summary.currency %}
{% endif %} {% endif %}
{% if customer is not same as(summary.customer) %} {% if customer is not same as(summary.customer) %}
<tr class="summary"> <tr class="summary">
<td colspan="2"> <td colspan="2">
</td> </td>
{% if showTimeBudget %} {% if showTimeBudget %}
<td></td> <td></td>
{% endif %}
{% if showRateBudget %}
<td></td>
{% endif %}
<td class="totals duration">{{ customerDuration|duration(decimal) }}</td>
{% if showRateColumn %}
{% if showInternalRate %}
<td class="totals cost">{{ customerInternalRate|money(customerCurrency) }}</td>
{% endif %} {% endif %}
{% if showRateBudget %} <td class="totals cost">{{ customerRate|money(customerCurrency) }}</td>
<td></td> {% endif %}
{% endif %} </tr>
<td class="totals duration">{{ customerDuration|duration(decimal) }}</td>
{% if showRateColumn %}
{% if showInternalRate %}
<td class="totals cost">{{ customerInternalRate|money(customerCurrency) }}</td>
{% endif %}
<td class="totals cost">{{ customerRate|money(customerCurrency) }}</td>
{% endif %}
</tr>
{% set customerCurrency = summary.currency %} {% set customerCurrency = summary.currency %}
{% set customer = summary.customer %} {% set customer = summary.customer %}
{% set customerDuration = 0 %} {% set customerDuration = 0 %}
@@ -210,6 +222,26 @@ mpdf-->
{% endif %} {% endif %}
</tr> </tr>
{% endif %} {% endif %}
{% if showTotalSummary and not multiCurrency %}
<tr class="summary-total">
<td class="totals" colspan="2">
{{ 'sum.total'|trans }}
</td>
{% if showTimeBudget %}
<td></td>
{% endif %}
{% if showRateBudget %}
<td></td>
{% endif %}
<td class="totals duration">{{ totalDuration|duration(decimal) }}</td>
{% if showRateColumn %}
{% if showInternalRate %}
<td class="totals cost">{{ totalInternalRate|money(customerCurrency) }}</td>
{% endif %}
<td class="totals cost">{{ totalRate|money(customerCurrency) }}</td>
{% endif %}
</tr>
{% endif %}
</tbody> </tbody>
</table> </table>

View File

@@ -92,14 +92,22 @@ class ExportControllerTest extends ControllerBaseTest
$this->assertDataTableRowCount($client, 'datatable_export', 23); $this->assertDataTableRowCount($client, 'datatable_export', 23);
// assert export type buttons are available // assert export type buttons are available
$expected = ['csv', 'default.html.twig', 'default-budget.pdf.twig', 'default-internal.pdf.twig', 'default.pdf.twig', 'xlsx']; $expected = [
'csv' => 'csv',
'default.html.twig' => 'default.html.twig',
'default-budget.pdf.twig' => 'default-budget.pdf.twig',
'default-internal.pdf.twig' => 'default-internal.pdf.twig',
'default.pdf.twig' => 'default.pdf.twig',
'xlsx' => 'xlsx'
];
$node = $client->getCrawler()->filter('#export-buttons .startExportBtn'); $node = $client->getCrawler()->filter('#export-buttons .startExportBtn');
$this->assertEquals(\count($expected), $node->count()); $this->assertGreaterThanOrEqual(\count($expected), $node->count());
/** @var \DOMElement $button */ /** @var \DOMElement $button */
foreach ($node->getIterator() as $button) { foreach ($node->getIterator() as $button) {
$type = $button->getAttribute('data-type'); $type = $button->getAttribute('data-type');
$this->assertContains($type, $expected); unset($expected[$type]);
} }
$this->assertEmpty($expected);
} }
public function testIndexActionWithEntriesForTeamleadDoesNotShowUserWithoutTeam() public function testIndexActionWithEntriesForTeamleadDoesNotShowUserWithoutTeam()
@@ -144,14 +152,22 @@ class ExportControllerTest extends ControllerBaseTest
$this->assertDataTableRowCount($client, 'datatable_export', 3); $this->assertDataTableRowCount($client, 'datatable_export', 3);
// assert export type buttons are available // assert export type buttons are available
$expected = ['csv', 'default.html.twig', 'default-budget.pdf.twig', 'default-internal.pdf.twig', 'default.pdf.twig', 'xlsx']; $expected = [
'csv' => 'csv',
'default.html.twig' => 'default.html.twig',
'default-budget.pdf.twig' => 'default-budget.pdf.twig',
'default-internal.pdf.twig' => 'default-internal.pdf.twig',
'default.pdf.twig' => 'default.pdf.twig',
'xlsx' => 'xlsx'
];
$node = $client->getCrawler()->filter('#export-buttons .startExportBtn'); $node = $client->getCrawler()->filter('#export-buttons .startExportBtn');
$this->assertEquals(\count($expected), $node->count()); $this->assertGreaterThanOrEqual(\count($expected), $node->count());
/** @var \DOMElement $button */ /** @var \DOMElement $button */
foreach ($node->getIterator() as $button) { foreach ($node->getIterator() as $button) {
$type = $button->getAttribute('data-type'); $type = $button->getAttribute('data-type');
$this->assertContains($type, $expected); unset($expected[$type]);
} }
$this->assertEmpty($expected);
} }
public function testExportActionWithMissingRenderer() public function testExportActionWithMissingRenderer()

View File

@@ -0,0 +1,37 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Export;
use App\Export\ExportContext;
use PHPUnit\Framework\TestCase;
/**
* @covers \App\Export\ExportContext
*/
class ExportContextTest extends TestCase
{
public function testEmptyObject()
{
$sut = new ExportContext();
self::assertIsArray($sut->getOptions());
self::assertEmpty($sut->getOptions());
self::assertNull($sut->getOption('unknown'));
}
public function testSetterAndGetter()
{
$sut = new ExportContext();
self::assertNull($sut->getOption('unknown'));
$sut->setOption('unknown', 'foo');
self::assertEquals('foo', $sut->getOption('unknown'));
}
}

View File

@@ -12,7 +12,6 @@ namespace App\Tests\Export\Renderer;
use App\Export\Renderer\PDFRenderer; use App\Export\Renderer\PDFRenderer;
use App\Export\Renderer\PdfRendererFactory; use App\Export\Renderer\PdfRendererFactory;
use App\Repository\ProjectRepository; use App\Repository\ProjectRepository;
use App\Timesheet\UserDateTimeFactory;
use App\Utils\HtmlToPdfConverter; use App\Utils\HtmlToPdfConverter;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Twig\Environment; use Twig\Environment;
@@ -26,7 +25,6 @@ class PdfRendererFactoryTest extends TestCase
{ {
$sut = new PdfRendererFactory( $sut = new PdfRendererFactory(
$this->createMock(Environment::class), $this->createMock(Environment::class),
$this->createMock(UserDateTimeFactory::class),
$this->createMock(HtmlToPdfConverter::class), $this->createMock(HtmlToPdfConverter::class),
$this->createMock(ProjectRepository::class) $this->createMock(ProjectRepository::class)
); );

View File

@@ -11,7 +11,6 @@ namespace App\Tests\Export\Renderer;
use App\Export\Renderer\PDFRenderer; use App\Export\Renderer\PDFRenderer;
use App\Repository\ProjectRepository; use App\Repository\ProjectRepository;
use App\Tests\Mocks\Security\UserDateTimeFactoryFactory;
use App\Utils\HtmlToPdfConverter; use App\Utils\HtmlToPdfConverter;
use App\Utils\MPdfConverter; use App\Utils\MPdfConverter;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -25,16 +24,10 @@ use Twig\Environment;
*/ */
class PdfRendererTest extends AbstractRendererTest class PdfRendererTest extends AbstractRendererTest
{ {
protected function getDateTimeFactory()
{
return (new UserDateTimeFactoryFactory($this))->create();
}
public function testConfiguration() public function testConfiguration()
{ {
$sut = new PDFRenderer( $sut = new PDFRenderer(
$this->createMock(Environment::class), $this->createMock(Environment::class),
$this->getDateTimeFactory(),
$this->createMock(HtmlToPdfConverter::class), $this->createMock(HtmlToPdfConverter::class),
$this->createMock(ProjectRepository::class) $this->createMock(ProjectRepository::class)
); );
@@ -42,6 +35,11 @@ class PdfRendererTest extends AbstractRendererTest
$this->assertEquals('pdf', $sut->getId()); $this->assertEquals('pdf', $sut->getId());
$this->assertEquals('pdf', $sut->getTitle()); $this->assertEquals('pdf', $sut->getTitle());
$this->assertEquals('pdf', $sut->getIcon()); $this->assertEquals('pdf', $sut->getIcon());
$this->assertEquals([], $sut->getPdfOptions());
$sut->setPdfOption('foo', 'bar');
$sut->setPdfOption('bar1', 'foo1');
$this->assertEquals(['foo' => 'bar', 'bar1' => 'foo1'], $sut->getPdfOptions());
} }
public function testRender() public function testRender()
@@ -56,7 +54,7 @@ class PdfRendererTest extends AbstractRendererTest
$request->setLocale('en'); $request->setLocale('en');
$stack->push($request); $stack->push($request);
$sut = new PDFRenderer($twig, $this->getDateTimeFactory(), $converter, $this->createMock(ProjectRepository::class)); $sut = new PDFRenderer($twig, $converter, $this->createMock(ProjectRepository::class));
$response = $this->render($sut); $response = $this->render($sut);

View File

@@ -11,7 +11,6 @@ namespace App\Tests\Export\Timesheet;
use App\Export\Timesheet\PDFRenderer; use App\Export\Timesheet\PDFRenderer;
use App\Repository\ProjectRepository; use App\Repository\ProjectRepository;
use App\Tests\Mocks\Security\UserDateTimeFactoryFactory;
use App\Utils\HtmlToPdfConverter; use App\Utils\HtmlToPdfConverter;
use App\Utils\MPdfConverter; use App\Utils\MPdfConverter;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -25,16 +24,10 @@ use Twig\Environment;
*/ */
class PdfRendererTest extends AbstractRendererTest class PdfRendererTest extends AbstractRendererTest
{ {
protected function getDateTimeFactory()
{
return (new UserDateTimeFactoryFactory($this))->create();
}
public function testConfiguration() public function testConfiguration()
{ {
$sut = new PDFRenderer( $sut = new PDFRenderer(
$this->createMock(Environment::class), $this->createMock(Environment::class),
$this->getDateTimeFactory(),
$this->createMock(HtmlToPdfConverter::class), $this->createMock(HtmlToPdfConverter::class),
$this->createMock(ProjectRepository::class) $this->createMock(ProjectRepository::class)
); );
@@ -54,7 +47,7 @@ class PdfRendererTest extends AbstractRendererTest
$request->setLocale('en'); $request->setLocale('en');
$stack->push($request); $stack->push($request);
$sut = new PDFRenderer($twig, $this->getDateTimeFactory(), $converter, $this->createMock(ProjectRepository::class)); $sut = new PDFRenderer($twig, $converter, $this->createMock(ProjectRepository::class));
$response = $this->render($sut); $response = $this->render($sut);

View File

@@ -10,6 +10,7 @@
namespace App\Tests\Twig; namespace App\Tests\Twig;
use App\Configuration\LanguageFormattings; use App\Configuration\LanguageFormattings;
use App\Entity\User;
use App\Twig\DateExtensions; use App\Twig\DateExtensions;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -54,7 +55,7 @@ class DateExtensionsTest extends TestCase
public function testGetFunctions() public function testGetFunctions()
{ {
$functions = ['get_format_duration']; $functions = ['get_format_duration', 'create_date'];
$sut = $this->getSut('de', []); $sut = $this->getSut('de', []);
$twigFunctions = $sut->getFunctions(); $twigFunctions = $sut->getFunctions();
$this->assertCount(\count($functions), $twigFunctions); $this->assertCount(\count($functions), $twigFunctions);
@@ -217,4 +218,21 @@ class DateExtensionsTest extends TestCase
/* @phpstan-ignore-next-line */ /* @phpstan-ignore-next-line */
$this->assertEquals(189.45, $sut->dateTimeFull(189.45)); $this->assertEquals(189.45, $sut->dateTimeFull(189.45));
} }
public function testCreateDate()
{
$user = new User();
$user->setTimezone('Europe/Berlin');
$sut = $this->getSut('en', []);
$date = $sut->createDate('now', $user);
$this->assertEquals('Europe/Berlin', $date->getTimezone()->getName());
$user->setTimezone('Asia/Dubai');
$date = $sut->createDate('2019-08-27 16:30:45', $user);
$this->assertEquals('2019-08-27T16:30:45+0400', $date->format(DATE_ISO8601));
$this->assertEquals('Asia/Dubai', $date->getTimezone()->getName());
$date = $sut->createDate('2019-08-27 16:30:45', null);
$this->assertEquals(date_default_timezone_get(), $date->getTimezone()->getName());
}
} }

View File

@@ -80,6 +80,10 @@
<source>rates.title</source> <source>rates.title</source>
<target>Gebühren</target> <target>Gebühren</target>
</trans-unit> </trans-unit>
<trans-unit id="sum.total">
<source>sum.total</source>
<target>Gesamt</target>
</trans-unit>
<!-- <!--
Login / Security Login / Security
--> -->

View File

@@ -80,7 +80,10 @@
<source>rates.title</source> <source>rates.title</source>
<target>Fees</target> <target>Fees</target>
</trans-unit> </trans-unit>
<trans-unit id="sum.total">
<source>sum.total</source>
<target>Total</target>
</trans-unit>
<!-- <!--
Login / Security Login / Security
--> -->