Skip to content
Open
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
41 changes: 40 additions & 1 deletion ux.symfony.com/src/Controller/ChangelogController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
namespace App\Controller;

use App\Service\Changelog\ChangelogProvider;
use App\Service\Packagist\PackagistDataProvider;
use App\Service\UxPackageRepository;
use App\Twig\Components\ChangelogItem;
use DateTimeImmutable;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
Expand All @@ -20,7 +24,8 @@ class ChangelogController extends AbstractController
{
public function __construct(
private readonly ChangelogProvider $changeLogProvider,
) {
)
{
}

#[Route('/changelog', name: 'app_changelog')]
Expand All @@ -32,4 +37,38 @@ public function __invoke(): Response
'changelog' => $changelog,
]);
}

#[Route('/{package}/changelog', name: 'app_changelog_package')]
public function package(string $package, UxPackageRepository $packageRepository, PackagistDataProvider $packagistData): Response
{
$uxPackage = $packageRepository->find($package);

$packageLog = $this->changeLogProvider->getPackageChangelog('ux-'.$uxPackage->getName());
$packageData = $packagistData->getPackageData($uxPackage->getComposerName());

$firstLine = array_shift($packageLog);

$changelog = [];
foreach ($packageLog as $a) {
$lines = explode("\n", $a);
$version = array_shift($lines);

$versionData = $packageData['versions']['v'.$version] ?? [];
if ([] === $versionData) {
continue;
}

$changelog[] = [
'body' => implode("\n", $lines),
'version' => $version,
'name' => 'v'.$version,
'date' => $versionData['time'] ?? null,
];
}

return $this->render('ux_packages/package_changelog.html.twig', [
'package' => $uxPackage,
'changelog' => $changelog,
]);
}
}
87 changes: 87 additions & 0 deletions ux.symfony.com/src/Controller/UxPackageController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace App\Controller;

use App\Service\Changelog\ChangelogProvider;
use App\Service\Packagist\PackagistDataProvider;
use App\Service\UxPackageRepository;
use App\Twig\Components\ChangelogItem;
use DateTimeImmutable;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/{package}', name: 'app_package')]
class UxPackageController extends AbstractController
{
public function __construct(
private readonly UxPackageRepository $packageRepository,
private readonly ChangelogProvider $changeLogProvider,
private readonly PackagistDataProvider $packagistData,
)
{
}

#[Route('/changelog', name: 'app_package_changelog')]
public function changelog(string $package): Response
{
$uxPackage = $this->packageRepository->find($package) ?? throw $this->createNotFoundException(sprintf('Package "%s" not found', $package));

$packageLog = $this->changeLogProvider->getPackageChangelog('ux-'.$uxPackage->getName());
$packageData = $this->packagistData->getPackageData($uxPackage->getComposerName());

$firstLine = array_shift($packageLog);

$changelog = [];
foreach ($packageLog as $a) {

$lines = explode("\n", $a);
$version = array_shift($lines);

$versionData = $packageData['versions']['v'.$version] ?? [];
if ([] === $versionData) {
continue;
}

$changelog[] = [
'body' => implode("\n", $lines),
'version' => $version,
'name' => 'v'.$version,
'date' => $versionData['time'] ?? null,
];
}

return $this->render('ux_packages/package_changelog.html.twig', [
'package' => $uxPackage,
'changelog' => $changelog,
]);
}

#[Route('/requirements', name: 'app_package_requirements')]
public function requirements(string $package): Response
{
$uxPackage = $this->packageRepository->find($package) ?? throw $this->createNotFoundException(sprintf('Package "%s" not found', $package));

$packageData = $this->packagistData->getPackageData($uxPackage->getComposerName());

$packageVersions = $packageData['versions'] ?? [];
$currentVersion = array_shift($packageVersions);

return $this->render('ux_packages/package_requirements.html.twig', [
'package' => $uxPackage,
'requirements' => [
'requires' => $currentVersion['require'] ?? [],
'devRequires' => $currentVersion['require-dev'] ?? [],
],
]);
}
}
36 changes: 32 additions & 4 deletions ux.symfony.com/src/Service/Changelog/ChangelogProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,48 @@ public function getChangelog(int $page = 1): array
{
$changelog = [];

foreach ($this->getReleases($page) as $release) {
foreach ($this->getReleases('ux', $page) as $release) {
$changelog[] = $release;
}

return $changelog;
}

private function getReleases(int $page = 1): array
public function getPackageChangelog(string $repo, int $page = 1): array
{
return $this->cache->get('releases-symfony-ux-'.$page, function (CacheItemInterface $item) use ($page) {
$changelogMd = $this->cache->get('changelog-symfony-'.$repo.'-'.$page, function (CacheItemInterface $item) use ($repo, $page) {
$item->expiresAfter(604800); // 1 week

return $this->fetchReleases('symfony', 'ux', $page);
return $this->fetchPackageChangelog('symfony', $repo, $page);
});

$changelog = [];
$changelogMarkdown = explode("\n## ", $changelogMd);

return $changelogMarkdown;
}

private function getReleases(string $repo, int $page = 1): array
{
return $this->cache->get('releases-symfony-'.$repo.'-'.$page, function (CacheItemInterface $item) use ($repo, $page) {
$item->expiresAfter(604800); // 1 week

return $this->fetchReleases('symfony', $repo, $page);
});
}

/**
* @return string
*
* @internal
*/
private function fetchPackageChangelog(string $owner, string $repo): string
{
// https://github.com/symfony/ux-twig-component/blob/2.x/CHANGELOG.md
// https://raw.githubusercontent.com/symfony/ux-twig-component/2.x/CHANGELOG.md
$response = $this->httpClient->request('GET', sprintf('https://raw.githubusercontent.com/%s/%s/2.x/CHANGELOG.md', $owner, $repo));

return $response->getContent();
}

/**
Expand Down
35 changes: 35 additions & 0 deletions ux.symfony.com/src/Service/Packagist/PackagistDataProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace App\Service\Packagist;

use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class PackagistDataProvider
{
public function __construct(
private HttpClientInterface $httpClient,
private CacheInterface $cache,
)
{
}

public function getPackageData(string $packageName): array
{
return $this->cache->get('package-data-'.str_replace('/', '--', $packageName), function (ItemInterface $item) use ($packageName) {
$item->expiresAfter(604800); // 1 week

return $this->fetchPackageData($packageName);
});
}

private function fetchPackageData(string $packageName): array
{
$response = $this->httpClient->request('GET', 'https://packagist.org/packages/'.$packageName.'.json');

$packageData = $response->toArray();

return $packageData['package'] ?? [];
}
}
58 changes: 58 additions & 0 deletions ux.symfony.com/templates/ux_packages/package_changelog.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{% extends 'base.html.twig' %}

{% set meta = {
title: package.humanName,
description: package.description,
canonical: url(package.route),
} %}

{% block header %}
{{ include('_header.html.twig', {
theme: 'white'
}) }}
{% endblock %}

{% block content %}

<article>

{% block package_header %}
{{ block('package_header', 'ux_packages/%s.html.twig'|format(package.name|u.snake)) }}

<nav style="display: flex; align-items: center; justify-content: center; background: #0A0A0A; color:#fff; padding: 1rem; gap: 2rem;">
<a href="{{ url(package.route) }}" style="color:#fff;">{{ package.humanName }}</a>

<a href="{{ url('app_packageapp_package_requirements', {package: package.name}) }}" style="color:#fff;">Requirements</a>
<a href="{{ url('app_packageapp_package_changelog', {package: package.name}) }}" style="color:#fff;">Install</a>

<a href="{{ url('app_packageapp_package_changelog', {package: package.name}) }}" style="color:#fff;">Install</a>

<a href="{{ url('app_packageapp_package_changelog', {package: package.name}) }}" style="color:#fff;">Doc</a>
<a href="{{ url('app_packageapp_package_changelog', {package: package.name}) }}" style="color:#fff;">Demos</a>

<a href="{{ url('app_packageapp_package_changelog', {package: package.name}) }}" style="color:#fff;">Changelog</a>
<a href="{{ url('app_packageapp_package_changelog', {package: package.name}) }}" style="color:#fff;">Issue</a>
</nav>
{% endblock %}

{% block package_content %}

<div class="hero">
<div class="container-fluid container-xxl px-4 pt-4 px-md-5">
<h1 class="text-center mt-5"><a href="{{ url('app_changelog') }}">Changelog</a></h1>
<p class="text-center mt-2 mb-5">New features, bug fixes, performances and security improvements.</p>
</div>
</div>

<div class="container-fluid container-xxl px-4 pt-4 px-md-5">
<div class="Changelog">
{% for entry in changelog %}
<twig:ChangelogItem item="{{ entry }}" isOpen="{{ loop.index < 4 }}" />
{% endfor %}
</div>
</div>

{% endblock %}
</article>

{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{% extends 'base.html.twig' %}

{% set meta = {
title: package.humanName,
description: package.description,
canonical: url(package.route),
} %}

{% block header %}
{{ include('_header.html.twig', {
theme: 'white'
}) }}
{% endblock %}

{% block content %}

<article>

{% block package_header %}
{{ block('package_header', 'ux_packages/%s.html.twig'|format(package.name|u.snake)) }}

<nav style="display: flex; align-items: center; justify-content: center; background: #0A0A0A; color:#fff; padding: 1rem; gap: 2rem;">
<a href="{{ url(package.route) }}" style="color:#fff;">{{ package.humanName }}</a>

<a href="{{ url('app_packageapp_package_requirements', {package: package.name}) }}" style="color:#fff;">Requirements</a>
<a href="{{ url('app_packageapp_package_changelog', {package: package.name}) }}" style="color:#fff;">Install</a>

<a href="{{ url('app_packageapp_package_changelog', {package: package.name}) }}" style="color:#fff;">Install</a>

<a href="{{ url('app_packageapp_package_changelog', {package: package.name}) }}" style="color:#fff;">Doc</a>
<a href="{{ url('app_packageapp_package_changelog', {package: package.name}) }}" style="color:#fff;">Demos</a>

<a href="{{ url('app_packageapp_package_changelog', {package: package.name}) }}" style="color:#fff;">Changelog</a>
<a href="{{ url('app_packageapp_package_changelog', {package: package.name}) }}" style="color:#fff;">Issue</a>
</nav>
{% endblock %}

{% block package_content %}

<div class="hero">
<div class="container-fluid container-xxl px-4 pt-4 px-md-5">
<h1 class="text-center mt-5"><a href="{{ url('app_changelog') }}">{{ package.name }} Requirements</a></h1>
<p class="text-center mt-2 mb-5">New features, bug fixes, performances and security improvements.</p>
</div>
</div>

<div class="container-fluid container-xxl px-4 pt-4 px-md-5">

{{ dump(requirements) }}
</div>

{% endblock %}
</article>

{% endblock %}