Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
980ccb2cc1 | ||
|
|
db4863de16 | ||
|
|
1016373e63 | ||
|
|
b6ebdde2cf | ||
|
|
0f1c5632d6 | ||
|
|
8bcb7f1303 | ||
|
|
2b162e581f | ||
|
|
90f388f49c | ||
|
|
05a2e1c7b6 | ||
|
|
cfcbcd5b02 | ||
|
|
96d4c07cc6 | ||
|
|
7b58dea6ff | ||
|
|
778563d991 | ||
|
|
d332fac418 | ||
|
|
2a7042bb4e | ||
|
|
78ccb38f36 |
@@ -31,6 +31,9 @@
|
|||||||
- Statistik-Widget mit Shortcode `[statistik_manager]`
|
- Statistik-Widget mit Shortcode `[statistik_manager]`
|
||||||
- Anpassbares Banner mit Position, Farbe und Icon
|
- Anpassbares Banner mit Position, Farbe und Icon
|
||||||
|
|
||||||
|
- **Gast Autoren:**
|
||||||
|
- Alphabetisches Index für Gast Autoren mit Shortcode `[guest_authors]`
|
||||||
|
|
||||||
- **Admin-Tools:**
|
- **Admin-Tools:**
|
||||||
- Dashboard-Widget für Plugin-Updates (mit Pre-Release-Unterstützung)
|
- Dashboard-Widget für Plugin-Updates (mit Pre-Release-Unterstützung)
|
||||||
- Pinnwand für Administratoren
|
- Pinnwand für Administratoren
|
||||||
|
|||||||
@@ -1,193 +0,0 @@
|
|||||||
/* Allgemeines Styling für die Admin-Seite */
|
|
||||||
.wrap {
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 30px;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrap h1 {
|
|
||||||
font-size: 28px;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 700;
|
|
||||||
text-transform: uppercase;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Logo */
|
|
||||||
/* Blaues Banner hinter dem Logo */
|
|
||||||
.statistik-manager-logo {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
background: linear-gradient(135deg, #0073aa, #00c6ff); /* Blaues Farbverlauf-Banner */
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Logo im Banner */
|
|
||||||
.statistik-manager-logo img {
|
|
||||||
max-width: 200px;
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Container, der die Statistikoptionen und die Werbung nebeneinander anordnet */
|
|
||||||
.statistik-manager-content {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 30px;
|
|
||||||
align-items: stretch; /* Stellt sicher, dass beide Boxen die gleiche Höhe haben */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Linke Spalte – Statistik-Einstellungen (größerer Bereich) */
|
|
||||||
.statistik-manager-settings {
|
|
||||||
flex: 2;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Titel in der Auswahlbox "Statistiken anzeigen" */
|
|
||||||
.statistik-manager-settings h2 {
|
|
||||||
font-size: 22px;
|
|
||||||
color: #0073aa;
|
|
||||||
text-transform: uppercase;
|
|
||||||
text-align: center;
|
|
||||||
padding: 10px 0;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
border-bottom: 2px solid #0073aa;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Rechte Spalte – Werbung für weitere Plugins (kleinerer Bereich) */
|
|
||||||
.statistik-manager-advertisement {
|
|
||||||
flex: 1;
|
|
||||||
background-color: #fff;
|
|
||||||
border: 1px solid #eaeaea;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
||||||
height: fit-content;
|
|
||||||
margin-top: 80px; /* Größerer Abstand nach oben */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Werbung – Überschrift */
|
|
||||||
.statistik-manager-advertisement h2 {
|
|
||||||
font-size: 20px;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Werbung – Liste */
|
|
||||||
.statistik-manager-advertisement ul {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistik-manager-advertisement ul li {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistik-manager-advertisement ul li a {
|
|
||||||
font-size: 16px;
|
|
||||||
color: #0073aa;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: color 0.3s ease, transform 0.3s ease;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistik-manager-advertisement ul li a:hover {
|
|
||||||
color: #005177;
|
|
||||||
transform: translateX(5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Formular-Tabelle für die Einstellungen */
|
|
||||||
.form-table {
|
|
||||||
width: 100%;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
||||||
padding: 25px 35px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-table th {
|
|
||||||
font-size: 16px;
|
|
||||||
color: #444;
|
|
||||||
padding-bottom: 15px;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-table td {
|
|
||||||
font-size: 15px;
|
|
||||||
color: #666;
|
|
||||||
padding: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Submit-Button */
|
|
||||||
button.submit, .submit {
|
|
||||||
font-size: 12px; /* Kleinere Schriftgröße */
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Zusätzlicher Abstand nach links für den Text in der Tabellenüberschrift */
|
|
||||||
.form-table th {
|
|
||||||
padding-left: 30px; /* Erhöht den Abstand nach links */
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistik-manager-banner {
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
padding: 10px;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistik-manager-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 20px; /* Abstand zwischen den Boxen */
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistik-manager-plugins,
|
|
||||||
.statistik-manager-advertisement {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistik-manager-plugins h3,
|
|
||||||
.statistik-manager-advertisement h3 {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistik-manager-plugins ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistik-manager-plugins ul li {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistik-manager-plugins ul li a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #0073aa;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistik-manager-plugins ul li a:hover {
|
|
||||||
color: #005077;
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 372 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 641 KiB |
@@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Admin-Panel-Banner & Plugin-Links
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Admin - Panel Banner
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Anzahl Tage, die das Banner nach dem Schließen ausgeblendet bleibt
|
||||||
|
if (!defined('WP_MULTI_BANNER_DAYS')) {
|
||||||
|
define('WP_MULTI_BANNER_DAYS', 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüft, ob das Banner für den aktuellen Benutzer noch ausgeblendet ist
|
||||||
|
function wp_multi_banner_is_dismissed() {
|
||||||
|
$ts = (int) get_user_meta(get_current_user_id(), 'wp_multi_banner_dismissed', true);
|
||||||
|
return $ts > 0 && (time() - $ts) < (WP_MULTI_BANNER_DAYS * DAY_IN_SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Admin-Banner als Notice mit Blauem Hintergrund (#0073aa)
|
||||||
|
function wp_multi_add_warning_banner() {
|
||||||
|
// Nach dem Schließen für WP_MULTI_BANNER_DAYS Tage nicht mehr anzeigen
|
||||||
|
if (wp_multi_banner_is_dismissed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verwende printf für bessere Lesbarkeit und Übersetzbarkeit
|
||||||
|
printf(
|
||||||
|
'<div class="notice notice-warning is-dismissible wp-multi-feedback-notice" data-wpm-nonce="%s" style="background-color: #0073aa; color: white; border-left: 4px solid #005177;">
|
||||||
|
<p><strong>%s</strong> %s <a href="%s" target="_blank" style="color: #FFDD00; text-decoration: none;">%s</a> %s</p>
|
||||||
|
<p><strong>%s:</strong> %s <a href="%s" target="_blank" style="color: #FFDD00; text-decoration: none;">%s</a> %s <a href="%s" target="_blank" style="color: #FFDD00; text-decoration: none;">%s</a> %s</p>
|
||||||
|
</div>',
|
||||||
|
esc_attr(wp_create_nonce('wp_multi_dismiss_banner')),
|
||||||
|
esc_html__('Danke, dass du WP Multi verwendest!', 'wp-multi'),
|
||||||
|
esc_html__('Dein Feedback hilft uns, das Plugin ständig zu verbessern. Wenn du Fehler entdeckst oder Verbesserungsvorschläge hast, besuche bitte unsere', 'wp-multi'),
|
||||||
|
'https://git.viper.ipv64.net/M_Viper/wp-multi',
|
||||||
|
esc_html__('Gitea-Seite', 'wp-multi'),
|
||||||
|
esc_html__('und teile uns deine Ideen mit!', 'wp-multi'),
|
||||||
|
esc_html__('Support', 'wp-multi'),
|
||||||
|
esc_html__('Bei Fragen oder Supportanfragen kannst du uns über', 'wp-multi'),
|
||||||
|
'https://discord.com/invite/FdRs4BRd8D',
|
||||||
|
esc_html__('Discord', 'wp-multi'),
|
||||||
|
esc_html__('oder', 'wp-multi'),
|
||||||
|
'https://t.me/M_Viper04',
|
||||||
|
esc_html__('Telegram', 'wp-multi'),
|
||||||
|
esc_html__('erreichen.', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Beim Klick auf das X den Schließ-Zeitpunkt speichern (per AJAX)
|
||||||
|
?>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var notice = document.querySelector('.wp-multi-feedback-notice');
|
||||||
|
if (!notice || typeof ajaxurl === 'undefined') { return; }
|
||||||
|
notice.addEventListener('click', function (e) {
|
||||||
|
if (e.target && e.target.classList.contains('notice-dismiss')) {
|
||||||
|
var body = 'action=wp_multi_dismiss_banner&nonce=' + encodeURIComponent(notice.getAttribute('data-wpm-nonce'));
|
||||||
|
fetch(ajaxurl, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
body: body
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
add_action('admin_notices', 'wp_multi_add_warning_banner');
|
||||||
|
|
||||||
|
// AJAX: Schließ-Zeitpunkt des Banners für den aktuellen Benutzer merken
|
||||||
|
function wp_multi_dismiss_banner() {
|
||||||
|
check_ajax_referer('wp_multi_dismiss_banner', 'nonce');
|
||||||
|
update_user_meta(get_current_user_id(), 'wp_multi_banner_dismissed', time());
|
||||||
|
wp_send_json_success();
|
||||||
|
}
|
||||||
|
add_action('wp_ajax_wp_multi_dismiss_banner', 'wp_multi_dismiss_banner');
|
||||||
|
|
||||||
|
// Support-Links in der Plugin-Übersicht anzeigen
|
||||||
|
function wp_multi_plugin_row_meta($links, $file) {
|
||||||
|
if ($file === plugin_basename(WP_MULTI_FILE)) {
|
||||||
|
$new_links = array(
|
||||||
|
'support_teams' => sprintf(
|
||||||
|
'<a href="%s" target="_blank" style="color: rgb(255, 0, 0);">%s</a>',
|
||||||
|
esc_url('https://discord.com/invite/FdRs4BRd8D'),
|
||||||
|
esc_html__('Discord Support', 'wp-multi')
|
||||||
|
),
|
||||||
|
'support_telegram' => sprintf(
|
||||||
|
'<a href="%s" target="_blank" style="color: rgb(255, 0, 0);">%s</a>',
|
||||||
|
esc_url('https://t.me/M_Viper04'),
|
||||||
|
esc_html__('Telegram Support', 'wp-multi')
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$links = array_merge($links, $new_links);
|
||||||
|
}
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
add_filter('plugin_row_meta', 'wp_multi_plugin_row_meta', 10, 2);
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Zentrale Menü-Registrierung
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Zentrale Menü-Registrierung
|
||||||
|
*
|
||||||
|
* Alle Admin-Seiten von WP Multi hängen als Submenüs am gemeinsamen
|
||||||
|
* Top-Level-Menü "WP Multi" (Slug 'wp-multi'), das das Toolkit mit
|
||||||
|
* admin_menu-Priorität 9 registriert. Diese Funktion läuft mit
|
||||||
|
* Priorität 20 und legt bei fehlendem Toolkit-Menü ein Fallback an.
|
||||||
|
*/
|
||||||
|
function wp_multi_register_admin_menus() {
|
||||||
|
global $admin_page_hooks;
|
||||||
|
|
||||||
|
$parent = 'wp-multi';
|
||||||
|
|
||||||
|
// Fallback: falls das Toolkit-Top-Level (noch) nicht existiert
|
||||||
|
if (empty($admin_page_hooks[$parent])) {
|
||||||
|
add_menu_page(
|
||||||
|
__('WP Multi', 'wp-multi'),
|
||||||
|
__('WP Multi', 'wp-multi'),
|
||||||
|
'manage_options',
|
||||||
|
$parent,
|
||||||
|
'wp_multi_security_settings_page',
|
||||||
|
'dashicons-screenoptions',
|
||||||
|
59
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$hooks = array();
|
||||||
|
|
||||||
|
// Sicherheit
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Sicherheit', 'wp-multi'), __('Sicherheit', 'wp-multi'), 'manage_options', 'wp-multi-security', 'wp_multi_security_settings_page');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('WP Multi Statistik', 'wp-multi'), __('Sicherheits-Statistik', 'wp-multi'), 'manage_options', 'wp-multi-statistics', 'wp_multi_statistics_page');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Sicherheitseinstellungen', 'wp-multi'), __('Benutzer-Sicherheit', 'wp-multi'), 'manage_options', 'wp_multi_users_security_settings', 'wp_multi_users_security_settings_page');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Blockierte IPs', 'wp-multi'), __('Blockierte IPs', 'wp-multi'), 'manage_options', 'wp_multi_blocked_ips', 'wp_multi_display_blocked_ips');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Gesperrte Benutzer', 'wp-multi'), __('Gesperrte Benutzer', 'wp-multi'), 'manage_options', 'wp-multi-blocked-users', 'wp_multi_blocked_users_page');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Login-URL', 'wp-multi'), __('Login-URL', 'wp-multi'), 'manage_options', 'wp_multi_login_url', 'wp_multi_login_url_page');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Login-Design', 'wp-multi'), __('Login-Design', 'wp-multi'), 'manage_options', 'wp_multi_login_design', 'wp_multi_login_design_page');
|
||||||
|
|
||||||
|
// Analyse & Inhalte
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Benutzer Analytics', 'wp-multi'), __('Benutzer-Analytics', 'wp-multi'), 'manage_options', 'wp_multi_analytics', 'wp_multi_display_user_analytics');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Gast-Autor Übersicht', 'wp-multi'), __('Gast-Autoren', 'wp-multi'), 'manage_options', 'guest_author_overview', 'wp_multi_guest_author_overview_page');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Veröffentlichungen', 'wp-multi'), __('Veröffentlichungen', 'wp-multi'), 'manage_options', 'wp-multi-publications', 'wp_multi_publication_stats_page');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Pinwand', 'wp-multi'), __('Pinwand', 'wp-multi'), 'manage_options', 'message-board', 'wp_multi_add_message_board');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Gemeldete Beiträge', 'wp-multi'), __('Gemeldete Beiträge', 'wp-multi'), 'manage_options', 'reported-posts', 'wp_stat_notice_reported_posts_page');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('WP Stat & Notice', 'wp-multi'), __('Statistik & Notice', 'wp-multi'), 'manage_options', 'statistik_manager', 'statistik_manager_options_page');
|
||||||
|
|
||||||
|
// Werkzeuge
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Custom Shortcodes', 'wp-multi'), __('Shortcodes', 'wp-multi'), 'manage_options', 'wp_multi_shortcodes', 'wp_multi_shortcode_page');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Automatische Tags', 'wp-multi'), __('Auto-Tags', 'wp-multi'), 'manage_options', 'wp-multi-auto-tags', 'wp_multi_auto_tags_page');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Custom Text Einstellungen', 'wp-multi'), __('Custom Text', 'wp-multi'), 'manage_options', 'wp_multi_custom_text_settings', 'wp_multi_custom_text_settings_page_content');
|
||||||
|
|
||||||
|
// Benachrichtigungen
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Notify', 'wp-multi'), __('Notify', 'wp-multi'), 'manage_options', 'wp-multi-notify', 'wp_multi_notify_page');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('DC-Notify Einstellungen', 'wp-multi'), __('DC-Notify', 'wp-multi'), 'manage_options', 'wp-multi-dc-notify', 'wp_multi_settings_page');
|
||||||
|
$hooks[] = add_submenu_page($parent, __('TG-Notify Einstellungen', 'wp-multi'), __('TG-Notify', 'wp-multi'), 'manage_options', 'tg-notify', 'tg_notify_page');
|
||||||
|
|
||||||
|
// Custom Admin-Links
|
||||||
|
$hooks[] = add_submenu_page($parent, __('Admin-Link hinzufügen', 'wp-multi'), __('Admin-Links', 'wp-multi'), 'manage_options', 'wp-stat-notice-custom-page', 'wp_stat_notice_add_custom_page_form');
|
||||||
|
|
||||||
|
// Benutzerdefinierte Admin-Link-Seiten (dynamisch aus Option)
|
||||||
|
$custom_pages = get_option('wp_stat_notice_custom_pages', []);
|
||||||
|
if (!empty($custom_pages) && is_array($custom_pages)) {
|
||||||
|
foreach ($custom_pages as $page) {
|
||||||
|
if (!isset($page['title'], $page['slug'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$hooks[] = add_submenu_page($parent, $page['title'], $page['title'], 'manage_options', $page['slug'], 'wp_stat_notice_custom_page');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hooks beim Design-System des Toolkits anmelden (lädt das gemeinsame CSS)
|
||||||
|
if (function_exists('wpmt_register_admin_hook')) {
|
||||||
|
foreach ($hooks as $hook) {
|
||||||
|
wpmt_register_admin_hook($hook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('admin_menu', 'wp_multi_register_admin_menus', 20);
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Inhaltsverzeichnis [alphabetical_index]
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Index Verzeichnis [alphabetical_index]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Shortcode zum Erstellen des Indexes
|
||||||
|
function wp_multi_alphabetical_index($atts) {
|
||||||
|
// Definiere die Argumente für den Shortcode
|
||||||
|
$atts = shortcode_atts(array(
|
||||||
|
'posts_per_page' => 20, // Maximale Beiträge pro Seite
|
||||||
|
), $atts, 'alphabetical_index');
|
||||||
|
|
||||||
|
// Hole alle Beiträge
|
||||||
|
$args = array(
|
||||||
|
'post_type' => 'post',
|
||||||
|
'posts_per_page' => -1,
|
||||||
|
'orderby' => 'title',
|
||||||
|
'order' => 'ASC',
|
||||||
|
);
|
||||||
|
|
||||||
|
$posts = get_posts($args);
|
||||||
|
|
||||||
|
// Beiträge nach Anfangsbuchstaben gruppieren
|
||||||
|
$alphabet = range('A', 'Z');
|
||||||
|
$posts_by_letter = array();
|
||||||
|
|
||||||
|
foreach ($posts as $post) {
|
||||||
|
$first_letter = strtoupper(substr($post->post_title, 0, 1));
|
||||||
|
if (in_array($first_letter, $alphabet)) {
|
||||||
|
$posts_by_letter[$first_letter][] = $post;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Holen des aktuellen Buchstabens aus der URL
|
||||||
|
$letter = isset($_GET['letter']) ? strtoupper($_GET['letter']) : ''; // Der Buchstabe aus der URL
|
||||||
|
|
||||||
|
// Bestimme, welche Beiträge angezeigt werden
|
||||||
|
$posts_in_letter = [];
|
||||||
|
if ($letter && isset($posts_by_letter[$letter])) {
|
||||||
|
$posts_in_letter = $posts_by_letter[$letter];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Teile die Beiträge in zwei Hälften für die Boxen
|
||||||
|
$halfway = ceil(count($posts_in_letter) / 2); // Rundet die Hälfte auf
|
||||||
|
$first_half = array_slice($posts_in_letter, 0, $halfway); // Erste Hälfte der Beiträge
|
||||||
|
$second_half = array_slice($posts_in_letter, $halfway); // Zweite Hälfte der Beiträge
|
||||||
|
|
||||||
|
// Ausgabe
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="alphabetical-index">
|
||||||
|
<!-- Links zu den Buchstaben -->
|
||||||
|
<div class="alphabet-links">
|
||||||
|
<?php foreach ($alphabet as $char): ?>
|
||||||
|
<a href="?letter=<?php echo $char; ?>" class="letter-link"><?php echo $char; ?></a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($letter): ?>
|
||||||
|
<!-- Box für den aktuellen Buchstaben -->
|
||||||
|
<div class="letter-heading-box">
|
||||||
|
<h2>Beiträge für: <?php echo $letter; ?></h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Zeige die Beiträge für den ausgewählten Buchstaben in zwei Boxen -->
|
||||||
|
<div class="letter-pair-container">
|
||||||
|
<div class="letter-box">
|
||||||
|
<ul class="post-list">
|
||||||
|
<?php foreach ($first_half as $post): ?>
|
||||||
|
<li><a href="<?php echo get_permalink($post->ID); ?>"><?php echo $post->post_title; ?></a></li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="letter-box">
|
||||||
|
<ul class="post-list">
|
||||||
|
<?php foreach ($second_half as $post): ?>
|
||||||
|
<li><a href="<?php echo get_permalink($post->ID); ?>"><?php echo $post->post_title; ?></a></li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.alphabetical-index {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alphabet-links {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alphabet-links a {
|
||||||
|
margin-right: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #0073aa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alphabet-links a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-heading-box {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-heading-box h2 {
|
||||||
|
font-size: 24px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-pair-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 30px;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-box {
|
||||||
|
width: 48%;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-box h2 {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-list {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-list li {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-list a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-list a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shortcode registrieren
|
||||||
|
add_shortcode('alphabetical_index', 'wp_multi_alphabetical_index');
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,452 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Benutzer-Analytics
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Benutzer-Analytics mit verbesserten Sicherheits-, Performance- und Benutzerfreundlichkeits-Features
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// global ist nötig, weil die Plugin-Datei bei der Aktivierung innerhalb einer
|
||||||
|
// Funktion eingebunden wird und $wpdb dort sonst nicht verfügbar ist.
|
||||||
|
global $wpdb;
|
||||||
|
define('WP_MULTI_ANALYTICS_TABLE', $wpdb->prefix . 'wp_multi_user_analytics');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt die Datenbanktabelle für Benutzer-Analytics.
|
||||||
|
*/
|
||||||
|
function wp_multi_create_analytics_table() {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = WP_MULTI_ANALYTICS_TABLE;
|
||||||
|
$charset_collate = $wpdb->get_charset_collate();
|
||||||
|
|
||||||
|
$sql = "CREATE TABLE $table_name (
|
||||||
|
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
||||||
|
user_id bigint(20) NOT NULL,
|
||||||
|
action varchar(255) NOT NULL,
|
||||||
|
post_id bigint(20) DEFAULT NULL,
|
||||||
|
timestamp datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
INDEX idx_timestamp (timestamp),
|
||||||
|
INDEX idx_action (action)
|
||||||
|
) $charset_collate;";
|
||||||
|
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||||
|
dbDelta($sql);
|
||||||
|
}
|
||||||
|
register_activation_hook(WP_MULTI_FILE, 'wp_multi_create_analytics_table');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verfolgt Benutzerinteraktionen (Kommentare und Beitragsaufrufe).
|
||||||
|
*
|
||||||
|
* @param int $user_id Benutzer-ID.
|
||||||
|
* @param string $action Aktion (z. B. 'view', 'comment').
|
||||||
|
* @param int $post_id Beitrag-ID (optional).
|
||||||
|
* @return bool Erfolg der Operation.
|
||||||
|
*/
|
||||||
|
function wp_multi_track_user_activity($user_id, $action, $post_id = null) {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = WP_MULTI_ANALYTICS_TABLE;
|
||||||
|
|
||||||
|
$user_id = absint($user_id);
|
||||||
|
$action = sanitize_text_field($action);
|
||||||
|
$post_id = $post_id ? absint($post_id) : null;
|
||||||
|
|
||||||
|
if ($action === 'view' && is_single()) {
|
||||||
|
$post_id = get_the_ID();
|
||||||
|
}
|
||||||
|
|
||||||
|
// user_id 0 ist gewollt: Die Seite bietet keinen Login, Aufrufe sind anonym.
|
||||||
|
if (!$action) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $wpdb->insert(
|
||||||
|
$table_name,
|
||||||
|
array(
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'action' => $action,
|
||||||
|
'post_id' => $post_id,
|
||||||
|
),
|
||||||
|
array('%d', '%s', '%d')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verfolgt Kommentar-Aktivitäten.
|
||||||
|
*
|
||||||
|
* @param int $comment_id Kommentar-ID.
|
||||||
|
*/
|
||||||
|
function wp_multi_comment_activity($comment_id) {
|
||||||
|
$comment = get_comment($comment_id);
|
||||||
|
$user_id = absint($comment->user_id);
|
||||||
|
if ($user_id) {
|
||||||
|
wp_multi_track_user_activity($user_id, 'comment', $comment->comment_post_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('comment_post', 'wp_multi_comment_activity');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erkennt Bots und Crawler anhand des User-Agents.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function wp_multi_is_bot_request() {
|
||||||
|
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? strtolower(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT']))) : '';
|
||||||
|
|
||||||
|
// Ohne User-Agent ist es so gut wie nie ein echter Besucher
|
||||||
|
if ($user_agent === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$signatures = apply_filters('wp_multi_bot_signatures', [
|
||||||
|
'bot', 'crawl', 'spider', 'slurp', 'archiver', 'preview', 'monitor', 'validator',
|
||||||
|
'facebookexternalhit', 'headless', 'phantomjs', 'python', 'curl', 'wget', 'go-http',
|
||||||
|
'java/', 'okhttp', 'axios', 'scrapy', 'lighthouse', 'pingdom', 'uptime',
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach ($signatures as $signature) {
|
||||||
|
if (strpos($user_agent, $signature) !== false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob dieser Besucher den Beitrag gerade erst aufgerufen hat.
|
||||||
|
*
|
||||||
|
* Ohne Login gibt es keine Benutzer-ID, deshalb dient ein kurzlebiger Hash aus
|
||||||
|
* IP und User-Agent als Erkennungsmerkmal. Gespeichert wird nur der Hash, nie
|
||||||
|
* die IP selbst – ein Reload zählt damit nicht mehrfach.
|
||||||
|
*
|
||||||
|
* @param int $post_id Beitrag-ID.
|
||||||
|
* @return bool True, wenn der Aufruf bereits gezählt wurde.
|
||||||
|
*/
|
||||||
|
function wp_multi_view_already_counted($post_id) {
|
||||||
|
$ip = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
|
||||||
|
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
|
||||||
|
|
||||||
|
$key = 'wp_multi_view_' . md5($post_id . '|' . $ip . '|' . $user_agent . '|' . wp_salt());
|
||||||
|
|
||||||
|
if (get_transient($key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sperrfrist je Besucher und Beitrag
|
||||||
|
$ttl = (int) apply_filters('wp_multi_view_throttle', 30 * MINUTE_IN_SECONDS);
|
||||||
|
set_transient($key, 1, $ttl);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verfolgt Beitragsaufrufe – auch von nicht eingeloggten Besuchern.
|
||||||
|
*/
|
||||||
|
function wp_multi_post_view_activity() {
|
||||||
|
if (is_admin() || !is_single() || is_feed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eigene Aufrufe der Redaktion verfälschen die Zahlen
|
||||||
|
if (is_user_logged_in() && current_user_can('edit_posts') && !apply_filters('wp_multi_track_editor_views', false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wp_multi_is_bot_request()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post_id = get_the_ID();
|
||||||
|
if (!$post_id || wp_multi_view_already_counted($post_id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ohne Login ist die Benutzer-ID 0 – der Aufruf zählt trotzdem
|
||||||
|
wp_multi_track_user_activity(get_current_user_id(), 'view', $post_id);
|
||||||
|
}
|
||||||
|
add_action('wp_head', 'wp_multi_post_view_activity');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baut die WHERE-Bedingung für den gewählten Zeitraum.
|
||||||
|
*
|
||||||
|
* @return array [WHERE-Fragment, Parameter]
|
||||||
|
*/
|
||||||
|
function wp_multi_analytics_date_where($start_datetime = null, $end_datetime = null) {
|
||||||
|
if (!$start_datetime) {
|
||||||
|
$start_datetime = gmdate('Y-m-d H:i:s', strtotime('-7 days'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$where = 'WHERE timestamp >= %s';
|
||||||
|
$params = [$start_datetime];
|
||||||
|
|
||||||
|
if ($end_datetime) {
|
||||||
|
$where .= ' AND timestamp <= %s';
|
||||||
|
$params[] = $end_datetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$where, $params];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ruft die Tagessummen je Aktion ab – Grundlage für das Diagramm.
|
||||||
|
*
|
||||||
|
* Aggregiert in der Datenbank statt in PHP: Seit auch anonyme Aufrufe gezählt
|
||||||
|
* werden, kämen hier sonst zehntausende Einzelzeilen an.
|
||||||
|
*
|
||||||
|
* @return array Zeilen mit date, action und count.
|
||||||
|
*/
|
||||||
|
function wp_multi_fetch_analytics_totals($start_datetime = null, $end_datetime = null) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
list($where, $params) = wp_multi_analytics_date_where($start_datetime, $end_datetime);
|
||||||
|
|
||||||
|
$sql = "SELECT DATE(timestamp) AS date, action, COUNT(*) AS count
|
||||||
|
FROM " . WP_MULTI_ANALYTICS_TABLE . "
|
||||||
|
$where
|
||||||
|
GROUP BY date, action
|
||||||
|
ORDER BY date ASC";
|
||||||
|
|
||||||
|
return $wpdb->get_results($wpdb->prepare($sql, $params));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ruft die jüngsten Einzelereignisse ab – Grundlage für die Detailtabelle.
|
||||||
|
*
|
||||||
|
* @return array Rohe Analytics-Daten (begrenzt).
|
||||||
|
*/
|
||||||
|
function wp_multi_fetch_raw_analytics($start_datetime = null, $end_datetime = null) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
list($where, $params) = wp_multi_analytics_date_where($start_datetime, $end_datetime);
|
||||||
|
|
||||||
|
// Ohne Limit würde die Detailtabelle bei anonymem Tracking die Seite sprengen
|
||||||
|
$params[] = (int) apply_filters('wp_multi_analytics_detail_limit', 200);
|
||||||
|
|
||||||
|
$sql = "SELECT DATE(timestamp) AS date, action, post_id, 1 AS count, user_id, timestamp
|
||||||
|
FROM " . WP_MULTI_ANALYTICS_TABLE . "
|
||||||
|
$where
|
||||||
|
ORDER BY timestamp DESC
|
||||||
|
LIMIT %d";
|
||||||
|
|
||||||
|
return $wpdb->get_results($wpdb->prepare($sql, $params));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verarbeitet rohe Analytics-Daten für Diagramm und Tabelle.
|
||||||
|
*
|
||||||
|
* @param array $results Rohe Analytics-Daten.
|
||||||
|
* @return array Verarbeitete Daten.
|
||||||
|
*/
|
||||||
|
function wp_multi_process_analytics_data($results, $recent = null) {
|
||||||
|
$dates = [];
|
||||||
|
$comment_counts = [];
|
||||||
|
$view_counts = [];
|
||||||
|
|
||||||
|
foreach ($results as $result) {
|
||||||
|
$date = $result->date;
|
||||||
|
if (!in_array($date, $dates)) {
|
||||||
|
$dates[] = $date;
|
||||||
|
}
|
||||||
|
if ($result->action === 'comment') {
|
||||||
|
$comment_counts[$date] = ($comment_counts[$date] ?? 0) + $result->count;
|
||||||
|
} elseif ($result->action === 'view') {
|
||||||
|
$view_counts[$date] = ($view_counts[$date] ?? 0) + $result->count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$all_dates = [];
|
||||||
|
$datasets = ['comments' => [], 'views' => []];
|
||||||
|
|
||||||
|
for ($i = 6; $i >= 0; $i--) {
|
||||||
|
$date = date('Y-m-d', strtotime("-$i day"));
|
||||||
|
$all_dates[] = $date;
|
||||||
|
$datasets['comments'][] = $comment_counts[$date] ?? 0;
|
||||||
|
$datasets['views'][] = $view_counts[$date] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dates' => array_reverse($all_dates),
|
||||||
|
'datasets' => [
|
||||||
|
[
|
||||||
|
'label' => __('Kommentare', 'wp-multi'),
|
||||||
|
'data' => array_reverse($datasets['comments']),
|
||||||
|
'borderColor' => 'rgba(75, 192, 192, 1)',
|
||||||
|
'borderWidth' => 1,
|
||||||
|
'fill' => false,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'label' => __('Beitragsaufrufe', 'wp-multi'),
|
||||||
|
'data' => array_reverse($datasets['views']),
|
||||||
|
'borderColor' => 'rgba(153, 102, 255, 1)',
|
||||||
|
'borderWidth' => 1,
|
||||||
|
'fill' => false,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'data' => ($recent === null) ? $results : $recent
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ruft Analytics-Daten mit Caching ab.
|
||||||
|
*
|
||||||
|
* @param string $date_query Datum für die Abfrage.
|
||||||
|
* @return array Analytics-Daten.
|
||||||
|
*/
|
||||||
|
function wp_multi_get_analytics_data($start_datetime = null, $end_datetime = null) {
|
||||||
|
$cache_key = 'wp_multi_analytics_data_' . md5($start_datetime . '|' . $end_datetime);
|
||||||
|
$cached_data = get_transient($cache_key);
|
||||||
|
|
||||||
|
if ($cached_data !== false) {
|
||||||
|
return $cached_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
$totals = wp_multi_fetch_analytics_totals($start_datetime, $end_datetime);
|
||||||
|
$recent = wp_multi_fetch_raw_analytics($start_datetime, $end_datetime);
|
||||||
|
$data = wp_multi_process_analytics_data($totals, $recent);
|
||||||
|
|
||||||
|
set_transient($cache_key, $data, HOUR_IN_SECONDS);
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeigt die Benutzer-Analytics-Seite im Admin-Bereich an.
|
||||||
|
*/
|
||||||
|
function wp_multi_display_user_analytics() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
if (!$wpdb->get_var("SHOW TABLES LIKE '" . WP_MULTI_ANALYTICS_TABLE . "'")) {
|
||||||
|
echo '<div class="error"><p>' . esc_html__('Die Analytics-Tabelle existiert nicht. Bitte aktiviere das Plugin erneut.', 'wp-multi') . '</p></div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$time_range = isset($_GET['time_range']) ? sanitize_text_field($_GET['time_range']) : '7days';
|
||||||
|
$start_datetime = gmdate('Y-m-d H:i:s', strtotime('-7 days'));
|
||||||
|
$end_datetime = null;
|
||||||
|
|
||||||
|
if ($time_range === '30days') {
|
||||||
|
$start_datetime = gmdate('Y-m-d H:i:s', strtotime('-30 days'));
|
||||||
|
} elseif ($time_range === 'custom' && isset($_GET['start_date'], $_GET['end_date'])) {
|
||||||
|
$custom_start = sanitize_text_field(wp_unslash($_GET['start_date']));
|
||||||
|
$custom_end = sanitize_text_field(wp_unslash($_GET['end_date']));
|
||||||
|
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $custom_start) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $custom_end)) {
|
||||||
|
$start_datetime = $custom_start . ' 00:00:00';
|
||||||
|
$end_datetime = $custom_end . ' 23:59:59';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = wp_multi_get_analytics_data($start_datetime, $end_datetime);
|
||||||
|
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Benutzer Analytics', 'wp-multi'),
|
||||||
|
__('Beitragsaufrufe und Kommentar-Aktivität deiner Besucher', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Zeitraum', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<form method="get">
|
||||||
|
<input type="hidden" name="page" value="wp_multi_analytics">
|
||||||
|
<select name="time_range" onchange="this.form.submit()" style="max-width:240px;">
|
||||||
|
<option value="7days" <?php selected($time_range, '7days'); ?>><?php esc_html_e('Letzte 7 Tage', 'wp-multi'); ?></option>
|
||||||
|
<option value="30days" <?php selected($time_range, '30days'); ?>><?php esc_html_e('Letzte 30 Tage', 'wp-multi'); ?></option>
|
||||||
|
<option value="custom" <?php selected($time_range, 'custom'); ?>><?php esc_html_e('Benutzerdefiniert', 'wp-multi'); ?></option>
|
||||||
|
</select>
|
||||||
|
<?php if ($time_range === 'custom') : ?>
|
||||||
|
<input type="date" name="start_date" value="<?php echo esc_attr($_GET['start_date'] ?? ''); ?>" style="max-width:180px;">
|
||||||
|
<input type="date" name="end_date" value="<?php echo esc_attr($_GET['end_date'] ?? ''); ?>" style="max-width:180px;">
|
||||||
|
<?php endif; ?>
|
||||||
|
<button type="submit" class="wpm-btn wpm-btn--primary"><?php esc_html_e('Filtern', 'wp-multi'); ?></button>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
if (empty($results['data'])) {
|
||||||
|
wpmt_admin_notice('info', __('Keine Daten für den gewählten Zeitraum verfügbar.', 'wp-multi'));
|
||||||
|
} else {
|
||||||
|
wpmt_admin_card_open(__('Aktivitätsverlauf', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<canvas id="userActivityChart" style="height: 300px; width: 100%;"></canvas>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<script>
|
||||||
|
if (typeof Chart !== 'undefined') {
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
var ctx = document.getElementById('userActivityChart').getContext('2d');
|
||||||
|
var chart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: <?php echo wp_json_encode($results['dates']); ?>,
|
||||||
|
datasets: <?php echo wp_json_encode($results['datasets']); ?>,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
x: { title: { display: true, text: '<?php echo esc_js(__('Datum', 'wp-multi')); ?>' } },
|
||||||
|
y: { title: { display: true, text: '<?php echo esc_js(__('Anzahl', 'wp-multi')); ?>' }, beginAtZero: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('Chart.js konnte nicht geladen werden.');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Einzelne Aktivitäten', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<div class="wpm-table-wrap">
|
||||||
|
<table class="wpm-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php esc_html_e('Benutzer ID', 'wp-multi'); ?></th>
|
||||||
|
<th><?php esc_html_e('Aktion', 'wp-multi'); ?></th>
|
||||||
|
<th><?php esc_html_e('Beitrag Titel', 'wp-multi'); ?></th>
|
||||||
|
<th><?php esc_html_e('Beitrag ID', 'wp-multi'); ?></th>
|
||||||
|
<th><?php esc_html_e('Zeitstempel', 'wp-multi'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($results['data'] as $row) : ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo $row->user_id ? esc_html($row->user_id) : esc_html__('Gast', 'wp-multi'); ?></td>
|
||||||
|
<td><span class="wpm-badge <?php echo $row->action === 'comment' ? 'wpm-badge--success' : ''; ?>"><?php echo esc_html($row->action); ?></span></td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
if ($row->post_id) {
|
||||||
|
$post_title = get_the_title($row->post_id);
|
||||||
|
echo esc_html($post_title ?: __('Kein Titel verfügbar', 'wp-multi'));
|
||||||
|
} else {
|
||||||
|
echo esc_html__('Kein Beitrag', 'wp-multi');
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
|
<td><?php echo esc_html($row->post_id ?: '-'); ?></td>
|
||||||
|
<td><?php echo esc_html($row->timestamp); ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Der Analytics-Menüpunkt wird zentral in wp_multi_register_admin_menus() registriert.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Anti-Spam Honeypot
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Anti Spam Honey
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Standardwerte setzen
|
||||||
|
function wp_multi_set_default_options() {
|
||||||
|
add_option('wp_multi_honeypot_field', 'iwlxja5187');
|
||||||
|
add_option('wp_multi_honeypot_error', 'Spamming or your Javascript is disabled !!');
|
||||||
|
add_option('wp_multi_honeypot_widget', 0);
|
||||||
|
add_option('wp_multi_max_links', 3);
|
||||||
|
add_option('wp_multi_blocked_keywords', 'viagra,casino,bitcoin');
|
||||||
|
add_option('wp_multi_blocked_ips', '');
|
||||||
|
add_option('wp_multi_blocked_comments', 0); // Zähler für blockierte Kommentare
|
||||||
|
add_option('wp_multi_honeypot_hits', 0); // Zähler für Honeypot-Aktivierungen
|
||||||
|
add_option('wp_multi_spammer_ips', []); // Liste der blockierten Spammer-IP-Adressen
|
||||||
|
add_option('wp_multi_spam_submissions', []); // Liste der Spam-Einreichungen
|
||||||
|
}
|
||||||
|
register_activation_hook(WP_MULTI_FILE, 'wp_multi_set_default_options');
|
||||||
|
|
||||||
|
// Die Sicherheits-Menüpunkte werden zentral in wp_multi_register_admin_menus() registriert.
|
||||||
|
|
||||||
|
// Einstellungsseite mit CSS & JS für Generator
|
||||||
|
function wp_multi_security_settings_page() {
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Sicherheit', 'wp-multi'),
|
||||||
|
__('Anti-Spam, Honeypot und Kommentarfilter konfigurieren', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
wpmt_admin_card_open();
|
||||||
|
?>
|
||||||
|
<form method="post" action="options.php">
|
||||||
|
<?php
|
||||||
|
settings_fields('wp_multi_security_settings');
|
||||||
|
do_settings_sections('wp-multi-security');
|
||||||
|
submit_button();
|
||||||
|
?>
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
function generateHoneypotName() {
|
||||||
|
let field = document.getElementById('wp_multi_honeypot_field');
|
||||||
|
let randomString = Math.random().toString(36).substring(2, 12);
|
||||||
|
field.value = randomString;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statistikseite im Dashboard
|
||||||
|
function wp_multi_statistics_page() {
|
||||||
|
$blocked_comments = get_option('wp_multi_blocked_comments', 0);
|
||||||
|
$honeypot_hits = get_option('wp_multi_honeypot_hits', 0);
|
||||||
|
$spammer_ips = get_option('wp_multi_spammer_ips', []);
|
||||||
|
$spam_submissions = get_option('wp_multi_spam_submissions', []);
|
||||||
|
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Anti-Spam Statistik', 'wp-multi'),
|
||||||
|
__('Blockierte Kommentare, Honeypot-Treffer und erfasste Spammer-IPs', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
echo '<div class="wpm-stats">';
|
||||||
|
wpmt_admin_stat_card(__('Blockierte Kommentare', 'wp-multi'), number_format_i18n($blocked_comments), 'dashicons-dismiss');
|
||||||
|
wpmt_admin_stat_card(__('Honeypot-Treffer', 'wp-multi'), number_format_i18n($honeypot_hits), 'dashicons-shield');
|
||||||
|
wpmt_admin_stat_card(__('Spammer-IPs', 'wp-multi'), number_format_i18n(count($spammer_ips)), 'dashicons-admin-site-alt3');
|
||||||
|
wpmt_admin_stat_card(__('Spam-Einreichungen', 'wp-multi'), number_format_i18n(count($spam_submissions)), 'dashicons-email-alt');
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Spammer-IP-Adressen', 'wp-multi'));
|
||||||
|
if (!empty($spammer_ips)) {
|
||||||
|
?>
|
||||||
|
<div class="wpm-table-wrap">
|
||||||
|
<table class="wpm-table">
|
||||||
|
<thead>
|
||||||
|
<tr><th><?php esc_html_e('IP-Adresse', 'wp-multi'); ?></th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($spammer_ips as $ip) : ?>
|
||||||
|
<tr><td><?php echo esc_html($ip); ?></td></tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
} else {
|
||||||
|
wpmt_admin_notice('info', __('Keine Spammer-IP-Adressen gefunden.', 'wp-multi'));
|
||||||
|
}
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Einstellungen registrieren
|
||||||
|
function wp_multi_register_security_settings() {
|
||||||
|
register_setting('wp_multi_security_settings', 'wp_multi_honeypot_field');
|
||||||
|
register_setting('wp_multi_security_settings', 'wp_multi_honeypot_error');
|
||||||
|
register_setting('wp_multi_security_settings', 'wp_multi_honeypot_widget');
|
||||||
|
register_setting('wp_multi_security_settings', 'wp_multi_max_links');
|
||||||
|
register_setting('wp_multi_security_settings', 'wp_multi_blocked_keywords');
|
||||||
|
register_setting('wp_multi_security_settings', 'wp_multi_blocked_ips');
|
||||||
|
|
||||||
|
add_settings_section('wp_multi_honeypot_section', 'Honeypot Einstellungen', null, 'wp-multi-security');
|
||||||
|
|
||||||
|
add_settings_field('wp_multi_honeypot_field', 'Honey Pot Field Name', 'wp_multi_honeypot_field_callback', 'wp-multi-security', 'wp_multi_honeypot_section');
|
||||||
|
add_settings_field('wp_multi_honeypot_error', 'Honey Pot Error Message', 'wp_multi_honeypot_error_callback', 'wp-multi-security', 'wp_multi_honeypot_section');
|
||||||
|
add_settings_field('wp_multi_honeypot_widget', 'Disable Honeypot Test Widget', 'wp_multi_honeypot_widget_callback', 'wp-multi-security', 'wp_multi_honeypot_section');
|
||||||
|
add_settings_field('wp_multi_max_links', 'Maximale Links im Kommentar', 'wp_multi_max_links_callback', 'wp-multi-security', 'wp_multi_honeypot_section');
|
||||||
|
add_settings_field('wp_multi_blocked_keywords', 'Blockierte Schlüsselwörter', 'wp_multi_blocked_keywords_callback', 'wp-multi-security', 'wp_multi_honeypot_section');
|
||||||
|
add_settings_field('wp_multi_blocked_ips', 'Blockierte IP-Adressen', 'wp_multi_blocked_ips_callback', 'wp-multi-security', 'wp_multi_honeypot_section');
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action('admin_init', 'wp_multi_register_security_settings');
|
||||||
|
|
||||||
|
function wp_multi_honeypot_field_callback() {
|
||||||
|
?>
|
||||||
|
<div class="wp-multi-honeypot-group">
|
||||||
|
<input type="text" id="wp_multi_honeypot_field" name="wp_multi_honeypot_field" value="<?php echo esc_attr(get_option('wp_multi_honeypot_field')); ?>">
|
||||||
|
<button type="button" onclick="generateHoneypotName()">Generieren</button>
|
||||||
|
</div>
|
||||||
|
<small>Verwenden Sie ein zufälliges Zeichenfolgen für das Honeypot-Feld.</small>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_honeypot_error_callback() {
|
||||||
|
?>
|
||||||
|
<input type="text" name="wp_multi_honeypot_error" value="<?php echo esc_attr(get_option('wp_multi_honeypot_error')); ?>">
|
||||||
|
<small>Die Nachricht, die angezeigt wird, wenn ein Honeypot ausgelöst wird.</small>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_honeypot_widget_callback() {
|
||||||
|
?>
|
||||||
|
<input type="checkbox" name="wp_multi_honeypot_widget" value="1" <?php checked(1, get_option('wp_multi_honeypot_widget'), true); ?>>
|
||||||
|
<small>Deaktivieren Sie das Honeypot-Test-Widget im Frontend.</small>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_max_links_callback() {
|
||||||
|
?>
|
||||||
|
<input type="number" name="wp_multi_max_links" value="<?php echo esc_attr(get_option('wp_multi_max_links')); ?>">
|
||||||
|
<small>Maximale Anzahl von Links, die in einem Kommentar erlaubt sind.</small>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_blocked_keywords_callback() {
|
||||||
|
?>
|
||||||
|
<input type="text" name="wp_multi_blocked_keywords" value="<?php echo esc_attr(get_option('wp_multi_blocked_keywords')); ?>">
|
||||||
|
<small>Schlüsselwörter, die blockiert werden sollen (durch Kommas getrennt).</small>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_blocked_ips_callback() {
|
||||||
|
?>
|
||||||
|
<textarea name="wp_multi_blocked_ips" rows="5"><?php echo esc_textarea(get_option('wp_multi_blocked_ips')); ?></textarea>
|
||||||
|
<small>Blockierte IP-Adressen (jede Adresse in einer neuen Zeile).</small>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
// Honeypot-Feld (unsichtbar für Menschen) im Kommentarformular ausgeben
|
||||||
|
function wp_multi_render_honeypot_field() {
|
||||||
|
$field_name = get_option('wp_multi_honeypot_field', 'iwlxja5187');
|
||||||
|
?>
|
||||||
|
<p class="wp-multi-honeypot" style="position:absolute !important; left:-9999px !important; top:-9999px !important;" aria-hidden="true">
|
||||||
|
<label for="<?php echo esc_attr($field_name); ?>"><?php esc_html_e('Website (bitte leer lassen)', 'wp-multi'); ?></label>
|
||||||
|
<input type="text" name="<?php echo esc_attr($field_name); ?>" id="<?php echo esc_attr($field_name); ?>" value="" tabindex="-1" autocomplete="off">
|
||||||
|
</p>
|
||||||
|
<input type="hidden" name="wp_multi_form_time" value="<?php echo esc_attr(time()); ?>">
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
add_action('comment_form_top', 'wp_multi_render_honeypot_field');
|
||||||
|
|
||||||
|
// Zähler/Statistik für einen geblockten Spam-Versuch aktualisieren
|
||||||
|
function wp_multi_register_spam_hit($reason, $ip = '') {
|
||||||
|
update_option('wp_multi_honeypot_hits', get_option('wp_multi_honeypot_hits', 0) + 1);
|
||||||
|
update_option('wp_multi_blocked_comments', get_option('wp_multi_blocked_comments', 0) + 1);
|
||||||
|
|
||||||
|
if ($ip) {
|
||||||
|
$spammer_ips = get_option('wp_multi_spammer_ips', []);
|
||||||
|
if (!in_array($ip, $spammer_ips, true)) {
|
||||||
|
$spammer_ips[] = $ip;
|
||||||
|
update_option('wp_multi_spammer_ips', $spammer_ips);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$submissions = get_option('wp_multi_spam_submissions', []);
|
||||||
|
$submissions[] = [
|
||||||
|
'reason' => $reason,
|
||||||
|
'ip' => $ip,
|
||||||
|
'timestamp' => current_time('mysql'),
|
||||||
|
];
|
||||||
|
// Auf die letzten 200 Einträge begrenzen, damit die Option nicht unbegrenzt wächst
|
||||||
|
if (count($submissions) > 200) {
|
||||||
|
$submissions = array_slice($submissions, -200);
|
||||||
|
}
|
||||||
|
update_option('wp_multi_spam_submissions', $submissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Honeypot-/Zeitfallen-Prüfung sowie IP-/Keyword-/Link-Filter beim Absenden eines Kommentars
|
||||||
|
function wp_multi_validate_comment_submission($commentdata) {
|
||||||
|
$ip = function_exists('get_user_ip') ? get_user_ip() : (isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '');
|
||||||
|
|
||||||
|
// Honeypot-Feld: darf von einem Menschen nie ausgefüllt werden
|
||||||
|
$field_name = get_option('wp_multi_honeypot_field', 'iwlxja5187');
|
||||||
|
if (!empty($_POST[$field_name])) {
|
||||||
|
wp_multi_register_spam_hit('honeypot', $ip);
|
||||||
|
wp_die(esc_html(get_option('wp_multi_honeypot_error', 'Spamming or your Javascript is disabled !!')));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zeit-Falle: Formular darf nicht schneller als 3 Sekunden nach dem Laden abgeschickt werden
|
||||||
|
if (isset($_POST['wp_multi_form_time'])) {
|
||||||
|
$elapsed = time() - absint($_POST['wp_multi_form_time']);
|
||||||
|
if ($elapsed < 3) {
|
||||||
|
wp_multi_register_spam_hit('honeypot_zeitfalle', $ip);
|
||||||
|
wp_die(esc_html(get_option('wp_multi_honeypot_error', 'Spamming or your Javascript is disabled !!')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blockierte IP-Adressen
|
||||||
|
$blocked_ips = array_filter(array_map('trim', explode("\n", (string) get_option('wp_multi_blocked_ips', ''))));
|
||||||
|
if ($ip && in_array($ip, $blocked_ips, true)) {
|
||||||
|
wp_multi_register_spam_hit('blocked_ip', $ip);
|
||||||
|
wp_die(esc_html__('Deine IP-Adresse wurde für Kommentare gesperrt.', 'wp-multi'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = isset($commentdata['comment_content']) ? $commentdata['comment_content'] : '';
|
||||||
|
|
||||||
|
// Blockierte Schlüsselwörter
|
||||||
|
$blocked_keywords = array_filter(array_map('trim', explode(',', (string) get_option('wp_multi_blocked_keywords', ''))));
|
||||||
|
foreach ($blocked_keywords as $keyword) {
|
||||||
|
if ($keyword !== '' && stripos($content, $keyword) !== false) {
|
||||||
|
wp_multi_register_spam_hit('keyword', $ip);
|
||||||
|
wp_die(esc_html__('Dein Kommentar enthält nicht erlaubte Inhalte.', 'wp-multi'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximale Anzahl an Links im Kommentar
|
||||||
|
$max_links = absint(get_option('wp_multi_max_links', 3));
|
||||||
|
if ($max_links > 0 && preg_match_all('#https?://#i', $content) > $max_links) {
|
||||||
|
wp_multi_register_spam_hit('links', $ip);
|
||||||
|
wp_die(esc_html__('Dein Kommentar enthält zu viele Links.', 'wp-multi'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kommentarfilter: Telefonnummern, E-Mails, URLs, IPs, Schimpfwörter durch * ersetzen
|
||||||
|
$commentdata['comment_content'] = wp_multi_apply_comment_content_filter($content);
|
||||||
|
|
||||||
|
return $commentdata;
|
||||||
|
}
|
||||||
|
add_filter('preprocess_comment', 'wp_multi_validate_comment_submission');
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Automatische Tags
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verbessertes Auto-Tag-Plugin mit optimierter Sicherheit, Performance und Benutzerfreundlichkeit
|
||||||
|
*/
|
||||||
|
|
||||||
|
define('WP_MULTI_AUTO_TAGS_OPTION', 'wp_multi_custom_stopwords');
|
||||||
|
define('WP_MULTI_AUTO_TAGS_QUEUE', 'wp_multi_auto_tags_queue');
|
||||||
|
define('WP_MULTI_AUTO_TAGS_NONCE', 'wp_multi_auto_tags_nonce');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt automatische Tags zu einem Beitrag hinzu.
|
||||||
|
*
|
||||||
|
* @param int $post_id ID des Beitrags.
|
||||||
|
*/
|
||||||
|
function wp_multi_auto_add_tags($post_id) {
|
||||||
|
if (get_post_type($post_id) !== 'post') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$existing_tags = wp_get_post_tags($post_id, ['fields' => 'names']);
|
||||||
|
if (!empty($existing_tags)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post = get_post($post_id);
|
||||||
|
if (!$post) {
|
||||||
|
error_log("WP Multi Auto Tags: Beitrag $post_id nicht gefunden.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = strip_tags($post->post_content);
|
||||||
|
$content = mb_strtolower($content, 'UTF-8');
|
||||||
|
|
||||||
|
// Stopwörter laden
|
||||||
|
$stopwords = wp_multi_get_stopwords();
|
||||||
|
$words = wp_multi_extract_words($content, $stopwords);
|
||||||
|
|
||||||
|
if (empty($words)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$word_counts = array_count_values($words);
|
||||||
|
arsort($word_counts);
|
||||||
|
|
||||||
|
$top_tags = array_slice(array_keys($word_counts), 0, 5);
|
||||||
|
if (!empty($top_tags)) {
|
||||||
|
wp_set_post_tags($post_id, $top_tags, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrahiert relevante Wörter aus dem Inhalt.
|
||||||
|
*
|
||||||
|
* @param string $content Der Inhalt des Beitrags.
|
||||||
|
* @param array $stopwords Liste der Stopwörter.
|
||||||
|
* @return array Liste der extrahierten Wörter.
|
||||||
|
*/
|
||||||
|
function wp_multi_extract_words($content, $stopwords) {
|
||||||
|
preg_match_all('/\b[a-zäöüß]{4,}\b/u', $content, $matches);
|
||||||
|
return array_unique(array_diff($matches[0], $stopwords));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lädt die kombinierten Stopwörter (Standard + benutzerdefiniert).
|
||||||
|
*
|
||||||
|
* @return array Liste der Stopwörter.
|
||||||
|
*/
|
||||||
|
function wp_multi_get_stopwords() {
|
||||||
|
$cache_key = 'wp_multi_stopwords';
|
||||||
|
$stopwords = get_transient($cache_key);
|
||||||
|
|
||||||
|
if ($stopwords !== false) {
|
||||||
|
return $stopwords;
|
||||||
|
}
|
||||||
|
|
||||||
|
$custom_stopwords = get_option(WP_MULTI_AUTO_TAGS_OPTION, '');
|
||||||
|
$custom_stopwords = array_filter(array_map('trim', explode(',', sanitize_text_field($custom_stopwords))));
|
||||||
|
|
||||||
|
$default_stopwords = [
|
||||||
|
'und', 'oder', 'ein', 'eine', 'der', 'die', 'das', 'in', 'mit', 'auf', 'zu', 'von',
|
||||||
|
'für', 'ist', 'es', 'im', 'an', 'am', 'bei', 'auch', 'aber', 'so', 'dass', 'kann',
|
||||||
|
'wenn', 'wie', 'wir', 'man', 'nur', 'nicht', 'mehr', 'als', 'sein', 'wurde', 'werden',
|
||||||
|
'hat', 'haben', 'schon', 'doch', 'denn', 'diese', 'dieser', 'dieses', 'nach', 'sehr', 'allgemein'
|
||||||
|
];
|
||||||
|
|
||||||
|
$stopwords = array_merge($default_stopwords, $custom_stopwords);
|
||||||
|
set_transient($cache_key, $stopwords, DAY_IN_SECONDS);
|
||||||
|
return $stopwords;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt das Admin-Menü für automatische Tags hinzu.
|
||||||
|
*/
|
||||||
|
// Der Auto-Tags-Menüpunkt wird zentral in wp_multi_register_admin_menus() registriert.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rendert die Admin-Seite für automatische Tags.
|
||||||
|
*/
|
||||||
|
function wp_multi_auto_tags_page() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_die(__('Zugriff verweigert.', 'wp-multi'));
|
||||||
|
}
|
||||||
|
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Automatische Tags', 'wp-multi'),
|
||||||
|
__('Fügt Beiträgen ohne Tags automatisch passende Schlagwörter hinzu', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Einstellungen', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<form method="post" action="options.php">
|
||||||
|
<?php
|
||||||
|
settings_fields('wp_multi_auto_tags_options');
|
||||||
|
do_settings_sections('wp-multi-auto-tags');
|
||||||
|
wp_nonce_field(WP_MULTI_AUTO_TAGS_NONCE, WP_MULTI_AUTO_TAGS_NONCE);
|
||||||
|
?>
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label" for="wp_multi_custom_stopwords"><?php esc_html_e('Stopwörter (kommagetrennt)', 'wp-multi'); ?>
|
||||||
|
<small class="wpm-field__hint"><?php esc_html_e('Diese Wörter werden nicht als Tags verwendet. Beispiele: wird, auch, aber', 'wp-multi'); ?></small>
|
||||||
|
</label>
|
||||||
|
<textarea id="wp_multi_custom_stopwords" name="wp_multi_custom_stopwords" rows="5"><?php echo esc_textarea(get_option(WP_MULTI_AUTO_TAGS_OPTION, '')); ?></textarea>
|
||||||
|
</div>
|
||||||
|
<p><input type="submit" value="<?php esc_attr_e('Speichern', 'wp-multi'); ?>" class="wpm-btn wpm-btn--primary"></p>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Alle Beiträge verarbeiten', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<p><?php esc_html_e('Durchsucht alle vorhandenen Beiträge ohne Tags und ergänzt automatisch passende Schlagwörter.', 'wp-multi'); ?></p>
|
||||||
|
<button id="start-auto-tags" class="wpm-btn wpm-btn--primary"><?php esc_html_e('Jetzt ausführen', 'wp-multi'); ?></button>
|
||||||
|
<div id="progress-container" style="display:none; width:100%; background:#f4f4f4; border-radius:8px; margin-top:20px; overflow:hidden;">
|
||||||
|
<div id="progress-bar" style="width:0%; height:30px; background:#2ecc71; text-align:center; color:#fff; line-height:30px; font-weight:bold; transition:width .4s;">0%</div>
|
||||||
|
</div>
|
||||||
|
<p id="status-message" style="margin-top:15px; font-weight:600; color:var(--wpm-primary,#0073aa);"></p>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
?>
|
||||||
|
<script>
|
||||||
|
var wpMultiAutoTags = <?php echo wp_json_encode([
|
||||||
|
'ajaxurl' => admin_url('admin-ajax.php'),
|
||||||
|
'nonce' => wp_create_nonce(WP_MULTI_AUTO_TAGS_NONCE),
|
||||||
|
'messages' => [
|
||||||
|
'processing' => __('Wird verarbeitet...', 'wp-multi'),
|
||||||
|
'run' => __('Jetzt ausführen', 'wp-multi'),
|
||||||
|
'loading' => __('Lade...', 'wp-multi'),
|
||||||
|
'no_posts' => __('Keine Beiträge gefunden.', 'wp-multi'),
|
||||||
|
'success' => __('Automatische Tags erfolgreich hinzugefügt!', 'wp-multi'),
|
||||||
|
'error' => __('Fehler bei der Verarbeitung.', 'wp-multi'),
|
||||||
|
'start_error' => __('Fehler beim Starten der Verarbeitung.', 'wp-multi'),
|
||||||
|
],
|
||||||
|
]); ?>;
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
$('#start-auto-tags').on('click', function() {
|
||||||
|
const button = $(this);
|
||||||
|
const progressContainer = $('#progress-container');
|
||||||
|
const progressBar = $('#progress-bar');
|
||||||
|
const statusMessage = $('#status-message');
|
||||||
|
|
||||||
|
button.prop('disabled', true).text(wpMultiAutoTags.messages.processing);
|
||||||
|
progressContainer.show();
|
||||||
|
progressBar.css('width', '0%').text('0%');
|
||||||
|
statusMessage.text(wpMultiAutoTags.messages.loading);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: wpMultiAutoTags.ajaxurl,
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'wp_multi_process_auto_tags',
|
||||||
|
nonce: wpMultiAutoTags.nonce,
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (!response.data.total) {
|
||||||
|
statusMessage.text(wpMultiAutoTags.messages.no_posts);
|
||||||
|
button.prop('disabled', false).text(wpMultiAutoTags.messages.run);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = response.data.total;
|
||||||
|
let processed = 0;
|
||||||
|
const batchSize = 10;
|
||||||
|
|
||||||
|
function processBatch() {
|
||||||
|
$.ajax({
|
||||||
|
url: wpMultiAutoTags.ajaxurl,
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'wp_multi_process_auto_tags_step',
|
||||||
|
batchSize: batchSize,
|
||||||
|
nonce: wpMultiAutoTags.nonce,
|
||||||
|
},
|
||||||
|
success: function(result) {
|
||||||
|
if (result.data.done) {
|
||||||
|
processed += batchSize;
|
||||||
|
const percent = Math.min(Math.round((processed / total) * 100), 100);
|
||||||
|
progressBar.css('width', percent + '%').text(percent + '%');
|
||||||
|
|
||||||
|
if (processed < total) {
|
||||||
|
processBatch();
|
||||||
|
} else {
|
||||||
|
button.prop('disabled', false).text(wpMultiAutoTags.messages.run);
|
||||||
|
statusMessage.text(wpMultiAutoTags.messages.success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
statusMessage.text(wpMultiAutoTags.messages.error);
|
||||||
|
button.prop('disabled', false).text(wpMultiAutoTags.messages.run);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
processBatch();
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
statusMessage.text(wpMultiAutoTags.messages.start_error);
|
||||||
|
button.prop('disabled', false).text(wpMultiAutoTags.messages.run);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registriert die Einstellungen für automatische Tags.
|
||||||
|
*/
|
||||||
|
function wp_multi_auto_tags_settings_init() {
|
||||||
|
register_setting('wp_multi_auto_tags_options', WP_MULTI_AUTO_TAGS_OPTION, [
|
||||||
|
'sanitize_callback' => function($input) {
|
||||||
|
$input = sanitize_text_field($input);
|
||||||
|
return implode(',', array_map('trim', explode(',', $input)));
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
add_action('admin_init', 'wp_multi_auto_tags_settings_init');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verarbeitet die Initialisierung der Auto-Tag-Verarbeitung via AJAX.
|
||||||
|
*/
|
||||||
|
function wp_multi_process_auto_tags() {
|
||||||
|
check_ajax_referer(WP_MULTI_AUTO_TAGS_NONCE, 'nonce');
|
||||||
|
|
||||||
|
$args = [
|
||||||
|
'post_type' => 'post',
|
||||||
|
'posts_per_page' => 100,
|
||||||
|
'fields' => 'ids',
|
||||||
|
'no_found_rows' => true, // Optimierung
|
||||||
|
];
|
||||||
|
|
||||||
|
$posts = get_posts($args);
|
||||||
|
if (empty($posts)) {
|
||||||
|
wp_send_json_success(['total' => 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_transient(WP_MULTI_AUTO_TAGS_QUEUE, $posts, 300);
|
||||||
|
wp_send_json_success(['total' => count($posts)]);
|
||||||
|
}
|
||||||
|
add_action('wp_ajax_wp_multi_process_auto_tags', 'wp_multi_process_auto_tags');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verarbeitet einen Batch von Beiträgen für die Auto-Tag-Verarbeitung.
|
||||||
|
*/
|
||||||
|
function wp_multi_process_auto_tags_step() {
|
||||||
|
check_ajax_referer(WP_MULTI_AUTO_TAGS_NONCE, 'nonce');
|
||||||
|
|
||||||
|
$queue = get_transient(WP_MULTI_AUTO_TAGS_QUEUE);
|
||||||
|
$batchSize = isset($_POST['batchSize']) ? absint($_POST['batchSize']) : 10;
|
||||||
|
$batchSize = max(1, min($batchSize, 50));
|
||||||
|
|
||||||
|
if (!$queue || empty($queue)) {
|
||||||
|
wp_send_json_success(['done' => false]);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_time_limit(30); // Timeout verhindern
|
||||||
|
$posts_to_process = array_splice($queue, 0, $batchSize);
|
||||||
|
|
||||||
|
foreach ($posts_to_process as $post_id) {
|
||||||
|
wp_multi_auto_add_tags($post_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_transient(WP_MULTI_AUTO_TAGS_QUEUE, $queue, 300);
|
||||||
|
wp_send_json_success(['done' => true]);
|
||||||
|
}
|
||||||
|
add_action('wp_ajax_wp_multi_process_auto_tags_step', 'wp_multi_process_auto_tags_step');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt Tags automatisch bei neuen oder aktualisierten Beiträgen hinzu.
|
||||||
|
*
|
||||||
|
* @param int $post_id ID des Beitrags.
|
||||||
|
*/
|
||||||
|
function wp_multi_auto_tag_on_save($post_id) {
|
||||||
|
if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_multi_auto_add_tags($post_id);
|
||||||
|
}
|
||||||
|
add_action('save_post', 'wp_multi_auto_tag_on_save', 10, 1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+11110
-332
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,198 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Benutzer für Kommentare sperren
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* User Kommentar Blocken
|
||||||
|
*/
|
||||||
|
|
||||||
|
function wp_multi_create_blocked_users_table() {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'blocked_users';
|
||||||
|
$charset_collate = $wpdb->get_charset_collate();
|
||||||
|
$sql = "CREATE TABLE $table_name (
|
||||||
|
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
||||||
|
username varchar(100) DEFAULT '' NOT NULL,
|
||||||
|
email varchar(100) DEFAULT '' NOT NULL,
|
||||||
|
ip_address varchar(45) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
) $charset_collate;";
|
||||||
|
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
||||||
|
dbDelta( $sql );
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_activate() {
|
||||||
|
wp_multi_create_blocked_users_table();
|
||||||
|
}
|
||||||
|
|
||||||
|
register_activation_hook( WP_MULTI_FILE, 'wp_multi_activate' );
|
||||||
|
|
||||||
|
function wp_multi_block_user($username = '', $email = '', $ip_address = '') {
|
||||||
|
global $wpdb;
|
||||||
|
if (empty($username) && empty($email) && empty($ip_address)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$result = $wpdb->insert(
|
||||||
|
$wpdb->prefix . 'blocked_users',
|
||||||
|
[
|
||||||
|
'username' => $username,
|
||||||
|
'email' => $email,
|
||||||
|
'ip_address' => $ip_address
|
||||||
|
]
|
||||||
|
);
|
||||||
|
return $result !== false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_delete_blocked_user($id) {
|
||||||
|
global $wpdb;
|
||||||
|
return $wpdb->delete($wpdb->prefix . 'blocked_users', ['id' => $id], ['%d']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Der Menüpunkt "Gesperrte Benutzer" wird zentral in wp_multi_register_admin_menus() registriert.
|
||||||
|
|
||||||
|
function wp_multi_blocked_users_page() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$notices = array();
|
||||||
|
|
||||||
|
if (isset($_POST['block_username']) || isset($_POST['block_email']) || isset($_POST['block_ip'])) {
|
||||||
|
check_admin_referer('wp_multi_block_user');
|
||||||
|
$username = sanitize_text_field($_POST['block_username']);
|
||||||
|
$email = sanitize_email($_POST['block_email']);
|
||||||
|
$ip_address = sanitize_text_field($_POST['block_ip']);
|
||||||
|
if (wp_multi_block_user($username, $email, $ip_address)) {
|
||||||
|
$notices[] = array('success', __('Benutzer wurde gesperrt!', 'wp-multi'));
|
||||||
|
} else {
|
||||||
|
$notices[] = array('error', __('Fehler beim Sperren des Benutzers!', 'wp-multi'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_GET['delete']) && isset($_GET['_wpnonce'])) {
|
||||||
|
if (wp_verify_nonce($_GET['_wpnonce'], 'wp_multi_delete_user_' . $_GET['delete'])) {
|
||||||
|
$user_id = intval($_GET['delete']);
|
||||||
|
if ($user_id > 0 && wp_multi_delete_blocked_user($user_id)) {
|
||||||
|
$notices[] = array('success', __('Benutzer wurde gelöscht!', 'wp-multi'));
|
||||||
|
} else {
|
||||||
|
$notices[] = array('error', __('Fehler beim Löschen des Benutzers!', 'wp-multi'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$search_query = '';
|
||||||
|
if (isset($_GET['search'])) {
|
||||||
|
$search_query = sanitize_text_field($_GET['search']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$blocked_users = $wpdb->get_results($wpdb->prepare(
|
||||||
|
"SELECT * FROM {$wpdb->prefix}blocked_users WHERE username LIKE %s OR email LIKE %s OR ip_address LIKE %s",
|
||||||
|
'%' . $search_query . '%', '%' . $search_query . '%', '%' . $search_query . '%'
|
||||||
|
));
|
||||||
|
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Gesperrte Benutzer', 'wp-multi'),
|
||||||
|
__('Benutzer per Name, E-Mail oder IP von Kommentaren ausschließen', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($notices as $notice) {
|
||||||
|
wpmt_admin_notice($notice[0], $notice[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="wpm-grid wpm-grid--2">';
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Benutzer sperren', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<form method="post">
|
||||||
|
<?php wp_nonce_field('wp_multi_block_user'); ?>
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php _e('Benutzername', 'wp-multi'); ?></label>
|
||||||
|
<input type="text" name="block_username" placeholder="Benutzername">
|
||||||
|
</div>
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php _e('E-Mail-Adresse', 'wp-multi'); ?></label>
|
||||||
|
<input type="email" name="block_email" placeholder="E-Mail-Adresse">
|
||||||
|
</div>
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php _e('IP-Adresse', 'wp-multi'); ?></label>
|
||||||
|
<input type="text" name="block_ip" placeholder="IP-Adresse">
|
||||||
|
</div>
|
||||||
|
<p><input type="submit" class="wpm-btn wpm-btn--primary" value="<?php esc_attr_e('Benutzer sperren', 'wp-multi'); ?>"></p>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Suchen', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<form method="get">
|
||||||
|
<input type="hidden" name="page" value="wp-multi-blocked-users">
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php _e('Suchbegriff', 'wp-multi'); ?></label>
|
||||||
|
<input type="text" name="search" value="<?php echo esc_attr($search_query); ?>" placeholder="<?php esc_attr_e('Benutzername, E-Mail oder IP', 'wp-multi'); ?>">
|
||||||
|
</div>
|
||||||
|
<p><input type="submit" class="wpm-btn" value="<?php esc_attr_e('Suchen', 'wp-multi'); ?>"></p>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Gesperrte Benutzer', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<div class="wpm-table-wrap">
|
||||||
|
<table class="wpm-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('Benutzername', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('E-Mail-Adresse', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('IP-Adresse', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('Aktionen', 'wp-multi'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ($blocked_users) : ?>
|
||||||
|
<?php foreach ($blocked_users as $user) : ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo esc_html($user->username); ?></td>
|
||||||
|
<td><?php echo esc_html($user->email); ?></td>
|
||||||
|
<td><?php echo esc_html($user->ip_address); ?></td>
|
||||||
|
<td>
|
||||||
|
<a href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=wp-multi-blocked-users&delete=' . $user->id), 'wp_multi_delete_user_' . $user->id)); ?>" class="wpm-btn wpm-btn--danger"><?php _e('Löschen', 'wp-multi'); ?></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else : ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4"><?php _e('Keine gesperrten Benutzer gefunden.', 'wp-multi'); ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_check_blocked_user($commentdata) {
|
||||||
|
global $wpdb;
|
||||||
|
$username = isset($commentdata['comment_author']) ? $commentdata['comment_author'] : '';
|
||||||
|
$email = isset($commentdata['comment_author_email']) ? $commentdata['comment_author_email'] : '';
|
||||||
|
$ip_address = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
|
||||||
|
$blocked_user = $wpdb->get_row($wpdb->prepare(
|
||||||
|
"SELECT * FROM {$wpdb->prefix}blocked_users WHERE username = %s OR email = %s OR ip_address = %s",
|
||||||
|
$username, $email, $ip_address
|
||||||
|
));
|
||||||
|
if ($blocked_user) {
|
||||||
|
wp_die('Ihr Kommentar konnte nicht abgesendet werden, da Sie gesperrt sind. Bitte wenden Sie sich an den Support.');
|
||||||
|
}
|
||||||
|
return $commentdata;
|
||||||
|
}
|
||||||
|
add_filter('preprocess_comment', 'wp_multi_check_blocked_user');
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,346 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Gast-Lesezeichen
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verbessertes Gast-Lesezeichen-Plugin mit erhöhter Sicherheit und Benutzerfreundlichkeit
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
define('STATISTIK_MANAGER_BOOKMARK_POST_TYPE', 'bookmark');
|
||||||
|
define('STATISTIK_MANAGER_COOKIE_NAME', 'guest_token');
|
||||||
|
define('STATISTIK_MANAGER_COOKIE_DURATION', 30 * DAY_IN_SECONDS);
|
||||||
|
define('STATISTIK_MANAGER_BOOKMARKS_CACHE_KEY', 'statistik_manager_bookmarks_');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt den benutzerdefinierten Post-Typ für Lesezeichen.
|
||||||
|
*/
|
||||||
|
function statistik_manager_create_bookmark_post_type() {
|
||||||
|
register_post_type(STATISTIK_MANAGER_BOOKMARK_POST_TYPE, [
|
||||||
|
'labels' => [
|
||||||
|
'name' => __('Lesezeichen', 'statistik-manager'),
|
||||||
|
'singular_name' => __('Lesezeichen', 'statistik-manager'),
|
||||||
|
],
|
||||||
|
'public' => false,
|
||||||
|
'show_ui' => false,
|
||||||
|
'show_in_menu' => false,
|
||||||
|
'supports' => ['title', 'custom-fields'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
add_action('init', 'statistik_manager_create_bookmark_post_type');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generiert oder holt den Gast-Token.
|
||||||
|
*
|
||||||
|
* @return string Der Gast-Token.
|
||||||
|
*/
|
||||||
|
function statistik_manager_get_guest_token() {
|
||||||
|
$guest_token = isset($_COOKIE[STATISTIK_MANAGER_COOKIE_NAME]) ? sanitize_text_field($_COOKIE[STATISTIK_MANAGER_COOKIE_NAME]) : null;
|
||||||
|
|
||||||
|
if (!$guest_token) {
|
||||||
|
$guest_token = wp_generate_uuid4();
|
||||||
|
setcookie(
|
||||||
|
STATISTIK_MANAGER_COOKIE_NAME,
|
||||||
|
$guest_token,
|
||||||
|
time() + STATISTIK_MANAGER_COOKIE_DURATION,
|
||||||
|
COOKIEPATH,
|
||||||
|
COOKIE_DOMAIN,
|
||||||
|
is_ssl(), // Secure
|
||||||
|
true // HttpOnly
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $guest_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speichert ein Lesezeichen für einen Gast.
|
||||||
|
*
|
||||||
|
* @param int $post_id Post-ID des Lesezeichens.
|
||||||
|
*/
|
||||||
|
function statistik_manager_save_bookmark($post_id) {
|
||||||
|
$guest_token = statistik_manager_get_guest_token();
|
||||||
|
update_post_meta($post_id, '_guest_token', $guest_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ruft die Lesezeichen eines Gastes ab.
|
||||||
|
*
|
||||||
|
* @return array Liste der Lesezeichen.
|
||||||
|
*/
|
||||||
|
function statistik_manager_get_guest_bookmarks() {
|
||||||
|
$guest_token = statistik_manager_get_guest_token();
|
||||||
|
$cache_key = STATISTIK_MANAGER_BOOKMARKS_CACHE_KEY . md5($guest_token);
|
||||||
|
$bookmarks = get_transient($cache_key);
|
||||||
|
|
||||||
|
if ($bookmarks !== false) {
|
||||||
|
return $bookmarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
$args = [
|
||||||
|
'post_type' => STATISTIK_MANAGER_BOOKMARK_POST_TYPE,
|
||||||
|
'meta_query' => [
|
||||||
|
[
|
||||||
|
'key' => '_guest_token',
|
||||||
|
'value' => $guest_token,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'posts_per_page' => -1,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
'orderby' => 'date',
|
||||||
|
'order' => 'DESC',
|
||||||
|
];
|
||||||
|
|
||||||
|
$bookmarks_query = new WP_Query($args);
|
||||||
|
$bookmarks = $bookmarks_query->posts;
|
||||||
|
|
||||||
|
set_transient($cache_key, $bookmarks, HOUR_IN_SECONDS);
|
||||||
|
return $bookmarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Löscht ein Lesezeichen via AJAX.
|
||||||
|
*/
|
||||||
|
function statistik_manager_delete_bookmark() {
|
||||||
|
check_ajax_referer('statistik_manager_bookmark_nonce', 'nonce');
|
||||||
|
|
||||||
|
if (!isset($_POST['bookmark_id']) || !isset($_COOKIE[STATISTIK_MANAGER_COOKIE_NAME])) {
|
||||||
|
wp_send_json_error(['message' => __('Ungültige Anfrage.', 'statistik-manager')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$bookmark_id = absint($_POST['bookmark_id']);
|
||||||
|
$guest_token = sanitize_text_field($_COOKIE[STATISTIK_MANAGER_COOKIE_NAME]);
|
||||||
|
$stored_token = get_post_meta($bookmark_id, '_guest_token', true);
|
||||||
|
|
||||||
|
if ($stored_token !== $guest_token) {
|
||||||
|
wp_send_json_error(['message' => __('Du kannst nur deine eigenen Lesezeichen löschen.', 'statistik-manager')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_delete_post($bookmark_id, true);
|
||||||
|
delete_transient(STATISTIK_MANAGER_BOOKMARKS_CACHE_KEY . md5($guest_token));
|
||||||
|
wp_send_json_success(['message' => __('Lesezeichen erfolgreich gelöscht.', 'statistik-manager')]);
|
||||||
|
}
|
||||||
|
add_action('wp_ajax_delete_bookmark', 'statistik_manager_delete_bookmark');
|
||||||
|
add_action('wp_ajax_nopriv_delete_bookmark', 'statistik_manager_delete_bookmark');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt ein Lesezeichen via AJAX hinzu.
|
||||||
|
*/
|
||||||
|
function statistik_manager_add_bookmark_ajax() {
|
||||||
|
check_ajax_referer('statistik_manager_bookmark_nonce', 'nonce');
|
||||||
|
|
||||||
|
if (!isset($_POST['bookmark_url']) || !isset($_POST['bookmark_name'])) {
|
||||||
|
wp_send_json_error(['message' => __('Bitte alle Felder ausfüllen.', 'statistik-manager')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$bookmark_url = esc_url_raw($_POST['bookmark_url']);
|
||||||
|
$bookmark_name = sanitize_text_field($_POST['bookmark_name']);
|
||||||
|
|
||||||
|
if (empty($bookmark_url) || empty($bookmark_name)) {
|
||||||
|
wp_send_json_error(['message' => __('Ungültige URL oder Name.', 'statistik-manager')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfen, ob die URL bereits existiert
|
||||||
|
$guest_token = statistik_manager_get_guest_token();
|
||||||
|
$args = [
|
||||||
|
'post_type' => STATISTIK_MANAGER_BOOKMARK_POST_TYPE,
|
||||||
|
'meta_query' => [
|
||||||
|
'relation' => 'AND',
|
||||||
|
[
|
||||||
|
'key' => '_guest_token',
|
||||||
|
'value' => $guest_token,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'key' => '_bookmark_url',
|
||||||
|
'value' => $bookmark_url,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'posts_per_page' => 1,
|
||||||
|
];
|
||||||
|
|
||||||
|
$existing = new WP_Query($args);
|
||||||
|
if ($existing->have_posts()) {
|
||||||
|
wp_send_json_error(['message' => __('Diese URL ist bereits ein Lesezeichen.', 'statistik-manager')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neues Lesezeichen erstellen
|
||||||
|
$post_id = wp_insert_post([
|
||||||
|
'post_type' => STATISTIK_MANAGER_BOOKMARK_POST_TYPE,
|
||||||
|
'post_title' => $bookmark_name,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
'meta_input' => [
|
||||||
|
'_bookmark_url' => $bookmark_url,
|
||||||
|
'_guest_token' => $guest_token,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (is_wp_error($post_id)) {
|
||||||
|
wp_send_json_error(['message' => __('Fehler beim Hinzufügen des Lesezeichens.', 'statistik-manager')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_transient(STATISTIK_MANAGER_BOOKMARKS_CACHE_KEY . md5($guest_token));
|
||||||
|
wp_send_json_success([
|
||||||
|
'message' => __('Lesezeichen erfolgreich hinzugefügt.', 'statistik-manager'),
|
||||||
|
'html' => '<li><button class="delete-bookmark-btn" data-bookmark-id="' . esc_attr($post_id) . '">' . esc_html__('Löschen', 'statistik-manager') . '</button><a href="' . esc_url($bookmark_url) . '" target="_blank">' . esc_html($bookmark_name) . '</a></li>',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
add_action('wp_ajax_add_bookmark', 'statistik_manager_add_bookmark_ajax');
|
||||||
|
add_action('wp_ajax_nopriv_add_bookmark', 'statistik_manager_add_bookmark_ajax');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeigt die Lesezeichen eines Gastes an.
|
||||||
|
*
|
||||||
|
* @return string HTML-Ausgabe der Lesezeichen.
|
||||||
|
*/
|
||||||
|
function statistik_manager_display_bookmarks() {
|
||||||
|
$bookmarks = statistik_manager_get_guest_bookmarks();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<div class="statistik-manager-bookmarks">
|
||||||
|
<h3><?php esc_html_e('Gespeicherte Lesezeichen', 'statistik-manager'); ?></h3>
|
||||||
|
<?php if (empty($bookmarks)) : ?>
|
||||||
|
<p><?php esc_html_e('Keine Lesezeichen gefunden.', 'statistik-manager'); ?></p>
|
||||||
|
<?php else : ?>
|
||||||
|
<ul>
|
||||||
|
<?php foreach ($bookmarks as $bookmark) : ?>
|
||||||
|
<li>
|
||||||
|
<button class="delete-bookmark-btn" data-bookmark-id="<?php echo esc_attr($bookmark->ID); ?>">
|
||||||
|
<?php esc_html_e('Löschen', 'statistik-manager'); ?>
|
||||||
|
</button>
|
||||||
|
<a href="<?php echo esc_url(get_post_meta($bookmark->ID, '_bookmark_url', true)); ?>" target="_blank">
|
||||||
|
<?php echo esc_html(get_the_title($bookmark)); ?>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcode zum Anzeigen der Lesezeichen.
|
||||||
|
*
|
||||||
|
* @return string HTML-Ausgabe.
|
||||||
|
*/
|
||||||
|
function statistik_manager_bookmarks_shortcode() {
|
||||||
|
return statistik_manager_display_bookmarks();
|
||||||
|
}
|
||||||
|
add_shortcode('display_bookmarks', 'statistik_manager_bookmarks_shortcode');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcode zum Hinzufügen eines Lesezeichens.
|
||||||
|
*
|
||||||
|
* @return string HTML-Ausgabe.
|
||||||
|
*/
|
||||||
|
function statistik_manager_add_bookmark_shortcode() {
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<form id="add-bookmark-form" class="statistik-manager-bookmark-form">
|
||||||
|
<input type="hidden" name="nonce" value="<?php echo esc_attr(wp_create_nonce('statistik_manager_bookmark_nonce')); ?>">
|
||||||
|
<div class="form-field">
|
||||||
|
<label for="bookmark_name"><?php esc_html_e('Name des Lesezeichens:', 'statistik-manager'); ?></label>
|
||||||
|
<input type="text" name="bookmark_name" id="bookmark_name" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-field">
|
||||||
|
<label for="bookmark_url"><?php esc_html_e('URL des Lesezeichens:', 'statistik-manager'); ?></label>
|
||||||
|
<input type="url" name="bookmark_url" id="bookmark_url" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-field">
|
||||||
|
<button type="submit"><?php esc_html_e('Lesezeichen hinzufügen', 'statistik-manager'); ?></button>
|
||||||
|
</div>
|
||||||
|
<div class="form-message"></div>
|
||||||
|
</form>
|
||||||
|
<style>
|
||||||
|
.statistik-manager-bookmark-form { max-width: 500px; margin: 20px 0; }
|
||||||
|
.statistik-manager-bookmark-form .form-field { margin-bottom: 15px; }
|
||||||
|
.statistik-manager-bookmark-form label { display: block; margin-bottom: 5px; font-weight: bold; }
|
||||||
|
.statistik-manager-bookmark-form input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
|
||||||
|
.statistik-manager-bookmark-form button { background-color: #0073aa; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
|
||||||
|
.statistik-manager-bookmark-form button:hover { background-color: #005177; }
|
||||||
|
.statistik-manager-bookmark-form .form-message { margin-top: 10px; color: green; }
|
||||||
|
.statistik-manager-bookmark-form .form-message.error { color: red; }
|
||||||
|
.statistik-manager-bookmarks ul { list-style: none; padding: 0; }
|
||||||
|
.statistik-manager-bookmarks li { margin-bottom: 10px; display: flex; align-items: center; }
|
||||||
|
.statistik-manager-bookmarks button { margin-right: 10px; background-color: #d63638; color: white; padding: 5px 10px; border: none; border-radius: 4px; cursor: pointer; }
|
||||||
|
.statistik-manager-bookmarks button:hover { background-color: #a12b2d; }
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.statistik-manager-bookmark-form input, .statistik-manager-bookmark-form button { font-size: 14px; }
|
||||||
|
.statistik-manager-bookmarks li { flex-direction: column; align-items: flex-start; }
|
||||||
|
.statistik-manager-bookmarks button { margin-bottom: 5px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<?php
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
add_shortcode('add_bookmark', 'statistik_manager_add_bookmark_shortcode');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt JavaScript für AJAX-Interaktionen hinzu.
|
||||||
|
*/
|
||||||
|
function statistik_manager_add_bookmark_script() {
|
||||||
|
if (!is_singular() && !has_shortcode(get_the_content(), 'add_bookmark') && !has_shortcode(get_the_content(), 'display_bookmarks')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<script type="text/javascript">
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
$('#add-bookmark-form').on('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var $form = $(this);
|
||||||
|
var $message = $form.find('.form-message');
|
||||||
|
var data = {
|
||||||
|
action: 'add_bookmark',
|
||||||
|
nonce: $form.find('[name="nonce"]').val(),
|
||||||
|
bookmark_url: $form.find('#bookmark_url').val(),
|
||||||
|
bookmark_name: $form.find('#bookmark_name').val(),
|
||||||
|
};
|
||||||
|
|
||||||
|
$.post('<?php echo esc_url(admin_url('admin-ajax.php')); ?>', data, function(response) {
|
||||||
|
$message.removeClass('error');
|
||||||
|
if (response.success) {
|
||||||
|
$message.addClass('success').text(response.data.message);
|
||||||
|
$('.statistik-manager-bookmarks ul').append(response.data.html);
|
||||||
|
$form.find('#bookmark_url, #bookmark_name').val('');
|
||||||
|
} else {
|
||||||
|
$message.addClass('error').text(response.data.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('body').on('click', '.delete-bookmark-btn', function() {
|
||||||
|
var $button = $(this);
|
||||||
|
var bookmarkId = $button.data('bookmark-id');
|
||||||
|
var data = {
|
||||||
|
action: 'delete_bookmark',
|
||||||
|
nonce: $('#add-bookmark-form [name="nonce"]').val(),
|
||||||
|
bookmark_id: bookmarkId,
|
||||||
|
};
|
||||||
|
|
||||||
|
$.post('<?php echo esc_url(admin_url('admin-ajax.php')); ?>', data, function(response) {
|
||||||
|
var $message = $('.statistik-manager-bookmark-form .form-message');
|
||||||
|
$message.removeClass('error success');
|
||||||
|
if (response.success) {
|
||||||
|
$message.addClass('success').text(response.data.message);
|
||||||
|
$button.closest('li').remove();
|
||||||
|
} else {
|
||||||
|
$message.addClass('error').text(response.data.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
add_action('wp_footer', 'statistik_manager_add_bookmark_script');
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,405 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Brute-Force-Schutz
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Schutz vor Brute-Force-Angriffen mit wöchentlicher Zusammenfassung
|
||||||
|
* Sperr-E-Mails entfernt, Zusammenfassung deaktivierbar unter Benutzer mit verbessertem Design
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Funktion zur Erfassung der echten IP-Adresse des Benutzers
|
||||||
|
function get_user_ip() {
|
||||||
|
if (!empty($_SERVER['HTTP_CLIENT_IP']) && filter_var($_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP)) {
|
||||||
|
return $_SERVER['HTTP_CLIENT_IP'];
|
||||||
|
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||||
|
$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
|
||||||
|
$ip = trim($ips[0]);
|
||||||
|
return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : $_SERVER['REMOTE_ADDR'];
|
||||||
|
}
|
||||||
|
return filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zur Verfolgung von fehlgeschlagenen Anmeldeversuchen
|
||||||
|
function wp_multi_log_failed_login($username) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$ip = get_user_ip();
|
||||||
|
if ($ip === '0.0.0.0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$blocked_table = $wpdb->prefix . 'blocked_ips';
|
||||||
|
$attempts_table = $wpdb->prefix . 'login_attempts';
|
||||||
|
$block_threshold = 5;
|
||||||
|
|
||||||
|
// Anmeldeversuch in attempts-Tabelle speichern
|
||||||
|
$wpdb->insert(
|
||||||
|
$attempts_table,
|
||||||
|
array(
|
||||||
|
'ip' => $ip,
|
||||||
|
'username' => $username,
|
||||||
|
'attempt_time' => current_time('mysql'),
|
||||||
|
),
|
||||||
|
array('%s', '%s', '%s')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prüfen, ob IP bereits in blocked_ips existiert
|
||||||
|
$row = $wpdb->get_row($wpdb->prepare("SELECT * FROM $blocked_table WHERE ip = %s", $ip));
|
||||||
|
|
||||||
|
if ($row) {
|
||||||
|
$wpdb->update(
|
||||||
|
$blocked_table,
|
||||||
|
array(
|
||||||
|
'attempts' => $row->attempts + 1,
|
||||||
|
'last_attempt' => current_time('mysql'),
|
||||||
|
),
|
||||||
|
array('ip' => $ip),
|
||||||
|
array('%d', '%s'),
|
||||||
|
array('%s')
|
||||||
|
);
|
||||||
|
$attempts = $row->attempts + 1;
|
||||||
|
} else {
|
||||||
|
$wpdb->insert(
|
||||||
|
$blocked_table,
|
||||||
|
array(
|
||||||
|
'ip' => $ip,
|
||||||
|
'attempts' => 1,
|
||||||
|
'last_attempt' => current_time('mysql'),
|
||||||
|
),
|
||||||
|
array('%s', '%d', '%s')
|
||||||
|
);
|
||||||
|
$attempts = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP sperren, wenn Schwellenwert erreicht
|
||||||
|
if ($attempts >= $block_threshold) {
|
||||||
|
wp_die(
|
||||||
|
__('Deine IP-Adresse wurde aufgrund zu vieler Fehlversuche gesperrt. Bitte versuche es später noch einmal.', 'wp-multi'),
|
||||||
|
__('Zugriff gesperrt', 'wp-multi'),
|
||||||
|
array('response' => 403)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook für fehlgeschlagene Anmeldungen
|
||||||
|
add_action('wp_login_failed', 'wp_multi_log_failed_login', 10, 1);
|
||||||
|
|
||||||
|
// Funktion zur Erstellung der Tabellen
|
||||||
|
function wp_multi_create_blocked_ips_table() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$blocked_table = $wpdb->prefix . 'blocked_ips';
|
||||||
|
$attempts_table = $wpdb->prefix . 'login_attempts';
|
||||||
|
$charset_collate = $wpdb->get_charset_collate();
|
||||||
|
$version = get_option('wp_multi_blocked_ips_version', '1.0');
|
||||||
|
|
||||||
|
if (version_compare($version, '1.1', '<')) {
|
||||||
|
$sql_blocked = "CREATE TABLE $blocked_table (
|
||||||
|
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
||||||
|
ip varchar(45) NOT NULL,
|
||||||
|
attempts int NOT NULL DEFAULT 0,
|
||||||
|
last_attempt datetime NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
KEY ip (ip)
|
||||||
|
) $charset_collate;";
|
||||||
|
|
||||||
|
$sql_attempts = "CREATE TABLE $attempts_table (
|
||||||
|
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
||||||
|
ip varchar(45) NOT NULL,
|
||||||
|
username varchar(255) NOT NULL,
|
||||||
|
attempt_time datetime NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
KEY ip (ip),
|
||||||
|
KEY attempt_time (attempt_time)
|
||||||
|
) $charset_collate;";
|
||||||
|
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||||
|
dbDelta($sql_blocked);
|
||||||
|
dbDelta($sql_attempts);
|
||||||
|
update_option('wp_multi_blocked_ips_version', '1.1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
register_activation_hook(WP_MULTI_FILE, 'wp_multi_create_blocked_ips_table');
|
||||||
|
|
||||||
|
// Wöchentliche Zusammenfassung
|
||||||
|
function wp_multi_send_weekly_summary() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Prüfen, ob Zusammenfassung aktiviert ist
|
||||||
|
$summary_enabled = get_option('wp_multi_summary_enabled', 1);
|
||||||
|
if (!$summary_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$attempts_table = $wpdb->prefix . 'login_attempts';
|
||||||
|
$last_email_time = get_option('wp_multi_last_summary_email', 0);
|
||||||
|
$current_time = time();
|
||||||
|
|
||||||
|
// Prüfen, ob eine Woche vergangen ist (7 Tage = 604800 Sekunden)
|
||||||
|
if (($current_time - $last_email_time) < 604800) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zeitraum für die Zusammenfassung (letzte Woche)
|
||||||
|
$week_ago = date('Y-m-d H:i:s', strtotime('-7 days'));
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
// Gesamtzahl der Anmeldeversuche
|
||||||
|
$total_attempts = $wpdb->get_var(
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT COUNT(*) FROM $attempts_table WHERE attempt_time >= %s",
|
||||||
|
$week_ago
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$total_attempts = $total_attempts ? $total_attempts : 0;
|
||||||
|
|
||||||
|
// Top 10 IPs mit den meisten Anmeldeversuchen
|
||||||
|
$top_attempts = $wpdb->get_results(
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT ip, username, COUNT(*) as attempt_count, MAX(attempt_time) as last_attempt
|
||||||
|
FROM $attempts_table
|
||||||
|
WHERE attempt_time >= %s
|
||||||
|
GROUP BY ip, username
|
||||||
|
ORDER BY attempt_count DESC
|
||||||
|
LIMIT 10",
|
||||||
|
$week_ago
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// E-Mail-Inhalt
|
||||||
|
$message = __("Wöchentliche Anmeldeversuche-Zusammenfassung\n\n", 'wp-multi');
|
||||||
|
$message .= sprintf(__("Zeitraum: %s bis %s\n\n", 'wp-multi'), $week_ago, $now);
|
||||||
|
$message .= sprintf(__("Gesamtzahl der Anmeldeversuche: %d\n\n", 'wp-multi'), $total_attempts);
|
||||||
|
$message .= __("Top 10 Anmeldeversuche (nach IP und Benutzername):\n", 'wp-multi');
|
||||||
|
|
||||||
|
if ($top_attempts) {
|
||||||
|
foreach ($top_attempts as $index => $attempt) {
|
||||||
|
$message .= sprintf(
|
||||||
|
__("%d. IP: %s, Benutzername: %s, Versuche: %d, Letzter Versuch: %s\n", 'wp-multi'),
|
||||||
|
$index + 1,
|
||||||
|
$attempt->ip,
|
||||||
|
$attempt->username,
|
||||||
|
$attempt->attempt_count,
|
||||||
|
$attempt->last_attempt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$message .= __("Keine Anmeldeversuche in diesem Zeitraum.\n", 'wp-multi');
|
||||||
|
}
|
||||||
|
|
||||||
|
// E-Mail an Admin senden
|
||||||
|
$admin_email = get_option('admin_email');
|
||||||
|
wp_mail(
|
||||||
|
$admin_email,
|
||||||
|
__('Wöchentliche Anmeldeversuche-Zusammenfassung', 'wp-multi'),
|
||||||
|
$message,
|
||||||
|
array('Content-Type: text/plain; charset=UTF-8')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Aktualisiere den Zeitstempel der letzten E-Mail
|
||||||
|
update_option('wp_multi_last_summary_email', $current_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Planen der wöchentlichen Zusammenfassung (jeden Montag um 8:00 Uhr)
|
||||||
|
function wp_multi_schedule_weekly_summary() {
|
||||||
|
if (!wp_next_scheduled('wp_multi_weekly_summary_event')) {
|
||||||
|
wp_schedule_event(strtotime('next Monday 08:00'), 'weekly', 'wp_multi_weekly_summary_event');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('wp', 'wp_multi_schedule_weekly_summary');
|
||||||
|
|
||||||
|
// Hook für die Ausführung der Zusammenfassung
|
||||||
|
add_action('wp_multi_weekly_summary_event', 'wp_multi_send_weekly_summary');
|
||||||
|
|
||||||
|
// Einstellungsoption für die Zusammenfassung
|
||||||
|
function wp_multi_users_register_settings() {
|
||||||
|
add_option('wp_multi_summary_enabled', 1);
|
||||||
|
register_setting('wp_multi_users_settings_group', 'wp_multi_summary_enabled', array(
|
||||||
|
'type' => 'integer',
|
||||||
|
'sanitize_callback' => 'absint',
|
||||||
|
'default' => 1,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Settings Section hinzufügen
|
||||||
|
add_settings_section(
|
||||||
|
'wp_multi_security_section',
|
||||||
|
__('Brute-Force-Schutz Einstellungen', 'wp-multi'),
|
||||||
|
'wp_multi_security_section_callback',
|
||||||
|
'wp_multi_users_security_settings'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Settings Field hinzufügen
|
||||||
|
add_settings_field(
|
||||||
|
'wp_multi_summary_enabled',
|
||||||
|
__('Wöchentliche Zusammenfassung', 'wp-multi'),
|
||||||
|
'wp_multi_summary_enabled_callback',
|
||||||
|
'wp_multi_users_security_settings',
|
||||||
|
'wp_multi_security_section'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
add_action('admin_init', 'wp_multi_users_register_settings');
|
||||||
|
|
||||||
|
// Callback für die Settings Section
|
||||||
|
function wp_multi_security_section_callback() {
|
||||||
|
echo '<p>' . __('Konfiguriere die Sicherheitseinstellungen für den Brute-Force-Schutz deiner Website. Aktiviere oder deaktiviere die wöchentliche Zusammenfassung der fehlgeschlagenen Anmeldeversuche.', 'wp-multi') . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback für das Settings Field
|
||||||
|
function wp_multi_summary_enabled_callback() {
|
||||||
|
$summary_enabled = get_option('wp_multi_summary_enabled', 1);
|
||||||
|
?>
|
||||||
|
<input type="checkbox" name="wp_multi_summary_enabled" id="wp_multi_summary_enabled" value="1" <?php checked(1, $summary_enabled); ?> />
|
||||||
|
<label for="wp_multi_summary_enabled"><?php _e('Wöchentliche E-Mail-Zusammenfassung für fehlgeschlagene Anmeldeversuche aktivieren', 'wp-multi'); ?></label>
|
||||||
|
<p class="description"><?php _e('Wenn aktiviert, erhält der Administrator jeden Montag um 8:00 Uhr eine E-Mail mit einer Zusammenfassung der fehlgeschlagenen Anmeldeversuche der letzten Woche.', 'wp-multi'); ?></p>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blockierte IPs & Sicherheitseinstellungen werden zentral in wp_multi_register_admin_menus() registriert.
|
||||||
|
|
||||||
|
// Einstellungsseite unter Benutzer
|
||||||
|
function wp_multi_users_security_settings_page() {
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Benutzer-Sicherheit', 'wp-multi'),
|
||||||
|
__('Brute-Force-Schutz und Benachrichtigungen für fehlgeschlagene Logins', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Brute-Force-Schutz', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<p><?php _e('Verwalte die Einstellungen für den Schutz vor Brute-Force-Angriffen. Passe die Benachrichtigungen für fehlgeschlagene Anmeldeversuche an.', 'wp-multi'); ?></p>
|
||||||
|
<form method="post" action="options.php">
|
||||||
|
<?php
|
||||||
|
settings_fields('wp_multi_users_settings_group');
|
||||||
|
do_settings_sections('wp_multi_users_security_settings');
|
||||||
|
submit_button(__('Einstellungen speichern', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anzeige der blockierten IPs im Admin-Bereich
|
||||||
|
function wp_multi_display_blocked_ips() {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'blocked_ips';
|
||||||
|
|
||||||
|
$five_days_ago = date('Y-m-d H:i:s', strtotime('-5 days'));
|
||||||
|
$per_page = 50;
|
||||||
|
$page = max(1, isset($_GET['paged']) ? intval($_GET['paged']) : 1);
|
||||||
|
$offset = ($page - 1) * $per_page;
|
||||||
|
|
||||||
|
$blocked_ips = $wpdb->get_results(
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT * FROM $table_name WHERE last_attempt >= %s ORDER BY last_attempt DESC LIMIT %d OFFSET %d",
|
||||||
|
$five_days_ago,
|
||||||
|
$per_page,
|
||||||
|
$offset
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$total_ips = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $table_name WHERE last_attempt >= %s", $five_days_ago));
|
||||||
|
$total_pages = ceil($total_ips / $per_page);
|
||||||
|
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Blockierte IPs', 'wp-multi'),
|
||||||
|
__('Vom Brute-Force-Schutz erfasste IP-Adressen der letzten 5 Tage', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
wpmt_admin_card_open();
|
||||||
|
if (empty($blocked_ips)) {
|
||||||
|
wpmt_admin_notice('info', __('Keine blockierten IPs gefunden.', 'wp-multi'));
|
||||||
|
} else {
|
||||||
|
?>
|
||||||
|
<div class="wpm-table-wrap">
|
||||||
|
<table class="wpm-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('ID', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('IP-Adresse', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('Versuche', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('Letzter Versuch', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('Aktionen', 'wp-multi'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($blocked_ips as $ip) : ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo esc_html($ip->id); ?></td>
|
||||||
|
<td><?php echo esc_html($ip->ip); ?></td>
|
||||||
|
<td><span class="wpm-badge <?php echo $ip->attempts >= 5 ? 'wpm-badge--danger' : 'wpm-badge--warning'; ?>"><?php echo esc_html($ip->attempts); ?></span></td>
|
||||||
|
<td><?php echo esc_html($ip->last_attempt); ?></td>
|
||||||
|
<td>
|
||||||
|
<a class="wpm-btn wpm-btn--danger" href="<?php echo esc_url(wp_nonce_url(admin_url('admin-post.php?action=remove_blocked_ip&id=' . $ip->id), 'wp_multi_remove_blocked_ip_' . $ip->id)); ?>">
|
||||||
|
<?php _e('Entfernen', 'wp-multi'); ?>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($total_pages > 1) : ?>
|
||||||
|
<div class="tablenav">
|
||||||
|
<div class="tablenav-pages">
|
||||||
|
<?php
|
||||||
|
echo paginate_links(array(
|
||||||
|
'base' => add_query_arg('paged', '%#%'),
|
||||||
|
'format' => '',
|
||||||
|
'prev_text' => __('«'),
|
||||||
|
'next_text' => __('»'),
|
||||||
|
'total' => $total_pages,
|
||||||
|
'current' => $page,
|
||||||
|
));
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
// Aufräumen: alte Einträge mit wenigen Versuchen entfernen
|
||||||
|
$three_days_ago = date('Y-m-d H:i:s', strtotime('-3 days'));
|
||||||
|
$wpdb->query(
|
||||||
|
$wpdb->prepare(
|
||||||
|
"DELETE FROM $table_name WHERE attempts < 10 AND last_attempt < %s",
|
||||||
|
$three_days_ago
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zum Entfernen einer blockierten IP
|
||||||
|
function wp_multi_remove_blocked_ip() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_die(__('Du hast nicht die erforderlichen Berechtigungen.', 'wp-multi'), '', array('response' => 403));
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
|
||||||
|
|
||||||
|
if (!$id || !isset($_GET['_wpnonce']) || !wp_verify_nonce(wp_unslash($_GET['_wpnonce']), 'wp_multi_remove_blocked_ip_' . $id)) {
|
||||||
|
wp_die(__('Ungültige oder abgelaufene Anfrage.', 'wp-multi'), '', array('response' => 403));
|
||||||
|
}
|
||||||
|
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
if ($id > 0) {
|
||||||
|
$table_name = $wpdb->prefix . 'blocked_ips';
|
||||||
|
$wpdb->delete($table_name, array('id' => $id), array('%d'));
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_safe_redirect(admin_url('admin.php?page=wp_multi_blocked_ips'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
add_action('admin_post_remove_blocked_ip', 'wp_multi_remove_blocked_ip');
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Kommentarfilter (Telefon/E-Mail/URL/IP/Schimpfwörter)
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kommentarfilter: Telefonnummern, E-Mails, URLs, IPs und Schimpfwörter
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Einstellungen für den Kommentarfilter registrieren (auf der bestehenden "Sicherheit"-Seite)
|
||||||
|
function wp_multi_comment_filter_register_settings() {
|
||||||
|
register_setting('wp_multi_security_settings', 'wp_multi_filter_phone', ['sanitize_callback' => 'absint', 'default' => 0]);
|
||||||
|
register_setting('wp_multi_security_settings', 'wp_multi_filter_email', ['sanitize_callback' => 'absint', 'default' => 0]);
|
||||||
|
register_setting('wp_multi_security_settings', 'wp_multi_filter_url', ['sanitize_callback' => 'absint', 'default' => 0]);
|
||||||
|
register_setting('wp_multi_security_settings', 'wp_multi_filter_ip', ['sanitize_callback' => 'absint', 'default' => 0]);
|
||||||
|
register_setting('wp_multi_security_settings', 'wp_multi_filter_swearwords', ['sanitize_callback' => 'absint', 'default' => 0]);
|
||||||
|
|
||||||
|
add_settings_section(
|
||||||
|
'wp_multi_comment_filter_section',
|
||||||
|
__('Kommentarfilter', 'wp-multi'),
|
||||||
|
'wp_multi_comment_filter_section_callback',
|
||||||
|
'wp-multi-security'
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field('wp_multi_filter_phone', __('Telefonnummern filtern', 'wp-multi'), 'wp_multi_filter_phone_callback', 'wp-multi-security', 'wp_multi_comment_filter_section');
|
||||||
|
add_settings_field('wp_multi_filter_email', __('E-Mail-Adressen filtern', 'wp-multi'), 'wp_multi_filter_email_callback', 'wp-multi-security', 'wp_multi_comment_filter_section');
|
||||||
|
add_settings_field('wp_multi_filter_url', __('URLs filtern', 'wp-multi'), 'wp_multi_filter_url_callback', 'wp-multi-security', 'wp_multi_comment_filter_section');
|
||||||
|
add_settings_field('wp_multi_filter_ip', __('IP-Adressen filtern', 'wp-multi'), 'wp_multi_filter_ip_callback', 'wp-multi-security', 'wp_multi_comment_filter_section');
|
||||||
|
add_settings_field('wp_multi_filter_swearwords', __('Schimpfwörter filtern', 'wp-multi'), 'wp_multi_filter_swearwords_callback', 'wp-multi-security', 'wp_multi_comment_filter_section');
|
||||||
|
}
|
||||||
|
add_action('admin_init', 'wp_multi_comment_filter_register_settings');
|
||||||
|
|
||||||
|
function wp_multi_comment_filter_section_callback() {
|
||||||
|
echo '<p>' . esc_html__('Lege fest, welche Inhalte in Kommentaren automatisch durch * ersetzt werden, statt den ganzen Kommentar zu blockieren.', 'wp-multi') . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_filter_phone_callback() {
|
||||||
|
?>
|
||||||
|
<input type="checkbox" name="wp_multi_filter_phone" value="1" <?php checked(1, get_option('wp_multi_filter_phone', 0)); ?>>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_filter_email_callback() {
|
||||||
|
?>
|
||||||
|
<input type="checkbox" name="wp_multi_filter_email" value="1" <?php checked(1, get_option('wp_multi_filter_email', 0)); ?>>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_filter_url_callback() {
|
||||||
|
?>
|
||||||
|
<input type="checkbox" name="wp_multi_filter_url" value="1" <?php checked(1, get_option('wp_multi_filter_url', 0)); ?>>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_filter_ip_callback() {
|
||||||
|
?>
|
||||||
|
<input type="checkbox" name="wp_multi_filter_ip" value="1" <?php checked(1, get_option('wp_multi_filter_ip', 0)); ?>>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_filter_swearwords_callback() {
|
||||||
|
?>
|
||||||
|
<input type="checkbox" name="wp_multi_filter_swearwords" value="1" <?php checked(1, get_option('wp_multi_filter_swearwords', 0)); ?>>
|
||||||
|
<small><?php esc_html_e('Nutzt die Wortliste aus includes/bad-words.json.', 'wp-multi'); ?></small>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lädt die Schimpfwortliste aus includes/bad-words.json (gecached via Transient)
|
||||||
|
function wp_multi_get_bad_words() {
|
||||||
|
$cache_key = 'wp_multi_bad_words';
|
||||||
|
$words = get_transient($cache_key);
|
||||||
|
if ($words !== false) {
|
||||||
|
return $words;
|
||||||
|
}
|
||||||
|
|
||||||
|
$words = [];
|
||||||
|
$file_path = plugin_dir_path(WP_MULTI_FILE) . 'includes/bad-words.json';
|
||||||
|
if (file_exists($file_path)) {
|
||||||
|
$data = json_decode(file_get_contents($file_path), true);
|
||||||
|
if (is_array($data) && !empty($data['words']) && is_array($data['words'])) {
|
||||||
|
$words = $data['words'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_transient($cache_key, $words, DAY_IN_SECONDS);
|
||||||
|
return $words;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Baut die (teuren) Regex-Patterns aus der Schimpfwortliste und cached sie.
|
||||||
|
// Die Wortliste hat mehrere tausend Einträge - in ein einziges Regex gepackt
|
||||||
|
// überschreitet das die PCRE-Größenlimits, daher wird in Blöcken gearbeitet.
|
||||||
|
function wp_multi_get_bad_words_patterns() {
|
||||||
|
$cache_key = 'wp_multi_bad_words_patterns';
|
||||||
|
$patterns = get_transient($cache_key);
|
||||||
|
if ($patterns !== false) {
|
||||||
|
return $patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
$words = wp_multi_get_bad_words();
|
||||||
|
$patterns = [];
|
||||||
|
foreach (array_chunk($words, 500) as $chunk) {
|
||||||
|
$patterns[] = '/\b(' . implode('|', array_map('preg_quote', $chunk)) . ')\b/iu';
|
||||||
|
}
|
||||||
|
|
||||||
|
set_transient($cache_key, $patterns, DAY_IN_SECONDS);
|
||||||
|
return $patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ersetzt einen Regex-Treffer durch eine gleich lange Folge von *
|
||||||
|
function wp_multi_mask_match($matches) {
|
||||||
|
$match = $matches[0];
|
||||||
|
return str_repeat('*', function_exists('mb_strlen') ? mb_strlen($match) : strlen($match));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wendet den Kommentarfilter (Telefon, E-Mail, URL, IP, Schimpfwörter) auf einen Text an
|
||||||
|
function wp_multi_apply_comment_content_filter($content) {
|
||||||
|
if (get_option('wp_multi_filter_phone', 0)) {
|
||||||
|
$content = preg_replace_callback('/(?:\+?\d[\d\s\-\/\(\)]{6,}\d)/', 'wp_multi_mask_match', $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_option('wp_multi_filter_email', 0)) {
|
||||||
|
$content = preg_replace_callback('/[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}/', 'wp_multi_mask_match', $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_option('wp_multi_filter_url', 0)) {
|
||||||
|
$content = preg_replace_callback('#\b(?:https?://|www\.)[^\s<>"]+#i', 'wp_multi_mask_match', $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_option('wp_multi_filter_ip', 0)) {
|
||||||
|
$content = preg_replace_callback('/\b(?:\d{1,3}\.){3}\d{1,3}\b/', 'wp_multi_mask_match', $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_option('wp_multi_filter_swearwords', 0)) {
|
||||||
|
foreach (wp_multi_get_bad_words_patterns() as $pattern) {
|
||||||
|
$content = preg_replace_callback($pattern, 'wp_multi_mask_match', $content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Text-Copy-Schutz
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Text Copy Schutz und Schutz vor Entwicklertools
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// JavaScript für die Kopierschutz-Funktion einbinden
|
||||||
|
function wp_multi_enqueue_scripts() {
|
||||||
|
wp_add_inline_script('jquery', "
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
// Verhindert das Öffnen der Entwicklertools mit F12, Strg+Shift+I und Strg+Shift+C
|
||||||
|
$(document).keydown(function(e) {
|
||||||
|
// Blockiert F12, Strg + Shift + I, Strg + Shift + C (Entwicklertools)
|
||||||
|
if (e.keyCode == 123 || (e.ctrlKey && e.shiftKey && e.keyCode == 73) || (e.ctrlKey && e.shiftKey && e.keyCode == 67)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verhindert das Öffnen des Quellcodes mit Strg + U (view-source)
|
||||||
|
if ((e.ctrlKey && e.keyCode == 85) || (e.ctrlKey && e.shiftKey && e.keyCode == 85)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verhindert den Zugriff auf die Konsole mit Strg + Shift + J (Konsole-Tab)
|
||||||
|
if ((e.ctrlKey && e.shiftKey && e.keyCode == 74) || (e.metaKey && e.altKey && e.keyCode == 74)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verhindert das Öffnen des Quellcodes mit view-source
|
||||||
|
if (e.ctrlKey && e.keyCode == 85) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verhindert das Öffnen des Kontextmenüs (Rechtsklick)
|
||||||
|
$('body').on('contextmenu', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Kopierschutz-Funktion
|
||||||
|
$('body').on('copy', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var selectedText = window.getSelection().toString();
|
||||||
|
var numericText = selectedText.replace(/./g, function(char) {
|
||||||
|
return Math.floor(Math.random() * 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
e.originalEvent.clipboardData.setData('text/plain', numericText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
");
|
||||||
|
}
|
||||||
|
add_action('wp_enqueue_scripts', 'wp_multi_enqueue_scripts');
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Custom Admin-Links
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Custom Link im Admin Sidebar hinzufügen
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Die benutzerdefinierten Admin-Link-Seiten werden zentral in wp_multi_register_admin_menus() registriert.
|
||||||
|
|
||||||
|
// Callback-Funktion für das Anzeigen der benutzerdefinierten Seiten
|
||||||
|
function wp_stat_notice_custom_page() {
|
||||||
|
$current_slug = isset($_GET['page']) ? sanitize_key($_GET['page']) : '';
|
||||||
|
$custom_pages = get_option('wp_stat_notice_custom_pages', []);
|
||||||
|
|
||||||
|
if (empty($custom_pages) || !is_array($custom_pages)) {
|
||||||
|
wpmt_admin_page_open(__('Benutzerdefinierte Seite', 'wp-stat-notice'));
|
||||||
|
wpmt_admin_notice('error', __('Keine benutzerdefinierten Seiten gefunden.', 'wp-stat-notice'));
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($custom_pages as $page) {
|
||||||
|
if ($page['slug'] === $current_slug) {
|
||||||
|
wpmt_admin_page_open($page['title']);
|
||||||
|
wpmt_admin_card_open();
|
||||||
|
if (isset($page['url']) && filter_var($page['url'], FILTER_VALIDATE_URL)) {
|
||||||
|
echo '<script>window.open("' . esc_url($page['url']) . '", "_blank");</script>';
|
||||||
|
echo '<p>' . __('Die externe Seite wird in einem neuen Fenster geöffnet.', 'wp-stat-notice') . '</p>';
|
||||||
|
echo '<p><a href="' . esc_url($page['url']) . '" target="_blank" class="wpm-btn wpm-btn--primary">' . esc_html__('Seite manuell öffnen', 'wp-stat-notice') . '</a></p>';
|
||||||
|
} else {
|
||||||
|
echo '<p>' . __('Dies ist eine benutzerdefinierte Seite im Admin-Bereich.', 'wp-stat-notice') . '</p>';
|
||||||
|
}
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wpmt_admin_page_open(__('Benutzerdefinierte Seite', 'wp-stat-notice'));
|
||||||
|
wpmt_admin_notice('error', __('Seite nicht gefunden.', 'wp-stat-notice'));
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zum Hinzufügen und Verwalten benutzerdefinierter Seiten
|
||||||
|
function wp_stat_notice_add_custom_page_form() {
|
||||||
|
$dashicons = [
|
||||||
|
'dashicons-admin-links', 'dashicons-admin-site', 'dashicons-admin-home',
|
||||||
|
'dashicons-admin-plugins', 'dashicons-admin-users', 'dashicons-analytics',
|
||||||
|
'dashicons-archive', 'dashicons-book', 'dashicons-calendar',
|
||||||
|
'dashicons-camera', 'dashicons-cart', 'dashicons-cloud',
|
||||||
|
'dashicons-clipboard', 'dashicons-clock', 'dashicons-cloud-upload',
|
||||||
|
'dashicons-email', 'dashicons-heart', 'dashicons-laptop',
|
||||||
|
'dashicons-lock', 'dashicons-phone', 'dashicons-rss',
|
||||||
|
'dashicons-search', 'dashicons-settings', 'dashicons-share',
|
||||||
|
'dashicons-tag', 'dashicons-thumbs-up', 'dashicons-welcome-learn-more',
|
||||||
|
'dashicons-welcome-write-blog'
|
||||||
|
];
|
||||||
|
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php _e('Benutzerdefinierten Adminlink hinzufügen', 'wp-stat-notice'); ?></h1>
|
||||||
|
|
||||||
|
<!-- Formular zum Hinzufügen -->
|
||||||
|
<form method="post" action="">
|
||||||
|
<?php wp_nonce_field('wp_stat_notice_add_page', 'wp_stat_notice_nonce'); ?>
|
||||||
|
<table class="form-table">
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="page_title"><?php _e('Titel der Seite', 'wp-stat-notice'); ?></label></th>
|
||||||
|
<td><input type="text" name="page_title" id="page_title" required class="regular-text"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="page_url"><?php _e('URL der Seite (optional)', 'wp-stat-notice'); ?></label></th>
|
||||||
|
<td><input type="url" name="page_url" id="page_url" class="regular-text" placeholder="https://example.com"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="page_slug"><?php _e('Slug der Seite', 'wp-stat-notice'); ?></label></th>
|
||||||
|
<td><input type="text" name="page_slug" id="page_slug" required class="regular-text"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="page_icon"><?php _e('Dashicon auswählen', 'wp-stat-notice'); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<select name="page_icon" id="page_icon" onchange="updateIconPreview(this)">
|
||||||
|
<?php foreach ($dashicons as $dashicon): ?>
|
||||||
|
<option value="<?php echo esc_attr($dashicon); ?>"><?php echo esc_html($dashicon); ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<div id="icon-preview" class="dashicons" style="font-size: 24px; margin-top: 10px;"></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p><input type="submit" name="add_custom_page" class="button button-primary" value="<?php _e('Seite hinzufügen', 'wp-stat-notice'); ?>"></p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// Formularverarbeitung für Hinzufügen
|
||||||
|
if (isset($_POST['add_custom_page']) && check_admin_referer('wp_stat_notice_add_page', 'wp_stat_notice_nonce')) {
|
||||||
|
$title = sanitize_text_field($_POST['page_title']);
|
||||||
|
$url = !empty($_POST['page_url']) ? esc_url_raw($_POST['page_url']) : '';
|
||||||
|
$slug = sanitize_key($_POST['page_slug']);
|
||||||
|
$icon = in_array($_POST['page_icon'], $dashicons, true) ? $_POST['page_icon'] : 'dashicons-admin-links';
|
||||||
|
|
||||||
|
if (empty($title) || empty($slug)) {
|
||||||
|
echo '<div class="notice notice-error"><p>' . __('Titel und Slug sind erforderlich.', 'wp-stat-notice') . '</p></div>';
|
||||||
|
} else {
|
||||||
|
$custom_pages = get_option('wp_stat_notice_custom_pages', []);
|
||||||
|
$slugs = array_column($custom_pages, 'slug');
|
||||||
|
if (in_array($slug, $slugs)) {
|
||||||
|
echo '<div class="notice notice-error"><p>' . __('Dieser Slug wird bereits verwendet.', 'wp-stat-notice') . '</p></div>';
|
||||||
|
} else {
|
||||||
|
$custom_pages[] = ['title' => $title, 'url' => $url, 'slug' => $slug, 'icon' => $icon];
|
||||||
|
update_option('wp_stat_notice_custom_pages', $custom_pages);
|
||||||
|
echo '<div class="updated"><p>' . __('Benutzerdefinierte Seite wurde hinzugefügt!', 'wp-stat-notice') . '</p></div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verwaltung der Seiten
|
||||||
|
$custom_pages = get_option('wp_stat_notice_custom_pages', []);
|
||||||
|
if (!empty($custom_pages)) {
|
||||||
|
echo '<h2>' . __('Verwaltung der benutzerdefinierten Seiten', 'wp-stat-notice') . '</h2>';
|
||||||
|
echo '<table class="widefat fixed" cellspacing="0">';
|
||||||
|
echo '<thead><tr><th>' . __('Titel', 'wp-stat-notice') . '</th><th>' . __('URL', 'wp-stat-notice') . '</th><th>' . __('Slug', 'wp-stat-notice') . '</th><th>' . __('Aktionen', 'wp-stat-notice') . '</th></tr></thead>';
|
||||||
|
echo '<tbody>';
|
||||||
|
foreach ($custom_pages as $index => $page) {
|
||||||
|
echo '<tr>';
|
||||||
|
echo '<td>' . esc_html($page['title']) . '</td>';
|
||||||
|
echo '<td>' . esc_html($page['url'] ?: '-') . '</td>';
|
||||||
|
echo '<td>' . esc_html($page['slug']) . '</td>';
|
||||||
|
$delete_url = wp_nonce_url(admin_url('admin.php?page=wp-stat-notice-custom-page&delete=' . $index), 'wp_stat_notice_delete_page_' . $index);
|
||||||
|
echo '<td><a href="' . esc_url(admin_url('admin.php?page=wp-stat-notice-custom-page&edit=' . $index)) . '">' . __('Bearbeiten', 'wp-stat-notice') . '</a> | ';
|
||||||
|
echo '<a href="' . esc_url($delete_url) . '" onclick="return confirm(\'' . esc_js(__('Möchtest du diese Seite wirklich löschen?', 'wp-stat-notice')) . '\')">' . __('Löschen', 'wp-stat-notice') . '</a></td>';
|
||||||
|
echo '</tr>';
|
||||||
|
}
|
||||||
|
echo '</tbody></table>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bearbeiten
|
||||||
|
if (isset($_GET['edit']) && isset($custom_pages[(int) $_GET['edit']])) {
|
||||||
|
$edit_index = (int) $_GET['edit'];
|
||||||
|
$edit_page = $custom_pages[$edit_index];
|
||||||
|
|
||||||
|
echo '<h2>' . __('Seite bearbeiten', 'wp-stat-notice') . '</h2>';
|
||||||
|
?>
|
||||||
|
<form method="post" action="">
|
||||||
|
<?php wp_nonce_field('wp_stat_notice_edit_page', 'wp_stat_notice_nonce'); ?>
|
||||||
|
<table class="form-table">
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="edit_page_title"><?php _e('Titel der Seite', 'wp-stat-notice'); ?></label></th>
|
||||||
|
<td><input type="text" name="edit_page_title" id="edit_page_title" value="<?php echo esc_attr($edit_page['title']); ?>" required class="regular-text"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="edit_page_url"><?php _e('URL der Seite (optional)', 'wp-stat-notice'); ?></label></th>
|
||||||
|
<td><input type="url" name="edit_page_url" id="edit_page_url" value="<?php echo esc_attr($edit_page['url']); ?>" class="regular-text"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="edit_page_slug"><?php _e('Slug der Seite', 'wp-stat-notice'); ?></label></th>
|
||||||
|
<td><input type="text" name="edit_page_slug" id="edit_page_slug" value="<?php echo esc_attr($edit_page['slug']); ?>" required class="regular-text"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="edit_page_icon"><?php _e('Dashicon', 'wp-stat-notice'); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<select name="edit_page_icon" id="edit_page_icon" onchange="updateIconPreview(this)">
|
||||||
|
<?php foreach ($dashicons as $dashicon): ?>
|
||||||
|
<option value="<?php echo esc_attr($dashicon); ?>" <?php selected($edit_page['icon'], $dashicon); ?>><?php echo esc_html($dashicon); ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<div id="icon-preview" class="dashicons <?php echo esc_attr($edit_page['icon']); ?>" style="font-size: 24px; margin-top: 10px;"></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p><input type="submit" name="save_custom_page" class="button button-primary" value="<?php _e('Änderungen speichern', 'wp-stat-notice'); ?>"></p>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if (isset($_POST['save_custom_page']) && check_admin_referer('wp_stat_notice_edit_page', 'wp_stat_notice_nonce')) {
|
||||||
|
$title = sanitize_text_field($_POST['edit_page_title']);
|
||||||
|
$url = !empty($_POST['edit_page_url']) ? esc_url_raw($_POST['edit_page_url']) : '';
|
||||||
|
$slug = sanitize_key($_POST['edit_page_slug']);
|
||||||
|
$icon = in_array($_POST['edit_page_icon'], $dashicons, true) ? $_POST['edit_page_icon'] : 'dashicons-admin-links';
|
||||||
|
|
||||||
|
if (empty($title) || empty($slug)) {
|
||||||
|
echo '<div class="notice notice-error"><p>' . __('Titel und Slug sind erforderlich.', 'wp-stat-notice') . '</p></div>';
|
||||||
|
} else {
|
||||||
|
$custom_pages[$edit_index] = ['title' => $title, 'url' => $url, 'slug' => $slug, 'icon' => $icon];
|
||||||
|
update_option('wp_stat_notice_custom_pages', $custom_pages);
|
||||||
|
echo '<div class="updated"><p>' . __('Seite erfolgreich bearbeitet!', 'wp-stat-notice') . '</p></div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Löschen
|
||||||
|
if (isset($_GET['delete']) && isset($custom_pages[(int) $_GET['delete']])) {
|
||||||
|
check_admin_referer('wp_stat_notice_delete_page_' . (int) $_GET['delete']);
|
||||||
|
$delete_index = (int) $_GET['delete'];
|
||||||
|
unset($custom_pages[$delete_index]);
|
||||||
|
$custom_pages = array_values($custom_pages);
|
||||||
|
update_option('wp_stat_notice_custom_pages', $custom_pages);
|
||||||
|
echo '<div class="updated"><p>' . __('Seite wurde gelöscht.', 'wp-stat-notice') . '</p></div>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function updateIconPreview(select) {
|
||||||
|
const preview = select.nextElementSibling;
|
||||||
|
preview.className = 'dashicons ' + select.value;
|
||||||
|
}
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
document.querySelectorAll('#page_icon, #edit_page_icon').forEach(select => {
|
||||||
|
updateIconPreview(select);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
// Der Menüpunkt "Admin-Link hinzufügen" wird zentral in wp_multi_register_admin_menus() registriert.
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Custom Text Box
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Custom Text Box
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Funktion zur Registrierung der zusätzlichen Einstellungen für benutzerdefinierte Texte
|
||||||
|
function wp_multi_custom_text_register_settings() {
|
||||||
|
add_option('wp_multi_custom_texts', '');
|
||||||
|
add_option('wp_multi_second_custom_text', '');
|
||||||
|
add_option('wp_multi_enable_custom_texts', '1');
|
||||||
|
register_setting('wp_multi_custom_text_options_group', 'wp_multi_custom_texts');
|
||||||
|
register_setting('wp_multi_custom_text_options_group', 'wp_multi_second_custom_text');
|
||||||
|
register_setting('wp_multi_custom_text_options_group', 'wp_multi_enable_custom_texts');
|
||||||
|
}
|
||||||
|
add_action('admin_init', 'wp_multi_custom_text_register_settings');
|
||||||
|
|
||||||
|
// Der Custom-Text-Menüpunkt wird zentral in wp_multi_register_admin_menus() registriert.
|
||||||
|
|
||||||
|
// Funktion zum Erstellen der Einstellungsseite
|
||||||
|
function wp_multi_custom_text_settings_page_content() {
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Custom Text', 'wp-multi'),
|
||||||
|
__('Vordefinierte Textboxen am Ende deiner Beiträge', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
wpmt_admin_card_open();
|
||||||
|
?>
|
||||||
|
<form method="post" action="options.php">
|
||||||
|
<?php settings_fields('wp_multi_custom_text_options_group'); ?>
|
||||||
|
<table class="form-table">
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row"><?php _e('Custom Texte aktivieren', 'wp-multi'); ?></th>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" name="wp_multi_enable_custom_texts" value="1" <?php checked(1, get_option('wp_multi_enable_custom_texts'), true); ?> />
|
||||||
|
<p class="description"><?php _e('Aktiviere oder deaktiviere die Anzeige der benutzerdefinierten Texte auf der Webseite.', 'wp-multi'); ?></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row"><?php _e('Custom Texte', 'wp-multi'); ?></th>
|
||||||
|
<td>
|
||||||
|
<textarea name="wp_multi_custom_texts" rows="10" cols="50" class="large-text"><?php echo esc_textarea(get_option('wp_multi_custom_texts')); ?></textarea>
|
||||||
|
<p class="description"><?php _e('Gib jeden Text in einer neuen Zeile ein.', 'wp-multi'); ?></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row"><?php _e('Zweiter Custom Text', 'wp-multi'); ?></th>
|
||||||
|
<td>
|
||||||
|
<textarea name="wp_multi_second_custom_text" rows="10" cols="50" class="large-text"><?php echo esc_textarea(get_option('wp_multi_second_custom_text')); ?></textarea>
|
||||||
|
<p class="description"><?php _e('Gib den zweiten Custom Text ein, der über der ersten Box angezeigt wird.', 'wp-multi'); ?></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<?php submit_button(); ?>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anzeige der Custom Texts in einer Box im Frontend
|
||||||
|
function wp_multi_custom_text_display($content) {
|
||||||
|
$enable_custom_texts = get_option('wp_multi_enable_custom_texts', '1');
|
||||||
|
if ($enable_custom_texts !== '1') {
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_single()) {
|
||||||
|
global $post;
|
||||||
|
$guest_author_name = get_post_meta($post->ID, '_guest_author', true);
|
||||||
|
$author_name = !empty($guest_author_name) ? $guest_author_name : get_the_author();
|
||||||
|
$custom_texts = get_option('wp_multi_custom_texts', '');
|
||||||
|
$second_custom_texts = get_option('wp_multi_second_custom_text', '');
|
||||||
|
$custom_texts_array = array_filter(array_map('trim', explode("\n", $custom_texts)));
|
||||||
|
$second_custom_texts_array = array_filter(array_map('trim', explode("\n", $second_custom_texts)));
|
||||||
|
$selected_custom_text = get_post_meta($post->ID, '_custom_text_choice', true);
|
||||||
|
$selected_second_custom_text = get_post_meta($post->ID, '_second_custom_text_choice', true);
|
||||||
|
|
||||||
|
if (empty($custom_texts_array) && empty($second_custom_texts_array)) {
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
$output = '<div class="custom-text-box" style="margin-top: 40px; padding: 20px; background-color: #f0f0f0; border: 2px solid #ddd; border-radius: 10px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); max-width: 400px; width: auto; font-size: 16px; line-height: 1.2; clear: both; margin-left: auto; margin-right: 0; display: block; margin-bottom: 20px; position: relative;">';
|
||||||
|
$output .= '<p><strong>' . __('Autor:', 'wp-multi') . ' ' . esc_html($author_name);
|
||||||
|
if ($selected_second_custom_text !== '' && isset($second_custom_texts_array[$selected_second_custom_text])) {
|
||||||
|
$output .= ' | ' . esc_html($second_custom_texts_array[$selected_second_custom_text]);
|
||||||
|
}
|
||||||
|
$output .= '</strong></p>';
|
||||||
|
if ($selected_custom_text !== '' && isset($custom_texts_array[$selected_custom_text])) {
|
||||||
|
$output .= '<p><em>' . esc_html($custom_texts_array[$selected_custom_text]) . '</em></p>';
|
||||||
|
}
|
||||||
|
$output .= '</div>';
|
||||||
|
|
||||||
|
return $content . $output;
|
||||||
|
}
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
add_filter('the_content', 'wp_multi_custom_text_display');
|
||||||
|
|
||||||
|
// Funktion zum Hinzufügen der Meta-Box für beide Custom Texts
|
||||||
|
function wp_multi_add_custom_text_fields($post) {
|
||||||
|
$custom_texts = get_option('wp_multi_custom_texts');
|
||||||
|
$custom_texts_array = array_filter(array_map('trim', explode("\n", $custom_texts)));
|
||||||
|
$second_custom_text = get_option('wp_multi_second_custom_text');
|
||||||
|
$second_custom_text_array = array_filter(array_map('trim', explode("\n", $second_custom_text)));
|
||||||
|
$selected_custom_text = get_post_meta($post->ID, '_custom_text_choice', true);
|
||||||
|
$selected_second_custom_text = get_post_meta($post->ID, '_second_custom_text_choice', true);
|
||||||
|
wp_nonce_field('wp_multi_custom_text_choice_nonce', 'wp_multi_custom_text_choice_nonce_field');
|
||||||
|
?>
|
||||||
|
<label for="custom_text_choice"><?php _e('Wähle den Custom Text (unterer Bereich):', 'wp-multi'); ?></label>
|
||||||
|
<select name="custom_text_choice" id="custom_text_choice" class="widefat">
|
||||||
|
<option value=""><?php _e('Keinen Text auswählen', 'wp-multi'); ?></option>
|
||||||
|
<?php foreach ($custom_texts_array as $key => $value) { ?>
|
||||||
|
<option value="<?php echo esc_attr($key); ?>" <?php selected($selected_custom_text, $key); ?>><?php echo esc_html($value); ?></option>
|
||||||
|
<?php } ?>
|
||||||
|
</select>
|
||||||
|
<label for="second_custom_text_choice"><?php _e('Wähle den zweiten Custom Text (oberer Bereich):', 'wp-multi'); ?></label>
|
||||||
|
<select name="second_custom_text_choice" id="second_custom_text_choice" class="widefat">
|
||||||
|
<option value=""><?php _e('Keinen Text auswählen', 'wp-multi'); ?></option>
|
||||||
|
<?php foreach ($second_custom_text_array as $key => $value) { ?>
|
||||||
|
<option value="<?php echo esc_attr($key); ?>" <?php selected($selected_second_custom_text, $key); ?>><?php echo esc_html($value); ?></option>
|
||||||
|
<?php } ?>
|
||||||
|
</select>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meta-Box hinzufügen
|
||||||
|
add_action('add_meta_boxes', function() {
|
||||||
|
add_meta_box('wp_multi_custom_text', __('Custom Text Auswahl', 'wp-multi'), 'wp_multi_add_custom_text_fields', 'post', 'normal', 'high');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Speichern der benutzerdefinierten Textauswahl im Beitrag
|
||||||
|
function wp_multi_save_custom_text_choice($post_id) {
|
||||||
|
if (!isset($_POST['wp_multi_custom_text_choice_nonce_field']) || !wp_verify_nonce($_POST['wp_multi_custom_text_choice_nonce_field'], 'wp_multi_custom_text_choice_nonce')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!current_user_can('edit_post', $post_id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isset($_POST['custom_text_choice'])) {
|
||||||
|
update_post_meta($post_id, '_custom_text_choice', sanitize_text_field(wp_unslash($_POST['custom_text_choice'])));
|
||||||
|
}
|
||||||
|
if (isset($_POST['second_custom_text_choice'])) {
|
||||||
|
update_post_meta($post_id, '_second_custom_text_choice', sanitize_text_field(wp_unslash($_POST['second_custom_text_choice'])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('save_post', 'wp_multi_save_custom_text_choice');
|
||||||
|
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Login deaktivieren (pro Benutzer)
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Login deaktivieren
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Checkbox zum Benutzerprofil hinzufügen
|
||||||
|
function wp_multi_add_disable_login_checkbox($user) {
|
||||||
|
?>
|
||||||
|
<h3>Login deaktivieren</h3>
|
||||||
|
<table class="form-table">
|
||||||
|
<tr>
|
||||||
|
<th><label for="disable_login">Login deaktivieren</label></th>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" name="disable_login" id="disable_login" value="1" <?php checked( get_user_meta($user->ID, 'disable_login', true), 1 ); ?> />
|
||||||
|
<span class="description">Markiere diese Option, um den Login des Benutzers zu deaktivieren.</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speichern der Checkbox-Option
|
||||||
|
function wp_multi_save_disable_login_checkbox($user_id) {
|
||||||
|
if ( isset( $_POST['disable_login'] ) ) {
|
||||||
|
update_user_meta( $user_id, 'disable_login', 1 );
|
||||||
|
} else {
|
||||||
|
delete_user_meta( $user_id, 'disable_login' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Die Checkbox in das Benutzerprofil einfügen
|
||||||
|
add_action( 'show_user_profile', 'wp_multi_add_disable_login_checkbox' );
|
||||||
|
add_action( 'edit_user_profile', 'wp_multi_add_disable_login_checkbox' );
|
||||||
|
|
||||||
|
// Speichern der Checkbox-Option
|
||||||
|
add_action( 'personal_options_update', 'wp_multi_save_disable_login_checkbox' );
|
||||||
|
add_action( 'edit_user_profile_update', 'wp_multi_save_disable_login_checkbox' );
|
||||||
|
|
||||||
|
// Login blockieren, wenn die Checkbox aktiviert ist
|
||||||
|
function wp_multi_block_login_if_disabled($user_login, $user) {
|
||||||
|
// Prüfen, ob der Benutzer das Flag "Login deaktivieren" gesetzt hat
|
||||||
|
if ( get_user_meta( $user->ID, 'disable_login', true ) ) {
|
||||||
|
// Fehlermeldung anzeigen, wenn der Login deaktiviert ist
|
||||||
|
wp_die( 'Dein Login wurde deaktiviert. Bitte kontaktiere den Administrator.' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Der Filter wird bei jedem Login-Versuch angewendet
|
||||||
|
add_action( 'wp_login', 'wp_multi_block_login_if_disabled', 10, 2 );
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Sperre Trash-Mail-Adressen
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sperre Trash Mail Adressen
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Funktion zum Laden der Liste von Einweg-Mail-Anbietern
|
||||||
|
function load_disposable_email_list() {
|
||||||
|
$file_path = plugin_dir_path(WP_MULTI_FILE) . 'includes/disposable_email_blocklist.conf'; // Pfad zur Datei im includes-Ordner
|
||||||
|
if (file_exists($file_path)) {
|
||||||
|
return file($file_path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zum Überprüfen der E-Mail-Adresse eines Kommentators
|
||||||
|
function check_disposable_email($commentdata) {
|
||||||
|
$disposable_list = load_disposable_email_list();
|
||||||
|
$email = $commentdata['comment_author_email'];
|
||||||
|
$domain = substr(strrchr($email, "@"), 1); // Nur die Domain extrahieren
|
||||||
|
|
||||||
|
// Überprüfen, ob die Domain auf der Liste steht
|
||||||
|
if (in_array($domain, $disposable_list)) {
|
||||||
|
wp_die(__('Fehler: Trash-Mail-Adressen sind in Kommentaren nicht erlaubt.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $commentdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Die Funktion wird beim Absenden eines Kommentars ausgeführt
|
||||||
|
add_filter('preprocess_comment', 'check_disposable_email');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,273 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Login-Design
|
||||||
|
*
|
||||||
|
* Ermöglicht die eigene Gestaltung der WordPress-Login-Seite:
|
||||||
|
* Logo, Hintergrund (Farbe/Bild), Formular-Farbe, Button- und Linkfarbe
|
||||||
|
* sowie eigenes CSS. Wird über den Loader in wp-multi.php eingebunden.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Einstellungen registrieren
|
||||||
|
*/
|
||||||
|
function wp_multi_login_design_register() {
|
||||||
|
$group = 'wp_multi_login_design_group';
|
||||||
|
register_setting($group, 'wp_multi_login_form_text', ['sanitize_callback' => 'sanitize_hex_color', 'default' => '']);
|
||||||
|
register_setting($group, 'wp_multi_login_logo', ['sanitize_callback' => 'esc_url_raw', 'default' => '']);
|
||||||
|
register_setting($group, 'wp_multi_login_logo_home', ['sanitize_callback' => 'absint', 'default' => 1]);
|
||||||
|
register_setting($group, 'wp_multi_login_bg_color', ['sanitize_callback' => 'sanitize_hex_color', 'default' => '']);
|
||||||
|
register_setting($group, 'wp_multi_login_bg_image', ['sanitize_callback' => 'esc_url_raw', 'default' => '']);
|
||||||
|
register_setting($group, 'wp_multi_login_bg_size', ['sanitize_callback' => 'wp_multi_login_design_sanitize_bg_size', 'default' => 'cover']);
|
||||||
|
register_setting($group, 'wp_multi_login_form_bg', ['sanitize_callback' => 'sanitize_hex_color', 'default' => '']);
|
||||||
|
register_setting($group, 'wp_multi_login_button', ['sanitize_callback' => 'sanitize_hex_color', 'default' => '']);
|
||||||
|
register_setting($group, 'wp_multi_login_link', ['sanitize_callback' => 'sanitize_hex_color', 'default' => '']);
|
||||||
|
register_setting($group, 'wp_multi_login_custom_css', ['sanitize_callback' => 'wp_multi_login_design_sanitize_css', 'default' => '']);
|
||||||
|
}
|
||||||
|
add_action('admin_init', 'wp_multi_login_design_register');
|
||||||
|
|
||||||
|
// Eigenes CSS grob absichern (kein </style>-Ausbruch, keine Tags)
|
||||||
|
function wp_multi_login_design_sanitize_css($css) {
|
||||||
|
$css = (string) $css;
|
||||||
|
$css = wp_strip_all_tags($css);
|
||||||
|
return trim($css);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anzeige-Art des Hintergrundbilds (Whitelist)
|
||||||
|
function wp_multi_login_design_sanitize_bg_size($value) {
|
||||||
|
$allowed = array('cover', 'contain', 'stretch', 'tile', 'center');
|
||||||
|
return in_array($value, $allowed, true) ? $value : 'cover';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Liefert die CSS-Regeln für die gewählte Hintergrund-Anzeigeart
|
||||||
|
function wp_multi_login_design_bg_rules($mode) {
|
||||||
|
switch ($mode) {
|
||||||
|
case 'contain':
|
||||||
|
return 'background-size:contain !important; background-repeat:no-repeat !important; background-position:center !important;';
|
||||||
|
case 'stretch':
|
||||||
|
return 'background-size:100% 100% !important; background-repeat:no-repeat !important; background-position:center !important;';
|
||||||
|
case 'tile':
|
||||||
|
return 'background-size:auto !important; background-repeat:repeat !important;';
|
||||||
|
case 'center':
|
||||||
|
return 'background-size:auto !important; background-repeat:no-repeat !important; background-position:center !important;';
|
||||||
|
case 'cover':
|
||||||
|
default:
|
||||||
|
return 'background-size:cover !important; background-repeat:no-repeat !important; background-position:center !important;';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ausgabe auf der Login-Seite
|
||||||
|
*/
|
||||||
|
function wp_multi_login_design_styles() {
|
||||||
|
$form_text = get_option('wp_multi_login_form_text', '');
|
||||||
|
$logo = get_option('wp_multi_login_logo', '');
|
||||||
|
$bg_color = get_option('wp_multi_login_bg_color', '');
|
||||||
|
$bg_image = get_option('wp_multi_login_bg_image', '');
|
||||||
|
$form_bg = get_option('wp_multi_login_form_bg', '');
|
||||||
|
$button = get_option('wp_multi_login_button', '');
|
||||||
|
$link = get_option('wp_multi_login_link', '');
|
||||||
|
$css = get_option('wp_multi_login_custom_css', '');
|
||||||
|
|
||||||
|
// Nur ausgeben, wenn überhaupt etwas konfiguriert ist
|
||||||
|
if (!$logo && !$bg_color && !$bg_image && !$form_bg && !$button && !$link && !$css && !$form_text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "<style id=\"wp-multi-login-design\">\n";
|
||||||
|
|
||||||
|
if ($logo) {
|
||||||
|
printf(
|
||||||
|
"#login h1 a, .login h1 a { background-image:url(%s) !important; background-size:contain !important; background-position:center !important; width:auto !important; height:90px !important; }\n",
|
||||||
|
esc_url($logo)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($bg_color) {
|
||||||
|
printf("body.login { background-color:%s !important; }\n", esc_attr($bg_color));
|
||||||
|
}
|
||||||
|
if ($bg_image) {
|
||||||
|
$bg_size = get_option('wp_multi_login_bg_size', 'cover');
|
||||||
|
printf(
|
||||||
|
"body.login { background-image:url(%s) !important; %s }\n",
|
||||||
|
esc_url($bg_image),
|
||||||
|
wp_multi_login_design_bg_rules($bg_size)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($form_bg) {
|
||||||
|
printf(".login form { background:%s !important; border-radius:8px !important; }\n", esc_attr($form_bg));
|
||||||
|
}
|
||||||
|
if ($button) {
|
||||||
|
printf(".login .button-primary { background:%1\$s !important; border-color:%1\$s !important; box-shadow:none !important; text-shadow:none !important; }\n", esc_attr($button));
|
||||||
|
}
|
||||||
|
if ($link) {
|
||||||
|
printf(".login #nav a, .login #backtoblog a, .login .wp-login-lost-password a { color:%s !important; }\n", esc_attr($link));
|
||||||
|
}
|
||||||
|
if ($form_text) {
|
||||||
|
printf(".login form label, .login form .forgetmenot label { color:%s !important; }\n", esc_attr($form_text));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($css) {
|
||||||
|
// bereits beim Speichern von Tags befreit
|
||||||
|
echo $css . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "</style>\n";
|
||||||
|
}
|
||||||
|
add_action('login_enqueue_scripts', 'wp_multi_login_design_styles');
|
||||||
|
|
||||||
|
// Logo-Link auf die eigene Startseite statt wordpress.org
|
||||||
|
function wp_multi_login_design_headerurl($url) {
|
||||||
|
return get_option('wp_multi_login_logo_home', 1) ? home_url('/') : $url;
|
||||||
|
}
|
||||||
|
add_filter('login_headerurl', 'wp_multi_login_design_headerurl');
|
||||||
|
|
||||||
|
// Logo-Titel = Seitenname
|
||||||
|
function wp_multi_login_design_headertext($text) {
|
||||||
|
return get_option('wp_multi_login_logo_home', 1) ? get_bloginfo('name') : $text;
|
||||||
|
}
|
||||||
|
add_filter('login_headertext', 'wp_multi_login_design_headertext');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assets für die Einstellungsseite (Color-Picker + Medienauswahl)
|
||||||
|
*/
|
||||||
|
function wp_multi_login_design_admin_assets($hook) {
|
||||||
|
if ($hook !== 'wp-multi_page_wp_multi_login_design' && $hook !== 'toplevel_page_wp_multi_login_design') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wp_enqueue_style('wp-color-picker');
|
||||||
|
wp_enqueue_script('wp-color-picker');
|
||||||
|
wp_enqueue_media();
|
||||||
|
}
|
||||||
|
add_action('admin_enqueue_scripts', 'wp_multi_login_design_admin_assets');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Einstellungsseite
|
||||||
|
*/
|
||||||
|
function wp_multi_login_design_page() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_die(__('Zugriff verweigert.', 'wp-multi'));
|
||||||
|
}
|
||||||
|
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Login-Design', 'wp-multi'),
|
||||||
|
__('Die WordPress-Login-Seite nach deinen Wünschen gestalten', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
$logo = get_option('wp_multi_login_logo', '');
|
||||||
|
$bg_image = get_option('wp_multi_login_bg_image', '');
|
||||||
|
|
||||||
|
wpmt_admin_notice('info', sprintf(
|
||||||
|
__('Vorschau: %s (in neuem Tab öffnen – als eingeloggter Nutzer wirst du ggf. direkt weitergeleitet, dann kurz ausloggen).', 'wp-multi'),
|
||||||
|
'<a href="' . esc_url(wp_login_url()) . '" target="_blank">' . esc_html(wp_login_url()) . '</a>'
|
||||||
|
));
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Gestaltung', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<form method="post" action="options.php">
|
||||||
|
<?php settings_fields('wp_multi_login_design_group'); ?>
|
||||||
|
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php esc_html_e('Logo', 'wp-multi'); ?>
|
||||||
|
<small class="wpm-field__hint"><?php esc_html_e('Ersetzt das WordPress-Logo über dem Formular.', 'wp-multi'); ?></small>
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" id="wp_multi_login_logo" name="wp_multi_login_logo" value="<?php echo esc_attr($logo); ?>" placeholder="https://…/logo.png" style="max-width:420px;">
|
||||||
|
<button type="button" class="wpm-btn wpm-media-btn" data-target="wp_multi_login_logo"><?php esc_html_e('Medien', 'wp-multi'); ?></button>
|
||||||
|
<div class="wpm-preview"><?php if ($logo) echo '<img src="' . esc_url($logo) . '" style="max-height:60px;margin-top:8px;">'; ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php esc_html_e('Logo verlinkt zur Startseite', 'wp-multi'); ?></label>
|
||||||
|
<div><label><input type="checkbox" name="wp_multi_login_logo_home" value="1" <?php checked(1, get_option('wp_multi_login_logo_home', 1)); ?>> <?php esc_html_e('Ja (statt zu wordpress.org)', 'wp-multi'); ?></label></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php esc_html_e('Seiten-Hintergrundfarbe', 'wp-multi'); ?></label>
|
||||||
|
<div><input type="text" class="wpm-color" name="wp_multi_login_bg_color" value="<?php echo esc_attr(get_option('wp_multi_login_bg_color', '')); ?>" data-default-color=""></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php esc_html_e('Hintergrundbild', 'wp-multi'); ?>
|
||||||
|
<small class="wpm-field__hint"><?php esc_html_e('Optional. Überlagert die Hintergrundfarbe.', 'wp-multi'); ?></small>
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
<input type="text" id="wp_multi_login_bg_image" name="wp_multi_login_bg_image" value="<?php echo esc_attr($bg_image); ?>" placeholder="https://…/bg.jpg" style="max-width:420px;">
|
||||||
|
<button type="button" class="wpm-btn wpm-media-btn" data-target="wp_multi_login_bg_image"><?php esc_html_e('Medien', 'wp-multi'); ?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php esc_html_e('Hintergrundbild-Anzeige', 'wp-multi'); ?>
|
||||||
|
<small class="wpm-field__hint"><?php esc_html_e('Wie das Bild die Login-Seite füllt.', 'wp-multi'); ?></small>
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
<?php $bg_size = get_option('wp_multi_login_bg_size', 'cover'); ?>
|
||||||
|
<select name="wp_multi_login_bg_size" style="max-width:280px;">
|
||||||
|
<option value="cover" <?php selected($bg_size, 'cover'); ?>><?php esc_html_e('Ausfüllen (beschneidet ggf.)', 'wp-multi'); ?></option>
|
||||||
|
<option value="contain" <?php selected($bg_size, 'contain'); ?>><?php esc_html_e('Anpassen (ganz sichtbar)', 'wp-multi'); ?></option>
|
||||||
|
<option value="stretch" <?php selected($bg_size, 'stretch'); ?>><?php esc_html_e('Strecken (exakt füllen)', 'wp-multi'); ?></option>
|
||||||
|
<option value="tile" <?php selected($bg_size, 'tile'); ?>><?php esc_html_e('Kacheln (wiederholen)', 'wp-multi'); ?></option>
|
||||||
|
<option value="center" <?php selected($bg_size, 'center'); ?>><?php esc_html_e('Zentriert (Originalgröße)', 'wp-multi'); ?></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php esc_html_e('Formular-Hintergrund', 'wp-multi'); ?></label>
|
||||||
|
<div><input type="text" class="wpm-color" name="wp_multi_login_form_bg" value="<?php echo esc_attr(get_option('wp_multi_login_form_bg', '')); ?>" data-default-color=""></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php esc_html_e('Formular-Textfarbe', 'wp-multi'); ?>
|
||||||
|
<small class="wpm-field__hint"><?php esc_html_e('Farbe der Beschriftungen im Formular (z. B. hell auf dunklem Formular-Hintergrund).', 'wp-multi'); ?></small>
|
||||||
|
</label>
|
||||||
|
<div><input type="text" class="wpm-color" name="wp_multi_login_form_text" value="<?php echo esc_attr(get_option('wp_multi_login_form_text', '')); ?>" data-default-color=""></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php esc_html_e('Button-Farbe', 'wp-multi'); ?></label>
|
||||||
|
<div><input type="text" class="wpm-color" name="wp_multi_login_button" value="<?php echo esc_attr(get_option('wp_multi_login_button', '')); ?>" data-default-color=""></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php esc_html_e('Link-Farbe', 'wp-multi'); ?></label>
|
||||||
|
<div><input type="text" class="wpm-color" name="wp_multi_login_link" value="<?php echo esc_attr(get_option('wp_multi_login_link', '')); ?>" data-default-color=""></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label"><?php esc_html_e('Eigenes CSS (Profi)', 'wp-multi'); ?>
|
||||||
|
<small class="wpm-field__hint"><?php esc_html_e('Wird zusätzlich auf der Login-Seite ausgegeben.', 'wp-multi'); ?></small>
|
||||||
|
</label>
|
||||||
|
<div><textarea name="wp_multi_login_custom_css" rows="6" style="width:100%;max-width:560px;font-family:monospace;"><?php echo esc_textarea(get_option('wp_multi_login_custom_css', '')); ?></textarea></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php submit_button(__('Speichern', 'wp-multi')); ?>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
jQuery(function ($) {
|
||||||
|
// Farbwähler
|
||||||
|
if ($.fn.wpColorPicker) { $('.wpm-color').wpColorPicker(); }
|
||||||
|
// Medienauswahl
|
||||||
|
$('.wpm-media-btn').on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var targetId = $(this).data('target');
|
||||||
|
var frame = wp.media({ title: '<?php echo esc_js(__('Bild auswählen', 'wp-multi')); ?>', multiple: false });
|
||||||
|
frame.on('select', function () {
|
||||||
|
var att = frame.state().get('selection').first().toJSON();
|
||||||
|
$('#' + targetId).val(att.url);
|
||||||
|
if (targetId === 'wp_multi_login_logo') {
|
||||||
|
$('.wpm-preview').html('<img src="' + att.url + '" style="max-height:60px;margin-top:8px;">');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
frame.open();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Custom Login-URL
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Custom Login-URL — Admin-Panel-Adresse ändern (versteckt wp-admin / wp-login.php)
|
||||||
|
*
|
||||||
|
* Ersetzt die Standard-Login-Adresse durch einen eigenen Slug, z. B. /team/.
|
||||||
|
* Standardmäßig DEAKTIVIERT (leerer Slug), damit ohne bewusste Aktivierung
|
||||||
|
* nichts an der Erreichbarkeit des Backends geändert wird.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Liefert den konfigurierten Login-Slug (leer = Feature aus)
|
||||||
|
function wp_multi_login_slug() {
|
||||||
|
$slug = get_option('wp_multi_login_slug', '');
|
||||||
|
return is_string($slug) ? trim($slug, '/') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feature ist nur aktiv, wenn ein Slug gesetzt ist UND Permalinks aktiv sind
|
||||||
|
function wp_multi_login_active() {
|
||||||
|
return wp_multi_login_slug() !== '' && get_option('permalink_structure') && !is_multisite();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vollständige neue Login-URL
|
||||||
|
function wp_multi_login_new_url($scheme = null) {
|
||||||
|
return user_trailingslashit(home_url('/', $scheme) . wp_multi_login_slug());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pfad-Anteil (ohne Domain) der Home-URL, z. B. "" oder "/blog"
|
||||||
|
function wp_multi_login_home_path() {
|
||||||
|
return untrailingslashit((string) parse_url(home_url(), PHP_URL_PATH));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sendet eine echte 404-Antwort und beendet die Ausführung (verstecktes Login)
|
||||||
|
function wp_multi_login_die_404() {
|
||||||
|
status_header(404);
|
||||||
|
nocache_headers();
|
||||||
|
if (function_exists('wp_die')) {
|
||||||
|
// Nutze die Theme-404 wenn möglich, sonst schlichte Meldung
|
||||||
|
wp_die(__('Seite nicht gefunden.', 'wp-multi'), '404', array('response' => 404));
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ersetzt "wp-login.php" in generierten URLs durch den eigenen Slug
|
||||||
|
function wp_multi_login_filter_url($url, $scheme = null) {
|
||||||
|
if (strpos($url, 'wp-login.php') === false) {
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
if (is_ssl()) {
|
||||||
|
$scheme = 'https';
|
||||||
|
}
|
||||||
|
$query = parse_url($url, PHP_URL_QUERY);
|
||||||
|
$args = array();
|
||||||
|
if ($query) {
|
||||||
|
parse_str($query, $args);
|
||||||
|
}
|
||||||
|
$new = wp_multi_login_new_url($scheme);
|
||||||
|
if (!empty($args)) {
|
||||||
|
// add_query_arg kodiert selbst; parse_str liefert bereits dekodierte Werte.
|
||||||
|
$new = add_query_arg($args, $new);
|
||||||
|
}
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_login_filter_site_url($url, $path, $scheme, $blog_id = null) {
|
||||||
|
return wp_multi_login_filter_url($url, $scheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
function wp_multi_login_filter_wp_redirect($location) {
|
||||||
|
return wp_multi_login_filter_url($location);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registriert die Filter und den Request-Handler, sobald WordPress geladen ist
|
||||||
|
function wp_multi_login_init() {
|
||||||
|
if (!wp_multi_login_active()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_filter('site_url', 'wp_multi_login_filter_site_url', 10, 4);
|
||||||
|
add_filter('network_site_url', 'wp_multi_login_filter_site_url', 10, 4);
|
||||||
|
add_filter('wp_redirect', 'wp_multi_login_filter_wp_redirect', 10, 1);
|
||||||
|
add_filter('login_url', 'wp_multi_login_filter_wp_redirect', 10, 1);
|
||||||
|
add_filter('logout_url', 'wp_multi_login_filter_wp_redirect', 10, 1);
|
||||||
|
add_filter('lostpassword_url', 'wp_multi_login_filter_wp_redirect', 10, 1);
|
||||||
|
add_filter('register_url', 'wp_multi_login_filter_wp_redirect', 10, 1);
|
||||||
|
}
|
||||||
|
add_action('plugins_loaded', 'wp_multi_login_init', 1);
|
||||||
|
|
||||||
|
// Fängt die eingehende Anfrage ab: geheimer Slug lädt das Login, wp-login.php/wp-admin werden versteckt
|
||||||
|
function wp_multi_login_handle_request() {
|
||||||
|
if (!wp_multi_login_active()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Systemendpunkte niemals blockieren
|
||||||
|
if ((defined('DOING_AJAX') && DOING_AJAX) || (defined('DOING_CRON') && DOING_CRON) || (defined('REST_REQUEST') && REST_REQUEST)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
|
||||||
|
$path = untrailingslashit((string) parse_url($uri, PHP_URL_PATH));
|
||||||
|
$home_path = wp_multi_login_home_path();
|
||||||
|
$login_path = $home_path . '/' . wp_multi_login_slug();
|
||||||
|
|
||||||
|
// 1) Aufruf des geheimen Slugs → echtes wp-login.php ausliefern
|
||||||
|
if ($path === $login_path) {
|
||||||
|
$GLOBALS['pagenow'] = 'wp-login.php';
|
||||||
|
require_once ABSPATH . 'wp-login.php';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Direkter Aufruf von wp-login.php (nicht über den Slug) → verstecken
|
||||||
|
if (strpos($uri, 'wp-login.php') !== false) {
|
||||||
|
wp_multi_login_die_404();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Der gesamte /wp-admin-Bereich für nicht eingeloggte Besucher → verstecken,
|
||||||
|
// statt zum versteckten Login weiterzuleiten. admin-ajax.php und admin-post.php
|
||||||
|
// bleiben ausdrücklich erreichbar (auch für Gäste / nopriv-Aktionen).
|
||||||
|
if (!is_user_logged_in()) {
|
||||||
|
$admin_path = $home_path . '/wp-admin';
|
||||||
|
$in_admin = ($path === $admin_path || strpos($path, $admin_path . '/') === 0);
|
||||||
|
$is_endpoint = (strpos($uri, 'admin-ajax.php') !== false || strpos($uri, 'admin-post.php') !== false);
|
||||||
|
if ($in_admin && !$is_endpoint) {
|
||||||
|
wp_multi_login_die_404();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('wp_loaded', 'wp_multi_login_handle_request');
|
||||||
|
|
||||||
|
// Einstellung registrieren (mit Sanitisierung gegen reservierte Slugs)
|
||||||
|
function wp_multi_login_register_setting() {
|
||||||
|
register_setting('wp_multi_login_url_group', 'wp_multi_login_slug', array(
|
||||||
|
'type' => 'string',
|
||||||
|
'sanitize_callback' => 'wp_multi_login_sanitize_slug',
|
||||||
|
'default' => '',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
add_action('admin_init', 'wp_multi_login_register_setting');
|
||||||
|
|
||||||
|
function wp_multi_login_sanitize_slug($value) {
|
||||||
|
$value = sanitize_title(trim((string) $value, '/'));
|
||||||
|
$reserved = array('wp-admin', 'wp-login', 'wp-content', 'wp-includes', 'admin-ajax', 'admin-post', 'wp-json', 'index');
|
||||||
|
if (in_array($value, $reserved, true)) {
|
||||||
|
add_settings_error('wp_multi_login_slug', 'reserved', __('Dieser Slug ist reserviert und kann nicht verwendet werden.', 'wp-multi'));
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Admin-Seite "Login-URL"
|
||||||
|
function wp_multi_login_url_page() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_die(__('Zugriff verweigert.', 'wp-multi'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$slug = wp_multi_login_slug();
|
||||||
|
$pretty = (bool) get_option('permalink_structure');
|
||||||
|
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Login-URL', 'wp-multi'),
|
||||||
|
__('Eigene Adresse für das Login / Admin-Panel festlegen', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (is_multisite()) {
|
||||||
|
wpmt_admin_notice('error', __('Dieses Feature ist in einem Multisite-Netzwerk nicht verfügbar.', 'wp-multi'));
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$pretty) {
|
||||||
|
wpmt_admin_notice('warning', __('Für dieses Feature müssen „hübsche Permalinks" aktiviert sein (Einstellungen → Permalinks, nicht „Einfach"). Solange das nicht der Fall ist, bleibt die Login-URL unverändert.', 'wp-multi'));
|
||||||
|
}
|
||||||
|
|
||||||
|
wpmt_admin_notice('warning', __('Achtung: Merke dir die neue Adresse gut! Wenn du sie vergisst, kommst du nicht mehr ins Backend und musst das Feld per Datenbank oder FTP zurücksetzen. Leeres Feld = Standard-Login (/wp-login.php) bleibt aktiv.', 'wp-multi'));
|
||||||
|
|
||||||
|
if ($slug && $pretty) {
|
||||||
|
wpmt_admin_notice('success', sprintf(
|
||||||
|
__('Aktive Login-Adresse: %s', 'wp-multi'),
|
||||||
|
'<a href="' . esc_url(wp_multi_login_new_url()) . '"><strong>' . esc_html(wp_multi_login_new_url()) . '</strong></a>'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Einstellung', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<form method="post" action="options.php">
|
||||||
|
<?php settings_fields('wp_multi_login_url_group'); ?>
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label" for="wp_multi_login_slug"><?php esc_html_e('Login-Slug', 'wp-multi'); ?>
|
||||||
|
<small class="wpm-field__hint"><?php esc_html_e('Nur Kleinbuchstaben, Zahlen und Bindestriche. Leer lassen = deaktiviert.', 'wp-multi'); ?></small>
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
<span style="color:var(--wpm-text-muted);"><?php echo esc_html(trailingslashit(home_url('/'))); ?></span>
|
||||||
|
<input type="text" id="wp_multi_login_slug" name="wp_multi_login_slug" value="<?php echo esc_attr($slug); ?>" placeholder="team" style="max-width:220px; display:inline-block;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php submit_button(__('Speichern', 'wp-multi')); ?>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Pinnwand
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Admin - Pinnwand
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Funktion zum Erstellen der Datenbanktabelle für Nachrichten
|
||||||
|
function wp_multi_create_message_board_table() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$table_name = $wpdb->prefix . 'message_board'; // Tabelle für Nachrichten
|
||||||
|
$charset_collate = $wpdb->get_charset_collate();
|
||||||
|
|
||||||
|
$sql = "CREATE TABLE $table_name (
|
||||||
|
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
||||||
|
message text NOT NULL,
|
||||||
|
user_id bigint(20) NOT NULL,
|
||||||
|
created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
) $charset_collate;";
|
||||||
|
|
||||||
|
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||||
|
dbDelta($sql);
|
||||||
|
}
|
||||||
|
register_activation_hook(WP_MULTI_FILE, 'wp_multi_create_message_board_table');
|
||||||
|
|
||||||
|
// Funktion zum Anzeigen der Nachrichten im Adminbereich
|
||||||
|
function wp_multi_add_message_board() {
|
||||||
|
if (!current_user_can('administrator')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'message_board';
|
||||||
|
$created = false;
|
||||||
|
|
||||||
|
// Nachricht erstellen (VOR der Ausgabe, damit sie sofort erscheint)
|
||||||
|
if (isset($_POST['submit_message']) && !empty($_POST['new_message'])) {
|
||||||
|
if (!isset($_POST['wp_multi_pinwand_nonce_field']) || !wp_verify_nonce(wp_unslash($_POST['wp_multi_pinwand_nonce_field']), 'wp_multi_pinwand_create_message')) {
|
||||||
|
wp_die(__('Ungültige oder abgelaufene Anfrage.', 'wp-multi'));
|
||||||
|
}
|
||||||
|
$wpdb->insert(
|
||||||
|
$table_name,
|
||||||
|
array(
|
||||||
|
'message' => sanitize_textarea_field(wp_unslash($_POST['new_message'])),
|
||||||
|
'user_id' => get_current_user_id(),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$created = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$messages = $wpdb->get_results("SELECT * FROM $table_name ORDER BY created_at DESC");
|
||||||
|
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Pinwand', 'wp-multi'),
|
||||||
|
__('Interne Nachrichten für das Admin-Team', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($created) {
|
||||||
|
wpmt_admin_notice('success', __('Nachricht wurde erfolgreich erstellt.', 'wp-multi'));
|
||||||
|
}
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Neue Nachricht erstellen', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<form method="post">
|
||||||
|
<?php wp_nonce_field('wp_multi_pinwand_create_message', 'wp_multi_pinwand_nonce_field'); ?>
|
||||||
|
<textarea name="new_message" rows="4" required style="width:100%;"></textarea>
|
||||||
|
<p><input type="submit" name="submit_message" value="<?php esc_attr_e('Nachricht erstellen', 'wp-multi'); ?>" class="wpm-btn wpm-btn--primary"></p>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Nachrichten', 'wp-multi'));
|
||||||
|
if ($messages) {
|
||||||
|
echo '<div class="wpm-modules">';
|
||||||
|
foreach ($messages as $message) {
|
||||||
|
$user_info = get_userdata($message->user_id);
|
||||||
|
$author = $user_info ? $user_info->user_login : __('Unbekannt', 'wp-multi');
|
||||||
|
printf(
|
||||||
|
'<div class="wpm-module" style="cursor:pointer;" onclick="openMessagePopup(%d)"><span class="dashicons dashicons-admin-comments"></span><span><span class="wpm-module__title">%s</span><p class="wpm-module__desc">%s</p><p class="wpm-module__desc">%s</p></span></div>',
|
||||||
|
(int) $message->id,
|
||||||
|
esc_html($author),
|
||||||
|
esc_html(date('d-m-Y H:i', strtotime($message->created_at))),
|
||||||
|
esc_html(wp_trim_words($message->message, 20))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
echo '</div>';
|
||||||
|
} else {
|
||||||
|
wpmt_admin_notice('info', __('Keine Nachrichten vorhanden.', 'wp-multi'));
|
||||||
|
}
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div id="messagePopup" style="display:none; position:fixed; top:20%; left:50%; transform:translateX(-50%); background:#fff; padding:24px; border-radius:12px; box-shadow:0 4px 30px rgba(0,0,0,.2); z-index:99999; max-width:600px; width:90%;">
|
||||||
|
<div id="messageContent"></div>
|
||||||
|
<div style="display:flex; gap:10px; margin-top:20px;">
|
||||||
|
<button onclick="closeMessagePopup()" class="wpm-btn wpm-btn--primary"><?php esc_html_e('Schließen', 'wp-multi'); ?></button>
|
||||||
|
<button id="deleteMessageBtn" class="wpm-btn wpm-btn--danger" onclick="deleteMessage()"><?php esc_html_e('Löschen', 'wp-multi'); ?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var wpMultiPinwandNonce = '<?php echo esc_js(wp_create_nonce('wp_multi_pinwand_nonce')); ?>';
|
||||||
|
|
||||||
|
function openMessagePopup(messageId) {
|
||||||
|
var data = { 'action': 'wp_multi_get_message', 'message_id': messageId, 'nonce': wpMultiPinwandNonce };
|
||||||
|
jQuery.post(ajaxurl, data, function(response) {
|
||||||
|
var messageData = JSON.parse(response);
|
||||||
|
document.getElementById('messageContent').innerHTML = '<h3>' + messageData.created_at + ' (' + messageData.user + ')</h3><p>' + messageData.message + '</p>';
|
||||||
|
document.getElementById('messagePopup').style.display = 'block';
|
||||||
|
document.getElementById('deleteMessageBtn').setAttribute('data-message-id', messageId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeMessagePopup() {
|
||||||
|
document.getElementById('messagePopup').style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteMessage() {
|
||||||
|
var messageId = document.getElementById('deleteMessageBtn').getAttribute('data-message-id');
|
||||||
|
var data = { 'action': 'wp_multi_delete_message', 'message_id': messageId, 'nonce': wpMultiPinwandNonce };
|
||||||
|
jQuery.post(ajaxurl, data, function(response) {
|
||||||
|
if (response == 'success') { closeMessagePopup(); location.reload(); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
// Der Pinwand-Menüpunkt wird zentral in wp_multi_register_admin_menus() registriert.
|
||||||
|
|
||||||
|
// Funktion zum Abrufen der vollständigen Nachricht
|
||||||
|
function wp_multi_get_message() {
|
||||||
|
check_ajax_referer('wp_multi_pinwand_nonce', 'nonce');
|
||||||
|
|
||||||
|
if (!current_user_can('administrator')) {
|
||||||
|
wp_die();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['message_id'])) {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'message_board';
|
||||||
|
$message_id = intval($_POST['message_id']);
|
||||||
|
$message = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name WHERE id = %d", $message_id));
|
||||||
|
|
||||||
|
if ($message) {
|
||||||
|
// Datum im gewünschten Format (DD-MM-JJJJ HH:MM:SS)
|
||||||
|
$formatted_date = date('d-m-Y H:i:s', strtotime($message->created_at));
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'created_at' => $formatted_date,
|
||||||
|
'message' => nl2br(esc_textarea($message->message)),
|
||||||
|
'user' => get_userdata($message->user_id)->user_login
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wp_die();
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action('wp_ajax_wp_multi_get_message', 'wp_multi_get_message');
|
||||||
|
|
||||||
|
// Funktion zum Löschen einer Nachricht
|
||||||
|
function wp_multi_delete_message() {
|
||||||
|
check_ajax_referer('wp_multi_pinwand_nonce', 'nonce');
|
||||||
|
|
||||||
|
if (!current_user_can('administrator')) {
|
||||||
|
wp_die();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['message_id'])) {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'message_board';
|
||||||
|
$message_id = intval($_POST['message_id']);
|
||||||
|
$wpdb->delete($table_name, array('id' => $message_id), array('%d'));
|
||||||
|
|
||||||
|
echo 'success';
|
||||||
|
}
|
||||||
|
wp_die();
|
||||||
|
}
|
||||||
|
add_action('wp_ajax_wp_multi_delete_message', 'wp_multi_delete_message');
|
||||||
|
|
||||||
|
// Funktion zum Deaktivieren der Pinwand bei der Deinstallation
|
||||||
|
function wp_multi_delete_message_board_table() {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'message_board';
|
||||||
|
$wpdb->query("DROP TABLE IF EXISTS $table_name");
|
||||||
|
}
|
||||||
|
register_deactivation_hook(WP_MULTI_FILE, 'wp_multi_delete_message_board_table');
|
||||||
|
|
||||||
|
// Funktion, um das Dashboard-Widget zu registrieren
|
||||||
|
function wp_multi_dashboard_widget() {
|
||||||
|
wp_add_dashboard_widget(
|
||||||
|
'wp_multi_pinwand_widget', // Widget-ID
|
||||||
|
'Pinwand Übersicht', // Widget-Titel
|
||||||
|
'wp_multi_dashboard_widget_content' // Callback-Funktion
|
||||||
|
);
|
||||||
|
}
|
||||||
|
add_action('wp_dashboard_setup', 'wp_multi_dashboard_widget');
|
||||||
|
|
||||||
|
// Callback-Funktion, die den Inhalt des Widgets erstellt
|
||||||
|
function wp_multi_dashboard_widget_content() {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'message_board';
|
||||||
|
$messages = $wpdb->get_results("SELECT * FROM $table_name ORDER BY created_at DESC LIMIT 5"); // Zeige die neuesten 5 Nachrichten an
|
||||||
|
|
||||||
|
if ($messages) {
|
||||||
|
echo '<ul>';
|
||||||
|
foreach ($messages as $message) {
|
||||||
|
$user_info = get_userdata($message->user_id);
|
||||||
|
echo '<li>';
|
||||||
|
echo '<strong>' . esc_html($user_info->user_login) . ' (' . date('d-m-Y H:i:s', strtotime($message->created_at)) . ')</strong>: ';
|
||||||
|
echo wp_trim_words($message->message, 10) . '...'; // Zeigt nur eine Vorschau der Nachricht
|
||||||
|
echo '</li>';
|
||||||
|
}
|
||||||
|
echo '</ul>';
|
||||||
|
} else {
|
||||||
|
echo '<p>Keine neuen Nachrichten.</p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Notify Dashboard-Zähler
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Admin-Dashboard Nachrichten sende Zähler
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Admin Dashboard Widget für Telegram und Discord Nachrichten Zähler
|
||||||
|
function wp_multi_add_dashboard_widgets() {
|
||||||
|
wp_add_dashboard_widget(
|
||||||
|
'wp_multi_dashboard_widget',
|
||||||
|
'Telegram & Discord Nachrichten Zähler',
|
||||||
|
'wp_multi_display_dashboard_widget'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
add_action('wp_dashboard_setup', 'wp_multi_add_dashboard_widgets');
|
||||||
|
|
||||||
|
// Callback-Funktion, die den Inhalt des Widgets anzeigt
|
||||||
|
function wp_multi_display_dashboard_widget() {
|
||||||
|
// Telegram-Nachrichtenzähler
|
||||||
|
$telegram_message_count = get_option('wp_multi_telegram_message_count', 0);
|
||||||
|
// Discord-Nachrichtenzähler
|
||||||
|
$discord_message_count = get_option('wp_multi_discord_message_count', 0);
|
||||||
|
|
||||||
|
// Ausgabe der Zähler
|
||||||
|
echo '<p><strong>Telegram Nachrichten gesendet:</strong> ' . esc_html($telegram_message_count) . '</p>';
|
||||||
|
echo '<p><strong>Discord Nachrichten gesendet:</strong> ' . esc_html($discord_message_count) . '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Discord-Benachrichtigung
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Discord Notify
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Einstellungsseite für Discord Webhook
|
||||||
|
function wp_multi_settings_page() {
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('DC-Notify', 'wp-multi'),
|
||||||
|
__('Discord-Benachrichtigungen bei neuen Beiträgen', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
wpmt_admin_card_open();
|
||||||
|
?>
|
||||||
|
<form method="post" action="options.php">
|
||||||
|
<?php
|
||||||
|
settings_fields('wp_multi_discord_options_group');
|
||||||
|
do_settings_sections('wp-multi');
|
||||||
|
?>
|
||||||
|
<table class="form-table">
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Discord Webhook URL</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="wp_multi_discord_webhook" value="<?php echo esc_attr(get_option('wp_multi_discord_webhook')); ?>" size="50">
|
||||||
|
<p class="description">Geben Sie die Webhook-URL für Discord ein, um Benachrichtigungen zu senden.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Bot Name</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="wp_multi_discord_bot_name" value="<?php echo esc_attr(get_option('wp_multi_discord_bot_name', 'WP Multi Bot')); ?>" size="50">
|
||||||
|
<p class="description">Geben Sie den Namen des Bots ein, der in Discord angezeigt werden soll.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Discord Nachricht (Vorlage)</th>
|
||||||
|
<td>
|
||||||
|
<textarea name="wp_multi_discord_message_template" rows="4" cols="50"><?php echo esc_textarea(get_option('wp_multi_discord_message_template', 'Beitrag "{post_title}" von {post_author} | Link: {post_url}')); ?></textarea>
|
||||||
|
<p class="description">Passen Sie die Nachricht an, die an Discord gesendet wird. Verwenden Sie Platzhalter wie {post_title}, {post_author}, und {post_url}.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Discord Benutzerrollen ID (für Ping)</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="wp_multi_discord_role_id" value="<?php echo esc_attr(get_option('wp_multi_discord_role_id')); ?>" size="50">
|
||||||
|
<p class="description">Geben Sie die ID der Discord-Benutzerrolle ein, die gepingt werden soll (z. B. @everyone).</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Discord Avatar-URL</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="wp_multi_discord_avatar_url" value="<?php echo esc_attr(get_option('wp_multi_discord_avatar_url')); ?>" size="50">
|
||||||
|
<p class="description">Geben Sie die URL des Avatar-Bildes ein, das in den Discord-Nachrichten angezeigt werden soll.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Footer Text (Custom Text 2)</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="wp_multi_discord_footer_text" value="<?php echo esc_attr(get_option('wp_multi_discord_footer_text')); ?>" size="50">
|
||||||
|
<p class="description">Geben Sie den benutzerdefinierten Text ein, der am Ende der Nachricht angezeigt wird (z. B. "Powered by WP Multi"). Sie können auch Platzhalter wie {post_title}, {post_author} und {post_url} verwenden.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<?php submit_button(); ?>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion, um die Einstellungen zu registrieren
|
||||||
|
function wp_multi_register_settings() {
|
||||||
|
register_setting('wp_multi_discord_options_group', 'wp_multi_discord_webhook');
|
||||||
|
register_setting('wp_multi_discord_options_group', 'wp_multi_discord_bot_name');
|
||||||
|
register_setting('wp_multi_discord_options_group', 'wp_multi_discord_message_template');
|
||||||
|
register_setting('wp_multi_discord_options_group', 'wp_multi_discord_role_id');
|
||||||
|
register_setting('wp_multi_discord_options_group', 'wp_multi_discord_avatar_url');
|
||||||
|
register_setting('wp_multi_discord_options_group', 'wp_multi_discord_footer_text');
|
||||||
|
}
|
||||||
|
add_action('admin_init', 'wp_multi_register_settings');
|
||||||
|
|
||||||
|
// Funktion, um die Discord-Benachrichtigung zu senden
|
||||||
|
function wp_multi_send_discord_notification($ID, $post) {
|
||||||
|
$send_notification = get_post_meta($ID, '_wp_multi_checkbox', true);
|
||||||
|
if ($send_notification !== '1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$webhook_url = get_option('wp_multi_discord_webhook');
|
||||||
|
if (empty($webhook_url)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bot_name = get_option('wp_multi_discord_bot_name', 'WP Multi Bot');
|
||||||
|
$avatar_url = get_option('wp_multi_discord_avatar_url');
|
||||||
|
$post_title = esc_html(get_the_title($ID));
|
||||||
|
$post_url = esc_url(get_permalink($ID));
|
||||||
|
$post_author = esc_html(get_the_author_meta('display_name', $post->post_author));
|
||||||
|
$content = get_post_field('post_content', $ID);
|
||||||
|
$excerpt = wp_trim_words($content, 60, '...');
|
||||||
|
$role_id = get_option('wp_multi_discord_role_id');
|
||||||
|
$mention_role = (!empty($role_id) && is_numeric($role_id)) ? "<@&" . esc_attr($role_id) . ">" : '';
|
||||||
|
$footer_text = get_option('wp_multi_discord_footer_text');
|
||||||
|
$footer = !empty($footer_text) ? str_replace(
|
||||||
|
['{post_title}', '{post_author}', '{post_url}'],
|
||||||
|
[$post_title, $post_author, $post_url],
|
||||||
|
$footer_text
|
||||||
|
) : '';
|
||||||
|
|
||||||
|
$message_template = get_option('wp_multi_discord_message_template', 'Beitrag "{post_title}" von {post_author} | Link: {post_url}');
|
||||||
|
$message = str_replace(
|
||||||
|
['{post_title}', '{post_author}', '{post_url}'],
|
||||||
|
[$post_title, $post_author, $post_url],
|
||||||
|
$message_template
|
||||||
|
);
|
||||||
|
$message .= "\n\n" . $excerpt . "\n\n" . $footer;
|
||||||
|
|
||||||
|
$data = json_encode([
|
||||||
|
'username' => $bot_name,
|
||||||
|
'avatar_url' => $avatar_url,
|
||||||
|
'content' => $mention_role . "\n" . $message
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = wp_remote_post($webhook_url, [
|
||||||
|
'method' => 'POST',
|
||||||
|
'body' => $data,
|
||||||
|
'headers' => ['Content-Type' => 'application/json']
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!is_wp_error($response)) {
|
||||||
|
wp_multi_increment_discord_message_count();
|
||||||
|
} else {
|
||||||
|
error_log('Discord Webhook Fehler: ' . $response->get_error_message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('publish_post', 'wp_multi_send_discord_notification', 10, 2);
|
||||||
|
|
||||||
|
// Funktion zum Erhöhen des Discord-Nachrichtenzählers
|
||||||
|
function wp_multi_increment_discord_message_count() {
|
||||||
|
$current_count = get_option('wp_multi_discord_message_count', 0);
|
||||||
|
update_option('wp_multi_discord_message_count', $current_count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion, um die Checkbox in der Sidebar des Beitrag Editors hinzuzufügen
|
||||||
|
function wp_multi_add_checkbox_to_sidebar() {
|
||||||
|
global $post;
|
||||||
|
wp_nonce_field('wp_multi_checkbox_nonce', 'wp_multi_checkbox_nonce_field');
|
||||||
|
$value = get_post_meta($post->ID, '_wp_multi_checkbox', true);
|
||||||
|
?>
|
||||||
|
<div class="misc-pub-section">
|
||||||
|
<label for="wp_multi_checkbox">
|
||||||
|
<input type="checkbox" name="wp_multi_checkbox" id="wp_multi_checkbox" value="1" <?php checked($value, '1'); ?>>
|
||||||
|
Discord Benachrichtigung senden
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
add_action('post_submitbox_misc_actions', 'wp_multi_add_checkbox_to_sidebar');
|
||||||
|
|
||||||
|
// Funktion, um den Wert der Checkbox zu speichern
|
||||||
|
function wp_multi_save_checkbox_value($post_id) {
|
||||||
|
if (!isset($_POST['wp_multi_checkbox_nonce_field']) || !wp_verify_nonce($_POST['wp_multi_checkbox_nonce_field'], 'wp_multi_checkbox_nonce')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isset($_POST['wp_multi_checkbox']) && $_POST['wp_multi_checkbox'] === '1') {
|
||||||
|
update_post_meta($post_id, '_wp_multi_checkbox', '1');
|
||||||
|
} else {
|
||||||
|
delete_post_meta($post_id, '_wp_multi_checkbox');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('save_post', 'wp_multi_save_checkbox_value');
|
||||||
|
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Telegram-Benachrichtigung
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Telegram Notify
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Admin-Seiten Callback
|
||||||
|
function tg_notify_page() {
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('TG-Notify', 'wp-stat-notice'),
|
||||||
|
__('Telegram-Benachrichtigungen bei neuen Beiträgen', 'wp-stat-notice')
|
||||||
|
);
|
||||||
|
|
||||||
|
wpmt_admin_card_open();
|
||||||
|
?>
|
||||||
|
<form method="post" action="options.php">
|
||||||
|
<?php
|
||||||
|
settings_fields('tg_notify_options_group');
|
||||||
|
do_settings_sections('tg-notify');
|
||||||
|
submit_button('Speichern', 'primary', 'submit', true);
|
||||||
|
?>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Einstellungen registrieren
|
||||||
|
function tg_notify_register_settings() {
|
||||||
|
register_setting('tg_notify_options_group', 'tg_notify_bot_name');
|
||||||
|
register_setting('tg_notify_options_group', 'tg_notify_bot_token');
|
||||||
|
register_setting('tg_notify_options_group', 'tg_notify_chat_ids');
|
||||||
|
register_setting('tg_notify_options_group', 'tg_notify_custom_message');
|
||||||
|
|
||||||
|
add_settings_section('tg_notify_main_section', __('Telegram Einstellungen', 'wp-stat-notice'), null, 'tg-notify');
|
||||||
|
|
||||||
|
add_settings_field('tg_notify_bot_name', __('Bot Name', 'wp-stat-notice'), 'tg_notify_bot_name_callback', 'tg-notify', 'tg_notify_main_section');
|
||||||
|
add_settings_field('tg_notify_bot_token', __('Bot Token', 'wp-stat-notice'), 'tg_notify_bot_token_callback', 'tg-notify', 'tg_notify_main_section');
|
||||||
|
add_settings_field('tg_notify_chat_ids', __('Kanal IDs', 'wp-stat-notice'), 'tg_notify_chat_ids_callback', 'tg-notify', 'tg_notify_main_section');
|
||||||
|
add_settings_field('tg_notify_custom_message', __('Custom Nachricht', 'wp-stat-notice'), 'tg_notify_custom_message_callback', 'tg-notify', 'tg_notify_main_section');
|
||||||
|
}
|
||||||
|
add_action('admin_init', 'tg_notify_register_settings');
|
||||||
|
|
||||||
|
// Callback-Funktionen
|
||||||
|
function tg_notify_bot_name_callback() {
|
||||||
|
$value = get_option('tg_notify_bot_name', '');
|
||||||
|
echo '<input type="text" name="tg_notify_bot_name" value="' . esc_attr($value) . '" class="regular-text">';
|
||||||
|
}
|
||||||
|
function tg_notify_bot_token_callback() {
|
||||||
|
$value = get_option('tg_notify_bot_token', '');
|
||||||
|
echo '<input type="text" name="tg_notify_bot_token" value="' . esc_attr($value) . '" class="regular-text">';
|
||||||
|
}
|
||||||
|
function tg_notify_chat_ids_callback() {
|
||||||
|
$value = get_option('tg_notify_chat_ids', '');
|
||||||
|
echo '<textarea name="tg_notify_chat_ids" class="large-text code" rows="3">' . esc_textarea($value) . '</textarea>';
|
||||||
|
echo '<p>Kanal ohne Thema: -1001234567890</p>';
|
||||||
|
echo '<p>Kanal mit Thema: -1001234567890_123</p>';
|
||||||
|
}
|
||||||
|
function tg_notify_custom_message_callback() {
|
||||||
|
$value = get_option('tg_notify_custom_message', '');
|
||||||
|
echo '<textarea name="tg_notify_custom_message" class="large-text code" rows="5">' . esc_textarea($value) . '</textarea>';
|
||||||
|
echo '<p>Verfügbare Variablen: {title}, {author}, {link}</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checkbox beim Beitrag hinzufügen
|
||||||
|
function tg_notify_add_meta_box() {
|
||||||
|
add_meta_box(
|
||||||
|
'tg_notify_meta_box',
|
||||||
|
__('Telegram Benachrichtigung', 'wp-stat-notice'),
|
||||||
|
'tg_notify_meta_box_callback',
|
||||||
|
'post',
|
||||||
|
'side',
|
||||||
|
'high'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
add_action('add_meta_boxes', 'tg_notify_add_meta_box');
|
||||||
|
|
||||||
|
function tg_notify_meta_box_callback($post) {
|
||||||
|
$value = get_post_meta($post->ID, '_tg_notify_send', true);
|
||||||
|
|
||||||
|
// Standardmäßig auf 1 setzen, wenn der Beitrag neu ist
|
||||||
|
if (empty($value) && get_post_status($post->ID) !== 'publish') {
|
||||||
|
$value = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_nonce_field('tg_notify_meta_box', 'tg_notify_meta_box_nonce');
|
||||||
|
echo '<label><input type="checkbox" name="tg_notify_send" value="1" ' . checked($value, 1, false) . '> ' . __('Benachrichtigung senden', 'wp-stat-notice') . '</label>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function tg_notify_save_post($post_id) {
|
||||||
|
// Sicherheitsprüfungen
|
||||||
|
if (!isset($_POST['tg_notify_meta_box_nonce']) || !wp_verify_nonce($_POST['tg_notify_meta_box_nonce'], 'tg_notify_meta_box')) return;
|
||||||
|
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
|
||||||
|
if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) return;
|
||||||
|
if (!current_user_can('edit_post', $post_id)) return;
|
||||||
|
|
||||||
|
// Prüfen, ob der Beitrag wirklich veröffentlicht wurde
|
||||||
|
if (get_post_status($post_id) !== 'publish') return;
|
||||||
|
|
||||||
|
// Prüfen, ob die Nachricht bereits gesendet wurde
|
||||||
|
$already_sent = get_post_meta($post_id, '_tg_notify_sent', true);
|
||||||
|
if ($already_sent) return;
|
||||||
|
|
||||||
|
$send_notification = isset($_POST['tg_notify_send']) ? 1 : 0;
|
||||||
|
update_post_meta($post_id, '_tg_notify_send', $send_notification);
|
||||||
|
|
||||||
|
if ($send_notification) {
|
||||||
|
tg_notify_send_telegram_message($post_id);
|
||||||
|
update_post_meta($post_id, '_tg_notify_sent', 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
add_action('save_post', 'tg_notify_save_post');
|
||||||
|
|
||||||
|
function tg_notify_send_telegram_message($post_id) {
|
||||||
|
$bot_token = get_option('tg_notify_bot_token');
|
||||||
|
$chat_ids = explode("\n", get_option('tg_notify_chat_ids'));
|
||||||
|
$message_template = get_option('tg_notify_custom_message');
|
||||||
|
|
||||||
|
$post = get_post($post_id);
|
||||||
|
// Überprüfen, ob der Beitrag von einem Gast-Author stammt
|
||||||
|
$author_name = get_the_author_meta('display_name', $post->post_author);
|
||||||
|
if (empty($author_name)) {
|
||||||
|
// Falls kein Name vorhanden ist (Gast-Author), den Gast-Namen verwenden oder einen Platzhalter setzen
|
||||||
|
$author_name = 'Gast-Author';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nachricht formatieren
|
||||||
|
$message = str_replace(
|
||||||
|
['{title}', '{author}', '{link}'],
|
||||||
|
[$post->post_title, $author_name, get_permalink($post_id)],
|
||||||
|
$message_template
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($chat_ids as $chat_id) {
|
||||||
|
$chat_id = trim($chat_id);
|
||||||
|
if (!empty($chat_id)) {
|
||||||
|
// Überprüfen, ob die ID das Thema enthält (Format: -1001234567890_123)
|
||||||
|
if (strpos($chat_id, '_') !== false) {
|
||||||
|
// Kanal-ID und Themen-ID trennen
|
||||||
|
list($channel_id, $topic_id) = explode('_', $chat_id);
|
||||||
|
$chat_id = $channel_id;
|
||||||
|
|
||||||
|
// Telegram API-Anfrage senden
|
||||||
|
$url = "https://api.telegram.org/bot$bot_token/sendMessage";
|
||||||
|
$args = [
|
||||||
|
'body' => json_encode([
|
||||||
|
'chat_id' => $chat_id,
|
||||||
|
'text' => $message,
|
||||||
|
'parse_mode' => 'HTML',
|
||||||
|
'reply_to_message_id' => $topic_id
|
||||||
|
]),
|
||||||
|
'headers' => ['Content-Type' => 'application/json'],
|
||||||
|
'method' => 'POST',
|
||||||
|
];
|
||||||
|
|
||||||
|
// API-Request senden und Fehlerprotokollierung
|
||||||
|
$response = wp_remote_post($url, $args);
|
||||||
|
if (is_wp_error($response)) {
|
||||||
|
$error_message = $response->get_error_message();
|
||||||
|
error_log("Telegram Fehler: $error_message");
|
||||||
|
} else {
|
||||||
|
// Erhöhe den Telegram-Nachrichtenzähler
|
||||||
|
tg_notify_increment_telegram_message_count();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Normaler Kanal ohne Thema
|
||||||
|
$url = "https://api.telegram.org/bot$bot_token/sendMessage";
|
||||||
|
$args = [
|
||||||
|
'body' => json_encode([
|
||||||
|
'chat_id' => $chat_id,
|
||||||
|
'text' => $message,
|
||||||
|
'parse_mode' => 'HTML'
|
||||||
|
]),
|
||||||
|
'headers' => ['Content-Type' => 'application/json'],
|
||||||
|
'method' => 'POST',
|
||||||
|
];
|
||||||
|
|
||||||
|
// API-Request senden und Fehlerprotokollierung
|
||||||
|
$response = wp_remote_post($url, $args);
|
||||||
|
if (is_wp_error($response)) {
|
||||||
|
$error_message = $response->get_error_message();
|
||||||
|
error_log("Telegram Fehler: $error_message");
|
||||||
|
} else {
|
||||||
|
// Erhöhe den Telegram-Nachrichtenzähler
|
||||||
|
tg_notify_increment_telegram_message_count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tg_notify_increment_telegram_message_count() {
|
||||||
|
$current_count = get_option('wp_multi_telegram_message_count', 0);
|
||||||
|
update_option('wp_multi_telegram_message_count', $current_count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Notify-Übersichtsseite
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notify Seite Discord & Telegram
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Die Notify-Menüpunkte werden zentral in wp_multi_register_admin_menus() registriert
|
||||||
|
// (DC-Notify-Slug wurde von 'wp-multi' zu 'wp-multi-dc-notify' umbenannt, da 'wp-multi'
|
||||||
|
// jetzt das gemeinsame Top-Level-Menü ist).
|
||||||
|
|
||||||
|
// Callback-Funktion für die Hauptseite Notify
|
||||||
|
function wp_multi_notify_page() {
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Notify', 'wp-multi'),
|
||||||
|
__('Discord- und Telegram-Benachrichtigungen für neue Beiträge', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
wpmt_admin_notice('info', __('Um Benachrichtigungen zu senden, müssen zuerst die entsprechenden Webhooks und Bots eingerichtet werden. Diese Seite erklärt die Einrichtung - die eigentlichen Einstellungen findest du unter DC-Notify bzw. TG-Notify.', 'wp-multi'));
|
||||||
|
|
||||||
|
echo '<div class="wpm-grid wpm-grid--2">';
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Discord', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<p><?php _e('Erstellen Sie einen Webhook in einem Discord-Kanal und fügen Sie die Webhook-URL in die entsprechenden Felder ein.', 'wp-multi'); ?></p>
|
||||||
|
<ol>
|
||||||
|
<li><?php _e('Gehen Sie zu Ihrem Discord-Server und öffnen Sie die Server-Einstellungen.', 'wp-multi'); ?></li>
|
||||||
|
<li><?php _e('Wählen Sie "Integrationen" und dann "Webhook erstellen".', 'wp-multi'); ?></li>
|
||||||
|
<li><?php _e('Kopieren Sie die Webhook-URL und fügen Sie diese in das entsprechende Feld ein.', 'wp-multi'); ?></li>
|
||||||
|
</ol>
|
||||||
|
<p><a href="<?php echo esc_url(admin_url('admin.php?page=wp-multi-dc-notify')); ?>" class="wpm-btn wpm-btn--primary"><?php _e('DC-Notify einrichten', 'wp-multi'); ?></a></p>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Telegram', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<p><?php _e('Erstellen Sie einen Bot über BotFather und fügen Sie den Token und die Kanal-ID in die entsprechenden Felder ein.', 'wp-multi'); ?></p>
|
||||||
|
<ol>
|
||||||
|
<li><?php _e('Öffnen Sie Telegram und suchen Sie nach "BotFather".', 'wp-multi'); ?></li>
|
||||||
|
<li><?php _e('Geben Sie /newbot ein, um einen neuen Bot zu erstellen.', 'wp-multi'); ?></li>
|
||||||
|
<li><?php _e('Speichern Sie den Bot-Token und die Kanal-ID und tragen Sie diese in die Felder ein.', 'wp-multi'); ?></li>
|
||||||
|
</ol>
|
||||||
|
<p><a href="<?php echo esc_url(admin_url('admin.php?page=tg-notify')); ?>" class="wpm-btn wpm-btn--primary"><?php _e('TG-Notify einrichten', 'wp-multi'); ?></a></p>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,338 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Beitrags-Report
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Beitrags Report
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Funktion zum Erstellen und Aktualisieren der Datenbanktabelle für gemeldete Beiträge
|
||||||
|
function wp_stat_notice_create_reported_posts_table() {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'reported_posts';
|
||||||
|
$charset_collate = $wpdb->get_charset_collate();
|
||||||
|
|
||||||
|
// SQL für die Tabelle
|
||||||
|
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
|
||||||
|
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
post_id BIGINT(20) NOT NULL,
|
||||||
|
report_date DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
reason TEXT NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
status VARCHAR(20) DEFAULT 'reported',
|
||||||
|
user_id BIGINT(20) UNSIGNED DEFAULT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
KEY post_id (post_id),
|
||||||
|
KEY user_id (user_id)
|
||||||
|
) $charset_collate;";
|
||||||
|
|
||||||
|
// Tabelle zuerst erstellen bzw. aktualisieren - erst danach kann die
|
||||||
|
// Spaltenprüfung laufen (vorher schlug SHOW COLUMNS bei der Erstaktivierung fehl).
|
||||||
|
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||||
|
dbDelta($sql);
|
||||||
|
|
||||||
|
// Migration für Alt-Installationen: Spalte `name` ergänzen, falls sie fehlt
|
||||||
|
$columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name");
|
||||||
|
|
||||||
|
$column_names = array_map(function($column) {
|
||||||
|
return $column->Field;
|
||||||
|
}, $columns);
|
||||||
|
|
||||||
|
if (!in_array('name', $column_names, true)) {
|
||||||
|
$wpdb->query("ALTER TABLE $table_name ADD COLUMN `name` VARCHAR(255) NOT NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register_activation_hook(WP_MULTI_FILE, 'wp_stat_notice_create_reported_posts_table');
|
||||||
|
|
||||||
|
|
||||||
|
// Shortcode für den "Beitrag melden"-Button
|
||||||
|
function wp_stat_notice_report_button($atts) {
|
||||||
|
global $post;
|
||||||
|
if (!is_user_logged_in()) return '';
|
||||||
|
|
||||||
|
$atts = shortcode_atts(array('post_id' => $post->ID), $atts, 'report_button');
|
||||||
|
$nonce = wp_create_nonce('report_post_nonce');
|
||||||
|
|
||||||
|
// Report-Button & Eingabefelder für Name und Grund
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<button class="report-post" data-post-id="<?php echo esc_attr($atts['post_id']); ?>" data-nonce="<?php echo esc_attr($nonce); ?>">
|
||||||
|
Beitrag melden
|
||||||
|
</button>
|
||||||
|
<div class="report-reason" style="display:none;">
|
||||||
|
<input type="text" class="report-name" placeholder="Geben Sie Ihren Namen an" required />
|
||||||
|
<textarea class="report-reason-text" placeholder="Geben Sie den Grund an" required></textarea>
|
||||||
|
<button class="submit-report">Bericht absenden</button>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
add_shortcode('report_button', 'wp_stat_notice_report_button');
|
||||||
|
|
||||||
|
// Stil für das Meldeformular
|
||||||
|
function wp_stat_notice_report_button_styles() {
|
||||||
|
?>
|
||||||
|
<style>
|
||||||
|
.report-reason {
|
||||||
|
display: none;
|
||||||
|
margin-top: 10px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 300px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.report-reason input, .report-reason textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px 0;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.report-reason button {
|
||||||
|
background-color: #0073aa;
|
||||||
|
color: white;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.report-reason button:hover {
|
||||||
|
background-color: #005177;
|
||||||
|
}
|
||||||
|
.report-post {
|
||||||
|
background-color: #ff7f00;
|
||||||
|
color: white;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.report-post:hover {
|
||||||
|
background-color: #e07b00;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
add_action('wp_head', 'wp_stat_notice_report_button_styles');
|
||||||
|
|
||||||
|
// Dashboard-Widget hinzufügen
|
||||||
|
function wp_stat_notice_add_dashboard_widget() {
|
||||||
|
wp_add_dashboard_widget(
|
||||||
|
'wp_stat_notice_dashboard_widget',
|
||||||
|
'Letzte 10 gemeldete Beiträge',
|
||||||
|
'wp_stat_notice_dashboard_widget_display'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
add_action('wp_dashboard_setup', 'wp_stat_notice_add_dashboard_widget');
|
||||||
|
|
||||||
|
// Funktion, die das Dashboard-Widget anzeigt
|
||||||
|
function wp_stat_notice_dashboard_widget_display() {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'reported_posts';
|
||||||
|
|
||||||
|
// Abfrage, um die letzten 10 gemeldeten Beiträge zu holen
|
||||||
|
$reports = $wpdb->get_results(
|
||||||
|
"SELECT * FROM $table_name ORDER BY report_date DESC LIMIT 10"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (empty($reports)) {
|
||||||
|
echo '<p>Es gibt keine gemeldeten Beiträge.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tabelle mit den letzten 10 gemeldeten Beiträgen anzeigen
|
||||||
|
echo '<table class="wp-list-table widefat fixed striped">';
|
||||||
|
echo '<thead><tr><th>Beitrag</th><th>Datum</th><th>Grund</th></tr></thead><tbody>';
|
||||||
|
|
||||||
|
foreach ($reports as $report) {
|
||||||
|
$post = get_post($report->post_id);
|
||||||
|
echo '<tr>';
|
||||||
|
echo '<td>' . esc_html($post->post_title) . '</td>';
|
||||||
|
echo '<td>' . esc_html($report->report_date) . '</td>';
|
||||||
|
echo '<td>' . esc_html($report->reason) . '</td>';
|
||||||
|
echo '</tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</tbody></table>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// AJAX-Handler zum Senden eines Reports
|
||||||
|
function wp_stat_notice_handle_report() {
|
||||||
|
check_ajax_referer('report_post_nonce', 'nonce');
|
||||||
|
|
||||||
|
if (!isset($_POST['post_id'], $_POST['reason'], $_POST['name']) || !is_user_logged_in()) {
|
||||||
|
wp_send_json_error(array('message' => 'Ungültige Anfrage.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'reported_posts';
|
||||||
|
|
||||||
|
$post_id = intval($_POST['post_id']);
|
||||||
|
$reason = sanitize_textarea_field($_POST['reason']);
|
||||||
|
$name = sanitize_text_field($_POST['name']);
|
||||||
|
$user_id = get_current_user_id();
|
||||||
|
|
||||||
|
// Versuche den Eintrag in die Datenbank zu schreiben
|
||||||
|
$result = $wpdb->insert(
|
||||||
|
$table_name,
|
||||||
|
array(
|
||||||
|
'post_id' => $post_id,
|
||||||
|
'reason' => $reason,
|
||||||
|
'name' => $name,
|
||||||
|
'status' => 'reported',
|
||||||
|
'user_id' => $user_id
|
||||||
|
),
|
||||||
|
array('%d', '%s', '%s', '%s', '%d')
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($result === false) {
|
||||||
|
error_log("Datenbankfehler: " . $wpdb->last_error); // WP Debug Log
|
||||||
|
wp_send_json_error(array('message' => 'Datenbankfehler: ' . $wpdb->last_error));
|
||||||
|
} else {
|
||||||
|
wp_send_json_success(array('message' => 'Bericht erfolgreich gesendet.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('wp_ajax_report_post', 'wp_stat_notice_handle_report');
|
||||||
|
|
||||||
|
|
||||||
|
// JavaScript in den Footer einfügen
|
||||||
|
function wp_stat_notice_inline_js() {
|
||||||
|
?>
|
||||||
|
<script>
|
||||||
|
jQuery(document).ready(function ($) {
|
||||||
|
$(document).on("click", ".report-post", function () {
|
||||||
|
let reasonBox = $(this).next(".report-reason");
|
||||||
|
reasonBox.toggle();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on("click", ".submit-report", function () {
|
||||||
|
let button = $(this);
|
||||||
|
let container = button.closest(".report-reason");
|
||||||
|
let reason = container.find(".report-reason-text").val();
|
||||||
|
let name = container.find(".report-name").val();
|
||||||
|
let postId = button.closest(".report-reason").prev(".report-post").data("post-id");
|
||||||
|
let nonce = button.closest(".report-reason").prev(".report-post").data("nonce");
|
||||||
|
|
||||||
|
if (!reason || !name) {
|
||||||
|
alert("Bitte geben Sie sowohl Ihren Namen als auch einen Grund an.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "<?php echo admin_url('admin-ajax.php'); ?>",
|
||||||
|
type: "POST",
|
||||||
|
data: {
|
||||||
|
action: "report_post",
|
||||||
|
post_id: postId,
|
||||||
|
reason: reason,
|
||||||
|
name: name,
|
||||||
|
nonce: nonce
|
||||||
|
},
|
||||||
|
success: function (response) {
|
||||||
|
if (response.success) {
|
||||||
|
alert("Der Bericht wurde erfolgreich gesendet.");
|
||||||
|
container.hide();
|
||||||
|
} else {
|
||||||
|
alert("Fehler: " + response.data.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
add_action('wp_footer', 'wp_stat_notice_inline_js');
|
||||||
|
|
||||||
|
// Admin-Seite für gemeldete Beiträge
|
||||||
|
function wp_stat_notice_reported_posts_page() {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'reported_posts';
|
||||||
|
$reports = $wpdb->get_results("SELECT * FROM $table_name ORDER BY report_date DESC");
|
||||||
|
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Gemeldete Beiträge', 'wp-stat-notice'),
|
||||||
|
__('Von Besuchern gemeldete Inhalte prüfen und bearbeiten', 'wp-stat-notice')
|
||||||
|
);
|
||||||
|
|
||||||
|
wpmt_admin_card_open();
|
||||||
|
if (empty($reports)) {
|
||||||
|
wpmt_admin_notice('info', __('Es liegen keine Meldungen vor.', 'wp-stat-notice'));
|
||||||
|
} else {
|
||||||
|
?>
|
||||||
|
<div class="wpm-table-wrap">
|
||||||
|
<table class="wpm-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('Beitrag', 'wp-stat-notice'); ?></th>
|
||||||
|
<th><?php _e('Datum', 'wp-stat-notice'); ?></th>
|
||||||
|
<th><?php _e('Name', 'wp-stat-notice'); ?></th>
|
||||||
|
<th><?php _e('Grund', 'wp-stat-notice'); ?></th>
|
||||||
|
<th><?php _e('Status', 'wp-stat-notice'); ?></th>
|
||||||
|
<th><?php _e('Aktionen', 'wp-stat-notice'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($reports as $report):
|
||||||
|
$post = get_post($report->post_id); ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo $post ? esc_html($post->post_title) : '—'; ?></td>
|
||||||
|
<td><?php echo esc_html($report->report_date); ?></td>
|
||||||
|
<td><?php echo esc_html($report->name); ?></td>
|
||||||
|
<td><?php echo esc_html($report->reason); ?></td>
|
||||||
|
<td><span class="wpm-badge <?php echo $report->status === 'reported' ? 'wpm-badge--warning' : ''; ?>"><?php echo esc_html($report->status); ?></span></td>
|
||||||
|
<td>
|
||||||
|
<a href="<?php echo esc_url(wp_nonce_url('?page=reported-posts&delete_report=' . $report->id, 'wp_stat_notice_delete_report_' . $report->id)); ?>" class="wpm-btn" onclick="return confirm('<?php esc_attr_e('Report wirklich löschen?', 'wp-stat-notice'); ?>');"><?php _e('Report löschen', 'wp-stat-notice'); ?></a>
|
||||||
|
<a href="<?php echo esc_url(wp_nonce_url('?page=reported-posts&unpublish_report=' . $report->id, 'wp_stat_notice_unpublish_report_' . $report->id)); ?>" class="wpm-btn"><?php _e('Unpublish', 'wp-stat-notice'); ?></a>
|
||||||
|
<a href="<?php echo esc_url(wp_nonce_url('?page=reported-posts&delete_post=' . $report->post_id, 'wp_stat_notice_delete_post_' . $report->post_id)); ?>" class="wpm-btn wpm-btn--danger" onclick="return confirm('<?php esc_attr_e('Beitrag wirklich unwiderruflich löschen?', 'wp-stat-notice'); ?>');"><?php _e('Beitrag löschen', 'wp-stat-notice'); ?></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aktionen für Report-Handling
|
||||||
|
function wp_stat_notice_handle_report_actions() {
|
||||||
|
if (!isset($_GET['page']) || $_GET['page'] !== 'reported-posts') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global $wpdb;
|
||||||
|
if (isset($_GET['delete_report'])) {
|
||||||
|
check_admin_referer('wp_stat_notice_delete_report_' . intval($_GET['delete_report']));
|
||||||
|
$wpdb->delete($wpdb->prefix . 'reported_posts', array('id' => intval($_GET['delete_report'])), array('%d'));
|
||||||
|
} elseif (isset($_GET['unpublish_report'])) {
|
||||||
|
check_admin_referer('wp_stat_notice_unpublish_report_' . intval($_GET['unpublish_report']));
|
||||||
|
$wpdb->update($wpdb->prefix . 'reported_posts', array('status' => 'unpublished'), array('id' => intval($_GET['unpublish_report'])), array('%s'), array('%d'));
|
||||||
|
} elseif (isset($_GET['delete_post'])) {
|
||||||
|
check_admin_referer('wp_stat_notice_delete_post_' . intval($_GET['delete_post']));
|
||||||
|
wp_delete_post(intval($_GET['delete_post']), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('admin_init', 'wp_stat_notice_handle_report_actions');
|
||||||
|
|
||||||
|
// Der Menüpunkt "Gemeldete Beiträge" wird zentral in wp_multi_register_admin_menus() registriert.
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,696 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Veröffentlichungs-Statistik
|
||||||
|
*
|
||||||
|
* Zeigt, wie viele Geschichten pro Monat und pro Jahr veröffentlicht wurden.
|
||||||
|
* Eigener Menüpunkt, unabhängig von der Gast-Autoren-Übersicht.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liefert die Reiter der Statistik-Seite.
|
||||||
|
*/
|
||||||
|
function wp_multi_get_publication_stats_tabs() {
|
||||||
|
return [
|
||||||
|
'month' => __('Monat', 'wp-multi'),
|
||||||
|
'year' => __('Jahr', 'wp-multi'),
|
||||||
|
'timeline' => __('Autoren-Zeitlinie', 'wp-multi'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Beitragstyp, der gezählt wird.
|
||||||
|
*/
|
||||||
|
function wp_multi_get_stats_post_type() {
|
||||||
|
return apply_filters('wp_multi_stats_post_type', 'post');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liefert alle Jahre mit veröffentlichten Beiträgen samt Anzahl.
|
||||||
|
*
|
||||||
|
* @return array Zeilen mit year und total, aufsteigend nach Jahr.
|
||||||
|
*/
|
||||||
|
function wp_multi_get_publication_years() {
|
||||||
|
static $years = null;
|
||||||
|
|
||||||
|
if ($years !== null) {
|
||||||
|
return $years;
|
||||||
|
}
|
||||||
|
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$years = $wpdb->get_results($wpdb->prepare("
|
||||||
|
SELECT YEAR(post_date) AS year, COUNT(*) AS total
|
||||||
|
FROM {$wpdb->posts}
|
||||||
|
WHERE post_status = 'publish'
|
||||||
|
AND post_type = %s
|
||||||
|
GROUP BY YEAR(post_date)
|
||||||
|
ORDER BY year ASC
|
||||||
|
", wp_multi_get_stats_post_type()));
|
||||||
|
|
||||||
|
return $years;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback für den Menüpunkt "Veröffentlichungen".
|
||||||
|
*/
|
||||||
|
function wp_multi_publication_stats_page() {
|
||||||
|
$tabs = wp_multi_get_publication_stats_tabs();
|
||||||
|
$current_tab = isset($_GET['tab']) ? sanitize_key($_GET['tab']) : 'month';
|
||||||
|
if (!isset($tabs[$current_tab])) {
|
||||||
|
$current_tab = 'month';
|
||||||
|
}
|
||||||
|
|
||||||
|
$base_url = add_query_arg(['page' => 'wp-multi-publications'], admin_url('admin.php'));
|
||||||
|
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Veröffentlichungen', 'wp-multi'),
|
||||||
|
__('Wie viele Geschichten pro Monat und pro Jahr veröffentlicht wurden', 'wp-multi')
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
<h2 class="nav-tab-wrapper wpm-tabs">
|
||||||
|
<?php foreach ($tabs as $tab_key => $tab_label) : ?>
|
||||||
|
<a href="<?php echo esc_url(add_query_arg('tab', $tab_key, $base_url)); ?>"
|
||||||
|
class="nav-tab<?php echo ($current_tab === $tab_key) ? ' nav-tab-active' : ''; ?>">
|
||||||
|
<?php echo esc_html($tab_label); ?>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</h2>
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if ($current_tab === 'year') {
|
||||||
|
wp_multi_render_publication_years($base_url);
|
||||||
|
} elseif ($current_tab === 'timeline') {
|
||||||
|
wp_multi_render_author_timeline($base_url);
|
||||||
|
} else {
|
||||||
|
wp_multi_render_publication_months($base_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt eine Balkenzeile für die Statistik aus.
|
||||||
|
*
|
||||||
|
* Bewusst ohne Diagramm-Bibliothek: ein <div> mit prozentualer Breite reicht,
|
||||||
|
* lädt nichts nach und funktioniert auch ohne JavaScript.
|
||||||
|
*
|
||||||
|
* @param string $label Beschriftung links.
|
||||||
|
* @param int $value Wert.
|
||||||
|
* @param int $max Größter Wert der Reihe (für die Balkenbreite).
|
||||||
|
*/
|
||||||
|
function wp_multi_render_stat_bar($label, $value, $max) {
|
||||||
|
$percent = $max > 0 ? round(($value / $max) * 100) : 0;
|
||||||
|
?>
|
||||||
|
<div class="wpm-stat-row" style="display:flex;align-items:center;gap:10px;margin-bottom:6px;">
|
||||||
|
<span class="wpm-stat-label" style="flex:0 0 6em;"><?php echo esc_html($label); ?></span>
|
||||||
|
<span class="wpm-stat-track" style="flex:1 1 auto;background:rgba(125,125,125,.2);border-radius:3px;overflow:hidden;">
|
||||||
|
<span class="wpm-stat-fill" style="display:block;height:14px;width:<?php echo (int) $percent; ?>%;min-width:<?php echo $value > 0 ? '2px' : '0'; ?>;background:#2271b1;"></span>
|
||||||
|
</span>
|
||||||
|
<span class="wpm-stat-value" style="flex:0 0 5em;text-align:right;"><?php echo esc_html(number_format_i18n($value)); ?></span>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reiter "Monat": Veröffentlichungen je Monat eines Jahres.
|
||||||
|
*
|
||||||
|
* @param string $base_url Basis-URL der Seite.
|
||||||
|
*/
|
||||||
|
function wp_multi_render_publication_months($base_url) {
|
||||||
|
global $wpdb, $wp_locale;
|
||||||
|
|
||||||
|
$years = wp_multi_get_publication_years();
|
||||||
|
|
||||||
|
if (!$years) {
|
||||||
|
wpmt_admin_card_open(__('Veröffentlichungen pro Monat', 'wp-multi'));
|
||||||
|
echo '<p class="wpm-muted">' . esc_html__('Noch keine veröffentlichten Beiträge gefunden.', 'wp-multi') . '</p>';
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$available_years = [];
|
||||||
|
foreach ($years as $row) {
|
||||||
|
$available_years[] = (int) $row->year;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jahr aus der URL, sonst das jüngste Jahr mit Beiträgen
|
||||||
|
$selected_year = isset($_GET['stats_year']) ? (int) $_GET['stats_year'] : 0;
|
||||||
|
if (!in_array($selected_year, $available_years, true)) {
|
||||||
|
$selected_year = end($available_years);
|
||||||
|
}
|
||||||
|
|
||||||
|
$months = $wpdb->get_results($wpdb->prepare("
|
||||||
|
SELECT MONTH(post_date) AS month, COUNT(*) AS total
|
||||||
|
FROM {$wpdb->posts}
|
||||||
|
WHERE post_status = 'publish'
|
||||||
|
AND post_type = %s
|
||||||
|
AND YEAR(post_date) = %d
|
||||||
|
GROUP BY MONTH(post_date)
|
||||||
|
ORDER BY month ASC
|
||||||
|
", wp_multi_get_stats_post_type(), $selected_year));
|
||||||
|
|
||||||
|
$month_totals = array_fill(1, 12, 0);
|
||||||
|
$max_month_total = 0;
|
||||||
|
$year_total = 0;
|
||||||
|
|
||||||
|
foreach ((array) $months as $row) {
|
||||||
|
$month_totals[(int) $row->month] = (int) $row->total;
|
||||||
|
$year_total += (int) $row->total;
|
||||||
|
if ((int) $row->total > $max_month_total) {
|
||||||
|
$max_month_total = (int) $row->total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$month_url = add_query_arg('tab', 'month', $base_url);
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Veröffentlichungen pro Monat', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<p class="wpm-muted">
|
||||||
|
<?php _e('Jahr:', 'wp-multi'); ?>
|
||||||
|
<?php foreach (array_reverse($available_years) as $year) : ?>
|
||||||
|
<a href="<?php echo esc_url(add_query_arg('stats_year', $year, $month_url)); ?>" class="wpm-filter-link<?php echo ($year === $selected_year) ? ' wpm-filter-active' : ''; ?>">
|
||||||
|
<?php if ($year === $selected_year) : ?><strong><?php endif; ?>
|
||||||
|
<?php echo esc_html($year); ?>
|
||||||
|
<?php if ($year === $selected_year) : ?></strong><?php endif; ?>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</p>
|
||||||
|
<p class="wpm-muted">
|
||||||
|
<?php
|
||||||
|
printf(
|
||||||
|
/* translators: 1: Jahr, 2: Anzahl der Geschichten */
|
||||||
|
esc_html__('%1$d: %2$s Geschichten', 'wp-multi'),
|
||||||
|
(int) $selected_year,
|
||||||
|
esc_html(number_format_i18n($year_total))
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
<div class="wpm-stat-chart" style="margin:16px 0;">
|
||||||
|
<?php for ($month = 1; $month <= 12; $month++) : ?>
|
||||||
|
<?php wp_multi_render_stat_bar($wp_locale->get_month_abbrev($wp_locale->get_month($month)), $month_totals[$month], $max_month_total); ?>
|
||||||
|
<?php endfor; ?>
|
||||||
|
</div>
|
||||||
|
<div class="wpm-table-wrap">
|
||||||
|
<table class="wpm-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('Monat', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('Geschichten', 'wp-multi'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php for ($month = 1; $month <= 12; $month++) : ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo esc_html($wp_locale->get_month($month)); ?></td>
|
||||||
|
<td>
|
||||||
|
<?php if ($month_totals[$month]) : ?>
|
||||||
|
<?php echo esc_html(number_format_i18n($month_totals[$month])); ?>
|
||||||
|
<?php else : ?>
|
||||||
|
<span class="wpm-muted">0</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endfor; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reiter "Jahr": Veröffentlichungen je Jahr.
|
||||||
|
*
|
||||||
|
* @param string $base_url Basis-URL der Seite.
|
||||||
|
*/
|
||||||
|
function wp_multi_render_publication_years($base_url) {
|
||||||
|
$years = wp_multi_get_publication_years();
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Veröffentlichungen pro Jahr', 'wp-multi'));
|
||||||
|
|
||||||
|
if (!$years) {
|
||||||
|
echo '<p class="wpm-muted">' . esc_html__('Noch keine veröffentlichten Beiträge gefunden.', 'wp-multi') . '</p>';
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$max_total = 0;
|
||||||
|
$grand_total = 0;
|
||||||
|
$totals_by_year = [];
|
||||||
|
|
||||||
|
foreach ($years as $row) {
|
||||||
|
$totals_by_year[(int) $row->year] = (int) $row->total;
|
||||||
|
$grand_total += (int) $row->total;
|
||||||
|
if ((int) $row->total > $max_total) {
|
||||||
|
$max_total = (int) $row->total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$year_keys = array_keys($totals_by_year);
|
||||||
|
$month_url = add_query_arg('tab', 'month', $base_url);
|
||||||
|
?>
|
||||||
|
<p class="wpm-muted">
|
||||||
|
<?php
|
||||||
|
printf(
|
||||||
|
/* translators: 1: Gesamtzahl, 2: erstes Jahr, 3: letztes Jahr */
|
||||||
|
esc_html__('%1$s Geschichten von %2$d bis %3$d.', 'wp-multi'),
|
||||||
|
esc_html(number_format_i18n($grand_total)),
|
||||||
|
(int) reset($year_keys),
|
||||||
|
(int) end($year_keys)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
<div class="wpm-stat-chart" style="margin:16px 0;">
|
||||||
|
<?php foreach ($totals_by_year as $year => $total) : ?>
|
||||||
|
<?php wp_multi_render_stat_bar($year, $total, $max_total); ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<div class="wpm-table-wrap">
|
||||||
|
<table class="wpm-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('Jahr', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('Geschichten', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('Veränderung', 'wp-multi'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach (array_reverse($totals_by_year, true) as $year => $total) : ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="<?php echo esc_url(add_query_arg('stats_year', (int) $year, $month_url)); ?>" title="<?php esc_attr_e('Monate dieses Jahres anzeigen', 'wp-multi'); ?>">
|
||||||
|
<?php echo esc_html($year); ?>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td><?php echo esc_html(number_format_i18n($total)); ?></td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
// Vergleich mit dem Vorjahr, sofern es Beiträge hatte
|
||||||
|
if (!isset($totals_by_year[$year - 1])) {
|
||||||
|
echo '<span class="wpm-muted">—</span>';
|
||||||
|
} else {
|
||||||
|
$diff = $total - $totals_by_year[$year - 1];
|
||||||
|
$sign = $diff > 0 ? '+' : '';
|
||||||
|
echo '<span class="wpm-muted">' . esc_html($sign . number_format_i18n($diff)) . '</span>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liefert die Beschriftungen der Autorengruppen.
|
||||||
|
*/
|
||||||
|
function wp_multi_get_timeline_groups() {
|
||||||
|
return [
|
||||||
|
'lost' => '🔥 ' . __('Verlorene Vielschreiber', 'wp-multi'),
|
||||||
|
'active' => '🟢 ' . __('Aktive Autoren', 'wp-multi'),
|
||||||
|
'new' => '🆕 ' . __('Neue Autoren', 'wp-multi'),
|
||||||
|
'quiet' => '🟡 ' . __('Stille Autoren', 'wp-multi'),
|
||||||
|
'one_off' => '💤 ' . __('Einmalige Autoren', 'wp-multi'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ordnet einen Autor anhand seiner Jahreswerte einer Gruppe zu.
|
||||||
|
*
|
||||||
|
* 🆕 Neue Autoren – erste Geschichte im aktuellen oder letzten Jahr
|
||||||
|
* 🔥 Verlorene Vielschreiber – früher ein starkes Jahr, seitdem so gut wie nichts
|
||||||
|
* 🟢 Aktive Autoren – im aktuellen oder letzten Jahr veröffentlicht
|
||||||
|
* 💤 Einmalige Autoren – höchstens zwei Geschichten und lange nichts mehr
|
||||||
|
* 🟡 Stille Autoren – alles dazwischen: mehr als zwei Geschichten, aber still
|
||||||
|
*
|
||||||
|
* @param array $per_year Zuordnung "Jahr => Anzahl".
|
||||||
|
* @param int $current_year Aktuelles Jahr.
|
||||||
|
* @return string Gruppenschlüssel.
|
||||||
|
*/
|
||||||
|
function wp_multi_get_author_timeline_group($per_year, $current_year) {
|
||||||
|
// Die letzten beiden Jahre gelten als "jetzt"
|
||||||
|
$recent_span = (int) apply_filters('wp_multi_timeline_recent_years', 2);
|
||||||
|
$recent_from = $current_year - ($recent_span - 1);
|
||||||
|
|
||||||
|
$total = 0;
|
||||||
|
$recent_total = 0;
|
||||||
|
$earlier_peak = 0;
|
||||||
|
$first_year = null;
|
||||||
|
|
||||||
|
foreach ($per_year as $year => $count) {
|
||||||
|
$year = (int) $year;
|
||||||
|
$count = (int) $count;
|
||||||
|
$total += $count;
|
||||||
|
|
||||||
|
if ($first_year === null || $year < $first_year) {
|
||||||
|
$first_year = $year;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($year >= $recent_from) {
|
||||||
|
$recent_total += $count;
|
||||||
|
} elseif ($count > $earlier_peak) {
|
||||||
|
$earlier_peak = $count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wer erst im "Jetzt"-Fenster angefangen hat, ist neu
|
||||||
|
if ($first_year !== null && $first_year >= $recent_from) {
|
||||||
|
return 'new';
|
||||||
|
}
|
||||||
|
|
||||||
|
$prolific_min = (int) apply_filters('wp_multi_timeline_prolific_min', 5);
|
||||||
|
$recent_max = (int) apply_filters('wp_multi_timeline_recent_max', 1);
|
||||||
|
|
||||||
|
if ($earlier_peak >= $prolific_min && $recent_total <= $recent_max) {
|
||||||
|
return 'lost';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($recent_total > 0) {
|
||||||
|
return 'active';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($total <= 2) {
|
||||||
|
return 'one_off';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'quiet';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liest die Veröffentlichungen je Autor und Jahr.
|
||||||
|
*
|
||||||
|
* @return array Zuordnung "Autor => [Jahr => Anzahl]".
|
||||||
|
*/
|
||||||
|
function wp_multi_get_author_timeline_data() {
|
||||||
|
static $data = null;
|
||||||
|
|
||||||
|
if ($data !== null) {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$rows = $wpdb->get_results($wpdb->prepare("
|
||||||
|
SELECT pm.meta_value AS guest_author, YEAR(p.post_date) AS year, COUNT(*) AS total
|
||||||
|
FROM {$wpdb->posts} p
|
||||||
|
INNER JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = '_guest_author' AND pm.meta_value != ''
|
||||||
|
WHERE p.post_status = 'publish'
|
||||||
|
AND p.post_type = %s
|
||||||
|
GROUP BY pm.meta_value, YEAR(p.post_date)
|
||||||
|
", wp_multi_get_stats_post_type()));
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
foreach ((array) $rows as $row) {
|
||||||
|
$data[$row->guest_author][(int) $row->year] = (int) $row->total;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reiter "Autoren-Zeitlinie": wer wann geschrieben hat und wer abgewandert ist.
|
||||||
|
*
|
||||||
|
* @param string $base_url Basis-URL der Seite.
|
||||||
|
*/
|
||||||
|
function wp_multi_render_author_timeline($base_url) {
|
||||||
|
$years = wp_multi_get_publication_years();
|
||||||
|
$timeline = wp_multi_get_author_timeline_data();
|
||||||
|
|
||||||
|
if (!$years || !$timeline) {
|
||||||
|
wpmt_admin_card_open(__('Autoren-Zeitlinie', 'wp-multi'));
|
||||||
|
echo '<p class="wpm-muted">' . esc_html__('Noch keine Daten für eine Zeitlinie vorhanden.', 'wp-multi') . '</p>';
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$available_years = [];
|
||||||
|
foreach ($years as $row) {
|
||||||
|
$available_years[] = (int) $row->year;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_year = (int) end($available_years);
|
||||||
|
|
||||||
|
// Nur die letzten Jahre als Spalten, sonst wird die Tabelle unlesbar
|
||||||
|
$columns = (int) apply_filters('wp_multi_timeline_columns', 6);
|
||||||
|
$column_years = array_slice($available_years, -$columns);
|
||||||
|
|
||||||
|
$groups = wp_multi_get_timeline_groups();
|
||||||
|
$group_filter = isset($_GET['group']) ? sanitize_key($_GET['group']) : '';
|
||||||
|
if (!isset($groups[$group_filter])) {
|
||||||
|
$group_filter = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autoren aufbereiten: Gruppe, Gesamtzahl und Jahreswerte
|
||||||
|
$authors = [];
|
||||||
|
$group_counts = array_fill_keys(array_keys($groups), 0);
|
||||||
|
|
||||||
|
foreach ($timeline as $name => $per_year) {
|
||||||
|
$group = wp_multi_get_author_timeline_group($per_year, $current_year);
|
||||||
|
$group_counts[$group]++;
|
||||||
|
|
||||||
|
$total = 0;
|
||||||
|
foreach ($per_year as $count) {
|
||||||
|
$total += (int) $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($group_filter !== '' && $group !== $group_filter) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$authors[] = [
|
||||||
|
'name' => $name,
|
||||||
|
'group' => $group,
|
||||||
|
'total' => $total,
|
||||||
|
'per_year' => $per_year,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nach Gesamtzahl sortieren – die produktivsten zuerst
|
||||||
|
usort($authors, function ($a, $b) {
|
||||||
|
if ($a['total'] === $b['total']) {
|
||||||
|
return strcasecmp($a['name'], $b['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $b['total'] - $a['total'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$timeline_url = add_query_arg('tab', 'timeline', $base_url);
|
||||||
|
$author_base_url = add_query_arg(['page' => 'guest_author_overview'], admin_url('admin.php'));
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Autoren-Zeitlinie', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<p class="wpm-muted">
|
||||||
|
<?php _e('Gruppe:', 'wp-multi'); ?>
|
||||||
|
<a href="<?php echo esc_url(remove_query_arg('group', $timeline_url)); ?>" class="wpm-filter-link<?php echo ($group_filter === '') ? ' wpm-filter-active' : ''; ?>">
|
||||||
|
<?php if ($group_filter === '') : ?><strong><?php endif; ?>
|
||||||
|
<?php printf(esc_html__('Alle (%d)', 'wp-multi'), count($timeline)); ?>
|
||||||
|
<?php if ($group_filter === '') : ?></strong><?php endif; ?>
|
||||||
|
</a>
|
||||||
|
<?php foreach ($groups as $key => $label) : ?>
|
||||||
|
|
|
||||||
|
<a href="<?php echo esc_url(add_query_arg('group', $key, $timeline_url)); ?>" class="wpm-filter-link<?php echo ($group_filter === $key) ? ' wpm-filter-active' : ''; ?>">
|
||||||
|
<?php if ($group_filter === $key) : ?><strong><?php endif; ?>
|
||||||
|
<?php echo esc_html($label); ?> (<?php echo (int) $group_counts[$key]; ?>)
|
||||||
|
<?php if ($group_filter === $key) : ?></strong><?php endif; ?>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</p>
|
||||||
|
<div class="wpm-table-wrap">
|
||||||
|
<table class="wpm-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('Autor', 'wp-multi'); ?></th>
|
||||||
|
<?php foreach ($column_years as $year) : ?>
|
||||||
|
<th><?php echo esc_html($year); ?></th>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<th><?php _e('Gesamt', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('Gruppe', 'wp-multi'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ($authors) : ?>
|
||||||
|
<?php foreach ($authors as $author) : ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<?php if (function_exists('wp_multi_get_guest_author_detail_url')) : ?>
|
||||||
|
<a href="<?php echo esc_url(wp_multi_get_guest_author_detail_url($author_base_url, $author['name'])); ?>"><?php echo esc_html($author['name']); ?></a>
|
||||||
|
<?php else : ?>
|
||||||
|
<?php echo esc_html($author['name']); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<?php foreach ($column_years as $year) : ?>
|
||||||
|
<?php $count = isset($author['per_year'][$year]) ? (int) $author['per_year'][$year] : 0; ?>
|
||||||
|
<td>
|
||||||
|
<?php if ($count) : ?>
|
||||||
|
<?php echo esc_html(number_format_i18n($count)); ?>
|
||||||
|
<?php else : ?>
|
||||||
|
<span class="wpm-muted">·</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<td><strong><?php echo esc_html(number_format_i18n($author['total'])); ?></strong></td>
|
||||||
|
<td><span class="wpm-muted"><?php echo esc_html($groups[$author['group']]); ?></span></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else : ?>
|
||||||
|
<tr><td colspan="<?php echo (int) (count($column_years) + 3); ?>"><?php _e('Keine Autoren in dieser Gruppe.', 'wp-multi'); ?></td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p class="wpm-muted">
|
||||||
|
<?php
|
||||||
|
printf(
|
||||||
|
/* translators: 1: Anzahl Jahre, 2: Mindestanzahl für Vielschreiber */
|
||||||
|
esc_html__('Spalten zeigen die letzten %1$d Jahre. Als "verlorener Vielschreiber" gilt, wer früher mindestens %2$d Geschichten in einem Jahr hatte und seither so gut wie nichts mehr veröffentlicht.', 'wp-multi'),
|
||||||
|
count($column_years),
|
||||||
|
(int) apply_filters('wp_multi_timeline_prolific_min', 5)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wp_multi_render_year_comparison($base_url, $available_years, $timeline, $author_base_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vergleicht zwei Jahre und zeigt, bei welchen Autoren die Differenz entsteht.
|
||||||
|
*
|
||||||
|
* @param string $base_url Basis-URL der Seite.
|
||||||
|
* @param array $available_years Alle Jahre mit Beiträgen.
|
||||||
|
* @param array $timeline Zuordnung "Autor => [Jahr => Anzahl]".
|
||||||
|
* @param string $author_base_url Basis-URL der Autoren-Detailansicht.
|
||||||
|
*/
|
||||||
|
function wp_multi_render_year_comparison($base_url, $available_years, $timeline, $author_base_url) {
|
||||||
|
if (count($available_years) < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard: die beiden letzten abgeschlossenen Jahre
|
||||||
|
$default_to = $available_years[count($available_years) - 2];
|
||||||
|
$default_from = $available_years[count($available_years) - 3] ?? $available_years[0];
|
||||||
|
|
||||||
|
$from = isset($_GET['cmp_from']) ? (int) $_GET['cmp_from'] : $default_from;
|
||||||
|
$to = isset($_GET['cmp_to']) ? (int) $_GET['cmp_to'] : $default_to;
|
||||||
|
|
||||||
|
if (!in_array($from, $available_years, true)) {
|
||||||
|
$from = $default_from;
|
||||||
|
}
|
||||||
|
if (!in_array($to, $available_years, true)) {
|
||||||
|
$to = $default_to;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = [];
|
||||||
|
$sum_from = 0;
|
||||||
|
$sum_to = 0;
|
||||||
|
|
||||||
|
foreach ($timeline as $name => $per_year) {
|
||||||
|
$count_from = isset($per_year[$from]) ? (int) $per_year[$from] : 0;
|
||||||
|
$count_to = isset($per_year[$to]) ? (int) $per_year[$to] : 0;
|
||||||
|
|
||||||
|
$sum_from += $count_from;
|
||||||
|
$sum_to += $count_to;
|
||||||
|
|
||||||
|
if ($count_from === $count_to) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows[] = [
|
||||||
|
'name' => $name,
|
||||||
|
'from' => $count_from,
|
||||||
|
'to' => $count_to,
|
||||||
|
'diff' => $count_to - $count_from,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Größte Rückgänge zuerst
|
||||||
|
usort($rows, function ($a, $b) {
|
||||||
|
if ($a['diff'] === $b['diff']) {
|
||||||
|
return strcasecmp($a['name'], $b['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $a['diff'] - $b['diff'];
|
||||||
|
});
|
||||||
|
|
||||||
|
$compare_url = add_query_arg('tab', 'timeline', $base_url);
|
||||||
|
$total_diff = $sum_to - $sum_from;
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Jahresvergleich', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<p class="wpm-muted">
|
||||||
|
<?php _e('Von:', 'wp-multi'); ?>
|
||||||
|
<?php foreach (array_reverse($available_years) as $year) : ?>
|
||||||
|
<a href="<?php echo esc_url(add_query_arg(['cmp_from' => $year, 'cmp_to' => $to], $compare_url)); ?>" class="wpm-filter-link<?php echo ($year === $from) ? ' wpm-filter-active' : ''; ?>">
|
||||||
|
<?php if ($year === $from) : ?><strong><?php endif; ?><?php echo esc_html($year); ?><?php if ($year === $from) : ?></strong><?php endif; ?>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</p>
|
||||||
|
<p class="wpm-muted">
|
||||||
|
<?php _e('Bis:', 'wp-multi'); ?>
|
||||||
|
<?php foreach (array_reverse($available_years) as $year) : ?>
|
||||||
|
<a href="<?php echo esc_url(add_query_arg(['cmp_from' => $from, 'cmp_to' => $year], $compare_url)); ?>" class="wpm-filter-link<?php echo ($year === $to) ? ' wpm-filter-active' : ''; ?>">
|
||||||
|
<?php if ($year === $to) : ?><strong><?php endif; ?><?php echo esc_html($year); ?><?php if ($year === $to) : ?></strong><?php endif; ?>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
printf(
|
||||||
|
/* translators: 1: erstes Jahr, 2: Anzahl, 3: zweites Jahr, 4: Anzahl, 5: Differenz */
|
||||||
|
esc_html__('%1$d: %2$s Geschichten – %3$d: %4$s Geschichten – Differenz: %5$s', 'wp-multi'),
|
||||||
|
(int) $from,
|
||||||
|
esc_html(number_format_i18n($sum_from)),
|
||||||
|
(int) $to,
|
||||||
|
esc_html(number_format_i18n($sum_to)),
|
||||||
|
esc_html(($total_diff > 0 ? '+' : '') . number_format_i18n($total_diff))
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
<div class="wpm-table-wrap">
|
||||||
|
<table class="wpm-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('Autor', 'wp-multi'); ?></th>
|
||||||
|
<th><?php echo esc_html($from); ?></th>
|
||||||
|
<th><?php echo esc_html($to); ?></th>
|
||||||
|
<th><?php _e('Differenz', 'wp-multi'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ($rows) : ?>
|
||||||
|
<?php foreach ($rows as $row) : ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<?php if (function_exists('wp_multi_get_guest_author_detail_url')) : ?>
|
||||||
|
<a href="<?php echo esc_url(wp_multi_get_guest_author_detail_url($author_base_url, $row['name'])); ?>"><?php echo esc_html($row['name']); ?></a>
|
||||||
|
<?php else : ?>
|
||||||
|
<?php echo esc_html($row['name']); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td><?php echo esc_html(number_format_i18n($row['from'])); ?></td>
|
||||||
|
<td><?php echo esc_html(number_format_i18n($row['to'])); ?></td>
|
||||||
|
<td>
|
||||||
|
<?php if ($row['diff'] < 0) : ?>
|
||||||
|
<span class="wpm-badge wpm-badge--danger"><?php echo esc_html(number_format_i18n($row['diff'])); ?></span>
|
||||||
|
<?php else : ?>
|
||||||
|
<span class="wpm-badge wpm-badge--success">+<?php echo esc_html(number_format_i18n($row['diff'])); ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else : ?>
|
||||||
|
<tr><td colspan="4"><?php _e('Kein Unterschied zwischen diesen Jahren.', 'wp-multi'); ?></td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p class="wpm-muted"><?php _e('Gezählt werden nur Geschichten mit hinterlegtem Gast-Autor. Beiträge ohne dieses Feld tauchen in der Jahresstatistik auf, hier aber nicht.', 'wp-multi'); ?></p>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
}
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Custom Shortcodes
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* custom shortcodes
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Funktion, um die Datenbanktabelle für Shortcodes zu erstellen
|
||||||
|
function wp_multi_create_shortcodes_table() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$table_name = $wpdb->prefix . 'wp_multi_shortcodes'; // Name der Tabelle mit Präfix
|
||||||
|
$charset_collate = $wpdb->get_charset_collate();
|
||||||
|
|
||||||
|
// SQL-Abfrage zum Erstellen der Tabelle
|
||||||
|
$sql = "CREATE TABLE $table_name (
|
||||||
|
id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
shortcode_name varchar(255) NOT NULL,
|
||||||
|
shortcode_content text NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE KEY shortcode_name (shortcode_name)
|
||||||
|
) $charset_collate;";
|
||||||
|
|
||||||
|
// Datenbank abfragen und ausführen
|
||||||
|
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
||||||
|
dbDelta( $sql );
|
||||||
|
}
|
||||||
|
register_activation_hook( WP_MULTI_FILE, 'wp_multi_create_shortcodes_table' );
|
||||||
|
|
||||||
|
// Der Shortcodes-Menüpunkt wird zentral in wp_multi_register_admin_menus() registriert.
|
||||||
|
|
||||||
|
// Callback-Funktion für das Shortcode-Verwaltungs-Interface
|
||||||
|
function wp_multi_shortcode_page() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$message = ''; // Variable für benutzerdefinierte Nachrichten
|
||||||
|
|
||||||
|
// Verarbeite das Speichern von Shortcodes
|
||||||
|
if (isset($_POST['wp_multi_shortcode_name']) && isset($_POST['wp_multi_shortcode_content'])) {
|
||||||
|
check_admin_referer('wp_multi_save_shortcode', 'wp_multi_shortcode_nonce');
|
||||||
|
|
||||||
|
// Hole die übermittelten Shortcodes
|
||||||
|
$name = sanitize_key($_POST['wp_multi_shortcode_name']);
|
||||||
|
$content = wp_kses_post(wp_unslash($_POST['wp_multi_shortcode_content']));
|
||||||
|
|
||||||
|
// Prüfen, ob der Shortcode bereits existiert
|
||||||
|
$existing_shortcode = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$wpdb->prefix}wp_multi_shortcodes WHERE shortcode_name = %s", $name));
|
||||||
|
|
||||||
|
if ($existing_shortcode) {
|
||||||
|
// Aktualisiere den Shortcode, falls er bereits existiert
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wp_multi_shortcodes',
|
||||||
|
['shortcode_content' => $content],
|
||||||
|
['shortcode_name' => $name]
|
||||||
|
);
|
||||||
|
$message = 'Shortcode wurde aktualisiert!';
|
||||||
|
} else {
|
||||||
|
// Andernfalls einen neuen Shortcode einfügen
|
||||||
|
$wpdb->insert(
|
||||||
|
$wpdb->prefix . 'wp_multi_shortcodes',
|
||||||
|
[
|
||||||
|
'shortcode_name' => $name,
|
||||||
|
'shortcode_content' => $content
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$message = 'Shortcode wurde hinzugefügt!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shortcode löschen
|
||||||
|
if (isset($_GET['delete_shortcode']) && !empty($_GET['delete_shortcode'])) {
|
||||||
|
check_admin_referer('wp_multi_delete_shortcode_' . intval($_GET['delete_shortcode']));
|
||||||
|
$delete_id = intval($_GET['delete_shortcode']);
|
||||||
|
$wpdb->delete(
|
||||||
|
$wpdb->prefix . 'wp_multi_shortcodes',
|
||||||
|
['id' => $delete_id]
|
||||||
|
);
|
||||||
|
$message = 'Shortcode wurde gelöscht!';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Holen der gespeicherten Shortcodes aus der Datenbank
|
||||||
|
$custom_shortcodes = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wp_multi_shortcodes");
|
||||||
|
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Shortcodes', 'wp-multi'),
|
||||||
|
__('Eigene Shortcodes erstellen, die im Editor eingefügt werden können', 'wp-multi')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!empty($message)) {
|
||||||
|
wpmt_admin_notice('success', $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Shortcode anlegen / aktualisieren', 'wp-multi'));
|
||||||
|
?>
|
||||||
|
<form method="post">
|
||||||
|
<?php wp_nonce_field('wp_multi_save_shortcode', 'wp_multi_shortcode_nonce'); ?>
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label" for="wp_multi_shortcode_name"><?php _e('Name des Shortcodes', 'wp-multi'); ?>
|
||||||
|
<small class="wpm-field__hint"><?php _e('Nur Kleinbuchstaben, Zahlen und Unterstriche.', 'wp-multi'); ?></small>
|
||||||
|
</label>
|
||||||
|
<input type="text" name="wp_multi_shortcode_name" id="wp_multi_shortcode_name" required />
|
||||||
|
</div>
|
||||||
|
<div class="wpm-field">
|
||||||
|
<label class="wpm-field__label" for="wp_multi_shortcode_content"><?php _e('Inhalt des Shortcodes', 'wp-multi'); ?></label>
|
||||||
|
<textarea name="wp_multi_shortcode_content" id="wp_multi_shortcode_content" rows="5" required></textarea>
|
||||||
|
</div>
|
||||||
|
<?php submit_button(__('Shortcode speichern', 'wp-multi')); ?>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Verfügbare Shortcodes', 'wp-multi'));
|
||||||
|
if (!empty($custom_shortcodes)) {
|
||||||
|
?>
|
||||||
|
<div class="wpm-table-wrap">
|
||||||
|
<table class="wpm-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('Shortcode', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('Inhalt', 'wp-multi'); ?></th>
|
||||||
|
<th><?php _e('Aktionen', 'wp-multi'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($custom_shortcodes as $shortcode) :
|
||||||
|
$delete_url = wp_nonce_url(
|
||||||
|
admin_url('admin.php?page=wp_multi_shortcodes&delete_shortcode=' . $shortcode->id),
|
||||||
|
'wp_multi_delete_shortcode_' . $shortcode->id
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td><code>[<?php echo esc_html($shortcode->shortcode_name); ?>]</code></td>
|
||||||
|
<td><?php echo esc_html(wp_trim_words($shortcode->shortcode_content, 15)); ?></td>
|
||||||
|
<td><a href="<?php echo esc_url($delete_url); ?>" class="wpm-btn wpm-btn--danger" onclick="return confirm('<?php esc_attr_e('Möchten Sie diesen Shortcode wirklich löschen?', 'wp-multi'); ?>');"><?php _e('Löschen', 'wp-multi'); ?></a></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
} else {
|
||||||
|
wpmt_admin_notice('info', __('Keine benutzerdefinierten Shortcodes gefunden.', 'wp-multi'));
|
||||||
|
}
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shortcode-Verwaltung: Ermöglicht Benutzern das Erstellen eigener Shortcodes
|
||||||
|
function wp_multi_register_custom_shortcodes() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Holen der gespeicherten Shortcodes aus der Datenbank
|
||||||
|
$custom_shortcodes = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wp_multi_shortcodes");
|
||||||
|
|
||||||
|
// Wenn keine benutzerdefinierten Shortcodes vorhanden sind, abbrechen
|
||||||
|
if (empty($custom_shortcodes)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Definiere die Shortcodes in WordPress
|
||||||
|
foreach ($custom_shortcodes as $shortcode) {
|
||||||
|
add_shortcode($shortcode->shortcode_name, function() use ($shortcode) {
|
||||||
|
return wp_kses_post($shortcode->shortcode_content);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('init', 'wp_multi_register_custom_shortcodes');
|
||||||
|
|
||||||
|
// Inhalt der Meta-Box anzeigen
|
||||||
|
function wp_multi_render_shortcode_meta_box($post) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Alle gespeicherten Shortcodes aus der Datenbank holen
|
||||||
|
$shortcodes = $wpdb->get_results("SELECT shortcode_name FROM {$wpdb->prefix}wp_multi_shortcodes");
|
||||||
|
|
||||||
|
if (!empty($shortcodes)) {
|
||||||
|
echo '<select id="wp_multi_shortcode_dropdown">';
|
||||||
|
echo '<option value="">-- Shortcode auswählen --</option>';
|
||||||
|
|
||||||
|
foreach ($shortcodes as $shortcode) {
|
||||||
|
echo '<option value="' . esc_attr($shortcode->shortcode_name) . '">' . esc_html($shortcode->shortcode_name) . '</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</select>';
|
||||||
|
echo '<button type="button" class="button button-primary" id="wp_multi_insert_shortcode">Einfügen</button>';
|
||||||
|
} else {
|
||||||
|
echo '<p>Keine Shortcodes vorhanden.</p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JavaScript für Meta-Box einbinden (Classic Editor: fügt den gewählten Shortcode ein)
|
||||||
|
function wp_multi_enqueue_admin_scripts($hook) {
|
||||||
|
if ('post.php' === $hook || 'post-new.php' === $hook) {
|
||||||
|
wp_enqueue_script('wp-multi-shortcode', plugin_dir_url(WP_MULTI_FILE) . 'js/classic-editor-shortcodes.js', array('jquery'), '1.0', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('admin_enqueue_scripts', 'wp_multi_enqueue_admin_scripts');
|
||||||
|
|
||||||
|
// Funktion zum Registrieren des TinyMCE Plugins
|
||||||
|
function wp_multi_add_shortcode_button() {
|
||||||
|
add_filter('mce_external_plugins', 'wp_multi_register_tinymce_plugin');
|
||||||
|
add_filter('mce_buttons', 'wp_multi_add_tinymce_button');
|
||||||
|
}
|
||||||
|
add_action('admin_head', 'wp_multi_add_shortcode_button');
|
||||||
|
|
||||||
|
// Plugin für TinyMCE registrieren (angepasster Pfad zum JS-File)
|
||||||
|
function wp_multi_register_tinymce_plugin($plugins) {
|
||||||
|
$plugins['wp_multi_shortcodes'] = plugin_dir_url(WP_MULTI_FILE) . 'js/tinymce-shortcodes.js';
|
||||||
|
return $plugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button zur TinyMCE Toolbar hinzufügen
|
||||||
|
function wp_multi_add_tinymce_button($buttons) {
|
||||||
|
array_push($buttons, 'wp_multi_shortcodes');
|
||||||
|
return $buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shortcodes aus der Datenbank für das JavaScript bereitstellen
|
||||||
|
function wp_multi_localize_shortcodes() {
|
||||||
|
global $wpdb;
|
||||||
|
$shortcodes = $wpdb->get_results("SELECT shortcode_name FROM {$wpdb->prefix}wp_multi_shortcodes", ARRAY_A);
|
||||||
|
|
||||||
|
// Shortcodes als JSON an das JS-File übergeben
|
||||||
|
wp_enqueue_script('wp-multi-tinymce', plugin_dir_url(WP_MULTI_FILE) . 'js/tinymce-shortcodes.js', array('jquery'), null, true);
|
||||||
|
wp_localize_script('wp-multi-tinymce', 'wpMultiShortcodes', $shortcodes);
|
||||||
|
}
|
||||||
|
add_action('admin_enqueue_scripts', 'wp_multi_localize_shortcodes');
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,583 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Statistik & Banner
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Statistik & Banner
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Funktion zum Einbinden von CSS direkt im Plugin-Code
|
||||||
|
function statistik_manager_inline_styles() {
|
||||||
|
?>
|
||||||
|
<style>
|
||||||
|
/* CSS für das Statistik Manager Plugin */
|
||||||
|
.statistik-manager-bookmarks {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.statistik-manager-bookmarks ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.statistik-manager-bookmarks li {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.statistik-manager-bookmarks a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #0073aa;
|
||||||
|
font-size: 1.6em; /* Größerer Text für den Titel */
|
||||||
|
}
|
||||||
|
.statistik-manager-bookmarks a:hover {
|
||||||
|
color: #005177;
|
||||||
|
}
|
||||||
|
.delete-bookmark-btn {
|
||||||
|
background-color: #f44336;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 10px; /* Abstand zwischen Button und Titel */
|
||||||
|
margin-bottom: 10px; /* Mehr Abstand nach unten */
|
||||||
|
}
|
||||||
|
.delete-bookmark-btn:hover {
|
||||||
|
background-color: #d32f2f;
|
||||||
|
}
|
||||||
|
.form-field {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.form-field label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.form-field input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.form-field button {
|
||||||
|
background-color: #0073aa;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.form-field button:hover {
|
||||||
|
background-color: #005177;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
add_action('wp_head', 'statistik_manager_inline_styles');
|
||||||
|
|
||||||
|
// Font Awesome einbinden
|
||||||
|
function statistik_manager_enqueue_fontawesome() {
|
||||||
|
wp_enqueue_style('fontawesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css', [], null);
|
||||||
|
}
|
||||||
|
add_action('wp_enqueue_scripts', 'statistik_manager_enqueue_fontawesome');
|
||||||
|
|
||||||
|
// Sprachdateien laden
|
||||||
|
function statistik_manager_load_textdomain() {
|
||||||
|
load_plugin_textdomain('statistik-manager', false, dirname(plugin_basename(WP_MULTI_FILE)) . '/languages');
|
||||||
|
}
|
||||||
|
add_action('plugins_loaded', 'statistik_manager_load_textdomain');
|
||||||
|
|
||||||
|
// Funktion zum Abrufen der Statistiken
|
||||||
|
function statistik_manager_get_statistics() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Beiträge
|
||||||
|
$posts_count = wp_count_posts()->publish;
|
||||||
|
|
||||||
|
// Kommentare
|
||||||
|
$comments_count = wp_count_comments()->total_comments;
|
||||||
|
|
||||||
|
// Kategorien
|
||||||
|
$selected_categories = get_option('statistik_manager_selected_categories', []);
|
||||||
|
$categories_count = 0;
|
||||||
|
|
||||||
|
if (!empty($selected_categories)) {
|
||||||
|
$categories_count = count($selected_categories);
|
||||||
|
} else {
|
||||||
|
// Alle Kategorien zählen, wenn keine ausgewählt wurden
|
||||||
|
$categories_count = wp_count_terms('category');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serien (angenommen, Serien sind benutzerdefinierte Taxonomie)
|
||||||
|
$series_count = 0;
|
||||||
|
if (taxonomy_exists('series')) {
|
||||||
|
$count = wp_count_terms('series');
|
||||||
|
$series_count = is_wp_error($count) ? 0 : (int) $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eröffnungsdatum
|
||||||
|
$opening_date = get_option('statistik_manager_opening_date', '');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'posts_count' => $posts_count,
|
||||||
|
'comments_count' => $comments_count,
|
||||||
|
'categories_count' => $categories_count,
|
||||||
|
'series_count' => $series_count,
|
||||||
|
'opening_date' => $opening_date
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Banner-Funktion
|
||||||
|
function statistik_manager_display_banner($position) {
|
||||||
|
static $rendered = false;
|
||||||
|
if ($rendered) {
|
||||||
|
return; // verhindert doppelte Ausgabe (wp_body_open + Footer-Fallback)
|
||||||
|
}
|
||||||
|
if (!get_option('statistik_manager_banner_enabled', 1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$banner_text = get_option('statistik_manager_banner_text', 'Willkommen auf unserer Webseite!');
|
||||||
|
$banner_color = get_option('statistik_manager_banner_color', '#0073aa');
|
||||||
|
$banner_position = get_option('statistik_manager_banner_position', 'top');
|
||||||
|
$font_size = get_option('statistik_manager_banner_font_size', 'medium');
|
||||||
|
$banner_icon = get_option('statistik_manager_banner_icon', 'fas fa-info-circle'); // Standard-Icon
|
||||||
|
$show_icon = get_option('statistik_manager_show_icon', 1); // Option zur Aktivierung des Icons
|
||||||
|
|
||||||
|
// Schriftgröße je nach Auswahl setzen
|
||||||
|
switch ($font_size) {
|
||||||
|
case 'small':
|
||||||
|
$font_size_css = '14px';
|
||||||
|
break;
|
||||||
|
case 'medium':
|
||||||
|
$font_size_css = '18px';
|
||||||
|
break;
|
||||||
|
case 'large':
|
||||||
|
$font_size_css = '24px';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$font_size_css = '18px';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($banner_position !== $position) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rendered = true;
|
||||||
|
|
||||||
|
// Standard-Styles für das Banner (auto-Höhe, zentriert, umbruchfähig).
|
||||||
|
// !important, damit aktive Themes (z. B. Sueva) Farbe/Breite/Position
|
||||||
|
// nicht per eigener !important-Regel überschreiben können.
|
||||||
|
// top/bottom bewusst ohne !important: wird per Script unter fixe Leisten gesetzt.
|
||||||
|
$vertical_imp = ($position === 'bottom') ? 'bottom: 0;' : 'top: 0;';
|
||||||
|
$style = "background-color: " . esc_attr($banner_color) . " !important;
|
||||||
|
color: #fff !important;
|
||||||
|
text-align: center !important;
|
||||||
|
padding: 12px 16px !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
z-index: 99990 !important;
|
||||||
|
position: fixed !important;
|
||||||
|
left: 0 !important;
|
||||||
|
$vertical_imp
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
gap: 8px !important;";
|
||||||
|
|
||||||
|
$text_style = "font-size: " . esc_attr($font_size_css) . " !important; color: #fff !important; margin: 0 !important; line-height: 1.4 !important;";
|
||||||
|
$icon_style = "font-size: 24px !important; margin-right: 8px !important; color: #fff !important;"; // Feste Größe für Icon
|
||||||
|
|
||||||
|
echo '<div class="statistik-manager-banner" id="statistik-manager-banner" style="' . esc_attr($style) . '">';
|
||||||
|
|
||||||
|
// Icon nur anzeigen, wenn gewünscht
|
||||||
|
if ($show_icon && !empty($banner_icon)) {
|
||||||
|
echo '<i class="' . esc_attr($banner_icon) . '" style="' . esc_attr($icon_style) . '"></i>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<span style="' . esc_attr($text_style) . '">' . esc_html($banner_text) . '</span>';
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Banner an der richtigen Stelle einhängen – abhängig von der konfigurierten
|
||||||
|
// Position. Nur wenn das Banner auch wirklich aktiviert ist.
|
||||||
|
function statistik_manager_register_banner() {
|
||||||
|
if (!get_option('statistik_manager_banner_enabled', 1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pos = get_option('statistik_manager_banner_position', 'top');
|
||||||
|
|
||||||
|
if ($pos === 'bottom') {
|
||||||
|
// Unten: im Footer ausgeben
|
||||||
|
add_action('wp_footer', function() {
|
||||||
|
statistik_manager_display_banner('bottom');
|
||||||
|
}, 10);
|
||||||
|
} else {
|
||||||
|
// Oben: bevorzugt direkt nach <body>, plus Footer-Fallback für Themes
|
||||||
|
// ohne wp_body_open (die statische Sperre verhindert Doppelausgabe).
|
||||||
|
add_action('wp_body_open', function() {
|
||||||
|
statistik_manager_display_banner('top');
|
||||||
|
});
|
||||||
|
add_action('wp_footer', function() {
|
||||||
|
statistik_manager_display_banner('top');
|
||||||
|
}, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstand für den Seiteninhalt, damit das fixe Banner nichts verdeckt
|
||||||
|
add_action('wp_head', 'statistik_manager_banner_offset');
|
||||||
|
|
||||||
|
// Am Seitenende sofort nachrechnen – dann sind Banner und fixe Leisten
|
||||||
|
// (z. B. ShiftNav) garantiert im DOM, auch wenn DOMContentLoaded spät kommt.
|
||||||
|
add_action('wp_footer', function() {
|
||||||
|
echo '<script>window.statistikManagerBannerAdjust&&window.statistikManagerBannerAdjust();</script>';
|
||||||
|
}, 999);
|
||||||
|
}
|
||||||
|
add_action('wp', 'statistik_manager_register_banner');
|
||||||
|
|
||||||
|
// Fügt NUR bei aktivem Banner den passenden Abstand hinzu – auf der richtigen
|
||||||
|
// Seite (oben/unten) und mit Rücksicht auf die WordPress-Admin-Leiste.
|
||||||
|
function statistik_manager_banner_offset() {
|
||||||
|
if (!get_option('statistik_manager_banner_enabled', 1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pos = get_option('statistik_manager_banner_position', 'top');
|
||||||
|
$h = 56; // ungefähre Bannerhöhe – nur für den No-JS-Fallback
|
||||||
|
|
||||||
|
// Fallback ohne JavaScript (WordPress verschiebt die Seite für die
|
||||||
|
// Admin-Leiste bereits selbst per html-margin, daher hier nicht addieren).
|
||||||
|
echo '<noscript><style>';
|
||||||
|
if ($pos === 'bottom') {
|
||||||
|
echo 'body { padding-bottom: ' . $h . 'px !important; }';
|
||||||
|
} else {
|
||||||
|
echo 'body { padding-top: ' . $h . 'px !important; }';
|
||||||
|
if (is_admin_bar_showing()) {
|
||||||
|
echo '#statistik-manager-banner { top: 32px !important; }';
|
||||||
|
echo '@media screen and (max-width: 782px) { #statistik-manager-banner { top: 46px !important; } }';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo '</style></noscript>';
|
||||||
|
|
||||||
|
// Mit JavaScript: Banner exakt unter fixe Leisten oben setzen (WP-Admin-Leiste,
|
||||||
|
// ShiftNav-Menüleiste), sonst verdecken diese es. Der Inhalt wird nur um die
|
||||||
|
// tatsächliche Bannerhöhe verschoben – zusätzlich zum vorhandenen Abstand
|
||||||
|
// des Themes, damit das Layout ansonsten unverändert bleibt.
|
||||||
|
$is_bottom = ($pos === 'bottom') ? 'true' : 'false';
|
||||||
|
echo '<script>(function(){'
|
||||||
|
. 'var isBottom=' . $is_bottom . ';'
|
||||||
|
. 'var bars=["wpadminbar","shiftnav-toggle-main"];'
|
||||||
|
. 'function adjust(){'
|
||||||
|
. 'var b=document.getElementById("statistik-manager-banner");'
|
||||||
|
. 'if(!b){return;}'
|
||||||
|
. 'var s=document.body.style,prop=isBottom?"padding-bottom":"padding-top";'
|
||||||
|
. 's.removeProperty(prop);'
|
||||||
|
. 'var base=parseFloat(getComputedStyle(document.body)[isBottom?"paddingBottom":"paddingTop"])||0;'
|
||||||
|
. 'var extra=0;'
|
||||||
|
. 'if(!isBottom){'
|
||||||
|
. 'var off=0;'
|
||||||
|
. 'bars.forEach(function(id){'
|
||||||
|
. 'var e=document.getElementById(id);if(!e){return;}'
|
||||||
|
. 'var c=getComputedStyle(e);'
|
||||||
|
. 'if(c.position!=="fixed"||c.display==="none"||c.visibility==="hidden"){return;}'
|
||||||
|
. 'var r=e.getBoundingClientRect();'
|
||||||
|
. 'if(r.height>0&&r.bottom>off){off=r.bottom;}'
|
||||||
|
. '});'
|
||||||
|
. 'b.style.setProperty("top",off+"px","important");'
|
||||||
|
// Inhalt komplett unter Leisten + Banner beginnen lassen; die Admin-Leiste
|
||||||
|
// hat WordPress per html-margin bereits berücksichtigt.
|
||||||
|
. 'var hm=parseFloat(getComputedStyle(document.documentElement).marginTop)||0;'
|
||||||
|
. 'extra=Math.max(0,off-hm);'
|
||||||
|
. '}'
|
||||||
|
. 's.setProperty(prop,(base+extra+b.offsetHeight)+"px","important");'
|
||||||
|
. '}'
|
||||||
|
. 'window.statistikManagerBannerAdjust=adjust;'
|
||||||
|
. 'function watch(){'
|
||||||
|
. 'adjust();'
|
||||||
|
// Nachzügler abfangen (verzögert geladene CSS/JS anderer Plugins)
|
||||||
|
. '[300,1000,2500,5000].forEach(function(t){setTimeout(adjust,t);});'
|
||||||
|
. 'if(window.ResizeObserver){'
|
||||||
|
. 'var ro=new ResizeObserver(function(){adjust();});'
|
||||||
|
. '["statistik-manager-banner"].concat(bars).forEach(function(id){var e=document.getElementById(id);if(e){ro.observe(e);}});'
|
||||||
|
. '}'
|
||||||
|
. '}'
|
||||||
|
. 'if(document.readyState!=="loading"){watch();}else{document.addEventListener("DOMContentLoaded",watch);}'
|
||||||
|
. 'window.addEventListener("load",adjust);'
|
||||||
|
. 'window.addEventListener("resize",adjust);'
|
||||||
|
. '})();</script>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shortcode für die Anzeige der Statistiken
|
||||||
|
function statistik_manager_shortcode() {
|
||||||
|
$statistics = statistik_manager_get_statistics();
|
||||||
|
$webseitenname = get_bloginfo('name');
|
||||||
|
|
||||||
|
$output = '<div class="statistik-manager">';
|
||||||
|
$output .= '<h3>Statistikübersicht</h3>';
|
||||||
|
$output .= '<div class="statistik-items">';
|
||||||
|
|
||||||
|
if (get_option('statistik_manager_show_posts')) {
|
||||||
|
$output .= '<div class="stat-item"><i class="fas fa-file-alt"></i>';
|
||||||
|
$output .= '<p><strong>Beiträge:</strong></br> ' . esc_html($statistics['posts_count']) . '</p></div>';
|
||||||
|
}
|
||||||
|
if (get_option('statistik_manager_show_comments')) {
|
||||||
|
$output .= '<div class="stat-item"><i class="fas fa-comments"></i>';
|
||||||
|
$output .= '<p><strong>Kommentare:</strong></br> ' . esc_html($statistics['comments_count']) . '</p></div>';
|
||||||
|
}
|
||||||
|
if (get_option('statistik_manager_show_categories')) {
|
||||||
|
$output .= '<div class="stat-item"><i class="fas fa-th-list"></i>';
|
||||||
|
$output .= '<p><strong>Kategorien:</strong></br> ' . esc_html($statistics['categories_count']) . '</p></div>';
|
||||||
|
}
|
||||||
|
if (get_option('statistik_manager_show_series')) {
|
||||||
|
$output .= '<div class="stat-item"><i class="fas fa-tv"></i>';
|
||||||
|
$output .= '<p><strong>Serien:</strong></br> ' . esc_html($statistics['series_count']) . '</p></div>';
|
||||||
|
}
|
||||||
|
$output .= '</div>';
|
||||||
|
|
||||||
|
// Eröffnungsdatum anzeigen, wenn gesetzt
|
||||||
|
if (!empty($statistics['opening_date'])) {
|
||||||
|
$formatted_date = date('d.m.Y', strtotime($statistics['opening_date']));
|
||||||
|
$output .= '<div class="stat-opening-date">';
|
||||||
|
$output .= '<i class="fas fa-calendar-alt"></i>';
|
||||||
|
$output .= '<p><strong>' . sprintf(__('%s wurde am %s eröffnet.', 'statistik-manager'), esc_html($webseitenname), esc_html($formatted_date)) . '</strong></p>';
|
||||||
|
$output .= '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$output .= '</div>';
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_shortcode('statistik_manager', 'statistik_manager_shortcode');
|
||||||
|
|
||||||
|
// Admin-Panel CSS einbinden, nur auf der Plugin-Seite
|
||||||
|
// Das frühere seitenspezifische admin-style.css wurde durch das gemeinsame
|
||||||
|
// Design-System (wp-multi-toolkit/assets/admin.css) ersetzt.
|
||||||
|
|
||||||
|
// Frontend CSS nur einbinden, wenn der Shortcode verwendet wird
|
||||||
|
function statistik_manager_enqueue_frontend_styles() {
|
||||||
|
if (has_shortcode(get_post()->post_content, 'statistik_manager')) {
|
||||||
|
wp_enqueue_style('statistik-manager-frontend-style', plugins_url('css/style.css', WP_MULTI_FILE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('wp_enqueue_scripts', 'statistik_manager_enqueue_frontend_styles');
|
||||||
|
|
||||||
|
// Der Menüpunkt "Statistik & Notice" wird zentral in wp_multi_register_admin_menus() registriert.
|
||||||
|
|
||||||
|
// Funktion für die Plugin-Optionen-Seite
|
||||||
|
function statistik_manager_options_page() {
|
||||||
|
wpmt_admin_page_open(
|
||||||
|
__('Statistik & Notice', 'statistik-manager'),
|
||||||
|
__('Statistik-Shortcode, Lesezeichen und Frontend-Banner konfigurieren', 'statistik-manager')
|
||||||
|
);
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Einstellungen', 'statistik-manager'));
|
||||||
|
?>
|
||||||
|
<form method="post" action="options.php">
|
||||||
|
<?php
|
||||||
|
settings_fields('statistik_manager_settings_group');
|
||||||
|
do_settings_sections('statistik_manager');
|
||||||
|
?>
|
||||||
|
<h2><?php _e('Statistiken anzeigen', 'statistik-manager'); ?></h2>
|
||||||
|
<table class="form-table">
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row"><?php _e('Anzeigen', 'statistik-manager'); ?></th>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" name="statistik_manager_show_posts" value="1" <?php checked(get_option('statistik_manager_show_posts'), 1); ?> />
|
||||||
|
<label for="statistik_manager_show_posts"><?php _e('Beiträge anzeigen', 'statistik-manager'); ?></label><br>
|
||||||
|
<input type="checkbox" name="statistik_manager_show_comments" value="1" <?php checked(get_option('statistik_manager_show_comments'), 1); ?> />
|
||||||
|
<label for="statistik_manager_show_comments"><?php _e('Kommentare anzeigen', 'statistik-manager'); ?></label><br>
|
||||||
|
<input type="checkbox" name="statistik_manager_show_categories" value="1" <?php checked(get_option('statistik_manager_show_categories'), 1); ?> />
|
||||||
|
<label for="statistik_manager_show_categories"><?php _e('Kategorien anzeigen', 'statistik-manager'); ?></label><br>
|
||||||
|
<input type="checkbox" name="statistik_manager_show_series" value="1" <?php checked(get_option('statistik_manager_show_series'), 1); ?> />
|
||||||
|
<label for="statistik_manager_show_series"><?php _e('Serien anzeigen', 'statistik-manager'); ?></label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2><?php _e('Kategorien auswählen', 'statistik-manager'); ?></h2>
|
||||||
|
<table class="form-table">
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row"><?php _e('Angezeigte Kategorien', 'statistik-manager'); ?></th>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
$categories = get_terms(array(
|
||||||
|
'taxonomy' => 'category',
|
||||||
|
'orderby' => 'name',
|
||||||
|
'order' => 'ASC',
|
||||||
|
'hide_empty' => false,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!empty($categories) && !is_wp_error($categories)) :
|
||||||
|
$selected_categories = get_option('statistik_manager_selected_categories', []);
|
||||||
|
?>
|
||||||
|
<select name="statistik_manager_selected_categories[]" multiple="multiple" style="width: 300px; height: 150px;">
|
||||||
|
<?php foreach ($categories as $category) : ?>
|
||||||
|
<option value="<?php echo esc_attr($category->term_id); ?>"
|
||||||
|
<?php echo in_array($category->term_id, $selected_categories) ? 'selected' : ''; ?>>
|
||||||
|
<?php echo esc_html($category->name); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2><?php _e('Eröffnungsdatum der Webseite', 'statistik-manager'); ?></h2>
|
||||||
|
<table class="form-table">
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row"><?php _e('Eröffnungsdatum', 'statistik-manager'); ?></th>
|
||||||
|
<td>
|
||||||
|
<input type="date" name="statistik_manager_opening_date" value="<?php echo esc_attr(get_option('statistik_manager_opening_date')); ?>" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Banner-Einstellungen</h2>
|
||||||
|
<table class="form-table">
|
||||||
|
<tr>
|
||||||
|
<th>Banner anzeigen</th>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" name="statistik_manager_banner_enabled" value="1" <?php checked(get_option('statistik_manager_banner_enabled', 1)); ?> />
|
||||||
|
<label for="statistik_manager_banner_enabled">Banner aktivieren</label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Banner Text</th>
|
||||||
|
<td><input type="text" name="statistik_manager_banner_text" value="<?php echo esc_attr(get_option('statistik_manager_banner_text', 'Willkommen auf unserer Webseite!')); ?>" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Banner Farbe</th>
|
||||||
|
<td><input type="color" name="statistik_manager_banner_color" value="<?php echo esc_attr(get_option('statistik_manager_banner_color', '#0073aa')); ?>" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Banner Position</th>
|
||||||
|
<td>
|
||||||
|
<select name="statistik_manager_banner_position">
|
||||||
|
<option value="top" <?php selected(get_option('statistik_manager_banner_position'), 'top'); ?>>Oben</option>
|
||||||
|
<option value="bottom" <?php selected(get_option('statistik_manager_banner_position'), 'bottom'); ?>>Unten</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Icon auswählen</th>
|
||||||
|
<td>
|
||||||
|
<select name="statistik_manager_banner_icon">
|
||||||
|
<option value="fas fa-info-circle" <?php selected(get_option('statistik_manager_banner_icon'), 'fas fa-info-circle'); ?>>ℹ️ Info</option>
|
||||||
|
<option value="fas fa-exclamation-triangle" <?php selected(get_option('statistik_manager_banner_icon'), 'fas fa-exclamation-triangle'); ?>>⚠️ Warnung</option>
|
||||||
|
<option value="fas fa-bell" <?php selected(get_option('statistik_manager_banner_icon'), 'fas fa-bell'); ?>>🔔 Benachrichtigung</option>
|
||||||
|
<option value="fas fa-thumbs-up" <?php selected(get_option('statistik_manager_banner_icon'), 'fas fa-thumbs-up'); ?>>👍 Daumen hoch</option>
|
||||||
|
<option value="">Kein Icon</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Icon anzeigen</th>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" name="statistik_manager_show_icon" value="1" <?php checked(get_option('statistik_manager_show_icon', 1)); ?> />
|
||||||
|
<label for="statistik_manager_show_icon">Icon anzeigen</label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Schriftgröße</th>
|
||||||
|
<td>
|
||||||
|
<select name="statistik_manager_banner_font_size">
|
||||||
|
<option value="small" <?php selected(get_option('statistik_manager_banner_font_size'), 'small'); ?>>Klein</option>
|
||||||
|
<option value="medium" <?php selected(get_option('statistik_manager_banner_font_size'), 'medium'); ?>>Mittel</option>
|
||||||
|
<option value="large" <?php selected(get_option('statistik_manager_banner_font_size'), 'large'); ?>>Groß</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<?php submit_button(); ?>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
echo '<div class="wpm-grid wpm-grid--2">';
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Kurzanleitung: Statistik-Box', 'statistik-manager'));
|
||||||
|
?>
|
||||||
|
<ol>
|
||||||
|
<li><strong>Shortcode einfügen:</strong> Füge <code>[statistik_manager]</code> an der gewünschten Stelle in einem Beitrag oder einer Seite ein.</li>
|
||||||
|
<li><strong>Statistiken wählen:</strong> Beiträge, Kommentare, Kategorien und Serien lassen sich einzeln ein- und ausblenden.</li>
|
||||||
|
<li><strong>Kategorien:</strong> Wähle aus, welche Kategorien in die Zählung einbezogen werden.</li>
|
||||||
|
<li><strong>Eröffnungsdatum:</strong> Wird automatisch in der Statistik-Box angezeigt.</li>
|
||||||
|
<li><strong>Banner:</strong> Ein konfigurierbares Hinweis-Banner kann im Header oder Footer der Website eingeblendet werden.</li>
|
||||||
|
</ol>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
wpmt_admin_card_open(__('Kurzanleitung: Lesezeichen', 'statistik-manager'));
|
||||||
|
?>
|
||||||
|
<ol>
|
||||||
|
<li><strong>Anzeigen:</strong> Der Shortcode <code>[display_bookmarks]</code> listet die gespeicherten Lesezeichen der Besucher auf.</li>
|
||||||
|
<li><strong>Hinzufügen:</strong> Über <code>[add_bookmark]</code> erhalten Besucher ein Formular zum Speichern eines Lesezeichens.</li>
|
||||||
|
<li><strong>Löschen:</strong> Nur der Besitzer eines Lesezeichens kann es über den Löschen-Button entfernen.</li>
|
||||||
|
</ol>
|
||||||
|
<p><?php printf(__('Weitere Plugins der WP-Multi-Familie findest du auf %s.', 'statistik-manager'), '<a href="https://git.viper.ipv64.net/M_Viper" target="_blank">git.viper.ipv64.net</a>'); ?></p>
|
||||||
|
<?php
|
||||||
|
wpmt_admin_card_close();
|
||||||
|
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
|
wpmt_admin_page_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionen registrieren
|
||||||
|
function statistik_manager_register_settings() {
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_show_posts');
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_show_comments');
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_show_categories');
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_show_series');
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_selected_categories');
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_opening_date');
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_banner_text');
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_banner_color', [
|
||||||
|
'sanitize_callback' => 'sanitize_hex_color',
|
||||||
|
]);
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_banner_position');
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_banner_enabled');
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_banner_icon', [
|
||||||
|
'sanitize_callback' => function($value) {
|
||||||
|
// Icon-Wert besteht aus mehreren space-getrennten CSS-Klassen (z. B. "fas fa-bell")
|
||||||
|
$classes = array_map('sanitize_html_class', explode(' ', (string) $value));
|
||||||
|
return implode(' ', array_filter($classes));
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_show_icon');
|
||||||
|
register_setting('statistik_manager_settings_group', 'statistik_manager_banner_font_size');
|
||||||
|
|
||||||
|
}
|
||||||
|
add_action('admin_init', 'statistik_manager_register_settings');
|
||||||
|
|
||||||
|
// Standardwerte setzen
|
||||||
|
function statistik_manager_set_default_options() {
|
||||||
|
if (get_option('statistik_manager_show_posts') === false) {
|
||||||
|
update_option('statistik_manager_show_posts', 1);
|
||||||
|
}
|
||||||
|
if (get_option('statistik_manager_show_comments') === false) {
|
||||||
|
update_option('statistik_manager_show_comments', 1);
|
||||||
|
}
|
||||||
|
if (get_option('statistik_manager_show_categories') === false) {
|
||||||
|
update_option('statistik_manager_show_categories', 1);
|
||||||
|
}
|
||||||
|
if (get_option('statistik_manager_show_series') === false) {
|
||||||
|
update_option('statistik_manager_show_series', 1);
|
||||||
|
}
|
||||||
|
if (get_option('statistik_manager_selected_categories') === false) {
|
||||||
|
update_option('statistik_manager_selected_categories', []);
|
||||||
|
}
|
||||||
|
if (get_option('statistik_manager_banner_enabled') === false) {
|
||||||
|
update_option('statistik_manager_banner_enabled', 1);
|
||||||
|
}
|
||||||
|
if (get_option('statistik_manager_banner_font_size') === false) {
|
||||||
|
update_option('statistik_manager_banner_font_size', 'medium');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
add_action('admin_init', 'statistik_manager_set_default_options');
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WP Multi – Modul: Admin-Dashboard Update-Anzeige
|
||||||
|
*
|
||||||
|
* Automatisch aus wp-multi.php ausgelagert. Wird über den Loader in
|
||||||
|
* wp-multi.php eingebunden (nur wenn das WP Multi Toolkit aktiv ist).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) { exit; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Admin Dashboard Update-Anzeige
|
||||||
|
*/
|
||||||
|
|
||||||
|
define('WP_MULTI_UPDATE_REPO', 'wp-multi');
|
||||||
|
define('WP_MULTI_UPDATE_CACHE_KEY', 'wp_multi_latest_release');
|
||||||
|
define('WP_MULTI_UPDATE_HOST', 'git.viper.ipv64.net');
|
||||||
|
|
||||||
|
// Fragt den Gitea-Server nach dem neuesten Release ab (gecached via Transient)
|
||||||
|
function wp_multi_get_latest_release() {
|
||||||
|
$cached = get_transient(WP_MULTI_UPDATE_CACHE_KEY);
|
||||||
|
if ($cached !== false) {
|
||||||
|
return $cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
$api_url = 'https://' . WP_MULTI_UPDATE_HOST . '/api/v1/repos/M_Viper/' . WP_MULTI_UPDATE_REPO . '/releases';
|
||||||
|
$response = wp_remote_get($api_url, array(
|
||||||
|
'timeout' => 10,
|
||||||
|
'headers' => array('Accept' => 'application/json'),
|
||||||
|
));
|
||||||
|
|
||||||
|
if (is_wp_error($response)) {
|
||||||
|
// Kurz erneut versuchen statt für 12 Stunden zu sperren
|
||||||
|
set_transient(WP_MULTI_UPDATE_CACHE_KEY, false, HOUR_IN_SECONDS);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = wp_remote_retrieve_body($response);
|
||||||
|
$releases = json_decode($body, true);
|
||||||
|
|
||||||
|
if (empty($releases) || !is_array($releases)) {
|
||||||
|
set_transient(WP_MULTI_UPDATE_CACHE_KEY, false, HOUR_IN_SECONDS);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($releases, function ($a, $b) {
|
||||||
|
$a_date = !empty($a['created_at']) ? $a['created_at'] : '';
|
||||||
|
$b_date = !empty($b['created_at']) ? $b['created_at'] : '';
|
||||||
|
return strtotime($b_date) - strtotime($a_date);
|
||||||
|
});
|
||||||
|
|
||||||
|
$latest = false;
|
||||||
|
foreach ($releases as $release) {
|
||||||
|
if (!empty($release['prerelease'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!empty($release['tag_name'])) {
|
||||||
|
// Nur Download-URLs auf dem erwarteten Host akzeptieren
|
||||||
|
$html_url = !empty($release['html_url']) ? $release['html_url'] : '';
|
||||||
|
$release_host = $html_url ? wp_parse_url($html_url, PHP_URL_HOST) : '';
|
||||||
|
$latest = array(
|
||||||
|
'version' => ltrim($release['tag_name'], 'v'),
|
||||||
|
'url' => ($release_host === WP_MULTI_UPDATE_HOST)
|
||||||
|
? esc_url_raw($html_url)
|
||||||
|
: 'https://' . WP_MULTI_UPDATE_HOST . '/M_Viper/' . WP_MULTI_UPDATE_REPO . '/releases',
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_transient(WP_MULTI_UPDATE_CACHE_KEY, $latest, 12 * HOUR_IN_SECONDS);
|
||||||
|
return $latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Liest die installierte Version aus dem Plugin-Header
|
||||||
|
function wp_multi_get_installed_version() {
|
||||||
|
if (!function_exists('get_plugin_data')) {
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||||
|
}
|
||||||
|
$data = get_plugin_data(WP_MULTI_FILE, false, false);
|
||||||
|
return !empty($data['Version']) ? $data['Version'] : '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dashboard-Widget registrieren
|
||||||
|
function wp_multi_add_update_dashboard_widget() {
|
||||||
|
wp_add_dashboard_widget(
|
||||||
|
'wp_multi_update_widget',
|
||||||
|
__('WP Multi - Update-Status', 'wp-multi'),
|
||||||
|
'wp_multi_update_dashboard_widget_content'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
add_action('wp_dashboard_setup', 'wp_multi_add_update_dashboard_widget');
|
||||||
|
|
||||||
|
// Zeigt im Dashboard-Widget an, ob WP Multi aktuell ist oder ein Update verfügbar ist
|
||||||
|
function wp_multi_update_dashboard_widget_content() {
|
||||||
|
$installed_version = wp_multi_get_installed_version();
|
||||||
|
$latest = wp_multi_get_latest_release();
|
||||||
|
|
||||||
|
echo '<p><strong>' . esc_html__('Installierte Version:', 'wp-multi') . '</strong> ' . esc_html($installed_version) . '</p>';
|
||||||
|
|
||||||
|
if (!$latest || empty($latest['version'])) {
|
||||||
|
echo '<p>' . esc_html__('Der Update-Server konnte nicht erreicht werden.', 'wp-multi') . '</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version_compare($latest['version'], $installed_version, '>')) {
|
||||||
|
echo '<p style="color:#dc3545;"><strong>' . esc_html__('Update verfügbar:', 'wp-multi') . '</strong> ' . esc_html($latest['version']) . '</p>';
|
||||||
|
echo '<p><a href="' . esc_url($latest['url']) . '" target="_blank" class="button button-primary">' . esc_html__('Update herunterladen', 'wp-multi') . '</a></p>';
|
||||||
|
} else {
|
||||||
|
echo '<p style="color:#28a745;"><strong>' . esc_html__('WP Multi ist aktuell.', 'wp-multi') . '</strong></p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Admin-Hinweis anzeigen, wenn ein Update verfügbar ist
|
||||||
|
function wp_multi_update_admin_notice() {
|
||||||
|
if (!current_user_can('manage_options') || !is_admin()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$installed_version = wp_multi_get_installed_version();
|
||||||
|
$latest = wp_multi_get_latest_release();
|
||||||
|
|
||||||
|
if (!$latest || empty($latest['version'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version_compare($latest['version'], $installed_version, '>')) {
|
||||||
|
printf(
|
||||||
|
'<div class="notice notice-warning is-dismissible"><p>%1$s <a href="%2$s" target="_blank">%3$s</a></p></div>',
|
||||||
|
sprintf(
|
||||||
|
esc_html__('Für "WP Multi" ist ein Update verfügbar: Version %1$s (installiert: %2$s).', 'wp-multi'),
|
||||||
|
esc_html($latest['version']),
|
||||||
|
esc_html($installed_version)
|
||||||
|
),
|
||||||
|
esc_url($latest['url']),
|
||||||
|
esc_html__('Jetzt herunterladen', 'wp-multi')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_action('admin_notices', 'wp_multi_update_admin_notice');
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
Aktuell eingebaut
|
||||||
|
|
||||||
|
1 - Statistik shortcode = [statistik_manager]
|
||||||
|
2 - Telegram Benachrichtigung
|
||||||
|
3 - Discord Benachrichtigung
|
||||||
|
4 - Custom Admin Links
|
||||||
|
5 - Gast-Autor
|
||||||
|
6 - Beitrags Report
|
||||||
|
7 - Custom Textbox (am ende eines Beitrags)
|
||||||
|
8 - Banner für Nachrichten
|
||||||
|
9 - Admin Dashboard Update Anzeige
|
||||||
|
10 - Gast Lesezeichen shortcode = [add_bookmark] & [display_bookmarks]
|
||||||
|
11 - custom Shortcode (können per Auswahl im Editor eingefügt werden)
|
||||||
|
12 - Benutzer für Kommentar Sperren
|
||||||
|
13 - Filter für Rufnummer, Mailadressen, URL, IP und schimpfwöter
|
||||||
|
14 - Besucher Analytics
|
||||||
|
15 - Pinwand
|
||||||
|
16 - Schutz vor Brute-Force-Angriffen
|
||||||
|
17 - Anti Spam Honey
|
||||||
|
18 - Auto Tag
|
||||||
|
19 - Login deaktivieren
|
||||||
|
20 - Text Copy schutz
|
||||||
|
21 - Sperre Trash Mail Adressen
|
||||||
|
22 - Inhaltsverzeichnis shortcode = [alphabetical_index]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Statistik:
|
||||||
|
Zeigt folgende Statistik an
|
||||||
|
- Anzahl der Beiträge
|
||||||
|
- Anzahl der Kommentare
|
||||||
|
- Anzahl der Kategorien
|
||||||
|
- Anzahl der Serien
|
||||||
|
|
||||||
|
Eröffnung der Webseite
|
||||||
|
Shortcode für die Seite:
|
||||||
|
[statistik_manager]
|
||||||
|
|
||||||
|
Telegram Benachrichtigung & Discord Benachrichtigung:
|
||||||
|
Schickt eine Benachrichtigung bei einem Neuen Beitrag
|
||||||
|
|
||||||
|
Custom Admin Links:
|
||||||
|
Fügt eigene Links im Adminpanel ein
|
||||||
|
|
||||||
|
Gast-Autor:
|
||||||
|
Name des Autoren kann hier eingegeben werden der wird auch im Frontend im Beitrag angezeigt
|
||||||
|
|
||||||
|
Beitrags Report:
|
||||||
|
Unangemessene oder Falsche Beiträge mit Grund an den Seiten Admin Melden (Der Admin sieht die Reports in einer Liste im AdminPanel)
|
||||||
|
|
||||||
|
Custom Textbox (fügt bei jedem Beitrag eine Box hinzu in dem Vordefinierte Teste Angezeigt werden (Texte können im AdminPanel hinterlegt werden))
|
||||||
|
|
||||||
|
Banner für Nachrichten:
|
||||||
|
Banner für Ankündigungen oder Informationen im Frontend (wahlweist im Header oder im Footer)
|
||||||
|
|
||||||
|
Admin Dashboard Update Anzeige:
|
||||||
|
Zeigt an ob das Plugin WP-Mulit Aktuell ist oder ob ein Update zur Verfügung steht
|
||||||
|
|
||||||
|
Gast Lesezeichen:
|
||||||
|
Gäste können eigene Lesezeichen anlegen die mittels Cookie zugeordnet werden
|
||||||
|
Shortcode:
|
||||||
|
[add_bookmark] & [display_bookmarks]
|
||||||
|
|
||||||
|
Benutzer für Kommentar Sperren:
|
||||||
|
Einzelne Benutzer können für Kommentare Gesperrt werden in dem man Banutzernamen, IP, Mailadresse eingibt
|
||||||
|
|
||||||
|
Kommentar Filter:
|
||||||
|
hier kann wahlweise folgendes verboten werden (Rufnummer, Mailadressen, URL, IP und schimpfwöter) sollte trotz verbot eines dieses gesendet werden wird der Betroffene Bereich durch * ersetzt
|
||||||
|
|
||||||
|
|
||||||
|
custom Shortcode:
|
||||||
|
es können eigene Shortcode erstellt werden die per Auswahl im Editor eingefügt werden
|
||||||
|
|
||||||
|
Besucher Analytics:
|
||||||
|
Hier kann man sehen welche Beiträge am Beliebtesten sind (Angezeigt wird Action (view oder comment) Titel des Beitrags, ID-Beitrag, Zeitstempel)
|
||||||
|
|
||||||
|
Pinwand:
|
||||||
|
hier können Nachrichten im AdminPanel für ander User Eingetragen, Bearbeitet oder auch Gelöscht werden
|
||||||
|
|
||||||
|
Schutz vor Brute-Force-Angriffen:
|
||||||
|
Logt jeden Fehlgeschlagenen Login versuch und sperrt ggf. nach 5 Fehlgeschlagenen Logins den Account und sendet eine MAil an den Kontoinhaber und den Seiten Admin
|
||||||
|
|
||||||
|
Anti Spam Honey:
|
||||||
|
Erkennt Spam, Bots und vieles mehr und Blockt diese (eine Übersicht ist im AdminPanel)
|
||||||
|
|
||||||
|
Auto Tag:
|
||||||
|
Fügt bei jedem Beitrag der keine Tags hat Automatisch die Passenden Tags hinzu, im AdminPanel können ausgewählte wörter Kommagetrennt hinzugefügt werden die nicht genutzt werden sollten)
|
||||||
|
|
||||||
|
Login deaktivieren:
|
||||||
|
Damit kann mal für Bestimmte Benutzer das Login deaktivieren (Aktivierbar im Entsprechenden Profil )
|
||||||
|
|
||||||
|
Text Copy schutz:
|
||||||
|
Schützt den Text vor unerlaubten Kopieren
|
||||||
|
|
||||||
|
Sperre Trash Mail Adressen:
|
||||||
|
Verbietet Trash-mails in Kommentaren die Liste kann nur vom Entwickler des Plugins erweitert werden
|
||||||
|
|
||||||
|
Inhaltsverzeichnis
|
||||||
|
Erstellt ein Inhaltsverzeichnis von mit allen Beiträgen
|
||||||
|
shortcode = [alphabetical_index]
|
||||||
+56
-4911
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user