diff --git a/bin/create-uploads-dir b/bin/create-uploads-dir index a91caf3..d60d844 100755 --- a/bin/create-uploads-dir +++ b/bin/create-uploads-dir @@ -150,4 +150,4 @@ printf( $imagesCopied === 1 ? '' : 's', $imagesMissing, PHP_EOL -); \ No newline at end of file +); diff --git a/bin/generate-llms-full b/bin/generate-llms-full new file mode 100755 index 0000000..a42ac3f --- /dev/null +++ b/bin/generate-llms-full @@ -0,0 +1,23 @@ +#!/usr/bin/env php +get(LlmsFullGenerator::class); + +$count = $llmsFullGenerator->write(); + +printf( + "Done. %d article%s written to %s%s", + $count, + $count === 1 ? '' : 's', + $llmsFullGenerator->getOutputFile(), + PHP_EOL +); diff --git a/config/autoload/local.php.dist b/config/autoload/local.php.dist index 0b9acc5..61e7f8d 100644 --- a/config/autoload/local.php.dist +++ b/config/autoload/local.php.dist @@ -59,6 +59,10 @@ return [ 'feed' => [ 'path' => realpath(__DIR__ . '/../../public/feed.xml'), ], + 'llms' => [ + 'sourceDir' => realpath(__DIR__ . '/../../public/md-articles'), + 'outputFile' => realpath(__DIR__ . '/../../public/llms-full.txt'), + ], /** * Credentials for `bin/generate-packages`. Reading public repositories needs no token * scopes at all; without a token the generator still runs, at a lower rate limit. diff --git a/public/md-articles/dotkernel/test-article.md b/public/md-articles/dotkernel/test-article.md new file mode 100644 index 0000000..038b11a --- /dev/null +++ b/public/md-articles/dotkernel/test-article.md @@ -0,0 +1,24 @@ +--- +title: "Test article" +description: "This is a test article used for fixture and testing purposes." +author: "admin" +date_published: "2026-08-06" +canonical_url: "https://www.dotkernel.com/dotkernel/test-article/" +category: "Dotkernel" +language: "en" +--- + +# Test article + +## TL;DR + +This is a test article added to the dotkernel category for testing purposes. + +## Overview + +This is a test article used for fixture and testing purposes. + +## FAQ + +**Q: What is this article for?** +A: This is a test article added to the Dotkernel category for fixture and testing purposes. diff --git a/public/uploads/test-article.jpg b/public/uploads/test-article.jpg new file mode 100644 index 0000000..77e2d5c Binary files /dev/null and b/public/uploads/test-article.jpg differ diff --git a/src/App/src/ConfigProvider.php b/src/App/src/ConfigProvider.php index 754779a..f32d5cd 100644 --- a/src/App/src/ConfigProvider.php +++ b/src/App/src/ConfigProvider.php @@ -19,6 +19,7 @@ use Light\App\Factory\GetPackagesViewHandlerFactory; use Light\App\Factory\GetSitemapViewHandlerFactory; use Light\App\Factory\GitHubClientFactory; +use Light\App\Factory\LlmsFullGeneratorFactory; use Light\App\Factory\PackageGeneratorFactory; use Light\App\Factory\SitemapGeneratorFactory; use Light\App\Handler\GetFeedViewHandler; @@ -30,6 +31,7 @@ use Light\App\Service\FeedGenerator; use Light\App\Service\GitHubClient; use Light\App\Service\GitHubClientInterface; +use Light\App\Service\LlmsFullGenerator; use Light\App\Service\PackageGenerator; use Light\App\Service\SitemapGenerator; use Mezzio\Application; @@ -138,6 +140,7 @@ public function getDependencies(): array SitemapGenerator::class => SitemapGeneratorFactory::class, GitHubClient::class => GitHubClientFactory::class, PackageGenerator::class => PackageGeneratorFactory::class, + LlmsFullGenerator::class => LlmsFullGeneratorFactory::class, ], 'aliases' => [ EntityManager::class => 'doctrine.entity_manager.orm_default', diff --git a/src/App/src/Factory/LlmsFullGeneratorFactory.php b/src/App/src/Factory/LlmsFullGeneratorFactory.php new file mode 100644 index 0000000..81faf8d --- /dev/null +++ b/src/App/src/Factory/LlmsFullGeneratorFactory.php @@ -0,0 +1,22 @@ +get('config'); + + return new LlmsFullGenerator( + $config['llms']['sourceDir'], + $config['llms']['outputFile'], + $config['application']['baseUrl'] ?? '', + ); + } +} diff --git a/src/App/src/Fixture/articles_cleaned.json b/src/App/src/Fixture/articles_cleaned.json index 91aa7d4..8523316 100644 --- a/src/App/src/Fixture/articles_cleaned.json +++ b/src/App/src/Fixture/articles_cleaned.json @@ -835,6 +835,19 @@ "opengraph_img": "\/opengraph\/article\/twitter-card-symfony-mailer.png", "excerpt": "What prompted the change According to the discussion from the LaminasTechnical steering Committee of 2023-12-04, it was decided that the laminas\/laminas-mail package would be abandoned. On the one hand, there is nobody to maintain the package and on the other, there are several alternatives available in the ecosystem: ddeboer\/imap for interacting with IMAP zbateson\/mail-mime-parser for parsing MIME messages symfony\/mailer for sending mail How Dotkernel handles the issue The Dotkernel team has also opted to replace the laminas\/laminas-mail package in the dotkernel\/dot-mail package.", "tl_dr": "The Laminas Technical Steering Committee decided on 2023-12-04 to abandon laminas\/laminas-mail.\nDotkernel responded by replacing it with symfony\/mailer inside the dotkernel\/dot-mail package (version 5), aiming for minimal impact on existing projects — calls to send mail stay the same, though mime and imap related functionality is removed." + }, + { + "post_title": "Test article", + "post_date": "2026-08-06 00:00:00", + "post_status": "publish", + "author": { + "display_name": "stefan", + "github": "OStefan2001" + }, + "isObsolete": false, + "opengraph_img": "\/opengraph\/article\/twitter-card-symfony-mailer.png", + "excerpt": "This is a test article used for fixture and testing purposes.", + "tl_dr": "This is a test article added to the dotkernel category for testing purposes." } ] }, diff --git a/src/App/src/Service/LlmsFullGenerator.php b/src/App/src/Service/LlmsFullGenerator.php new file mode 100644 index 0000000..699bf9a --- /dev/null +++ b/src/App/src/Service/LlmsFullGenerator.php @@ -0,0 +1,96 @@ +'; + + public function __construct( + private string $sourceDir, + private string $outputFile, + private string $baseUrl, + ) { + } + + public function getOutputFile(): string + { + return $this->outputFile; + } + + public function write(): int + { + $relativePaths = $this->collectRelativePaths(); + + $sections = array_map( + fn (string $relativePath): string => $this->buildSection($relativePath), + $relativePaths + ); + + $written = implode("\n\n\n", $sections) . "\n\n"; + + if (file_put_contents($this->outputFile, $written) === false) { + throw new RuntimeException('Unable to write llms-full.txt.'); + } + + return count($relativePaths); + } + + /** + * @return list + */ + private function collectRelativePaths(): array + { + $categoryFiles = glob($this->sourceDir . '/*/*.md') ?: []; + $categoryFiles = array_map( + fn (string $path): string => ltrim(str_replace($this->sourceDir, '', $path), '/'), + $categoryFiles + ); + sort($categoryFiles); + + $paths = is_file($this->sourceDir . '/index.md') ? ['index.md'] : []; + + return array_merge($paths, $categoryFiles); + } + + private function buildSection(string $relativePath): string + { + $contents = file_get_contents($this->sourceDir . '/' . $relativePath); + if ($contents === false) { + throw new RuntimeException("Unable to read file: {$relativePath}"); + } + + $contents = str_replace('{{base_url}}', $this->baseUrl, $contents); + + return sprintf( + "%s\n\n%s\n\n%s", + self::SEPARATOR, + $relativePath, + self::SEPARATOR, + trim($contents) + ); + } +} diff --git a/src/Blog/templates/page/JSON-LD/dotkernel/test-article.jsonld.twig b/src/Blog/templates/page/JSON-LD/dotkernel/test-article.jsonld.twig new file mode 100644 index 0000000..7ced4ba --- /dev/null +++ b/src/Blog/templates/page/JSON-LD/dotkernel/test-article.jsonld.twig @@ -0,0 +1,49 @@ + diff --git a/src/Blog/templates/page/blog-resource/dotkernel/test-article.html.twig b/src/Blog/templates/page/blog-resource/dotkernel/test-article.html.twig new file mode 100644 index 0000000..012bb94 --- /dev/null +++ b/src/Blog/templates/page/blog-resource/dotkernel/test-article.html.twig @@ -0,0 +1,18 @@ +{% extends '@layout/blog-post.html.twig' %} + +{% block body %} + + +

This is a test article used for fixture and testing purposes.

+ +

Frequently Asked Questions

+ +
+
+ What is this article for? + +
+

This is a test article added to the Dotkernel category for fixture and testing purposes.

+
+
+
+{% endblock %}