Entwickler-Dokumentation
Alles was du brauchst, um Plugins, Themes und eigene Module für RadioCMS Community Edition zu entwickeln. Das System basiert auf dem WordPress-Prinzip, Actions, Filters und Widgets, ist aber deutlich schlanker.
Plugins
Erweitere RadioCMS um neue Features, ohne den Core anzufassen.
Module
Core-Features deaktivieren und durch eigene Plugins ersetzen.
Themes
Eigene Designs per CSS, kein PHP, keine Angst vor kaputten Seiten.
Safe by Default
Plugin-Crashes legen nie die ganze Seite lahm, Fehler werden gefangen.
Übersicht
RadioCMS Community Edition ist ein Open-Source-CMS speziell für Webradios, lizenziert unter MIT. Du darfst es kostenlos nutzen, ändern und weitergeben, auch kommerziell.
Das Erweiterungs-System besteht aus drei Bausteinen:
- Plugins, PHP-Code der sich in Aktionen und Filter einhakt
- Module, Core-Features die man an- und ausschalten kann
- Themes, CSS-basierte Designs die man einfach hochlädt
Plugins und Themes dürfen und sollen geteilt werden. Wenn du etwas Gutes baust, teile es im Community-Discord oder auf GitHub.
Getting Started
Ein minimales Plugin besteht aus zwei Dateien:
{
"id": "mein-plugin",
"name": "Mein Plugin",
"version": "1.0.0",
"description": "Kurze Beschreibung.",
"author": "Dein Name",
"main": "index.php",
"enabled": false,
"requires": "1.0.0"
}
<?php
// Nachricht am Seitenende ausgeben
radiocms_add_action('footer', function() {
echo '<!-- Mein Plugin ist aktiv -->';
});
Das war's. Ordner hochladen, im Admin unter Plugins aktivieren, fertig.
Architektur
RadioCMS lädt Plugins während der Initialisierung in config/config.php.
Jedes Plugin wird in einem try/catch geladen, ein fehlerhaftes Plugin
kann die Seite nicht lahmlegen.
config.php
├─ database.php (getDB)
├─ themes.php (Theme-Helper)
└─ plugins.php
└─ radiocms_load_plugins()
├─ plugins/a/plugin.json → try: require index.php
├─ plugins/b/plugin.json → catch: log, skip
└─ plugins/c/plugin.json → try: require index.php
Nach dem Laden liegen alle registrierten Actions, Filters und Widgets im RAM bereit.
Werden Hook-Punkte im Frontend erreicht, werden die entsprechenden Callbacks
(wieder in try/catch) ausgeführt.
Was ist ein Plugin?
Ein Plugin ist ein Ordner unter /plugins/ mit mindestens einer plugin.json
und einer Haupt-PHP-Datei (meist index.php).
Plugin-Struktur
plugins/
└── mein-plugin/
├── plugin.json # Metadaten (Pflicht)
├── index.php # Einstiegspunkt (Pflicht)
├── assets/ # CSS/JS/Bilder (optional)
│ └── style.css
└── includes/ # Eigene Helper (optional)
└── helper.php
plugin.json, Felder
| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
id | string | ✓ | Eindeutige ID, [a-z0-9-]+ |
name | string | ✓ | Anzeigename |
version | string | ✓ | SemVer (z.B. 1.0.0) |
author | string | ✓ | Autor |
description | string | - | Kurze Beschreibung |
main | string | - | Einstiegsdatei (default: index.php) |
enabled | bool | - | Standard-Status (wird beim Installieren verwendet) |
requires | string | - | Mindest-RadioCMS-Version |
author_url | string | - | Autor-Website |
Actions Action
Actions sind Ereignis-Hooks. Du registrierst einen Callback, der an einer bestimmten Stelle ausgeführt wird (z.B. im Footer, nach dem Player, …).
Hinzufügen
radiocms_add_action(string $hook, callable $callback, int $priority = 10): void
Niedrigere $priority = früher ausgeführt. Default ist 10.
radiocms_add_action('footer', function() {
echo '<script>console.log("Plugin loaded");</script>';
});
Auslösen (nur im Core)
radiocms_do_action(string $hook, mixed ...$args): void
Der Core ruft diese an den definierten Stellen auf, du machst das normalerweise nicht selbst.
Verfügbare Actions
| Hook | Ort | Args |
|---|---|---|
wp_head | Vor </head> | - |
footer | Vor </body> | - |
home_top | Homepage, vor Hero | - |
home_after_player | Homepage, nach Hero | - |
home_bottom | Homepage, vor Footer | - |
Wir erweitern die Hook-Liste laufend. Brauchst du einen bestimmten Hook? Schreib uns, wir fügen sinnvolle hinzu.
Filters Filter
Filters verändern einen Wert. Sie bekommen einen Input, dein Callback gibt einen (möglicherweise veränderten) Wert zurück.
radiocms_add_filter(string $hook, callable $callback, int $priority = 10): void
radiocms_add_filter('station_name', function(string $name): string {
return $name . ' 🎵';
});
radiocms_add_filter('navbar_items', function(array $items): array {
$items[] = [
'label' => 'Blog',
'url' => '/blog.php',
'slug' => 'blog',
];
return $items;
});
Verfügbare Filters
| Hook | Input | Beschreibung |
|---|---|---|
station_name | string | Name des Radios |
navbar_items | array | Zusätzliche Navbar-Einträge |
Widgets
Widgets sind UI-Blöcke, die Admins frei an Positionen (Footer, Sidebar, Homepage, …) platzieren können. Dein Plugin registriert sie, der Admin entscheidet wo sie erscheinen.
radiocms_register_widget(
string $id,
string $name,
callable $render,
array $options = []
): void
radiocms_register_widget(
id: 'wetter-widget',
name: 'Wetter',
render: function() {
echo '<div class="card p-3">';
echo '<h5>🌤️ 22°C in Berlin</h5>';
echo '<p class="text-muted">Sonnig, leichter Wind</p>';
echo '</div>';
},
options: [
'description' => 'Zeigt aktuelles Wetter an.',
'icon' => 'bi-cloud-sun',
'plugin' => 'wetter-plugin',
]
);
Der Render-Callback kann beliebiges HTML ausgeben, auch Bootstrap, eigenes CSS, JS-Snippets etc.
Widget-Positionen
| Position | Wo wird gerendert |
|---|---|
home_top | Startseite, ganz oben |
home_after_player | Startseite, nach dem Player |
home_bottom | Startseite, vor dem Footer |
footer | Footer (jede Seite) |
admin_dashboard | Admin-Dashboard |
Fehlerbehandlung
Du musst dich nicht um Try/Catch kümmern, der Core fängt alle Fehler. Wenn dein Plugin eine Exception wirft, wird sie geloggt und das Plugin übersprungen.
Error-Log: /logs/plugin_errors.log
[2026-04-16 14:32:01] [PLUGIN:action:footer] Undefined variable $foo
Im Admin unter Plugins siehst du Fehlermeldungen der Plugins direkt.
Core-Module
Core-Module sind die eingebauten Features: Sendeplan, News, DJs, Team, Partner, Song-Wünsche, Podcasts, Chat, DJ-Bewerbungen, Kontakt.
Admins können sie unter Admin → Module einzeln aktivieren oder deaktivieren.
Deaktivierte Module:
- Verschwinden aus der Frontend-Navigation
- Verschwinden aus der Admin-Sidebar
- Zeigen 404 wenn man die URL direkt aufruft
- Verschwinden aus den Footer-Links
Ein Modul durch ein Plugin ersetzen
Du willst den eingebauten Sendeplan durch deinen eigenen ersetzen?
- Im Admin unter Module das Modul
scheduledeaktivieren - In deinem Plugin einen eigenen Endpoint anbieten, z.B.
/mein-sendeplan.phpoder via Plugin-Router - Über
navbar_items-Filter einen eigenen Nav-Eintrag hinzufügen
<?php
// Eigenen Navbar-Eintrag
radiocms_add_filter('navbar_items', function(array $items): array {
if (!radiocms_module_active('schedule')) {
// Nur wenn Core-Modul deaktiviert
$items[] = [
'label' => 'Sendeplan',
'url' => '/plugins/mein-sendeplan/view.php',
'slug' => 'schedule',
];
}
return $items;
});
Alle Core-Module
| Module-ID | Feature |
|---|---|
schedule | Sendeplan |
news | News |
djs | DJs |
team | Team |
partners | Partner |
requests | Song-Wünsche |
podcasts | Podcasts |
chat | Chat |
applications | DJ-Bewerbungen |
contact | Kontakt |
Theme-Entwicklung
Themes in RadioCMS sind bewusst minimalistisch: nur CSS und Bilder, kein PHP. So können Themes die Seite nicht kaputt machen.
Theme-Struktur
mein-theme.zip
├── theme.json # Metadaten (Pflicht)
├── style.css # CSS-Overrides (Pflicht)
├── screenshot.png # Vorschaubild (optional, 16:9 empfohlen)
└── assets/ # Bilder, Fonts (optional)
├── logo.png
└── background.jpg
theme.json
{
"id": "mein-theme",
"name": "Mein Theme",
"version": "1.0.0",
"author": "Dein Name",
"author_url": "https://example.com",
"description": "Dunkles Theme mit orangenen Akzenten.",
"requires": "1.0.0",
"tags": ["dark", "orange", "modern"]
}
CSS-Variablen
RadioCMS nutzt CSS-Custom-Properties für alle Farben. Dein Theme überschreibt sie einfach:
:root {
--primary: #ff6b35; /* Hauptfarbe (z.B. Buttons) */
--primary-dark: #e0551d; /* Hover-Farbe */
--secondary: #ffd23f; /* Akzentfarbe */
--bg-primary: #0d0d1a; /* Haupt-Hintergrund */
--bg-card: #1a1a2e; /* Karten-Hintergrund */
--text-main: #ffffff; /* Haupt-Textfarbe */
--text-muted: #94a3b8; /* Sekundärer Text */
--border-color: #2a2f42; /* Rahmen */
}
/* Navbar-Logo in Theme-Farbe */
.navbar-brand {
color: var(--primary) !important;
}
/* Hero-Bereich mit Gradient */
.hero-section {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
}
Upload & Validierung
Der Admin lädt dein Theme als ZIP unter Admin → Themes hoch. RadioCMS validiert
streng und zeigt eine klare Fehlermeldung wenn etwas nicht passt:
| Prüfung | Fehlermeldung bei Verstoß |
|---|---|
Nur .zip | „Datei muss eine .zip sein" |
| Max 5 MB | „ZIP zu groß: 12 MB. Maximum: 5 MB" |
theme.json vorhanden | „theme.json fehlt im ZIP-Archiv" |
| JSON valide | „theme.json ist kein valides JSON: …" |
| Pflichtfelder | „theme.json fehlt Pflichtfeld: author" |
| ID-Pattern | „Theme-ID 'My Theme!' ist ungültig" |
style.css vorhanden | „style.css fehlt im ZIP-Archiv" |
| Keine PHP-Dateien | „Unerlaubte Datei im ZIP: evil.php" |
Keine .htaccess | „Unerlaubte Datei im ZIP: .htaccess" |
| Kein Path-Traversal | „Verdächtiger Dateipfad im ZIP: ../../../etc/passwd" |
PHP-Dateien werden aus Sicherheitsgründen komplett verweigert. Der /themes/-Ordner hat zusätzlich eine .htaccess die PHP-Ausführung blockiert, mehrfacher Schutz.
API-Referenz
Plugin-Funktionen
| Funktion | Beschreibung |
|---|---|
radiocms_add_action($hook, $cb, $priority=10) | Action-Hook registrieren |
radiocms_do_action($hook...$args) | Alle Actions eines Hooks ausführen (Core) |
radiocms_add_filter($hook, $cb, $priority=10) | Filter-Hook registrieren |
radiocms_apply_filters($hook, $value...$args) | Filter anwenden (Core) |
radiocms_register_widget($id, $name, $render, $options=[]) | Widget registrieren |
radiocms_render_widgets($position) | Alle Widgets einer Position rendern (Core) |
radiocms_plugin_loaded($id): bool | Ist ein bestimmtes Plugin aktiv? |
radiocms_log_plugin_error($context, $msg) | Eigenen Fehler loggen |
Modul-Funktionen
| Funktion | Beschreibung |
|---|---|
radiocms_module_active($id): bool | Ist ein Core-Modul aktiv? |
radiocms_require_module($id) | 404 wenn Modul deaktiviert (für Seitenschutz) |
radiocms_get_core_modules(): array | Liste aller Core-Module |
Theme-Funktionen
| Funktion | Beschreibung |
|---|---|
radiocms_get_active_theme(): string | ID des aktuell aktiven Themes |
radiocms_get_theme_url(): string | URL-Pfad zum aktiven Theme |
radiocms_get_all_themes(): array | Liste aller installierten Themes |
Nützliche Core-Funktionen
| Funktion | Beschreibung |
|---|---|
getSetting($key, $default=''): string | Einstellung aus settings-Tabelle lesen |
getDB(): PDO | Datenbank-Verbindung (PDO) |
t($key): string | Übersetzung (nur in Callbacks, nicht beim Plugin-Laden!) |
Hook-Übersicht (Spickzettel)
Actions
// ── Frontend ──
radiocms_add_action('wp_head', fn() => /* CSS/JS in Head */);
radiocms_add_action('footer', fn() => /* Skripte am Ende */);
radiocms_add_action('home_top', fn() => /* Oben auf Startseite */);
radiocms_add_action('home_after_player', fn() => /* Nach Player */);
radiocms_add_action('home_bottom', fn() => /* Vor Footer */);
Filters
radiocms_add_filter('station_name', fn(string $n) => $n);
radiocms_add_filter('navbar_items', fn(array $items) => $items);
Tutorial: Mein erstes Plugin
Wir bauen ein Plugin das einen Gruß in den Footer schreibt.
1. Ordner anlegen
plugins/
└── greetings/
├── plugin.json
└── index.php
2. Plugin-Manifest
{
"id": "greetings",
"name": "Greetings",
"version": "1.0.0",
"author": "Du",
"description": "Zeigt einen Gruß im Footer.",
"main": "index.php"
}
3. Logik
<?php
radiocms_add_action('footer', function() {
$hour = (int)date('H');
if ($hour < 12) $greet = '🌅 Guten Morgen';
elseif ($hour < 18) $greet = '☀️ Guten Tag';
else $greet = '🌙 Guten Abend';
echo '<div class="alert alert-info text-center m-3">';
echo htmlspecialchars($greet) . ', schön dass du da bist!';
echo '</div>';
});
4. Im Admin aktivieren
Gehe zu Admin → Plugins, finde „Greetings", klick auf Aktivieren. Fertig.
Tutorial: Ein Widget bauen
Wir bauen ein Stream-Statistik-Widget das die aktuelle Hörerzahl zeigt.
<?php
radiocms_register_widget(
id: 'listener-stats',
name: 'Hörer-Statistik',
render: function() {
// Hörerzahl aus Icecast-Status lesen (gecacht)
$status = @file_get_contents('http://localhost:8000/status-json.xsl');
$data = @json_decode($status, true);
$listeners = $data['icestats']['source']['listeners'] ?? '?';
?>
<div class="card bg-dark text-white p-3 mb-3">
<div class="d-flex align-items-center gap-3">
<i class="bi bi-people-fill fs-2 text-primary"></i>
<div>
<div class="small text-muted">Hören gerade zu</div>
<div class="fw-bold fs-4"><?= htmlspecialchars((string)$listeners) ?></div>
</div>
</div>
</div>
<?php
},
options: [
'description' => 'Zeigt aktuelle Hörerzahl.',
'icon' => 'bi-people-fill',
'plugin' => 'listener-stats',
]
);
Der Admin geht unter Plugins → Widgets, wählt das Widget,
die Position (z.B. sidebar) und speichert.
Tutorial: Eigenes Theme
Wir bauen ein einfaches „Sunset" Theme mit orangen Akzenten.
1. Dateien erstellen
sunset/
├── theme.json
├── style.css
└── screenshot.png
2. theme.json
{
"id": "sunset",
"name": "Sunset",
"version": "1.0.0",
"author": "Du",
"description": "Warmes Theme mit orangen und gelben Tönen.",
"tags": ["warm", "orange", "sunset"]
}
3. style.css
:root {
--primary: #ff6b35;
--primary-light: #ff9b7a;
--secondary: #ffd23f;
--bg-primary: #1a0f0a;
--bg-card: #2d1810;
--border-color: #4a2817;
}
/* Hero mit Sonnenuntergang-Gradient */
.hero-section {
background:
linear-gradient(180deg, rgba(26, 15, 10, 0.4), rgba(26, 15, 10, 0.9)),
linear-gradient(135deg, #ff6b35, #ffd23f);
}
/* Buttons warm */
.btn-primary-radio.btn-hero-primary {
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: #1a0f0a;
font-weight: 700;
}
/* Logo orange */
.brand-icon i { color: var(--primary); }
/* Player-Akzente */
.player-play-btn {
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: #1a0f0a;
}
4. ZIP erstellen & hochladen
- Ordner
sunset/zippen (wichtig: ordnerinhalt zippen, nicht den Ordner selbst rein) - Im Admin unter
Themeshochladen - Aktivieren & fertig
Ein screenshot.png (Größe 1280×720 oder 16:9) macht dein Theme im Admin deutlich ansprechender.
FAQ
Ist RadioCMS Community wirklich komplett kostenlos?
Ja, MIT-Lizenz. Du darfst es kostenlos nutzen, anpassen, weitergeben, auch kommerziell einsetzen.
Darf ich Plugins verkaufen?
Ja, absolut. Deine Plugins gehören dir, du entscheidest über deren Lizenz.
Wer baut Custom Plugins auf Anfrage?
Der RadioCMS-Autor bietet das als bezahlten Service an, siehe radio.dgnshop.com. Die Community wächst ebenfalls.
Kann ich mehrere Themes gleichzeitig haben?
Installieren: ja. Aktiv: nur eins. Du kannst aber jederzeit wechseln.
Was passiert wenn mein aktives Theme plötzlich kaputt ist?
RadioCMS fällt automatisch auf das Default-Theme zurück. Deine Seite bleibt online.
Kann ich Hooks in eigenen Templates definieren?
Ja, benutze radiocms_do_action('mein_hook') in deinen Plugin-Templates. Andere Plugins können sich dann einhaken.
Warum keine PHP-Dateien in Themes?
Sicherheit. Ein kaputtes PHP-Theme könnte die Seite sprengen oder als Einfallstor für Angreifer dienen. CSS allein reicht für 99% aller Designwünsche, für komplexe Logik nimmst du ein Plugin.
Hilfe & Support
GitHub
Issues melden, Pull Requests einreichen, Quellcode browsen.
Community-Discord
Austausch mit anderen Entwicklern & Radio-Betreibern. (bald)
Custom Plugins
Brauchst du ein spezielles Feature? Wir bauen Custom Plugins auf Anfrage.
Beispiel-Plugin
In der Installation: plugins/hello-world/, Starterpunkt zum Kopieren.
Starter-Templates zum Download
Fertige Boilerplates mit Beispiel-Code und Kommentaren, Zip runterladen, anpassen, loslegen.
Plugin-Template
plugin.json + index.php mit Action-, Filter- und Widget-Beispielen als Kommentar.
plugin-template.zipTheme-Template
theme.json + style.css mit allen CSS-Variablen und auskommentierten Beispielen.
theme-template.zipBereit loszulegen?
Lad dir RadioCMS herunter, schnapp dir ein Starter-Template und bau dein erstes Plugin in 10 Minuten.
RadioCMS herunterladen