Hallo Cesar
alles klar. Ich habe jetzt mit dem AI von Siquando versucht ein Script zu erstellen welches Du per I-Frame einbinden könntes. Tja vielleicht ist es das was Du suchst und erst noch völlig Kostenfrei
<?php
declare(strict_types=1);
/**
* Aktuelle Themennachrichten für SIQUANDO Pro Web 10
* --------------------------------------------------
* Liest einen Google-News-RSS-Suchfeed ein und stellt die Meldungen
* als responsives Modul dar. Empfohlene Einbindung in Pro Web 10: Iframe.
*
* Voraussetzungen:
* - PHP 8.0 oder neuer
* - PHP-Erweiterung cURL oder allow_url_fopen
* - SimpleXML
*/
// -----------------------------------------------------------------------------
// KONFIGURATION
// -----------------------------------------------------------------------------
$config = [
// Suchthema, z. B. "Fussball WM", "Petrollampen" oder "Gemeinde Weggis".
'topic' => 'Fussball WM',
// Optional: Aktualität eingrenzen. Beispiele: 'when:1d', 'when:7d' oder ''.
// Der Ausdruck wird an die Google-News-Suche angehängt.
'freshness' => 'when:7d',
// Region und Sprache für die Schweiz / Deutsch.
'language' => 'de',
'interface_language' => 'de-CH',
'country' => 'CH',
'edition' => 'CH:de',
// Anzahl sichtbarer Nachrichten.
'max_items' => 8,
// Cache-Dauer in Sekunden. 1800 = 30 Minuten.
'cache_seconds' => 1800,
// Überschrift des Moduls. Leer lassen, um automatisch das Thema zu verwenden.
'module_title' => 'Aktuelles zur Fussball-WM',
// Ziel für Links: '_blank' oder '_top'.
'link_target' => '_blank',
// Bei true erscheint unterhalb der Meldungen ein Hinweis auf Google News.
'show_source_note' => true,
];
// -----------------------------------------------------------------------------
// FUNKTIONEN
// -----------------------------------------------------------------------------
function e(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
function buildFeedUrl(array $config): string
{
$query = trim((string) $config['topic']);
$freshness = trim((string) $config['freshness']);
if ($freshness !== '') {
$query .= ' ' . $freshness;
}
return 'https://news.google.com/rss/search?' . http_build_query([
'q' => $query,
'hl' => $config['interface_language'],
'gl' => $config['country'],
'ceid' => $config['edition'],
], '', '&', PHP_QUERY_RFC3986);
}
function fetchUrl(string $url): string
{
if (function_exists('curl_init')) {
$ch = curl_init($url);
if ($ch === false) {
throw new RuntimeException('cURL konnte nicht gestartet werden.');
}
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 8,
CURLOPT_TIMEOUT => 15,
CURLOPT_MAXREDIRS => 4,
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; SIQUANDO-Newsmodul/1.0)',
CURLOPT_HTTPHEADER => ['Accept: application/rss+xml, application/xml, text/xml'],
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
]);
$body = curl_exec($ch);
$error = curl_error($ch);
$status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if (!is_string($body) || $body === '' || $status >= 400) {
throw new RuntimeException('RSS-Feed nicht erreichbar' . ($error !== '' ? ': ' . $error : '.'));
}
return $body;
}
if ((bool) ini_get('allow_url_fopen')) {
$context = stream_context_create([
'http' => [
'timeout' => 15,
'follow_location' => 1,
'user_agent' => 'Mozilla/5.0 (compatible; SIQUANDO-Newsmodul/1.0)',
'header' => "Accept: application/rss+xml, application/xml, text/xml\r\n",
],
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
],
]);
$body = @file_get_contents($url, false, $context);
if (!is_string($body) || $body === '') {
throw new RuntimeException('RSS-Feed konnte nicht geladen werden.');
}
return $body;
}
throw new RuntimeException('Auf dem Webserver ist weder cURL noch allow_url_fopen verfügbar.');
}
function cachedFeed(string $url, int $cacheSeconds): string
{
$cacheDir = __DIR__ . DIRECTORY_SEPARATOR . 'rss-cache';
$cacheFile = $cacheDir . DIRECTORY_SEPARATOR . hash('sha256', $url) . '.xml';
if (is_file($cacheFile) && (time() - (int) filemtime($cacheFile)) < $cacheSeconds) {
$cached = file_get_contents($cacheFile);
if (is_string($cached) && $cached !== '') {
return $cached;
}
}
try {
$feed = fetchUrl($url);
if (!is_dir($cacheDir)) {
@mkdir($cacheDir, 0755, true);
}
if (is_dir($cacheDir) && is_writable($cacheDir)) {
@file_put_contents($cacheFile, $feed, LOCK_EX);
}
return $feed;
} catch (Throwable $exception) {
// Bei einer vorübergehenden Störung darf ein älterer Cache weiterlaufen.
if (is_file($cacheFile)) {
$cached = file_get_contents($cacheFile);
if (is_string($cached) && $cached !== '') {
return $cached;
}
}
throw $exception;
}
}
function parseFeed(string $xml, int $maxItems): array
{
libxml_use_internal_errors(true);
$rss = simplexml_load_string($xml, SimpleXMLElement::class, LIBXML_NOCDATA | LIBXML_NONET);
if ($rss === false || !isset($rss->channel->item)) {
throw new RuntimeException('Der RSS-Feed enthält keine lesbaren Meldungen.');
}
$items = [];
foreach ($rss->channel->item as $item) {
if (count($items) >= $maxItems) {
break;
}
$title = trim((string) $item->title);
$link = trim((string) $item->link);
$pubDate = trim((string) $item->pubDate);
$source = isset($item->source) ? trim((string) $item->source) : '';
if ($title === '' || !filter_var($link, FILTER_VALIDATE_URL)) {
continue;
}
// Google News hängt die Quelle häufig mit " - Quelle" an den Titel an.
if ($source !== '') {
$suffix = ' - ' . $source;
if (str_ends_with($title, $suffix)) {
$title = substr($title, 0, -strlen($suffix));
}
}
$timestamp = strtotime($pubDate);
$items[] = [
'title' => $title,
'link' => $link,
'source' => $source,
'date' => $timestamp !== false ? date('d.m.Y, H:i', $timestamp) : '',
];
}
return $items;
}
// -----------------------------------------------------------------------------
// DATEN LADEN
// -----------------------------------------------------------------------------
$items = [];
$errorMessage = '';
$feedUrl = buildFeedUrl($config);
try {
$xml = cachedFeed($feedUrl, max(300, (int) $config['cache_seconds']));
$items = parseFeed($xml, max(1, min(25, (int) $config['max_items'])));
} catch (Throwable $exception) {
$errorMessage = $exception->getMessage();
}
$moduleTitle = trim((string) $config['module_title']);
if ($moduleTitle === '') {
$moduleTitle = 'Aktuelles zu ' . (string) $config['topic'];
}
?>
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,follow">
<title><?= e($moduleTitle) ?></title>
<style>
:root {
--news-bg: #ffffff;
--news-text: #33271d;
--news-muted: #75685d;
--news-line: #e5ddd4;
--news-accent: #9b6b2f;
--news-hover: #f7f2ea;
}
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
background: transparent;
color: var(--news-text);
font-family: Arial, Helvetica, sans-serif;
}
.news-module {
width: 100%;
background: var(--news-bg);
border: 1px solid var(--news-line);
border-radius: 8px;
overflow: hidden;
}
.news-heading {
margin: 0;
padding: 16px 18px;
font-size: 1.25rem;
line-height: 1.3;
border-bottom: 1px solid var(--news-line);
}
.news-list {
margin: 0;
padding: 0;
list-style: none;
}
.news-item + .news-item {
border-top: 1px solid var(--news-line);
}
.news-link {
display: block;
padding: 14px 18px;
color: inherit;
text-decoration: none;
transition: background-color .15s ease;
}
.news-link:hover,
.news-link:focus-visible {
background: var(--news-hover);
}
.news-title {
display: block;
font-size: 1rem;
font-weight: 700;
line-height: 1.4;
}
.news-meta {
display: flex;
flex-wrap: wrap;
gap: 5px 10px;
margin-top: 7px;
color: var(--news-muted);
font-size: .82rem;
line-height: 1.3;
}
.news-source {
color: var(--news-accent);
font-weight: 700;
}
.news-note,
.news-error,
.news-empty {
margin: 0;
padding: 13px 18px;
color: var(--news-muted);
font-size: .8rem;
line-height: 1.4;
}
.news-note {
border-top: 1px solid var(--news-line);
}
.news-note a {
color: var(--news-accent);
}
@media (max-width: 520px) {
.news-heading { padding: 14px; font-size: 1.1rem; }
.news-link { padding: 13px 14px; }
.news-note, .news-error, .news-empty { padding: 12px 14px; }
}
</style>
</head>
<body>
<section class="news-module" aria-labelledby="news-heading">
<h2 class="news-heading" id="news-heading"><?= e($moduleTitle) ?></h2>
<?php if ($errorMessage !== ''): ?>
<p class="news-error">Die aktuellen Meldungen sind vorübergehend nicht verfügbar.</p>
<?php elseif ($items === []): ?>
<p class="news-empty">Zu diesem Thema wurden derzeit keine aktuellen Meldungen gefunden.</p>
<?php else: ?>
<ul class="news-list">
<?php foreach ($items as $item): ?>
<li class="news-item">
<a class="news-link"
href="<?= e($item['link']) ?>"
target="<?= e((string) $config['link_target']) ?>"
rel="noopener noreferrer nofollow">
<span class="news-title"><?= e($item['title']) ?></span>
<span class="news-meta">
<?php if ($item['source'] !== ''): ?>
<span class="news-source"><?= e($item['source']) ?></span>
<?php endif; ?>
<?php if ($item['date'] !== ''): ?>
<time><?= e($item['date']) ?> Uhr</time>
<?php endif; ?>
</span>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php if ((bool) $config['show_source_note']): ?>
<p class="news-note">
Automatisch zusammengestellte Schlagzeilen aus
<a href="<?= e($feedUrl) ?>" target="_blank" rel="noopener noreferrer nofollow">Google News</a>.
Für den Inhalt der verlinkten Seiten sind deren Betreiber verantwortlich.
</p>
<?php endif; ?>
</section>
</body>
</html>