RadioCMS DEV

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:

Community-First

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:

plugins/mein-plugin/plugin.json
{
 "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"
}
plugins/mein-plugin/index.php
<?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

FeldTypPflichtBeschreibung
idstringEindeutige ID, [a-z0-9-]+
namestringAnzeigename
versionstringSemVer (z.B. 1.0.0)
authorstringAutor
descriptionstring-Kurze Beschreibung
mainstring-Einstiegsdatei (default: index.php)
enabledbool-Standard-Status (wird beim Installieren verwendet)
requiresstring-Mindest-RadioCMS-Version
author_urlstring-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.

Beispiel, etwas in den Footer schreiben
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

HookOrtArgs
wp_headVor </head>-
footerVor </body>-
home_topHomepage, vor Hero-
home_after_playerHomepage, nach Hero-
home_bottomHomepage, vor Footer-
Mehr Hook-Punkte kommen

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
Beispiel, Stationsnamen erweitern
radiocms_add_filter('station_name', function(string $name): string {
 return $name . ' 🎵';
});
Beispiel, Navbar-Eintrag hinzufügen
radiocms_add_filter('navbar_items', function(array $items): array {
 $items[] = [
 'label' => 'Blog',
 'url' => '/blog.php',
 'slug' => 'blog',
 ];
 return $items;
});

Verfügbare Filters

HookInputBeschreibung
station_namestringName des Radios
navbar_itemsarrayZusä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
Beispiel, Wetter-Widget
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

PositionWo wird gerendert
home_topStartseite, ganz oben
home_after_playerStartseite, nach dem Player
home_bottomStartseite, vor dem Footer
footerFooter (jede Seite)
admin_dashboardAdmin-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:

Ein Modul durch ein Plugin ersetzen

Du willst den eingebauten Sendeplan durch deinen eigenen ersetzen?

  1. Im Admin unter Module das Modul schedule deaktivieren
  2. In deinem Plugin einen eigenen Endpoint anbieten, z.B. /mein-sendeplan.php oder via Plugin-Router
  3. Über navbar_items-Filter einen eigenen Nav-Eintrag hinzufügen
plugin.php, Beispiel: Eigener Sendeplan
<?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-IDFeature
scheduleSendeplan
newsNews
djsDJs
teamTeam
partnersPartner
requestsSong-Wünsche
podcastsPodcasts
chatChat
applicationsDJ-Bewerbungen
contactKontakt

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

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:

style.css, Einfachstes Theme
: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üfungFehlermeldung 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"
Sicherheit

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

FunktionBeschreibung
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): boolIst ein bestimmtes Plugin aktiv?
radiocms_log_plugin_error($context, $msg)Eigenen Fehler loggen

Modul-Funktionen

FunktionBeschreibung
radiocms_module_active($id): boolIst ein Core-Modul aktiv?
radiocms_require_module($id)404 wenn Modul deaktiviert (für Seitenschutz)
radiocms_get_core_modules(): arrayListe aller Core-Module

Theme-Funktionen

FunktionBeschreibung
radiocms_get_active_theme(): stringID des aktuell aktiven Themes
radiocms_get_theme_url(): stringURL-Pfad zum aktiven Theme
radiocms_get_all_themes(): arrayListe aller installierten Themes

Nützliche Core-Funktionen

FunktionBeschreibung
getSetting($key, $default=''): stringEinstellung aus settings-Tabelle lesen
getDB(): PDODatenbank-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

plugins/greetings/plugin.json
{
 "id": "greetings",
 "name": "Greetings",
 "version": "1.0.0",
 "author": "Du",
 "description": "Zeigt einen Gruß im Footer.",
 "main": "index.php"
}

3. Logik

plugins/greetings/index.php
<?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.

plugins/listener-stats/index.php
<?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

  1. Ordner sunset/ zippen (wichtig: ordnerinhalt zippen, nicht den Ordner selbst rein)
  2. Im Admin unter Themes hochladen
  3. Aktivieren & fertig
Tipp

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.zip

Theme-Template

theme.json + style.css mit allen CSS-Variablen und auskommentierten Beispielen.

theme-template.zip

Bereit loszulegen?

Lad dir RadioCMS herunter, schnapp dir ein Starter-Template und bau dein erstes Plugin in 10 Minuten.

RadioCMS herunterladen