diff --git a/src/new/src/PSC/Backend/DashboardBundle/Controller/DashboardController.php b/src/new/src/PSC/Backend/DashboardBundle/Controller/DashboardController.php
index 9f2e77776..72a81c23d 100755
--- a/src/new/src/PSC/Backend/DashboardBundle/Controller/DashboardController.php
+++ b/src/new/src/PSC/Backend/DashboardBundle/Controller/DashboardController.php
@@ -20,6 +20,7 @@ use PSC\Shop\QueueBundle\Service\Queue\Manager;
use PSC\System\SettingsBundle\Service\DiskUsage;
use PSC\System\SettingsBundle\Service\Instance;
use PSC\System\SettingsBundle\Service\Shop;
+use PSC\System\SettingsBundle\Service\Version;
use PSC\System\UpdateBundle\Service\Migration;
use Symfony\Bridge\Twig\Attribute\Template;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -61,6 +62,7 @@ class DashboardController extends AbstractController
ContactRepository $contactRepository,
Order $orderService,
DiskUsage $diskUsage,
+ Version $version,
) {
// Muss vor dem ersten Laden des Shops geprüft werden: ausstehende
// Migrationen können Spalten ergänzen, die das Shop-Entity bereits mappt
@@ -173,6 +175,9 @@ class DashboardController extends AbstractController
'instance' => $instanceService->getInstance(),
'chart' => $chart,
'diskUsage' => $this->isGranted('ROLE_ADMIN') ? $diskUsage->getUsage() : null,
+ 'currentVersion' => $version->getRelease(),
+ 'latestVersion' => $this->isGranted('ROLE_ADMIN') ? $version->getLatestRelease() : null,
+ 'updateAvailable' => $this->isGranted('ROLE_ADMIN') ? $version->isUpdateAvailable() : false,
];
}
}
diff --git a/src/new/src/PSC/Backend/DashboardBundle/Resources/views/dashboard/index.html.twig b/src/new/src/PSC/Backend/DashboardBundle/Resources/views/dashboard/index.html.twig
index 1efdf8e17..c3d7272e3 100755
--- a/src/new/src/PSC/Backend/DashboardBundle/Resources/views/dashboard/index.html.twig
+++ b/src/new/src/PSC/Backend/DashboardBundle/Resources/views/dashboard/index.html.twig
@@ -25,6 +25,17 @@
{{'revokePossible'|trans}}
diff --git a/src/new/src/PSC/Shop/OrderBundle/Service/Order.php b/src/new/src/PSC/Shop/OrderBundle/Service/Order.php
index 8a7b394a6..fda51813c 100755
--- a/src/new/src/PSC/Shop/OrderBundle/Service/Order.php
+++ b/src/new/src/PSC/Shop/OrderBundle/Service/Order.php
@@ -176,6 +176,12 @@ class Order
$positionDoc = new \PSC\Shop\EntityBundle\Document\Position();
$positionEntity->setOrder($orderEntity);
$this->positionTransformer->toDb($position, $positionEntity, $positionDoc);
+ // Snapshot zum Bestellzeitpunkt: War der Widerruf für das Produkt
+ // möglich? Nur bei neu angelegten Positionen setzen, damit spätere
+ // Produktänderungen bestehende Bestellungen nicht beeinflussen.
+ if ($positionEntity->getProduct()) {
+ $positionEntity->setRevokeAllowed($positionEntity->getProduct()->isRevokeButtonEnable());
+ }
$this->entityManager->persist($positionEntity);
$this->entityManager->flush();
diff --git a/src/new/src/PSC/System/SettingsBundle/Service/Version.php b/src/new/src/PSC/System/SettingsBundle/Service/Version.php
index 605c5f492..1caf860b9 100644
--- a/src/new/src/PSC/System/SettingsBundle/Service/Version.php
+++ b/src/new/src/PSC/System/SettingsBundle/Service/Version.php
@@ -4,13 +4,23 @@ namespace PSC\System\SettingsBundle\Service;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Yaml\Yaml;
+use Symfony\Contracts\Cache\CacheInterface;
+use Symfony\Contracts\Cache\ItemInterface;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
class Version
{
+ /**
+ * RSS-Feed mit den veröffentlichten Releases.
+ */
+ private const RELEASES_FEED = 'https://git.thomas-peterson.de/boonkerz/printshopcreator/releases.rss';
+
private ?array $data = null;
public function __construct(
private readonly KernelInterface $kernel,
+ private readonly HttpClientInterface $httpClient,
+ private readonly CacheInterface $cache,
) {}
private function load(): void
@@ -37,4 +47,64 @@ class Version
$this->load();
return $this->data['changelog'] ?? [];
}
+
+ /**
+ * Neueste verfügbare Version aus dem Release-RSS-Feed.
+ * Ergebnis wird zwischengespeichert, damit das Dashboard schnell bleibt und
+ * der Feed nicht bei jedem Aufruf abgefragt wird. Bei Fehlern: null.
+ */
+ public function getLatestRelease(): ?string
+ {
+ return $this->cache->get('psc_latest_release_version', function (ItemInterface $item): ?string {
+ $item->expiresAfter(3600);
+
+ try {
+ $response = $this->httpClient->request('GET', self::RELEASES_FEED, [
+ 'timeout' => 5,
+ ]);
+
+ $content = $response->getContent();
+ $xml = @simplexml_load_string($content);
+
+ if ($xml === false) {
+ return null;
+ }
+
+ // RSS 2.0 (channel->item) bzw. Atom (entry) unterstützen.
+ $title = null;
+ if (isset($xml->channel->item[0]->title)) {
+ $title = (string) $xml->channel->item[0]->title;
+ } elseif (isset($xml->entry[0]->title)) {
+ $title = (string) $xml->entry[0]->title;
+ }
+
+ if ($title === null || $title === '') {
+ return null;
+ }
+
+ // Versionsnummer (z.B. 2.3.7) aus dem Titel extrahieren.
+ if (preg_match('/\d+\.\d+(?:\.\d+)*/', $title, $matches)) {
+ return $matches[0];
+ }
+
+ return ltrim(trim($title), 'vV');
+ } catch (\Throwable) {
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Ist eine neuere Version als die installierte verfügbar?
+ */
+ public function isUpdateAvailable(): bool
+ {
+ $latest = $this->getLatestRelease();
+
+ if ($latest === null || $latest === '') {
+ return false;
+ }
+
+ return version_compare($latest, $this->getRelease(), '>');
+ }
}
diff --git a/src/new/src/PSC/System/UpdateBundle/Migrations/Version20260624120000.php b/src/new/src/PSC/System/UpdateBundle/Migrations/Version20260624120000.php
new file mode 100644
index 000000000..0e24862e9
--- /dev/null
+++ b/src/new/src/PSC/System/UpdateBundle/Migrations/Version20260624120000.php
@@ -0,0 +1,12 @@
+entityManager->getConnection();
+ $connection->executeQuery("ALTER TABLE orderspos ADD COLUMN revoke_allowed TINYINT(1) NULL DEFAULT NULL;");
+ }
+}
diff --git a/src/old/application/data/models/generated/BaseOrderspos.php b/src/old/application/data/models/generated/BaseOrderspos.php
index 14d493f0d..7a8e0cd3a 100755
--- a/src/old/application/data/models/generated/BaseOrderspos.php
+++ b/src/old/application/data/models/generated/BaseOrderspos.php
@@ -106,6 +106,7 @@ abstract class BaseOrderspos extends Doctrine_Record
$this->hasColumn('revoked', 'boolean', 1);
$this->hasColumn('revoked_at', 'timestamp');
+ $this->hasColumn('revoke_allowed', 'boolean', 1);
}
public function setUp()
diff --git a/src/old/application/design/vorlagen/bootstrap4_api/layout/default.phtml b/src/old/application/design/vorlagen/bootstrap4_api/layout/default.phtml
index eebaf16c6..c43efacf1 100755
--- a/src/old/application/design/vorlagen/bootstrap4_api/layout/default.phtml
+++ b/src/old/application/design/vorlagen/bootstrap4_api/layout/default.phtml
@@ -330,6 +330,7 @@ if($linkend == "") { ?>
designsettings()->get('b2bshop')): ?>
translate('AGB')?>
translate('Widerrufsbelehrung')?>
+
translate('Widerruf')?>
translate('Datenschutzerklärung')?>
translate('Impressum')?>
diff --git a/src/old/application/design/vorlagen/bootstrap4_api/templates/revocationform/order.phtml b/src/old/application/design/vorlagen/bootstrap4_api/templates/revocationform/order.phtml
index 7bf5da00a..7f046908d 100644
--- a/src/old/application/design/vorlagen/bootstrap4_api/templates/revocationform/order.phtml
+++ b/src/old/application/design/vorlagen/bootstrap4_api/templates/revocationform/order.phtml
@@ -24,7 +24,7 @@
revoked): ?>
translate('Widerrufen am') ?> escape($pos->revoked_at) ?>
- Article->revoke_button_enable): ?>
+ revoke_allowed): ?>
|