Skip to content

Feature/maintenance improvements#159

Closed
skerbis wants to merge 51 commits intomainfrom
feature/maintenance-improvements
Closed

Feature/maintenance improvements#159
skerbis wants to merge 51 commits intomainfrom
feature/maintenance-improvements

Conversation

@skerbis
Copy link
Member

@skerbis skerbis commented Oct 29, 2025

Summary by CodeRabbit

Release Notes – Maintenance AddOn 4.0.0-beta1

  • Neue Funktionen

    • Silent Mode für reine HTTP-Status-Responses
    • Zeitbasierte Wartung mit Cronjob-Unterstützung
    • Wartungsplanungsseite für geplante Wartungen
    • Mehrsprachige Wartungsseite (DE/EN mit Sprachwechsler)
    • Domänenverwaltung direkt in der Oberfläche
    • Erweiterte Konsolenbefehle für Wartungsmodus-Steuerung
  • Breaking Changes

    • Manuelle Domänen-Whitelist entfernt – nur noch über Maintenance > Domains
  • Verbesserungen

    • Überarbeitete Benutzeroberfläche mit modernem Design und Dark Mode
    • Vereinfachte IP-Whitelist-Verwaltung
    • Automatische Konfigurationsmigration

skerbis and others added 30 commits October 29, 2025 17:57
- Emojis durch Font Awesome 6 Icons ersetzt
- YRewrite Domain-Verwaltung hinzugefügt mit Migration
- Settings in übersichtliche Unterseiten reorganisiert (Wartung, Einstellungen, Ankündigung, Domains)
- Mehrsprachige Wartungstexte (Deutsch/Englisch) mit Sprachswitcher
- Modernes upkeep-inspiriertes Design übernommen
  - Card-basiertes Layout mit Animationen
  - Dark Mode Support
  - Responsive Design
  - Dropdown-Sprachauswahl mit Globe-Icon
- IP-Adressen per Klick zur Whitelist hinzufügen
- Login-Formular nur bei gesetztem Passwort anzeigen
- Intelligente Bypass-URL-Anzeige (nur für gesperrte Domains)
- Sidebar-Komponente für alle Frontend-Seiten
- Editor-Konfiguration in Einstellungen verschoben
- Begriffe vereinheitlicht (Wartungsmodus -> Wartung)
- Weiterleitungs-URL zu allgemeinen Einstellungen verschoben
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Check rex_addon::exists('yrewrite') before calling methods
- Prevents exceptions when yrewrite addon is not installed
- Affected files: frontend.php, frontend.sidebar.php (2x), Maintenance.php (2x)
- Fix: Internationalize hardcoded German text 'hinzugefügt'
  Added maintenance_ip_added translation key

- Fix: Correct spelling 'authentification' to 'authentication'
  Updated in package.yml, lang files, PHP code
  Added migration for existing installations

- Already fixed: Ternary operator logic in frontend.php
- Already fixed: YRewrite existence checks
- Fix login.php: Use authentication_mode instead of authentification_mode
- Improve update.php migration: Always remove old config key
- All files now consistently use 'authentication' (correct spelling)
README changes:
- Set Thomas Skerbis as lead developer
- Remove sponsor references
- Update project team structure

Console command enhancements:
- Add 'status' command to show current maintenance state
- Add 'frontend on/off' for frontend maintenance
- Add 'backend on/off' for backend maintenance
- Add 'all on/off' to toggle all modes at once
- Add 'domain <name> --lock/--unlock' for YRewrite domains
- Maintain backward compatibility with legacy 'on/off' commands
- Add comprehensive documentation for all commands
Problem: When only German text (maintenance_text_de) was filled and English
text was empty, no text was displayed because the DE block didn't receive
the 'active' class.

Solution: Add fallback logic for DE block:
- DE is active if multilanguage is disabled OR only DE text exists
- EN is active if multilanguage is disabled OR only EN text exists

This ensures at least one text block is always visible when text is provided.
Removed overly restrictive condition that required both password mode AND
a set secret. The login form should be displayed whenever password mode
is selected, regardless of whether a secret is actually configured.

This allows users to see the login form immediately after selecting
password authentication mode, even before setting a password.
- Add id attribute to password input field for better accessibility
- Add autocomplete='off' for better security
- Improve code formatting and comments
- Add debug comment placeholder for troubleshooting
Implements feature request for permanent site blocking without maintenance
messaging, ideal for staging environments in deployment processes.

Features:
- New 'silent_mode' config option
- Sends only HTTP status code (503/403) without HTML content
- Prevents information disclosure about CMS
- Perfect for staging systems that should be permanently offline
- Accessible only via backend login, IP whitelist, or secret URL

Changes:
- Added silent_mode to package.yml default_config
- Added silent_mode checkbox in advanced settings
- Implemented logic in Maintenance::checkFrontend()
- Added German translations
- Updated README with Silent Mode documentation

Use case: Production preview systems that need permanent protection
without revealing 'maintenance mode' or CMS information.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- Behebt Ternary-Operator Fehler: EN-Text war immer als 'active' markiert
- DE-Text ist jetzt standardmäßig 'active', EN-Text nur wenn kein DE-Text vorhanden
- DE-Text ist jetzt standardmäßig aktiv
- EN-Text nur aktiv wenn kein DE-Text vorhanden
- Konsistente Logik zwischen frontend.php und frontend_old.php
- Behebt kritischen Bug: Early-Return umging block_frontend-Schalter
- checkFrontend() prüft jetzt sowohl block_frontend als auch domainbasierte Wartung
- Frontend wird nur dann erlaubt, wenn BEIDE Flags inaktiv sind

Fixes: #156 (comment)
- YRewrite Existenz-Check in domains.php hinzugefügt (verhindert Exception)
- Redundanten Domain-Check in boot.php entfernt (isDomainInMaintenance in checkFrontend)
- Silent Mode Check früher durchgeführt (direkt nach Maintenance-Check)
  * Spart unnötige IP/Host/Secret-Checks bei Silent Mode
  * Verbessert Performance für Staging-Umgebungen
Implementiert automatische Aktivierung/Deaktivierung des Wartungsmodus:

Features:
- Request-basierte Prüfung: Automatische Aktivierung bei jedem Request
- Optionaler Cronjob für präzisere Steuerung (jede Minute)
- Konfiguration über Start- und Endzeitpunkt (YYYY-MM-DD HH:MM:SS)
- Automatische Bereinigung nach Deaktivierung
- Status-Anzeige im Backend

Neue Config-Felder:
- scheduled_start: Automatischer Aktivierungszeitpunkt
- scheduled_end: Automatischer Deaktivierungszeitpunkt

Neue Klassen:
- Maintenance::checkScheduledMaintenance(): Request-basierte Prüfung
- rex_cronjob_scheduled_maintenance: Optionaler Cronjob

UI-Erweiterungen:
- Zeitgesteuerte Wartung Sektion in Frontend-Einstellungen
- Echtzeit-Status der geplanten Wartung
- Mehrsprachige Labels (DE)

Dokumentation:
- README erweitert mit Verwendungsbeispielen
- Inline-Kommentare für alle neuen Funktionen
Probleme behoben:
- Button zum Hinzufügen der eigenen IP fehlte in Sidebar
- JavaScript nutzt jetzt Event-Delegation (document.on)
- Timeout für Tokenfield-Initialisierung hinzugefügt
- Feedback für bereits vorhandene IPs

Verbesserungen:
- Zeigt eigene IP-Adresse in Sidebar (nur auf Advanced-Seite)
- Button wechselt zu Erfolg (grün) oder Warnung (gelb)
- CSP-Nonce für inline JavaScript
- Neue Sprachschlüssel: maintenance_my_ip_title, maintenance_add_my_ip
- rex_i18n::msg() funktioniert nicht im Frontend (AddOn-Sprachdateien nicht geladen)
- Hardcode 'Language' statt translate:maintenance_language
- Behebt [translate:maintenance_language] Anzeige im Frontend
Fixes:
- $currentPage wird jetzt am Anfang der Sidebar definiert (Zeile 8 statt 94)
- Verhindert 'Undefined variable' Warning

IP-Button Verbesserungen:
- waitForTokenfield() Funktion für robuste Tokenfield-Initialisierung
- Verwendet tokenfield('getTokens') statt String-Splitting
- Besseres Timing-Handling mit Retry-Mechanismus
- Entfernt btn-xs Klasse bei Feedback für bessere Sichtbarkeit
- Console-Fehlerbehandlung
- Tokenfield-Abhängigkeit entfernt, einfaches Textfeld verwendet
- IP-Sektion aus Sidebar entfernt
- Vereinfachte JavaScript-Logik nach Upkeep-Vorbild
- Verbesserte UI mit formatierten IP-Zeilen
- Sprachschlüssel ergänzt (maintenance_your_ip, maintenance_server_ip, etc.)
- package.yml: Cronjob-Definition vereinfacht (war falsch formatiert)
- boot.php: Tokenfield JavaScript/CSS entfernt (nicht mehr benötigt)
- Cronjob sollte nun im Cronjob-Addon sichtbar sein
skerbis and others added 21 commits October 29, 2025 19:51
- lib/cronjob/scheduled_maintenance.php → lib/cronjob.php (REDAXO-Standard)
- package.yml: Cronjob-Deklaration entfernt (automatische Erkennung)
- Inline JavaScript nach assets/js/frontend-advanced.js ausgelagert
- Inline JavaScript nach assets/js/domains.js ausgelagert
- Inline CSS nach assets/css/ip-addresses.css ausgelagert
- IP-Adressen als data-Attribute statt PHP-Interpolation
- Fragments behalten inline CSS/JS (Sperrseiten)
- Überschrift zeigt 'Maintenance' (EN) / 'Wartung' (DE) je nach Sprachwahl
- Domain wird unter der Überschrift angezeigt (YRewrite oder ServerName)
- CSS angepasst für inline maintenance-text Spans in Überschrift
- Funktioniert nur bei aktivierter Mehrsprachigkeit
- max-width: 450px, width: 100% für Container
- padding-top: 4rem (Desktop) / 3.5rem (Mobile) für Platz über Icon
- Language-Switcher bleibt in oberer rechter Ecke (auch mobil)
- Kompaktere Buttons auf Mobile (120px statt 140px)
- Verhindert Überlappung mit Info-Icon
- Überschrift 'Wartung' (DE) wird initial angezeigt
- Entspricht dem Verhalten der Texte (DE ist Standard)
- Sprachwechsler funktioniert wie bisher
- Language-Option DE initial als 'active' markiert
- Überschrift 'Wartung' (DE) mit 'active' Klasse
- Konsistent mit den Nachrichtentexten (DE ist Standard)
- Sprachwechsler zeigt nun korrekt DE als gewählte Sprache
- Initial 'Deutsch' statt 'Language'
- Button-Text wechselt bei Sprachwahl (English/Deutsch)
- Extrahiert Sprachname aus Option-Text (ohne EN/DE Prefix)
- Konsistent mit aktivem Dropdown-Item
- execute(): bool → execute()
- getTypeName(): string → getTypeName()
- getParamFields(): array → getParamFields()
- Entspricht REDAXO-Standard (wie PHPMailer, Backup)
- Cronjob sollte nun in Auswahl erscheinen
- Feldauswahl mit name-Attribut statt ID (rex_config_form Standard)
- Event-Delegation mit $(document).on() für dynamische Elemente
- Null-Check für data-ip Attribut
- Error-Logging wenn Feld nicht gefunden
- Entspricht genau der Upkeep-Implementierung
- Exakte Struktur wie phpmailer cronjob
- Eigenständige Logik ohne Aufruf von Maintenance::checkScheduledMaintenance()
- Direkte Config-Manipulation für aktivieren/deaktivieren
- Besseres Logging mit Status-Updates
- rex_cronjob_manager::registerType() im boot.php
- Prüfung ob Cronjob-Addon verfügbar ist
- Folgt dem Standard von Backup/PHPMailer AddOns
- Schritt-für-Schritt-Anleitung zur Cronjob-Konfiguration
- Erklärung aller relevanten Einstellungen
- Hinweis auf optionale Nutzung (Request-basiert funktioniert auch)
- checkScheduledMaintenance() aus checkFrontend() entfernt
- Cronjob-Status-Warnung in Frontend-Einstellungen
- Direkt-Link zum Cronjob erstellen wenn nicht vorhanden
- README und Sprachdateien aktualisiert
- Klarstellung: Funktion nur über Cronjob, nicht request-basiert
…niert

- Neue Unterseite 'Planung' (frontend.scheduled.php) unter Frontend
- Zeitgesteuerte Wartung von frontend.index.php entfernt
- Wartungsankündigung von frontend.announcement.php integriert
- Beide Features jetzt unter Maintenance > Frontend > Planung
- package.yml: scheduled als Unterseite von frontend
- Icon: fa-calendar-alt für Planungs-Seite
- boot.php: OUTPUT_FILTER für frontend/scheduled aktiviert
- Editor-Einstellung wird wieder berücksichtigt
- Sidebar mit Schnellzugriffen und Code-Beispiel für Ankündigung
Copilot AI review requested due to automatic review settings October 29, 2025 20:32
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

Caution

Review failed

The pull request is closed.

Moin.

Walkthrough

Das Maintenance AddOn wird auf Version 4.0.0-beta1 erweitert mit zeitgesteuerten Wartungsfenstern (cron-basiert), Silent-Mode für HTTP-Only-Responses, mehrsprachiger Frontend-Wartungsseite, domänenspezifischen Wartungssperren via YRewrite und erweiterten CLI-Befehlen. Die Konfiguration wird migriert, das UI reorganisiert und Legacy-Kompatibilität gewährleistet.

Changes

Cohort / Datei(en) Zusammenfassung
Dokumentation
README.md, RELEASE.md
Dokumentation aktualisiert mit neuen Features (zeitgesteuerte Wartung, Silent-Mode, Planung, multilinguales UI, Domains) und Konfigurationsmigration
CSS-Styling
assets/css/ip-addresses.css, assets/css/maintenance-icons.css
Neue Stylesheets für IP-Whitelist-UI (Flexbox-Layout) und Font-Awesome-basierte Icons (Status, Auth-Modi, Badges)
JavaScript-Assets
assets/js/domains.js, assets/js/frontend-advanced.js
Domains-Toggle für Sections-Sichtbarkeit; IP-Whitelist-Verwaltung mit Duplikatsprüfung und Datenvalidierung
Core-Logik
lib/Maintenance.php
Neue Methoden isDomainInMaintenance() und checkScheduledMaintenance() für domänenbasierte und zeitgesteuerte Wartung; setIndicators() erweitert
CLI-Befehle
lib/command/maintenance_mode.php
Neue Action-basierte Struktur mit showStatus, toggleFrontend, toggleBackend, handleDomain, toggleAll; granulare Kontrolle über CLI
Cronjob-Typ
lib/cronjob.php
Neue Klasse rex_cronjob_scheduled_maintenance für automatische Aktivierung/Deaktivierung in konfigurierten Zeitfenstern
Konfiguration & Migration
package.yml, update.php
Neue Config-Keys (maintenance_secret, domain_status, all_domains_locked, silent_mode, scheduled_start/end); Migration von authentification_mode→authentication_mode und allowed_yrewrite_domains→domain_status
Wartungs-Fragments
fragments/maintenance/frontend.php, fragments/maintenance/login.php
Mehrsprachiges Frontend mit DE/EN-Umschalter, SessionStorage-Persistenz; Login-Form mit neuen Attributen (id, autocomplete)
Backend-Seiten (Neue)
pages/frontend.index.php, pages/frontend.advanced.php, pages/frontend.scheduled.php, pages/domains.php, pages/frontend.sidebar.php
Neue Verwaltungsseiten für Basis-Einstellungen, Erweiterte Einstellungen, zeitgesteuerte Wartung/Ankündigung, Domain-Locks; Sidebar mit Quick-Links und Bypass-URLs
Backend-Seiten (Entfernt)
pages/frontend.php
Alte monolithische Frontend-Einstellungsseite aufgelöst in spezialisierte Seiten (index, advanced, scheduled)
Sprachdateien
lang/de_de.lang, lang/en_gb.lang
Umfangreiche Rebennung und Erweiterung deutscher/englischer Keys (z. B. Wartungsmodus→Wartung, neue Keys für Planung, Domains, IPs); neue UI-Labels
Bootstrap
boot.php
Cronjob-Typ registriert; Frontend-Gating erweitert um isDomainInMaintenance(); JS-Assets bereinigt, CSS-Assets hinzugefügt; OUTPUT_FILTER auf neue Seiten ausgedehnt

Sequence Diagram(s)

sequenceDiagram
    participant Cron as Cronjob<br/>(periodisch)
    participant Check as checkScheduledMaintenance()
    participant Config as Config
    participant Frontend as Frontend<br/>Blocking
    
    Cron->>Check: Führe aus
    Check->>Config: Lese scheduled_start,<br/>scheduled_end, block_frontend
    
    alt Zeitfenster aktiv & blocking inaktiv
        Check->>Config: Setze block_frontend=true
        Check->>Frontend: Wartung aktiviert
    else Zeitfenster abgelaufen & blocking aktiv
        Check->>Config: Setze block_frontend=false<br/>Lösche Zeiten
        Check->>Frontend: Wartung deaktiviert
    else Kein Zustandswechsel
        Check->>Frontend: Status unverändert
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Zeitgesteuerte Wartung: Neue checkScheduledMaintenance()-Logik mit Zeitfenster-Berechnung und Auto-Deaktivierung erfordert sorgfältige Überprüfung auf Zeitzonen-Handling und Edge-Cases
  • Domain-Maintenance: isDomainInMaintenance() integriert YRewrite-Domains und globale Locks; Wechselwirkung mit existing block_frontend-Logik prüfen
  • Konfigurationsmigration: update.php migriert allowed_yrewrite_domains zu domain_status-Mapping; Auswirkungen auf Legacy-Installationen validieren
  • CLI-Kommandos: Fünf neue Private-Methoden mit Action-Branching; toggleAll-Konsistenz über Frontend/Backend/All-Domains
  • Multilinguales Frontend: Große inlined-CSS-Blöcke und SessionStorage-basierter State in frontend.php; responsive Design und Language-Toggle-Logik überprüfen
  • Page-Refaktorierung: Alte monolithische frontend.php in fünf Speziaseiten aufgeteilt; UI-Kohärenz und Formular-Validierung across pages überprüfen

Possibly related PRs

Suggested reviewers

  • alxndr-w
  • crydotsnake
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/maintenance-improvements

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20d8a39 and 40470c9.

📒 Files selected for processing (22)
  • README.md (3 hunks)
  • RELEASE.md (1 hunks)
  • assets/css/ip-addresses.css (1 hunks)
  • assets/css/maintenance-icons.css (1 hunks)
  • assets/js/domains.js (1 hunks)
  • assets/js/frontend-advanced.js (1 hunks)
  • boot.php (2 hunks)
  • fragments/maintenance/frontend.php (1 hunks)
  • fragments/maintenance/login.php (1 hunks)
  • lang/de_de.lang (3 hunks)
  • lang/en_gb.lang (1 hunks)
  • lib/Maintenance.php (6 hunks)
  • lib/command/maintenance_mode.php (3 hunks)
  • lib/cronjob.php (1 hunks)
  • package.yml (3 hunks)
  • pages/domains.php (1 hunks)
  • pages/frontend.advanced.php (1 hunks)
  • pages/frontend.index.php (1 hunks)
  • pages/frontend.php (0 hunks)
  • pages/frontend.scheduled.php (1 hunks)
  • pages/frontend.sidebar.php (1 hunks)
  • update.php (2 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@skerbis skerbis closed this Oct 29, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request introduces version 4.0.0-beta1 of the Maintenance addon with significant feature enhancements and a major refactoring of the UI and architecture. The update focuses on improving domain-specific maintenance management, adding scheduled maintenance capabilities, and modernizing the user interface.

Key Changes:

  • Adds scheduled/timed maintenance mode with cronjob support
  • Introduces domain-specific maintenance management for YRewrite
  • Implements Silent Mode for staging environments
  • Refactors frontend settings pages into modular subpages (index, advanced, scheduled)
  • Corrects misspelling: authentification_modeauthentication_mode

Reviewed Changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
update.php Migration logic for config renaming and domain status conversion
package.yml Version bump, new subpages, updated default configs
lib/Maintenance.php New methods for domain-based and scheduled maintenance
lib/cronjob.php New cronjob class for scheduled maintenance
lib/command/maintenance_mode.php Enhanced CLI with domain management and status commands
pages/frontend.index.php New main frontend settings page (split from old frontend.php)
pages/frontend.advanced.php New admin-only advanced settings page
pages/frontend.scheduled.php New scheduled maintenance configuration page
pages/frontend.sidebar.php Shared sidebar for frontend pages with bypass URLs
pages/domains.php New domain management interface for YRewrite
fragments/maintenance/frontend.php Complete redesign with multilingual support and modern UI
fragments/maintenance/login.php Updated authentication mode reference
boot.php Cronjob registration and domain-based maintenance check
lang/*.lang Updated translations with new features
assets/js/*.js New JS for IP management and domain toggles
assets/css/*.css New styling for icons and IP addresses

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// Authentifizierung prüfen - für URL-Parameter und auch bei leerem Modus
$authentification_mode = (string) self::getConfig('authentification_mode', '');
if (('' === $authentification_mode || 'URL' === $authentification_mode || 'password' === $authentification_mode)
$authentication_mode = (string) self::getConfig('authentication_mode', '');
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate assignment: $authentication_mode is already assigned on line 169. Remove the redundant assignment on line 172.

Suggested change
$authentication_mode = (string) self::getConfig('authentication_mode', '');

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +5
// Debug output - remove this later
// echo '<!-- DEBUG: authentication_mode = ' . htmlspecialchars($authentication_mode) . ' -->';
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented debug code should be removed before release. This debug output is no longer needed in production code.

Suggested change
// Debug output - remove this later
// echo '<!-- DEBUG: authentication_mode = ' . htmlspecialchars($authentication_mode) . ' -->';

Copilot uses AI. Check for mistakes.
}

if (rex_addon::get('cronjob')->isAvailable()) {
$content .= '<li class="list-group-item"><a href="' . rex_url::backendPage('cronjob/cronjobs') . '"><i class="rex-icon fa-clock-o"></i> Cronjob-Verwaltung</a></li>';
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded German text 'Cronjob-Verwaltung' should use i18n translation for consistency with the rest of the addon. Use $addon->i18n('maintenance_cronjob_management') or similar.

Suggested change
$content .= '<li class="list-group-item"><a href="' . rex_url::backendPage('cronjob/cronjobs') . '"><i class="rex-icon fa-clock-o"></i> Cronjob-Verwaltung</a></li>';
$content .= '<li class="list-group-item"><a href="' . rex_url::backendPage('cronjob/cronjobs') . '"><i class="rex-icon fa-clock-o"></i> ' . $addon->i18n('maintenance_cronjob_management') . '</a></li>';

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants