Skip to content
Merged
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
10 changes: 10 additions & 0 deletions .dagger/src/TestsGotenbergBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ public function phpCsFixer(): string
;
}

#[DaggerFunction]
#[Doc('Validate all URL docs.')]
public function validateUrlDoc(): string
{
return $this->symfonyContainer
->withExec(['php', './docs/ValidateUrlDoc.php'])
->stdout()
;
}

#[DaggerFunction]
#[Doc('Run all tests.')]
#[ReturnsListOfType('string')]
Expand Down
26 changes: 26 additions & 0 deletions .github/workflows/url-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: 'Doc URLS test'

on:
push:
branches: [ 'main', '1.x' ]
pull_request:
branches: [ 'main', '1.x' ]
schedule:
- cron: '0 1 1 * *'

jobs:

check:
name: 'Validate URL docs'
runs-on: 'ubuntu-latest'
steps:
- uses: 'actions/checkout@v4'

- name: 'Validate URLS into PhpDoc'
uses: 'dagger/dagger-for-github@8.0.0'
with:
version: 'latest'
call: 'test validate-url-doc'
dagger-flags: '--silent'
env:
DAGGER_NO_NAG: '1'
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
"phpunit/phpunit": "^10.5.46",
"shipmonk/composer-dependency-analyser": "^1.8",
"symfony/asset": "^6.4 || ^7.0 || ^8.0",
"symfony/css-selector": "^6.4 || ^7.0 || ^8.0",
"symfony/dom-crawler": "^6.4 || ^7.0 || ^8.0",
"symfony/error-handler": "^6.4 || ^7.0 || ^8.0",
"symfony/framework-bundle": "^6.4 || ^7.0 || ^8.0",
"symfony/http-client": "^6.4 || ^7.0 || ^8.0",
Expand Down
147 changes: 147 additions & 0 deletions docs/ValidateUrlDoc.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#!/usr/bin/env php
<?php

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressIndicator;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\HttpClient\HttpClient;

require_once \dirname(__DIR__).'/vendor/autoload.php';

class ValidateUrlDoc
{
private const AVAILABLE_EXTENSIONS = ['php', 'md'];

private const DIR_AND_FILES_TO_CHECK = [
__DIR__.'/../src/Builder/Pdf',
__DIR__.'/../src/Builder/Screenshot',
__DIR__.'/../src/Builder/Behaviors',
__DIR__,
__DIR__.'/../README.md',
__DIR__.'/../src/DependencyInjection/Configuration.php',
];

public function validate(OutputInterface $output, SymfonyStyle $io): int
{
$progressBar = new ProgressIndicator($output);
$progressBar->start('Processing...');

$urls = [];
$files = $this->getFiles(self::DIR_AND_FILES_TO_CHECK);
foreach ($files as $file) {
array_push($urls, ...$this->extractUrls($file));
}

$urls = array_unique($urls);

$client = HttpClient::create();
$allResponses = [];
foreach ($urls as $url) {
$allResponses[] = $client->request('GET', $url);
}

$failedUrls = [];
foreach ($allResponses as $response) {
try {
$url = $response->getInfo('url');

$statusCode = $response->getStatusCode();
if (200 !== $statusCode) {
$failedUrls[$url] = "HTTP {$statusCode} error for: {$url}";
}

if (!\array_key_exists($url, $failedUrls)) {
$checkError = $this->checkContentResponse($response->getInfo('url'), $response->getContent());
if (\is_string($checkError)) {
$failedUrls[$url] = $checkError;
}
}
} catch (Throwable $e) {
$url = $response->getInfo('url');
$failedUrls[$url] = "Error for {$url}: {$e->getMessage()}";
} finally {
$progressBar->advance();
}
}

$progressBar->finish('Finished');
$failedUrls = array_unique($failedUrls);

if (\count($failedUrls) > 0) {
$io->error('Some external links are invalid:');
$io->listing($failedUrls);

return Command::FAILURE;
}

$io->success('All external links are valid.');

return Command::SUCCESS;
}

private function getFiles(array $paths): Generator
{
foreach ($paths as $path) {
if (is_file($path)) {
$splInfo = new SplFileInfo($path);
if (!\in_array($splInfo->getExtension(), self::AVAILABLE_EXTENSIONS, true)) {
throw new RuntimeException(\sprintf('File "%s" with "%s" extension is not allowed.', $splInfo->getFilename(), $splInfo->getExtension()));
}

yield $splInfo;
continue;
}

$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
foreach ($iterator as $file) {
if ($file->isFile() && \in_array(pathinfo($file, \PATHINFO_EXTENSION), self::AVAILABLE_EXTENSIONS, true)) {
yield $file;
}
}
}
}

private function extractUrls(SplFileInfo $file): array
{
$content = file_get_contents($file->getPathname());

// https://regex101.com/r/t5uiUp/1
// https://regex101.com/r/p62cij/1
match ($file->getExtension()) {
'php' => preg_match_all('/(?:@see\s+|->info\([^)]*)(https?:\/\/[^\s\'")]+)(?:[^)]*\))?/', $content, $matches),
'md' => preg_match_all('/\[[^\]]+\]\((https?:\/\/[^\s\*)]+)\)/', $content, $matches),
};

return $matches[1] ?? [];
}

private function checkContentResponse(string $url, string $content): string|null
{
$crawler = new Crawler($content);
$parsedUrl = parse_url($url);

if (\array_key_exists('fragment', $parsedUrl)) {
$fragment = $parsedUrl['fragment'];
if ($crawler->filter('#'.$fragment)->count() > 0 || $crawler->filter('a[name="'.$fragment.'"]')->count() > 0) {
return null;
}

return \sprintf('Cannot find anchor "%s" for "%s", remove or update the link in the PHPdoc', $fragment, $url);
}

return null;
}
}

$application = new Application();
$application->register('check')
->setCode(function (OutputInterface $output, SymfonyStyle $io) {
return (new ValidateUrlDoc())->validate($output, $io);
})
;

$application->setDefaultCommand('check');
$application->run();
22 changes: 11 additions & 11 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ sensiolabs_gotenberg:
# Override the default User-Agent HTTP header. - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium
user_agent: null

# HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers
# HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium
extra_http_headers:

# Prototype
Expand Down Expand Up @@ -386,7 +386,7 @@ sensiolabs_gotenberg:
# Override the default User-Agent HTTP header. - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium
user_agent: null

# HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers
# HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium
extra_http_headers: []

# Example:
Expand Down Expand Up @@ -588,7 +588,7 @@ sensiolabs_gotenberg:
# Override the default User-Agent HTTP header. - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium
user_agent: null

# HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers
# HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium
extra_http_headers: []

# Example:
Expand Down Expand Up @@ -775,16 +775,16 @@ sensiolabs_gotenberg:
# Specify that a stream is inserted to the PDF file which contains the original document for archiving purposes. - default false. https://gotenberg.dev/docs/routes#page-properties-libreoffice
add_original_document_as_stream: null

# Specify if images are exported to PDF using a lossless compression format like PNG or compressed using the JPEG format. - default false. https://gotenberg.dev/docs/routes#images-libreoffice
# Specify if images are exported to PDF using a lossless compression format like PNG or compressed using the JPEG format. - default false. https://gotenberg.dev/docs/routes#compress-libreoffice
lossless_image_compression: null

# Specify the quality of the JPG export. A higher value produces a higher-quality image and a larger file. Between 1 and 100. - default 90. https://gotenberg.dev/docs/routes#images-libreoffice
# Specify the quality of the JPG export. A higher value produces a higher-quality image and a larger file. Between 1 and 100. - default 90. https://gotenberg.dev/docs/routes#compress-libreoffice
quality: null

# Specify if the resolution of each image is reduced to the resolution specified by the form field maxImageResolution. - default false. https://gotenberg.dev/docs/routes#images-libreoffice
# Specify if the resolution of each image is reduced to the resolution specified by the form field maxImageResolution. - default false. https://gotenberg.dev/docs/routes#compress-libreoffice
reduce_image_resolution: null

# If the form field reduceImageResolution is set to true, tell if all images will be reduced to the given value in DPI. Possible values are: 75, 150, 300, 600 and 1200. - default 300. https://gotenberg.dev/docs/routes#images-libreoffice
# If the form field reduceImageResolution is set to true, tell if all images will be reduced to the given value in DPI. Possible values are: 75, 150, 300, 600 and 1200. - default 300. https://gotenberg.dev/docs/routes#compress-libreoffice
max_image_resolution: null # One of 75; 150; 300; 600; 1200

# URLs to download files from (JSON format). - default None. https://gotenberg.dev/docs/routes#download-from
Expand Down Expand Up @@ -972,7 +972,7 @@ sensiolabs_gotenberg:
# Override the default User-Agent HTTP header. - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium
user_agent: null

# HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers
# HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium
extra_http_headers: []

# Example:
Expand Down Expand Up @@ -1099,7 +1099,7 @@ sensiolabs_gotenberg:
# Override the default User-Agent HTTP header. - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium
user_agent: null

# HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers
# HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium
extra_http_headers: []

# Example:
Expand Down Expand Up @@ -1226,7 +1226,7 @@ sensiolabs_gotenberg:
# Override the default User-Agent HTTP header. - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium
user_agent: null

# HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers
# HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium
extra_http_headers: []

# Example:
Expand Down Expand Up @@ -1370,7 +1370,7 @@ sensiolabs_gotenberg:
```

> [!TIP]
> For more information about [custom HTTP headers](https://gotenberg.dev/docs/routes#custom-http-headers) & [webhook custom HTTP headers](https://gotenberg.dev/docs/configuration#webhook).
> For more information about [custom HTTP headers](https://gotenberg.dev/docs/routes#custom-http-headers-chromium) & [webhook custom HTTP headers](https://gotenberg.dev/docs/configuration#webhook).

## Invalid HTTP Status Codes

Expand Down
2 changes: 1 addition & 1 deletion docs/screenshot/HtmlScreenshotBuilder.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ return $gotenberg
The compression quality from range 0 to 100 (jpeg only). (default 100).<br />

> [!TIP]
> See: [https://gotenberg.dev/docs/routes#screenshots-rout](https://gotenberg.dev/docs/routes#screenshots-rout)
> See: [https://gotenberg.dev/docs/routes#screenshots-route](https://gotenberg.dev/docs/routes#screenshots-route)

```php
return $gotenberg
Expand Down
2 changes: 1 addition & 1 deletion docs/screenshot/MarkdownScreenshotBuilder.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ return $gotenberg
The compression quality from range 0 to 100 (jpeg only). (default 100).<br />

> [!TIP]
> See: [https://gotenberg.dev/docs/routes#screenshots-rout](https://gotenberg.dev/docs/routes#screenshots-rout)
> See: [https://gotenberg.dev/docs/routes#screenshots-route](https://gotenberg.dev/docs/routes#screenshots-route)

```php
return $gotenberg
Expand Down
2 changes: 1 addition & 1 deletion docs/screenshot/UrlScreenshotBuilder.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ return $gotenberg
The compression quality from range 0 to 100 (jpeg only). (default 100).<br />

> [!TIP]
> See: [https://gotenberg.dev/docs/routes#screenshots-rout](https://gotenberg.dev/docs/routes#screenshots-rout)
> See: [https://gotenberg.dev/docs/routes#screenshots-route](https://gotenberg.dev/docs/routes#screenshots-route)

```php
return $gotenberg
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public function format(ScreenshotFormat $format): static
*
* @param int<0, 100> $quality
*
* @see https://gotenberg.dev/docs/routes#screenshots-rout
* @see https://gotenberg.dev/docs/routes#screenshots-route
*
* @example quality(50)
*/
Expand Down
2 changes: 1 addition & 1 deletion src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ private function addExtraHttpHeadersNode(): NodeDefinition
$treeBuilder = new TreeBuilder('extra_http_headers');

return $treeBuilder->getRootNode()
->info('HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers')
->info('HTTP headers to send by Chromium while loading the HTML document - default None. https://gotenberg.dev/docs/routes#custom-http-headers-chromium')
->defaultValue([])
->normalizeKeys(false)
->useAttributeAsKey('name')
Expand Down