src/CoreBundle/Twig/Extension/GlobalExtension.php line 59

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of SolidInvoice project.
  5.  *
  6.  * (c) Pierre du Plessis <open-source@solidworx.co>
  7.  *
  8.  * This source file is subject to the MIT license that is bundled
  9.  * with this source code in the file LICENSE.
  10.  */
  11. namespace SolidInvoice\CoreBundle\Twig\Extension;
  12. use Carbon\Carbon;
  13. use DateTime;
  14. use SolidInvoice\CoreBundle\Company\CompanySelector;
  15. use SolidInvoice\CoreBundle\Pdf\Generator;
  16. use SolidInvoice\CoreBundle\SolidInvoiceCoreBundle;
  17. use SolidInvoice\MoneyBundle\Calculator;
  18. use SolidInvoice\SettingsBundle\SystemConfig;
  19. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  20. use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
  21. use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
  22. use Symfony\Component\HttpFoundation\Request;
  23. use Symfony\Component\HttpFoundation\RequestStack;
  24. use Symfony\Component\Security\Core\Security;
  25. use Symfony\Component\Security\Core\User\UserInterface;
  26. use Twig\Environment;
  27. use Twig\Error\LoaderError;
  28. use Twig\Error\SyntaxError;
  29. use Twig\Extension\AbstractExtension;
  30. use Twig\Extension\GlobalsInterface;
  31. use Twig\TwigFilter;
  32. use Twig\TwigFunction;
  33. use function implode;
  34. class GlobalExtension extends AbstractExtension implements GlobalsInterface
  35. {
  36.     private const DEFAULT_LOGO 'png|iVBORw0KGgoAAAANSUhEUgAAADIAAAAoCAYAAAC8cqlMAAAABGdBTUEAALGPC/xhBQAACjppQ0NQUGhvdG9zaG9wIElDQyBwcm9maWxlAABIiZ2Wd1RU1xaHz713eqHNMBQpQ++9DSC9N6nSRGGYGWAoAw4zNLEhogIRRUQEFUGCIgaMhiKxIoqFgGDBHpAgoMRgFFFReTOyVnTl5b2Xl98fZ31rn733PWfvfda6AJC8/bm8dFgKgDSegB/i5UqPjIqmY/sBDPAAA8wAYLIyMwJCPcOASD4ebvRMkRP4IgiAN3fEKwA3jbyD6HTw/0malcEXiNIEidiCzclkibhQxKnZggyxfUbE1PgUMcMoMfNFBxSxvJgTF9nws88iO4uZncZji1h85gx2GlvMPSLemiXkiBjxF3FRFpeTLeJbItZMFaZxRfxWHJvGYWYCgCKJ7QIOK0nEpiIm8cNC3ES8FAAcKfErjv+KBZwcgfhSbukZuXxuYpKArsvSo5vZ2jLo3pzsVI5AYBTEZKUw+Wy6W3paBpOXC8DinT9LRlxbuqjI1ma21tZG5sZmXxXqv27+TYl7u0ivgj/3DKL1fbH9lV96PQCMWVFtdnyxxe8FoGMzAPL3v9g0DwIgKepb+8BX96GJ5yVJIMiwMzHJzs425nJYxuKC/qH/6fA39NX3jMXp/igP3Z2TwBSmCujiurHSU9OFfHpmBpPFoRv9eYj/ceBfn8MwhJPA4XN4oohw0ZRxeYmidvPYXAE3nUfn8v5TE/9h2J+0ONciURo+AWqsMZAaoALk1z6AohABEnNAtAP90Td/fDgQv7wI1YnFuf8s6N+zwmXiJZOb+DnOLSSMzhLysxb3xM8SoAEBSAIqUAAqQAPoAiNgDmyAPXAGHsAXBIIwEAVWARZIAmmAD7JBPtgIikAJ2AF2g2pQCxpAE2gBJ0AHOA0ugMvgOrgBboMHYASMg+dgBrwB8xAEYSEyRIEUIFVICzKAzCEG5Ah5QP5QCBQFxUGJEA8SQvnQJqgEKoeqoTqoCfoeOgVdgK5Cg9A9aBSagn6H3sMITIKpsDKsDZvADNgF9oPD4JVwIrwazoML4e1wFVwPH4Pb4Qvwdfg2PAI/h2cRgBARGqKGGCEMxA0JRKKRBISPrEOKkUqkHmlBupBe5CYygkwj71AYFAVFRxmh7FHeqOUoFmo1ah2qFFWNOoJqR/WgbqJGUTOoT2gyWgltgLZD+6Aj0YnobHQRuhLdiG5DX0LfRo+j32AwGBpGB2OD8cZEYZIxazClmP2YVsx5zCBmDDOLxWIVsAZYB2wglokVYIuwe7HHsOewQ9hx7FscEaeKM8d54qJxPFwBrhJ3FHcWN4SbwM3jpfBaeDt8IJ6Nz8WX4RvwXfgB/Dh+niBN0CE4EMIIyYSNhCpCC+ES4SHhFZFIVCfaEoOJXOIGYhXxOPEKcZT4jiRD0ie5kWJIQtJ20mHSedI90isymaxNdiZHkwXk7eQm8kXyY/JbCYqEsYSPBFtivUSNRLvEkMQLSbyklqSL5CrJPMlKyZOSA5LTUngpbSk3KabUOqkaqVNSw1Kz0hRpM+lA6TTpUumj0lelJ2WwMtoyHjJsmUKZQzIXZcYoCEWD4kZhUTZRGiiXKONUDFWH6kNNppZQv6P2U2dkZWQtZcNlc2RrZM/IjtAQmjbNh5ZKK6OdoN2hvZdTlnOR48htk2uRG5Kbk18i7yzPkS+Wb5W/Lf9ega7goZCisFOhQ+GRIkpRXzFYMVvxgOIlxekl1CX2S1hLipecWHJfCVbSVwpRWqN0SKlPaVZZRdlLOUN5r/JF5WkVmoqzSrJKhcpZlSlViqqjKle1QvWc6jO6LN2FnkqvovfQZ9SU1LzVhGp1av1q8+o66svVC9Rb1R9pEDQYGgkaFRrdGjOaqpoBmvmazZr3tfBaDK0krT1avVpz2jraEdpbtDu0J3XkdXx08nSadR7qknWddFfr1uve0sPoMfRS9Pbr3dCH9a30k/Rr9AcMYANrA67BfoNBQ7ShrSHPsN5w2Ihk5GKUZdRsNGpMM/Y3LjDuMH5homkSbbLTpNfkk6mVaappg+kDMxkzX7MCsy6z3831zVnmNea3LMgWnhbrLTotXloaWHIsD1jetaJYBVhtseq2+mhtY823brGestG0ibPZZzPMoDKCGKWMK7ZoW1fb9banbd/ZWdsJ7E7Y/WZvZJ9if9R+cqnOUs7ShqVjDuoOTIc6hxFHumOc40HHESc1J6ZTvdMTZw1ntnOj84SLnkuyyzGXF66mrnzXNtc5Nzu3tW7n3RF3L/di934PGY/lHtUejz3VPRM9mz1nvKy81nid90Z7+3nv9B72UfZh+TT5zPja+K717fEj+YX6Vfs98df35/t3BcABvgG7Ah4u01rGW9YRCAJ9AncFPgrSCVod9GMwJjgouCb4aYhZSH5IbyglNDb0aOibMNewsrAHy3WXC5d3h0uGx4Q3hc9FuEeUR4xEmkSujbwepRjFjeqMxkaHRzdGz67wWLF7xXiMVUxRzJ2VOitzVl5dpbgqddWZWMlYZuzJOHRcRNzRuA/MQGY9czbeJ35f/AzLjbWH9ZztzK5gT3EcOOWciQSHhPKEyUSHxF2JU0lOSZVJ01w3bjX3ZbJ3cm3yXEpgyuGUhdSI1NY0XFpc2imeDC+F15Oukp6TPphhkFGUMbLabvXu1TN8P35jJpS5MrNTQBX9TPUJdYWbhaNZjlk1WW+zw7NP5kjn8HL6cvVzt+VO5HnmfbsGtYa1pjtfLX9j/uhal7V166B18eu612usL1w/vsFrw5GNhI0pG38qMC0oL3i9KWJTV6Fy4YbCsc1em5uLJIr4RcNb7LfUbkVt5W7t32axbe+2T8Xs4mslpiWVJR9KWaXXvjH7puqbhe0J2/vLrMsO7MDs4O24s9Np55Fy6fK88rFdAbvaK+gVxRWvd8fuvlppWVm7h7BHuGekyr+qc6/m3h17P1QnVd+uca1p3ae0b9u+uf3s/UMHnA+01CrXltS+P8g9eLfOq669Xru+8hDmUNahpw3hDb3fMr5talRsLGn8eJh3eORIyJGeJpumpqNKR8ua4WZh89SxmGM3vnP/rrPFqKWuldZachwcFx5/9n3c93dO+J3oPsk42fKD1g/72ihtxe1Qe277TEdSx0hnVOfgKd9T3V32XW0/Gv94+LTa6ZozsmfKzhLOFp5dOJd3bvZ8xvnpC4kXxrpjux9cjLx4qye4p/+S36Urlz0vX+x16T13xeHK6at2V09dY1zruG59vb3Pqq/tJ6uf2vqt+9sHbAY6b9je6BpcOnh2yGnowk33m5dv+dy6fnvZ7cE7y+/cHY4ZHrnLvjt5L/Xey/tZ9+cfbHiIflj8SOpR5WOlx/U/6/3cOmI9cmbUfbTvSeiTB2Ossee/ZP7yYbzwKflp5YTqRNOk+eTpKc+pG89WPBt/nvF8frroV+lf973QffHDb86/9c1Ezoy/5L9c+L30lcKrw68tX3fPBs0+fpP2Zn6u+K3C2yPvGO9630e8n5jP/oD9UPVR72PXJ79PDxfSFhb+BQOY8/wldxZ1AAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfkBRYJDBPbSUFaAAAGOElEQVRYw92YW2wc1RnHf9/M7HqN7dghJpjmhqHJJo7jS2JaFQomghgeEFRIWwkQlXigBbVVH5BAQhCQQYAE4oUKkT5ULX2pukIKIgiFcBc0RbRWQxw7NkS4iRPixI4XfNnL7MzHw46dcbz27uw6BvUvze7ZmTNn9jff5XznQEBdfWjbbHvNwU1zrm34dGvQ4dB4/nZQSak3XvXfbY0oXepqSl0+G7qmtw9g3SdNnLiur2gIiRU+V4yMIJ03HmnJWeXzlk4R9gGvAH9BeXP9v7Y+CXDiuj7WftxUrCXEa/9a4/xN49wssdy1oNYp2iKb+lsZ3HKIjUdadqjyibpaoQ6oq6gDrquoowddlxtPXt+XWfPRFk7e0F/QGhrnJeB33uks8CeJ8dug1ikKJDqwnYFoD5v6WxtVGcRVy/UALgBBlROu4157qvPocCF30jiPAM/l6fYP4F6JkSkWpqBrbf6ijYFoD9HB9nqEtwSsAresE5F317y/pQnginc2L2SRncATC4zxS+AljRNeUtdqGmqznDQvo3q/ujkrLGIR1FFw6VVXd5266ejphrejnO4a8EOsAl4DOgs8+mmJ8bjGEYmhZQe7a7NLhPsCpVW0WZUDAKe7Brh8f3QGQoDbi4AAeEzjxCSGFgr+BUG2DrUDsOVY+wox5BmkoEvlU3PD29H9ACO3zFqkCng6wBh/1zgbZ7JZyRYR4UGgrfSpiq7L90f9Af0g8KOAU8RfNU71YkGfN0aaj2+nd30PW4fa1wPH1MGajYEiY0T9111SGeXuc4mBfViMe1YJ5qnwsMR4YaEsZhQwx8tQkktdqEha5dmjoYrnS4CYeeEPa5zGol1r2/AOetf30Hx8+88QbmSJZKHRF0Mr/1DGEJcBjywUK/NADq/9D6oKBg+U+PbymwTlVbOWPxu15QzzG42zLR9MXtdqOdXRAtzMEkqBGnXptlbRJ+FyhtpTlGu1fr3DALqQQJmlaE1i8LhRv/jstrh+qnFuuDDgjTxvrkqMYJNfUB2USl6TmlJvN4DfF7SIiLQDTRcTZFIMXqGWRLBVhF8dGmddXpDWMx0zie6hMtZbRSmkyj+lkr1UlzrEKoSOvCCHVv+b1tGOCq8OuugKozyq9TilvDSlBpuN80DaRq/xqkPuucjG8D1YGcfgKV2ZcwAJtvB2vjabxxqqInNAjPN11V0soyIoe5xazoyE4IygE4LaMjODQsj3HcqBalLI/s8k87m5IdzmVJ+fcIGe+s/oGP9JOJvVVspJjCUohfDiaB3PHU6QqhQIgZgKJogFmJ43ZQEbNCO4kwIZVCLqzgEBcBxtAyIss1TgQEOl/UD/VGb1hFOlxa33zmHwTPXe9Ll5IEDUM+Cya2CFlWm8d21M7x48MLWzYjNwJXAFcClwiUeWBEaAI9V7058CTP6iguq96Xkga5ao0g0sU6mqGbfvkDebD/Dz3l6gt+Bc5IOYOyEKYViunJW3Fuus/mrqsqpXryyqvx9iDoibciLfJwiwGaif+tVQyXULAKMfDK/OfpM2EPn+cFQ7yinAAJg4PBoe2fcVk0dGcZNZDMtAjGUmsrPXlw1ihE3DHksy9v4wI68fY/zjU9jjKSRsIpYgcpGhRJDp9G0AdbuDJ09zdpbtNDOI3AlYbjJLZmSaqf4xpgcTuGkHI2IhFabPC7zVknfM/mbu+XnX8x3el3n2m6rKa90/Jp7KJksCqdsdItFtD0Y6zXeA7UDtzJziphzSJyeY7B0j+eU42bEkznQ2t5viqLerqOchJPchCqqCv1KY+1tyTVfBdpGz32JMTIPIG6kP3eOl7E7gg5lpx4DbvN3ADf4cqXr+j0uFiXlJCIlYORcMW2CZYAliGKjnjorMbiOp4+bgsy6ayaKpLJJMQ9qGXP+diW77g5JB8sCEgbVeWrzDK+8b8i7G/f4hOX/HkJzfi6/brNt5FnTd2fjwaUWi254oC8QfaD4gAEl021q3O9QI3Ars8qx16RKGexa4M9Ftv1FSriim04VwvvMrgVag2avVfuxth4a9I+TFoel7lgIOkAHSwBjwHrAn0W0P+71iyUHygS32sLrdoYiXMGq8oq/ClyEdrwD8Fjib6LaT/NBUSv4v577/S30HjQm5qmWrOHsAAAAASUVORK5CYII=';
  37.     public function __construct(
  38.         private readonly Calculator $calculator,
  39.         private readonly Generator $pdfGenerator,
  40.         private readonly SystemConfig $systemConfig,
  41.         private readonly RequestStack $requestStack,
  42.         private readonly Security $security,
  43.         private readonly CompanySelector $companySelector,
  44.         private readonly ?string $installed
  45.     ) {
  46.     }
  47.     /**
  48.      * @return array<string, mixed>
  49.      *
  50.      * @throws InvalidArgumentException|ServiceCircularReferenceException|ServiceNotFoundException
  51.      */
  52.     public function getGlobals(): array
  53.     {
  54.         return [
  55.             'query' => $this->getQuery(),
  56.             'app_version' => SolidInvoiceCoreBundle::VERSION,
  57.             'app_name' => SolidInvoiceCoreBundle::APP_NAME,
  58.         ];
  59.     }
  60.     /**
  61.      * @return array<string, string>
  62.      *
  63.      * @throws ServiceCircularReferenceException|ServiceNotFoundException
  64.      */
  65.     protected function getQuery(): array
  66.     {
  67.         $request $this->requestStack->getCurrentRequest();
  68.         if (! $request instanceof Request) {
  69.             return [];
  70.         }
  71.         $params array_merge($request->query->all(), $request->attributes->all());
  72.         foreach (array_keys($params) as $key) {
  73.             if (str_starts_with($key'_')) {
  74.                 unset($params[$key]);
  75.             }
  76.         }
  77.         return $params;
  78.     }
  79.     public function getFilters(): array
  80.     {
  81.         return [
  82.             new TwigFilter('percentage'$this->calculator->calculatePercentage(...)),
  83.             new TwigFilter('diff'$this->dateDiff(...)),
  84.             new TwigFilter('md5''md5'),
  85.         ];
  86.     }
  87.     public function getFunctions(): array
  88.     {
  89.         return [
  90.             new TwigFunction('icon'$this->displayIcon(...), ['is_safe' => ['html']]),
  91.             new TwigFunction('app_logo'$this->displayAppLogo(...), ['is_safe' => ['html'], 'needs_environment' => true]),
  92.             new TwigFunction('company_name', function (): string {
  93.                 if ($this->security->getUser() instanceof UserInterface) {
  94.                     return $this->systemConfig->get('system/company/company_name') ?? SolidInvoiceCoreBundle::APP_NAME;
  95.                 }
  96.                 return SolidInvoiceCoreBundle::APP_NAME;
  97.             }),
  98.             new TwigFunction('company_id'$this->companySelector->getCompany(...)),
  99.             new TwigFunction('can_print_pdf'$this->pdfGenerator->canPrintPdf(...)),
  100.         ];
  101.     }
  102.     /**
  103.      * @throws InvalidArgumentException|ServiceCircularReferenceException|ServiceNotFoundException|LoaderError|SyntaxError
  104.      */
  105.     public function displayAppLogo(Environment $envstring $width 'auto'bool $showDefault true): string
  106.     {
  107.         $logo $showDefault self::DEFAULT_LOGO null;
  108.         if ($this->installed) {
  109.             $logo $this->systemConfig->get('system/company/logo');
  110.             if (null === $logo) {
  111.                 $logo $showDefault self::DEFAULT_LOGO null;
  112.             }
  113.         }
  114.         if (null === $logo) {
  115.             return '';
  116.         }
  117.         [$type$logo] = explode('|'$logo);
  118.         return $env->createTemplate('<img src="data:image/{{ type }};base64,{{ logo }}" class="brand-image" width="' $width '"/>')->render(['type' => $type'logo' => $logo]);
  119.     }
  120.     /**
  121.      * @param list<string> $options
  122.      */
  123.     public function displayIcon(string $iconName, array $options = []): string
  124.     {
  125.         $class sprintf('fa fa-%s'$iconName);
  126.         if ([] !== $options) {
  127.             $class .= ' ' implode(' '$options);
  128.         }
  129.         return sprintf('<i class="%s"></i>'$class);
  130.     }
  131.     /**
  132.      * Returns a human-readable diff for dates.
  133.      */
  134.     public function dateDiff(DateTime $date): string
  135.     {
  136.         return Carbon::instance($date)->diffForHumans();
  137.     }
  138. }