Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle assets from the Symfony component #125

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CHANGELOG

## 0.3.0

* [BC BREAK] Rename `sensiolabs_gotenberg.twig.asset_extension` to `sensiolabs_gotenberg.twig.gotenberg_asset_extension`
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"phpstan/phpstan-symfony": "^1.3",
"phpunit/phpunit": "^10.4",
"shipmonk/composer-dependency-analyser": "^1.7",
"symfony/asset": "^6.4 || ^7.0",
"symfony/framework-bundle": "^6.4 || ^7.0",
"symfony/http-client": "^6.4 || ^7.0",
"symfony/monolog-bundle": "^3.10",
Expand Down
19 changes: 19 additions & 0 deletions config/asset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

use Sensiolabs\GotenbergBundle\Twig\AssetExtension;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use function Symfony\Component\DependencyInjection\Loader\Configurator\param;
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;

return static function (ContainerConfigurator $container): void {
$services = $container->services();

$services->set('sensiolabs_gotenberg.twig.asset_extension', AssetExtension::class)
->decorate('twig.extension.assets')
->args([
service('.inner'),
service('assets.packages'),
param('kernel.project_dir'),
])
;
};
2 changes: 1 addition & 1 deletion config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
->alias(AssetBaseDirFormatter::class, 'sensiolabs_gotenberg.asset.base_dir_formatter')
;

$services->set('sensiolabs_gotenberg.twig.asset_extension', GotenbergAssetExtension::class)
$services->set('sensiolabs_gotenberg.twig.gotenberg_asset_extension', GotenbergAssetExtension::class)
->tag('twig.extension')
;

Expand Down
21 changes: 17 additions & 4 deletions docs/assets.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,22 @@ You can add assets in several ways, and it's available for most builders.
| Screenshot | :white_check_mark: | :white_check_mark: | :white_check_mark: | N/A |

> [!WARNING]
> As a reminder, we can only load assets in the content. And not in Header or Footer.
> As a reminder, we can only load assets in the content, not in Header or Footer.
> However, you can load images using a base64 encoded source.
> For more information about [Header and Footer restriction](https://gotenberg.dev/docs/routes#header-footer-chromium)
>

## Using public assets with the Symfony Asset component

Public assets loaded using the [Symfony Asset component](https://symfony.com/doc/current/components/asset.html) can be
used when generating your PDF.

> [!WARNING]
> Since the Asset component handle assets from the public folder, prefer the Gotenberg bundle to display private assets.
> See below.

## Using private assets with the Gotenberg bundle

> [!WARNING]
> By default, the assets are fetch in the `assets` folder of your application.
> If your assets files are in another folder, you can override the
> default value of `assets_directory` in your configuration file
Expand Down Expand Up @@ -47,7 +60,7 @@ You can add assets in several ways, and it's available for most builders.
> ```
> </details>

## Twig file
### Twig file

`{{ gotenberg_asset() }}` Twig function will help you to generate an asset path.
This function work as [asset() Twig function](https://symfony.com/doc/current/templates.html#linking-to-css-javascript-and-image-assets).
Expand Down Expand Up @@ -104,7 +117,7 @@ class YourController
}
```

## HTML file
### HTML file

If your file is an HTML file and not a Twig template, you can also
add some assets as below.
Expand Down
6 changes: 3 additions & 3 deletions src/Builder/Pdf/AbstractChromiumPdfBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -343,13 +343,13 @@ public function assets(string ...$paths): static
/**
* Adds a file, like an image, font, stylesheet, and so on.
*/
public function addAsset(string $path): static
public function addAsset(string $path, string|null $name = null): static
{
$resolvedPath = $this->asset->resolve($path);

$dataPart = new DataPart(new DataPartFile($resolvedPath));
$dataPart = new DataPart(new DataPartFile($resolvedPath, $name));

$this->formFields['assets'][$resolvedPath] = $dataPart;
$this->formFields['assets'][$name ?? $resolvedPath] = $dataPart;

return $this;
}
Expand Down
6 changes: 3 additions & 3 deletions src/Builder/Screenshot/AbstractChromiumScreenshotBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -351,13 +351,13 @@ public function assets(string ...$paths): static
/**
* Adds a file, like an image, font, stylesheet, and so on.
*/
public function addAsset(string $path): static
public function addAsset(string $path, string|null $name = null): static
{
$resolvedPath = $this->asset->resolve($path);

$dataPart = new DataPart(new DataPartFile($resolvedPath));
$dataPart = new DataPart(new DataPartFile($resolvedPath, $name));

$this->formFields['assets'][$resolvedPath] = $dataPart;
$this->formFields['assets'][$name ?? $resolvedPath] = $dataPart;

return $this;
}
Expand Down
5 changes: 5 additions & 0 deletions src/DependencyInjection/SensiolabsGotenbergExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Sensiolabs\GotenbergBundle\Builder\Pdf\PdfBuilderInterface;
use Sensiolabs\GotenbergBundle\Builder\Screenshot\ScreenshotBuilderInterface;
use Symfony\Bridge\Twig\Extension\AssetExtension;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand Down Expand Up @@ -57,6 +58,10 @@ public function load(array $configs, ContainerBuilder $container): void
$loader->load('builder_screenshot.php');
$loader->load('services.php');

if ($container::willBeAvailable('symfony/asset', AssetExtension::class, ['symfony/framework-bundle'])) {
$loader->load('asset.php');
}

if (false === $config['controller_listener']) {
$container->removeDefinition('sensiolabs_gotenberg.http_kernel.stream_builder');
}
Expand Down
2 changes: 1 addition & 1 deletion src/Exception/ClientException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

namespace Sensiolabs\GotenbergBundle\Exception;

class ClientException extends \RuntimeException implements ExceptionInterface
class ClientException extends RuntimeException
{
}
2 changes: 1 addition & 1 deletion src/Exception/InvalidBuilderConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

namespace Sensiolabs\GotenbergBundle\Exception;

final class InvalidBuilderConfiguration extends \RuntimeException implements ExceptionInterface
final class InvalidBuilderConfiguration extends RuntimeException
{
}
2 changes: 1 addition & 1 deletion src/Exception/MissingRequiredFieldException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

namespace Sensiolabs\GotenbergBundle\Exception;

final class MissingRequiredFieldException extends \RuntimeException implements ExceptionInterface
final class MissingRequiredFieldException extends RuntimeException
{
}
2 changes: 1 addition & 1 deletion src/Exception/PdfPartRenderingException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

namespace Sensiolabs\GotenbergBundle\Exception;

final class PdfPartRenderingException extends \RuntimeException implements ExceptionInterface
final class PdfPartRenderingException extends RuntimeException
{
}
2 changes: 1 addition & 1 deletion src/Exception/ProcessorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

namespace Sensiolabs\GotenbergBundle\Exception;

class ProcessorException extends \RuntimeException implements ExceptionInterface
class ProcessorException extends RuntimeException
{
}
7 changes: 7 additions & 0 deletions src/Exception/RenderingException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Sensiolabs\GotenbergBundle\Exception;

class RenderingException extends RuntimeException
{
}
7 changes: 7 additions & 0 deletions src/Exception/RuntimeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Sensiolabs\GotenbergBundle\Exception;

class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
2 changes: 1 addition & 1 deletion src/Exception/ScreenshotPartRenderingException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

namespace Sensiolabs\GotenbergBundle\Exception;

final class ScreenshotPartRenderingException extends \RuntimeException implements ExceptionInterface
final class ScreenshotPartRenderingException extends RuntimeException
{
}
2 changes: 1 addition & 1 deletion src/Exception/WebhookConfigurationException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

namespace Sensiolabs\GotenbergBundle\Exception;

final class WebhookConfigurationException extends \RuntimeException implements ExceptionInterface
final class WebhookConfigurationException extends RuntimeException
{
}
63 changes: 63 additions & 0 deletions src/Twig/AssetExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace Sensiolabs\GotenbergBundle\Twig;

use Sensiolabs\GotenbergBundle\Builder\Pdf\AbstractChromiumPdfBuilder;
use Sensiolabs\GotenbergBundle\Builder\Screenshot\AbstractChromiumScreenshotBuilder;
use Sensiolabs\GotenbergBundle\Exception\RenderingException;
use Symfony\Component\Asset\Packages;
use Symfony\Bridge\Twig\Extension\AssetExtension as TwigAssetExtension;
use Symfony\Component\Asset\PathPackage;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

final class AssetExtension extends AbstractExtension
{
public function __construct(
private readonly TwigAssetExtension $inner,
private readonly Packages $packages,
private readonly string $projectDir,
) {
}

public function getFunctions(): array
{
$functions = [];

foreach ($this->inner->getFunctions() as $function) {
if ('asset' === $function->getName()) {
$function = new TwigFunction('asset', $this->getAsset(...), ['needs_context' => true]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this extension will decorate my entire app usage of "asset" ? ... well...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the PathPackage does not include the host, asset cannot work in a Gotenberg context.

How can we make it working without decorating it ? I was also thinking about the <base> tag to solve this problem but it's not recommended.

}
$functions[$function->getName()] = $function;
}

return $functions;
}

/**
* @param array<string, mixed> $context
*/
public function getAsset(array $context, string $path, string|null $packageName = null): string
{
$builder = $context['_builder'] ?? null;

if (!$builder) {
return $this->inner->getAssetUrl($path, $packageName);
}

$package = $this->packages->getPackage($packageName);
if (!$package instanceof PathPackage) {
return $this->inner->getAssetUrl($path, $packageName);
}

if (!$builder instanceof AbstractChromiumPdfBuilder && !$builder instanceof AbstractChromiumScreenshotBuilder) {
throw new RenderingException('The gotenberg_asset function must be used with a class extending AbstractChromiumPdfBuilder or AbstractChromiumScreenshotBuilder.');
}

$name = uniqid();

$builder->addAsset($this->projectDir.'/public'.$package->getBasePath().$path, $name);

return $name;
}
}
2 changes: 1 addition & 1 deletion src/Twig/GotenbergAssetExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function getFunctions(): array
*/
public function getAssetUrl(array $context, string $path): string
{
$builder = $context['_builder'];
$builder = $context['_builder'] ?? null;

if (!$builder instanceof AbstractChromiumPdfBuilder && !$builder instanceof AbstractChromiumScreenshotBuilder) {
throw new \LogicException('You need to extend from AbstractChromiumPdfBuilder to use gotenberg_asset function.');
Expand Down
Loading