1619 lines
88 KiB
PHP
1619 lines
88 KiB
PHP
<?php
|
||
/*
|
||
Plugin Name: WP Ingame Shop Pro - NO RCON & CUSTOM CURRENCY
|
||
Description: Vollautomatischer Shop mit Warenkorb.
|
||
Version: 1.0.0
|
||
Author: M_Viper
|
||
*/
|
||
if (!defined('ABSPATH')) exit;
|
||
|
||
// ===========================================================
|
||
// 1. ACTIVATOR & TABLES
|
||
// ===========================================================
|
||
class WIS_Activator {
|
||
public static function activate() {
|
||
self::create_tables();
|
||
self::setup_daily_deal();
|
||
|
||
if(get_option('wis_currency_name') === false) {
|
||
add_option('wis_currency_name', 'Coins');
|
||
}
|
||
if(get_option('wis_image_base_url') === false) {
|
||
add_option('wis_image_base_url', 'https://assets.minecraft-ids.com/1_21_10/');
|
||
}
|
||
if(get_option('wis_header_text') === false) {
|
||
add_option('wis_header_text', '✅ Auto-Bilder | 💰 Sicherer Checkout | 🎮 Ingame-Bestätigung | 🎫 Gutscheine');
|
||
}
|
||
if(get_option('wis_coupon_exclude_offers') === false) {
|
||
add_option('wis_coupon_exclude_offers', '0');
|
||
}
|
||
|
||
// Neue Settings
|
||
if(get_option('wis_daily_deal_enabled') === false) {
|
||
add_option('wis_daily_deal_enabled', '0');
|
||
}
|
||
if(get_option('wis_daily_deal_discount') === false) {
|
||
add_option('wis_daily_deal_discount', '20');
|
||
}
|
||
|
||
// Cron scheduling
|
||
if ( ! wp_next_scheduled( 'wis_daily_deal_event' ) ) {
|
||
wp_schedule_event( time(), 'daily', 'wis_daily_deal_event' );
|
||
}
|
||
|
||
flush_rewrite_rules();
|
||
}
|
||
|
||
public static function deactivate() {
|
||
wp_clear_scheduled_hook('wis_daily_deal_event');
|
||
flush_rewrite_rules();
|
||
}
|
||
|
||
private static function create_tables() {
|
||
global $wpdb;
|
||
$table_name = $wpdb->prefix . 'wis_orders';
|
||
$coupon_table_name = $wpdb->prefix . 'wis_coupons';
|
||
$charset_collate = $wpdb->get_charset_collate();
|
||
|
||
$sql = "CREATE TABLE $table_name (
|
||
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
||
player_name varchar(100) NOT NULL,
|
||
server varchar(100) NOT NULL,
|
||
item_id varchar(100) NOT NULL,
|
||
item_title varchar(255) NOT NULL,
|
||
price int(11) NOT NULL,
|
||
quantity int(11) DEFAULT 1,
|
||
status varchar(20) DEFAULT 'pending',
|
||
response text,
|
||
created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||
PRIMARY KEY (id)
|
||
) $charset_collate;";
|
||
|
||
$sql2 = "CREATE TABLE $coupon_table_name (
|
||
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
||
code varchar(50) NOT NULL,
|
||
value int(11) NOT NULL,
|
||
type varchar(10) DEFAULT 'fixed',
|
||
usage_limit int(11) DEFAULT 1,
|
||
used_count int(11) DEFAULT 0,
|
||
expiry date DEFAULT NULL,
|
||
created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||
PRIMARY KEY (id)
|
||
) $charset_collate;";
|
||
|
||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||
dbDelta($sql);
|
||
dbDelta($sql2);
|
||
|
||
// Alte Installationen updaten (type Spalte hinzufügen)
|
||
$existing_columns = $wpdb->get_col_info("SELECT * FROM $coupon_table_name LIMIT 1");
|
||
if(!in_array('type', $existing_columns)) {
|
||
$wpdb->query("ALTER TABLE $coupon_table_name ADD COLUMN type varchar(10) DEFAULT 'fixed'");
|
||
}
|
||
}
|
||
|
||
public static function setup_daily_deal() {
|
||
// Initial setup beim ersten Aktivieren
|
||
}
|
||
|
||
public static function run_daily_deal() {
|
||
if(get_option('wis_daily_deal_enabled') === '0') return;
|
||
|
||
global $wpdb;
|
||
$discount_percent = intval(get_option('wis_daily_deal_discount', 20));
|
||
|
||
// 1. Aktuellen Daily Deal deaktivieren
|
||
$wpdb->update(
|
||
$wpdb->prefix.'postmeta',
|
||
['meta_value' => 0],
|
||
['meta_key' => '_wis_daily_deal', 'meta_value' => 1]
|
||
);
|
||
|
||
// 2. Zufälliges Item auswählen
|
||
$args = [
|
||
'post_type' => 'wis_item',
|
||
'post_status' => 'publish',
|
||
'posts_per_page' => 1,
|
||
'meta_query' => [
|
||
'relation' => 'AND',
|
||
['key' => '_wis_price', 'value' => 0, 'compare' => '>'],
|
||
['key' => '_wis_daily_deal', 'compare' => 'NOT EXISTS'] // Eines holen, das noch kein Daily Deal ist
|
||
]
|
||
];
|
||
$items = get_posts($args);
|
||
|
||
if(!empty($items)) {
|
||
$item_id = $items[0]->ID;
|
||
$price = intval(get_post_meta($item_id, '_wis_price', true));
|
||
|
||
// Rabatt berechnen
|
||
$offer_price = max(0, floor($price - ($price * ($discount_percent / 100))));
|
||
|
||
// Flags setzen
|
||
update_post_meta($item_id, '_wis_daily_deal', 1);
|
||
update_post_meta($item_id, '_wis_is_offer', 1);
|
||
update_post_meta($item_id, '_wis_offer_price', $offer_price);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ===========================================================
|
||
// 2. ADMIN & CPT
|
||
// ===========================================================
|
||
class WIS_CPT {
|
||
public static function register_post_types() {
|
||
register_post_type('wis_item', [
|
||
'labels' => ['name' => 'Shop Items', 'singular_name' => 'Shop Item'],
|
||
'public' => false,
|
||
'show_ui' => true,
|
||
'show_in_menu' => false,
|
||
'show_in_admin_all_list' => true,
|
||
'capability_type' => 'post',
|
||
'capabilities' => [
|
||
'edit_post' => 'manage_options',
|
||
'read_post' => 'manage_options',
|
||
'delete_post' => 'manage_options',
|
||
'edit_posts' => 'manage_options',
|
||
'delete_posts' => 'manage_options',
|
||
'delete_others_posts' => 'manage_options'
|
||
],
|
||
'supports' => ['title', 'editor'],
|
||
'taxonomies' => ['wis_category']
|
||
]);
|
||
register_post_type('wis_server', [
|
||
'labels' => ['name' => 'Servers', 'singular_name' => 'Server'],
|
||
'public' => false,
|
||
'show_ui' => true,
|
||
'show_in_menu' => false,
|
||
'capability_type' => 'post',
|
||
'supports' => ['title']
|
||
]);
|
||
register_post_type('wis_coupon', [
|
||
'labels' => ['name' => 'Gutscheine', 'singular_name' => 'Gutschein'],
|
||
'public' => false,
|
||
'show_ui' => true,
|
||
'show_in_menu' => false,
|
||
'capability_type' => 'post',
|
||
'supports' => ['title']
|
||
]);
|
||
|
||
// NEU: Kategorien Taxonomie
|
||
$labels = [
|
||
'name' => 'Kategorien',
|
||
'singular_name' => 'Kategorie',
|
||
'search_items' => 'Kategorien suchen',
|
||
'all_items' => 'Alle Kategorien',
|
||
'parent_item' => 'Übergeordnete Kategorie',
|
||
'edit_item' => 'Kategorie bearbeiten',
|
||
'update_item' => 'Kategorie aktualisieren',
|
||
'add_new_item' => 'Neue Kategorie',
|
||
'new_item_name' => 'Kategorie Name',
|
||
'menu_name' => 'Kategorien',
|
||
'popular_items' => 'Beliebte Kategorien',
|
||
'separate_items_with_commas' => "Kategorien mit Kommas trennen",
|
||
'add_or_remove_items' => 'Kategorien hinzufügen oder entfernen',
|
||
'choose_from_most_used' => 'Häufigste Kategorien auswählen',
|
||
'not_found' => 'Keine Kategorien gefunden'
|
||
];
|
||
$args = [
|
||
'hierarchical' => true,
|
||
'labels' => $labels,
|
||
'show_ui' => true,
|
||
'show_in_menu' => true, // Erscheint als Menüpunkt
|
||
'show_in_nav_menus' => false,
|
||
'show_admin_column' => true,
|
||
'public' => false,
|
||
'rewrite' => false,
|
||
];
|
||
register_taxonomy('wis_category', 'wis_item', $args);
|
||
}
|
||
|
||
public static function add_meta_boxes() {
|
||
add_meta_box('wis_item_meta', 'Item Einstellungen', [self::class, 'render_item_meta'], 'wis_item', 'normal', 'high');
|
||
add_meta_box('wis_server_meta', 'Server Einstellungen', [self::class, 'render_server_meta'], 'wis_server', 'normal', 'high');
|
||
add_meta_box('wis_coupon_meta', 'Gutschein Einstellungen', [self::class, 'render_coupon_meta'], 'wis_coupon', 'normal', 'high');
|
||
}
|
||
|
||
public static function render_item_meta($post) {
|
||
wp_nonce_field('wis_save_item', 'wis_item_nonce');
|
||
$price = get_post_meta($post->ID,'_wis_price',true);
|
||
$item_id = get_post_meta($post->ID,'_wis_item_id',true);
|
||
$servers = get_post_meta($post->ID,'_wis_servers',true);
|
||
if(!is_array($servers)) $servers = [];
|
||
|
||
$is_offer = get_post_meta($post->ID,'_wis_is_offer',true);
|
||
$is_daily_deal = get_post_meta($post->ID,'_wis_daily_deal',true);
|
||
$offer_price = get_post_meta($post->ID,'_wis_offer_price',true);
|
||
$manual_daily = get_post_meta($post->ID,'_wis_manual_daily_deal',true);
|
||
|
||
$all_servers = get_posts(['post_type'=>'wis_server','numberposts'=>-1]);
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
$exclude_offers = get_option('wis_coupon_exclude_offers');
|
||
|
||
// NEU: Kategorien
|
||
$cats = wp_get_object_terms($post->ID, 'wis_category');
|
||
$cat_ids = array_map(function($t) { return $t->term_id; }, $cats);
|
||
|
||
$current_status = get_post_status($post->ID);
|
||
$is_active = ($current_status === 'publish') && ($price > 0);
|
||
$status_msg = $is_active ? '<span style="color:green; font-weight:bold;">✅ Aktiv im Shop</span>' : '<span style="color:orange; font-weight:bold;">⏸️ Inaktiv (Kein Preis oder Entwurf)</span>';
|
||
|
||
echo '<p>'.$status_msg.'</p>';
|
||
echo '<p><label><strong>Titel (Anzeige im Shop)</strong></label>';
|
||
echo '<p class="description">Wird automatisch aus dem Post-Titel übernommen</p></p>';
|
||
|
||
echo '<p><label><strong>Preis ('.esc_html($currency).')</strong></label><input type="number" name="wis_price" value="'.esc_attr($price).'" style="width:100%"></p>';
|
||
echo '<p class="description">Wenn du hier einen Preis einträgst (>0), wird das Item automatisch aktiv.</p>';
|
||
|
||
echo '<p><label><strong>Item ID (z.B. minecraft:diamond)</strong></label><input type="text" name="wis_item_id" value="'.esc_attr($item_id).'" style="width:100%" placeholder="z.B. minecraft:diamond">';
|
||
echo '<p class="description"><span style="color:green;">✅ Daraus wird automatisch das Bild generiert.</span></p></p>';
|
||
|
||
// NEU: Kategorie Auswahl
|
||
echo '<p><label><strong>Kategorie(n)</strong></label>';
|
||
$terms = get_terms([
|
||
'taxonomy' => 'wis_category',
|
||
'hide_empty' => false,
|
||
]);
|
||
echo '<div style="border:1px solid #ddd;padding:10px;background:#f9f9f9;max-height:200px;overflow-y:auto;">';
|
||
if(!empty($terms)) {
|
||
foreach($terms as $term) {
|
||
$selected = in_array($term->term_id, $cat_ids) ? 'checked' : '';
|
||
echo '<label style="display:block;margin:5px 0;">';
|
||
echo '<input type="checkbox" name="wis_cats[]" value="'.$term->term_id.'" '.$selected.'> '.esc_html($term->name);
|
||
echo '</label>';
|
||
}
|
||
} else {
|
||
echo '<em>Noch keine Kategorien erstellt.</em>';
|
||
}
|
||
echo '</div></p>';
|
||
|
||
echo '<div style="background:#e3f2fd; padding:10px; border-left:4px solid #2196f3; margin-bottom:15px;">';
|
||
echo '<p><label><strong>Daily Deal (Angebot des Tages)</strong></label></p>';
|
||
echo '<p><label><input type="checkbox" name="wis_manual_daily_deal" value="1" '.checked($manual_daily_deal, 1, false).'> <strong>Manuell als "Angebot des Tages" setzen 🎁</strong></label>';
|
||
echo '<br><small>Überschreibt die Automatik bis Mitternacht. Praktisch zum Testen.</small>';
|
||
if($is_daily_deal) {
|
||
echo '<br><strong style="color:#2196f3;">ℹ️ Info:</strong> Dieses Item ist aktuell das Angebot des Tages. ';
|
||
echo 'Der Rabatt wurde automatisch berechnet.';
|
||
}
|
||
echo '</div>';
|
||
|
||
echo '<div style="background:#fff3cd; padding:10px; border-left:4px solid #ffc107; margin-bottom:15px;">';
|
||
echo '<p><label><strong>Angebot / Sale</strong></label></p>';
|
||
echo '<p><label><input type="checkbox" name="wis_is_offer" value="1" '.checked($is_offer, 1, false).'> Als "Angebot" markieren 🔥</label>';
|
||
|
||
echo '<br><small>Hervorgehobenes Item im Shop (rot/gold Badge).</small>';
|
||
|
||
if($exclude_offers) {
|
||
echo '<br><strong style="color:red;">Hinweis:</strong> Gutscheine sind aktuell global für Angebote deaktiviert.';
|
||
}
|
||
|
||
echo '</p>';
|
||
echo '<p><label>Angebots-Preis (Optional - wird ignoriert wenn Daily Deal aktiv)</label>';
|
||
echo '<input type="number" name="wis_offer_price" value="'.esc_attr($offer_price).'" style="width:100%" placeholder="Preis nur im Angebot">';
|
||
echo '<br><small>Wenn ausgefüllt, wird der normale Preis durchgestrichen und dieser angezeigt.</small></p>';
|
||
echo '</div>';
|
||
|
||
echo '<p><label><strong>Beschreibung</strong></label>';
|
||
echo '<p class="description">Dies ist der lange Text, der unter dem Titel im Shop angezeigt wird.</p>';
|
||
|
||
echo '<p><label><strong>Server zuweisen (Mehrfachauswahl möglich)</strong></label>';
|
||
echo '<div style="border:1px solid #ddd;padding:10px;background:#f9f9f9;max-height:200px;overflow-y:auto;">';
|
||
if(empty($all_servers)) echo '<em>Noch keine Server erstellt.</em>';
|
||
foreach($all_servers as $s){
|
||
$checked = in_array($s->post_name, $servers) ? 'checked' : '';
|
||
echo '<label style="display:block;margin:5px 0;">';
|
||
echo '<input type="checkbox" name="wis_servers[]" value="'.esc_attr($s->post_name).'" '.$checked.'> ';
|
||
echo esc_html($s->post_title);
|
||
echo '</label>';
|
||
}
|
||
echo '</div></p>';
|
||
}
|
||
|
||
public static function render_server_meta($post){
|
||
echo '<p style="background:#e2e3e5;padding:15px;border-left:4px solid #6c757d;">';
|
||
echo '<strong>Info:</strong> Da keine RCON-Verbindung benötigt wird (Vault/Plugin bestätigt), sind hier keine weiteren Einstellungen nötig.<br>';
|
||
echo 'Der Servername dient nur zur Identifikat im Shop-Frontend.';
|
||
echo '</p>';
|
||
}
|
||
|
||
public static function render_coupon_meta($post) {
|
||
wp_nonce_field('wis_save_coupon', 'wis_coupon_nonce');
|
||
$code = get_post_meta($post->ID,'_wis_code',true);
|
||
$value = get_post_meta($post->ID,'_wis_value',true);
|
||
$limit = get_post_meta($post->ID,'_wis_usage_limit',true);
|
||
$expiry = get_post_meta($post->ID,'_wis_expiry',true);
|
||
$type = get_post_meta($post->ID,'_wis_type',true);
|
||
$exclude_offers = get_option('wis_coupon_exclude_offers');
|
||
|
||
if(empty($type)) $type = 'fixed';
|
||
|
||
echo '<p><label><strong>Gutschein Code</strong></label>';
|
||
echo '<input type="text" name="wis_code" value="'.esc_attr($code).'" style="width:100%; text-transform:uppercase;" placeholder="z.B. SUMMER20">';
|
||
|
||
echo '<p><label><strong>Rabattart</strong></label>';
|
||
echo '<select name="wis_type" style="width:100%;">';
|
||
echo '<option value="fixed" '.selected($type, 'fixed', false).'>Festbetrag (z.B. -100 Coins)</option>';
|
||
echo '<option value="percent" '.selected($type, 'percent', false).'>Prozentual (z.B. 10% Rabatt)</option>';
|
||
echo '</select>';
|
||
|
||
echo '<p><label><strong>Wert</strong></label>';
|
||
echo '<input type="number" name="wis_value" value="'.esc_attr($value).'" style="width:100%" placeholder="Je nach Typ">';
|
||
|
||
if($exclude_offers) {
|
||
echo '<p style="background:#ffeeba; color:#856404; padding:10px; border:1px solid #ffeeba;">';
|
||
echo '⚠️ <strong>Achtung:</strong> In den Shop-Einstellungen ist aktiviert, dass Gutscheine <strong>nicht</strong> auf Angebot-Items (Sale) angewendet werden. Dieser Gutschein wirkt nur auf reguläre Items.';
|
||
echo '</p>';
|
||
}
|
||
|
||
echo '<p><label><strong>Nutzungslimit</strong></label>';
|
||
echo '<input type="number" name="wis_usage_limit" value="'.esc_attr($limit).'" style="width:100%;" placeholder="z.B. 50">';
|
||
|
||
echo '<p><label><strong>Ablaufdatum (Optional)</strong></label>';
|
||
echo '<input type="date" name="wis_expiry" value="'.esc_attr($expiry).'" style="width:100%;">';
|
||
}
|
||
|
||
public static function save_meta($post_id){
|
||
if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
|
||
|
||
if(get_post_type($post_id)==='wis_item' && isset($_POST['wis_item_nonce']) && wp_verify_nonce($_POST['wis_item_nonce'],'wis_save_item')){
|
||
update_post_meta($post_id,'_wis_price',intval($_POST['wis_price']));
|
||
update_post_meta($post_id,'_wis_item_id',sanitize_text_field($_POST['wis_item_id']));
|
||
|
||
$is_offer = isset($_POST['wis_is_offer']) ? 1 : 0;
|
||
update_post_meta($post_id,'_wis_is_offer', $is_offer);
|
||
update_post_meta($post_id,'_wis_offer_price', intval($_POST['wis_offer_price']));
|
||
|
||
// NEU: Manual Daily Deal Logic
|
||
$manual_daily = isset($_POST['wis_manual_daily_deal']) ? 1 : 0;
|
||
if($manual_daily) {
|
||
update_post_meta($post_id,'_wis_daily_deal', 1);
|
||
update_post_meta($post_id,'_wis_is_offer', 1);
|
||
} else {
|
||
// Wenn manuell abgewählt, löschen wir das Flag, damit der Cron ran darf
|
||
// update_post_meta($post_id,'_wis_daily_deal', 0); // Optional: Reset flag
|
||
}
|
||
|
||
$servers = isset($_POST['wis_servers']) && is_array($_POST['wis_servers']) ? array_map('sanitize_text_field', $_POST['wis_servers']) : [];
|
||
update_post_meta($post_id,'_wis_servers', $servers);
|
||
|
||
$cats = isset($_POST['wis_cats']) ? array_map('intval', $_POST['wis_cats']) : [];
|
||
wp_set_object_terms($post_id, $cats, 'wis_category');
|
||
}
|
||
if(get_post_type($post_id)==='wis_server' && isset($_POST['wis_server_nonce']) && wp_verify_nonce($_POST['wis_server_nonce'],'wis_save_server')){
|
||
// Nichts zu tun
|
||
}
|
||
if(get_post_type($post_id)==='wis_coupon' && isset($_POST['wis_coupon_nonce']) && wp_verify_nonce($_POST['wis_coupon_nonce'],'wis_save_coupon')){
|
||
$code = sanitize_text_field(strtoupper($_POST['wis_code']));
|
||
$value = intval($_POST['wis_value']);
|
||
$limit = intval($_POST['wis_usage_limit']);
|
||
$expiry = sanitize_text_field($_POST['wis_expiry']);
|
||
$type = sanitize_text_field($_POST['wis_type']);
|
||
if(!in_array($type, ['fixed', 'percent'])) $type = 'fixed';
|
||
|
||
update_post_meta($post_id,'_wis_code', $code);
|
||
update_post_meta($post_id,'_wis_value', $value);
|
||
update_post_meta($post_id,'_wis_type', $type);
|
||
update_post_meta($post_id,'_wis_usage_limit', $limit);
|
||
update_post_meta($post_id,'_wis_expiry', $expiry);
|
||
|
||
global $wpdb;
|
||
$table_name = $wpdb->prefix . 'wis_coupons';
|
||
|
||
$data = ['code' => $code, 'value' => $value, 'type' => $type, 'usage_limit' => $limit, 'expiry' => $expiry];
|
||
|
||
$existing = $wpdb->get_row($wpdb->prepare("SELECT id FROM $table_name WHERE code = %s", $code));
|
||
|
||
if($existing) {
|
||
$wpdb->update($table_name, $data, ['id' => $existing->id]);
|
||
} else {
|
||
$wpdb->insert($table_name, $data);
|
||
}
|
||
|
||
$db_coupon = $wpdb->get_row($wpdb->prepare("SELECT used_count FROM $table_name WHERE code = %s", $code));
|
||
if($db_coupon) {
|
||
update_post_meta($post_id, '_wis_used_count', $db_coupon->used_count);
|
||
}
|
||
}
|
||
}
|
||
|
||
public static function auto_update_status($post_id) {
|
||
static $processing = [];
|
||
if (isset($processing[$post_id])) return;
|
||
$processing[$post_id] = true;
|
||
if (get_post_type($post_id) != 'wis_item') {
|
||
unset($processing[$post_id]);
|
||
return;
|
||
}
|
||
$price = get_post_meta($post_id, '_wis_price', true);
|
||
$current_status = get_post_status($post_id);
|
||
$new_status = ($price > 0) ? 'publish' : 'draft';
|
||
if ($current_status !== $new_status) {
|
||
remove_action('save_post', [__CLASS__, 'auto_update_status'], 10);
|
||
wp_update_post(['ID' => $post_id, 'post_status' => $new_status]);
|
||
add_action('save_post', [__CLASS__, 'auto_update_status'], 10);
|
||
}
|
||
unset($processing[$post_id]);
|
||
}
|
||
|
||
public static function delete_coupon_from_table($post_id) {
|
||
if(get_post_type($post_id) != 'wis_coupon') return;
|
||
$code = get_post_meta($post_id, '_wis_code', true);
|
||
if($code) {
|
||
global $wpdb;
|
||
$wpdb->delete($wpdb->prefix . 'wis_coupons', ['code' => $code]);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ===========================================================
|
||
// Bulk Delete Funktionalität
|
||
// ===========================================================
|
||
class WIS_Bulk_Actions {
|
||
public static function register_bulk_actions($bulk_actions) {
|
||
$bulk_actions['delete'] = __('Löschen', 'wis');
|
||
return $bulk_actions;
|
||
}
|
||
public static function handle_bulk_actions($redirect_to, $action, $post_ids) {
|
||
if ($action !== 'delete') return $redirect_to;
|
||
$deleted = 0;
|
||
foreach ($post_ids as $post_id) {
|
||
if (current_user_can('delete_post', $post_id)) {
|
||
wp_delete_post($post_id, true);
|
||
$deleted++;
|
||
}
|
||
}
|
||
if ($deleted > 0) {
|
||
$redirect_to = add_query_arg([
|
||
'bulk_deleted' => $deleted,
|
||
'post_type' => get_post_type($post_ids[0] ?? 0)
|
||
], $redirect_to);
|
||
}
|
||
return $redirect_to;
|
||
}
|
||
public static function admin_notices() {
|
||
if (!empty($_GET['bulk_deleted'])) {
|
||
$count = intval($_GET['bulk_deleted']);
|
||
$msg = sprintf(_n('%d Eintrag gelöscht.', '%d Einträge gelöscht.', $count, 'wis'), $count);
|
||
echo '<div class="updated"><p>✅ ' . esc_html($msg) . '</p></div>';
|
||
}
|
||
}
|
||
}
|
||
|
||
// ===========================================================
|
||
// 3. ADMIN MENU
|
||
// ===========================================================
|
||
class WIS_Admin {
|
||
public static function register_menu() {
|
||
add_menu_page('Ingame Shop', 'Ingame Shop', 'manage_options', 'wis_shop', [self::class, 'page_overview'], 'dashicons-cart', 6);
|
||
add_submenu_page('wis_shop', 'Bestellungen', 'Bestellungen', 'manage_options', 'wis_orders', [self::class, 'page_orders']);
|
||
add_submenu_page('wis_shop', 'Top Spender', 'Top Spender', 'manage_options', 'wis_top_spenders', [self::class, 'page_top_spenders']);
|
||
add_submenu_page('wis_shop', 'Items', 'Items', 'manage_options', 'edit.php?post_type=wis_item');
|
||
add_submenu_page('wis_shop', 'Kategorien', 'Kategorien', 'manage_options', 'edit-tags.php?taxonomy=wis_category');
|
||
add_submenu_page('wis_shop', 'Servers', 'Servers', 'manage_options', 'edit.php?post_type=wis_server');
|
||
add_submenu_page('wis_shop', 'Gutscheine', 'Gutscheine', 'manage_options', 'edit.php?post_type=wis_coupon');
|
||
}
|
||
|
||
public static function page_overview() {
|
||
if(isset($_POST['wis_save_settings']) && check_admin_referer('wis_settings_nonce')) {
|
||
update_option('wis_currency_name', sanitize_text_field($_POST['wis_currency_name']));
|
||
update_option('wis_image_base_url', sanitize_text_field($_POST['wis_image_base_url']));
|
||
update_option('wis_header_text', sanitize_textarea_field($_POST['wis_header_text']));
|
||
update_option('wis_coupon_exclude_offers', isset($_POST['wis_coupon_exclude_offers']) ? '1' : '0');
|
||
|
||
// NEU: Daily Deal Settings
|
||
update_option('wis_daily_deal_enabled', isset($_POST['wis_daily_deal_enabled']) ? '1' : '0');
|
||
update_option('wis_daily_deal_discount', intval($_POST['wis_daily_deal_discount']));
|
||
|
||
echo '<div class="updated"><p>✅ Einstellungen gespeichert!</p></div>';
|
||
}
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
$img_base = get_option('wis_image_base_url', 'https://assets.minecraft-ids.com/1_21_10/');
|
||
$header_text = get_option('wis_header_text', '✅ Auto-Bilder | 💰 Sicherer Checkout');
|
||
$exclude_offers = get_option('wis_coupon_exclude_offers', '0');
|
||
|
||
// NEU: Daily Deal Settings
|
||
$daily_deal_enabled = get_option('wis_daily_deal_enabled', '0');
|
||
$daily_deal_discount = get_option('wis_daily_deal_discount', '20');
|
||
|
||
echo '<div class="wrap"><h1>🛒 Ingame Shop Pro <span style="color:#28a745;">v1.0.0</span></h1>';
|
||
|
||
echo '<div class="card" style="max-width:800px; margin-top:20px;"><h2>✅ Feature Übersicht</h2><ul>';
|
||
echo '<li>🛒 <strong>Warenkorb:</strong> Kauf mehrerer Items auf einmal.</li>';
|
||
echo '<li>🔗 <strong>Einzelne Bestellung:</strong> Alles wird in einer einzigen Datenbankzeile gespeichert.</li>';
|
||
echo '<li>📋 <strong>Detail-Anzeige:</strong> Im Backend siehst du genau "1x Diamant, 64x Stein" etc. & Gutschein.</li>';
|
||
echo '<li>✨ <strong>Sauberer Prozess:</strong> Alle Items werden sofort nach einer Bestätigung ausgegeben.</li>';
|
||
echo '<li>🔥 <strong>Angebote:</strong> Items können hervorgehoben werden.</li>';
|
||
echo '<li>🎁 <strong>Daily Deal:</strong> Automatisches Angebot des Tages.</li>';
|
||
echo '<li>🎫 <strong>Smart Coupons:</strong> Gutscheine (Festbetrag & Prozent) werden korrekt bei reinen Angeboten ignoriert.</li>';
|
||
echo '<li>🏷️ <strong>Kategorien:</strong> Items können in Kategorien gruppiert werden.</li>';
|
||
echo '<li>🏆 <strong>Top Spender:</strong> Statistiken im Backend.</li>';
|
||
echo '</ul></div>';
|
||
|
||
echo '<div class="card" style="max-width:800px; margin-top:20px; padding:20px; border:1px solid #ccc; background:#fff;">';
|
||
echo '<h2>⚙️ Globale Einstellungen</h2>';
|
||
echo '<form method="post">';
|
||
wp_nonce_field('wis_settings_nonce');
|
||
echo '<table class="form-table"><tbody>';
|
||
|
||
echo '<tr><th scope="row"><label for="wis_header_text">Shop Header Text</label></th>';
|
||
echo '<td><textarea id="wis_header_text" name="wis_header_text" class="large-text" rows="2">'.esc_textarea($header_text).'</textarea>';
|
||
echo '<p class="description">Der Text, der ganz oben im Shop angezeigt wird. <br><strong>Hinweis:</strong> Wenn dieses Feld leer ist, wird auch kein grüner Balken im Shop angezeigt.</p></td></tr>';
|
||
|
||
echo '<tr><th scope="row"><label for="wis_currency_name">Währungsname</label></th>';
|
||
echo '<td><input type="text" id="wis_currency_name" name="wis_currency_name" value="'.esc_attr($currency).'" class="regular-text"></td></tr>';
|
||
|
||
echo '<tr><th scope="row"><label for="wis_image_base_url">Bilder Basis-URL</label></th>';
|
||
echo '<td><input type="text" id="wis_image_base_url" name="wis_image_base_url" value="'.esc_attr($img_base).'" class="large-text code">';
|
||
echo '<p class="description">Wird für die Bildgenerierung verwendet.</p></td></tr>';
|
||
|
||
echo '<tr><th scope="row"><label>Gutscheine bei Angeboten ausschließen?</label></th>';
|
||
echo '<td><label><input type="checkbox" name="wis_coupon_exclude_offers" value="1" '.checked($exclude_offers, '1', false).'> Ja, Gutscheine sollen NICHT auf Sale-Items (Angebote) angewendet werden.</label>';
|
||
echo '<p class="description">Wenn aktiv, wird der Rabatt nur auf den Warenkorbwert der regulären Items berechnet. Angebote werden vollgezahlt.</p></td></tr>';
|
||
|
||
// NEU: Daily Deal Settings
|
||
echo '<tr><th scope="row"><label>Daily Deal (Angebot des Tages)</label></th>';
|
||
echo '<td><label><input type="checkbox" name="wis_daily_deal_enabled" value="1" '.checked($daily_deal_enabled, '1', false).'> Automatischer Daily Deal aktivieren 🎁</label>';
|
||
echo '<p class="description">Wenn aktiv, wird jeden Tag um Mitternacht ein zufälliges Item als "Angebot des Tages" markiert mit einem Rabatt.</p>';
|
||
echo '</td></tr>';
|
||
|
||
echo '<tr><th scope="row"><label>Daily Deal Rabatt (%)</label></th>';
|
||
echo '<td><input type="number" min="1" max="99" id="wis_daily_deal_discount" name="wis_daily_deal_discount" value="'.esc_attr($daily_deal_discount).'">';
|
||
echo '<p class="description">Der Prozentsatz, um den der Preis gesenkt wird (z.B. 20 für 20%).</p></td></tr>';
|
||
|
||
echo '</tbody></table>';
|
||
echo '<p class="submit"><input type="submit" name="wis_save_settings" class="button button-primary" value="Einstellungen speichern"></p>';
|
||
echo '</form>';
|
||
echo '</div>';
|
||
|
||
echo '<div class="card" style="max-width:800px; margin-top:20px; padding:20px; border:1px solid #ccc; background:#f9f9f9;">';
|
||
echo '<h2>🚀 Bulk Import</h2>';
|
||
echo '<p><strong>Smart Import:</strong> Lädt Items in Paketen (20 pro Durchlauf). Vorhandene Items werden übersprungen.</p>';
|
||
echo '<div id="wis-import-controls">';
|
||
echo '<label for="wis_import_url">Import URL:</label>';
|
||
echo '<input type="text" id="wis_import_url" value="https://minecraft-ids.com/data/1.21.10.json" class="large-text code" style="width:100%; margin-top:10px; margin-bottom:10px;">';
|
||
echo '<div class="wis-import-actions">';
|
||
echo '<button type="button" id="wis-btn-fetch" class="button button-secondary">1. Daten laden</button>';
|
||
echo '<button type="button" id="wis-btn-start" class="button button-primary" disabled>2. Import starten</button>';
|
||
echo '</div></div>';
|
||
echo '<div id="wis-progress-container" style="display:none; margin-top:20px;">';
|
||
echo '<div style="display:flex; justify-content:space-between; margin-bottom:5px;">';
|
||
echo '<span id="wis-progress-text">0 / 0</span><span id="wis-percent">0%</span></div>';
|
||
echo '<div style="width:100%; height:20px; background:#e9ecef; border-radius:10px; overflow:hidden;">';
|
||
echo '<div id="wis-progress-bar" style="width:0%; height:100%; background:#28a745; transition:width 0.3s;"></div>';
|
||
echo '</div></div>';
|
||
echo '</div>';
|
||
?>
|
||
<script>
|
||
const WisImport = {
|
||
data: [], batchSize: 20, currentIndex: 0,
|
||
fetch: function() {
|
||
const url = document.getElementById('wis_import_url').value.trim();
|
||
const btn = document.getElementById('wis-btn-fetch');
|
||
if(!url) { alert('Bitte URL eingeben'); return; }
|
||
btn.disabled = true; btn.textContent = '⏳ Lade Daten...';
|
||
fetch('<?php echo rest_url('wis/v1/fetch_remote_data'); ?>', {
|
||
method: 'POST', headers: {'Content-Type':'application/json','X-WP-Nonce':'<?php echo wp_create_nonce('wp_rest'); ?>'},
|
||
body: JSON.stringify({url: url})
|
||
})
|
||
.then(r => r.json()).then(res => {
|
||
if(res.success) { this.data = res.items; document.getElementById('wis-btn-start').disabled = false; document.getElementById('wis-btn-start').textContent = '✅ 2. Import starten (' + this.data.length + ' Items)'; alert('Daten geladen! ' + this.data.length + ' Items bereit.'); } else { alert('Fehler: ' + res.message); }
|
||
}).catch(e => { alert('Fehler: ' + e.message); }).finally(() => { btn.disabled = false; btn.textContent = '1. Daten laden'; });
|
||
},
|
||
start: function() {
|
||
const btn = document.getElementById('wis-btn-start');
|
||
document.getElementById('wis-progress-container').style.display = 'block'; btn.disabled = true; this.currentIndex = 0; this.processBatch();
|
||
},
|
||
processBatch: function() {
|
||
if (this.currentIndex >= this.data.length) { alert('✅ Import abgeschlossen!'); window.location.href = '<?php echo admin_url('edit.php?post_type=wis_item'); ?>'; return; }
|
||
const batch = this.data.slice(this.currentIndex, this.currentIndex + this.batchSize);
|
||
const total = this.data.length;
|
||
const percent = Math.floor((this.currentIndex / total) * 100);
|
||
document.getElementById('wis-progress-bar').style.width = percent + '%';
|
||
document.getElementById('wis-percent').textContent = percent + '%';
|
||
document.getElementById('wis-progress-text').textContent = 'Verarbeite ' + this.currentIndex + ' / ' + total;
|
||
fetch('<?php echo rest_url('wis/v1/import_batch'); ?>', {
|
||
method: 'POST', headers: {'Content-Type':'application/json','X-WP-Nonce':'<?php echo wp_create_nonce('wp_rest'); ?>'},
|
||
body: JSON.stringify({items: batch})
|
||
}).then(r => r.json()).then(res => {
|
||
if(!res.success) { alert('Fehler im Batch: ' + res.message); return; }
|
||
this.currentIndex += res.processed || this.batchSize; this.processBatch();
|
||
}).catch(e => { alert('Kommunikationsfehler: ' + e.message); });
|
||
}
|
||
};
|
||
document.getElementById('wis-btn-fetch').addEventListener('click', () => WisImport.fetch());
|
||
document.getElementById('wis-btn-start').addEventListener('click', () => WisImport.start());
|
||
</script>
|
||
<?php
|
||
echo '</div>';
|
||
|
||
}
|
||
|
||
public static function page_top_spenders() {
|
||
global $wpdb;
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
|
||
$results = $wpdb->get_results("
|
||
SELECT player_name, SUM(price) as total_spent, COUNT(*) as order_count
|
||
FROM {$wpdb->prefix}wis_orders
|
||
WHERE status = 'completed' OR status = 'cancelled'
|
||
GROUP BY player_name
|
||
ORDER BY total_spent DESC
|
||
LIMIT 50
|
||
");
|
||
|
||
echo '<div class="wrap"><h1>🏆 Top Spender</h1>';
|
||
echo '<p>Hier siehst du die Spieler, die insgesamt am meisten in deinem Shop ausgegeben haben.</p>';
|
||
|
||
echo '<table class="widefat fixed striped">';
|
||
echo '<thead><tr><th>Rang</th><th>Spieler</th><th>Ausgegeben (Total)</th><th>Anzahl Bestellungen</th></tr></thead><tbody>';
|
||
|
||
if(empty($results)) {
|
||
echo '<tr><td colspan="4" style="text-align:center;">Noch keine Statistiken vorhanden.</td></tr>';
|
||
} else {
|
||
$rank = 1;
|
||
foreach($results as $row) {
|
||
$total = intval($row->total_spent);
|
||
$count = intval($row->order_count);
|
||
echo '<tr>';
|
||
echo '<td>#' . esc_html($rank++) . '</td>';
|
||
echo '<td><strong>' . esc_html($row->player_name) . '</strong></td>';
|
||
echo '<td>' . esc_html($total) . ' ' . esc_html($currency) . '</td>';
|
||
echo '<td>' . esc_html($count) . '</td>';
|
||
echo '</tr>';
|
||
}
|
||
}
|
||
|
||
echo '</tbody></table>';
|
||
echo '</div>';
|
||
}
|
||
|
||
public static function page_orders() {
|
||
global $wpdb;
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
|
||
if(isset($_GET['action']) && isset($_GET['order_id']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'wis_order_action')) {
|
||
$action = sanitize_text_field($_GET['action']);
|
||
$id = intval($_GET['order_id']);
|
||
if($action === 'delete') { $wpdb->delete($wpdb->prefix.'wis_orders', ['id' => $id]); echo '<div class="updated"><p>✓ Bestellung gelöscht.</p></div>'; }
|
||
elseif($action === 'complete') { $wpdb->update($wpdb->prefix.'wis_orders', ['status' => 'completed'], ['id' => $id]); echo '<div class="updated"><p>✓ Manuell erledigt.</p></div>'; }
|
||
}
|
||
|
||
if(isset($_GET['order_id'])) {
|
||
$order = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wis_orders WHERE id = %d", intval($_GET['order_id'])));
|
||
if($order) {
|
||
$status_colors = ['pending' => '#ffc107', 'processing' => '#0073aa', 'completed' => 'green', 'cancelled' => 'red', 'failed' => 'red'];
|
||
$status_labels = ['pending' => 'Warte auf Ingame', 'processing' => 'In Bearbeitung', 'completed' => 'Erledigt', 'cancelled' => 'Abgebrochen', 'failed' => 'Fehler'];
|
||
$status_label = $status_labels[$order->status] ?? $order->status;
|
||
$status_color = $status_colors[$order->status] ?? 'red';
|
||
|
||
$items_list = '<em>Keine Daten</em>';
|
||
$coupon_row = '<em>Kein Gutschein</em>';
|
||
$json_data = $order->response;
|
||
$decoded = json_decode($json_data, true);
|
||
|
||
if(is_array($decoded)) {
|
||
if(isset($decoded['items']) && is_array($decoded['items'])) {
|
||
$items_list = '<ul style="margin:0; padding-left:20px;">';
|
||
foreach($decoded['items'] as $item) {
|
||
$amount = isset($item['amount']) ? intval($item['amount']) :1;
|
||
$id = isset($item['id']) ? esc_html($item['id']) : 'Unbekannt';
|
||
$items_list .= "<li><strong>{$amount}x</strong> {$id}</li>";
|
||
}
|
||
$items_list .= '</ul>';
|
||
|
||
if(isset($decoded['coupon']) && !empty($decoded['coupon']['code'])) {
|
||
$code = esc_html($decoded['coupon']['code']);
|
||
$disc = intval($decoded['coupon']['discount']);
|
||
$coupon_row = "<span style='color:green; font-weight:bold;'>{$code}</span> (-{$disc} {$currency})";
|
||
}
|
||
} elseif(!empty($decoded)) {
|
||
$items_list = '<ul style="margin:0; padding-left:20px;">';
|
||
foreach($decoded as $item) {
|
||
$amount = isset($item['amount']) ? intval($item['amount']) :1;
|
||
$id = isset($item['id']) ? esc_html($item['id']) : 'Unbekannt';
|
||
$items_list .= "<li><strong>{$amount}x</strong> {$id}</li>";
|
||
}
|
||
$items_list .= '</ul>';
|
||
}
|
||
}
|
||
|
||
echo '<div class="wrap"><h1>Bestellung #'.$order->id.' - Details</h1>';
|
||
echo '<a href="'.admin_url('admin.php?page=wis_orders').'" class="button">← Zurück</a><br><br>';
|
||
echo '<table class="widefat" style="max-width:800px;"><tbody>';
|
||
echo '<tr><th style="width:200px;">ID</th><td>'.$order->id.'</td></tr>';
|
||
echo '<tr><th>Datum</th><td>'.date('d.m.Y H:i', strtotime($order->created_at)).'</td></tr>';
|
||
echo '<tr><th>Spieler</th><td><strong>'.$order->player_name.'</strong></td></tr>';
|
||
echo '<tr><th>Server</th><td>'.$order->server.'</td></tr>';
|
||
echo '<tr><th>Zusammenfassung</th><td>'.esc_html($order->item_title).'</td></tr>';
|
||
echo '<tr><th>Gutschein</th><td>'.$coupon_row.'</td></tr>';
|
||
echo '<tr><th>Gekaufte Items</th><td>'.$items_list.'</td></tr>';
|
||
echo '<tr><th>Gesamtpreis</th><td>'.$order->price.' '.esc_html($currency).'</td></tr>';
|
||
echo '<tr><th>Status</th><td style="color:'.$status_color.';font-weight:bold;">'.esc_html($status_label).'</td></tr>';
|
||
echo '<tr><th>JSON (Debug)</th><td><code style="display:block; background:#eee; padding:5px; font-size:0.8em;">'.esc_html($json_data).'</code></td></tr>';
|
||
echo '</tbody></table>';
|
||
echo '</div>';
|
||
return;
|
||
}
|
||
}
|
||
|
||
$orders = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wis_orders ORDER BY created_at DESC LIMIT 100");
|
||
echo '<div class="wrap"><h1>Bestellungen</h1>';
|
||
echo '<table class="widefat fixed striped"><thead><tr><th style="width:140px;">Datum</th><th style="width:150px;">Spieler</th><th>Inhalt</th><th style="width:100px;">Preis</th><th style="width:120px;">Status</th><th style="width:200px;">Aktion</th></tr></thead><tbody>';
|
||
|
||
if(empty($orders)) { echo '<tr><td colspan="7" style="text-align:center;padding:40px;">Noch keine Bestellungen vorhanden.</td></tr>'; }
|
||
|
||
foreach($orders as $o){
|
||
$status_map = ['pending' => 'Warte', 'processing' => 'Geben...', 'completed' => 'Fertig', 'cancelled' => 'Abgebrochen', 'failed' => 'Fehler'];
|
||
$status_label = $status_map[$o->status] ?? $o->status;
|
||
$status_colors = ['pending' => '#ffc107', 'processing' => '#0073aa', 'completed' => 'green', 'cancelled' => 'red', 'failed' => 'red'];
|
||
$status_color = $status_colors[$o->status] ?? 'red';
|
||
|
||
$delete_url = wp_nonce_url(admin_url('admin.php?page=wis_orders&action=delete&order_id='.$o->id), 'wis_order_action');
|
||
$details_url = admin_url('admin.php?page=wis_orders&order_id='.$o->id);
|
||
|
||
$item_label = strlen($o->item_title) > 50 ? substr($o->item_title, 0, 50).'...' : $o->item_title;
|
||
|
||
echo '<tr>';
|
||
echo '<td>'.esc_html(date('d.m.Y H:i', strtotime($o->created_at))).'</td>';
|
||
echo '<td><strong>'.esc_html($o->player_name).'</strong></td>';
|
||
echo '<td>'.esc_html($item_label).'</td>';
|
||
echo '<td>'.esc_html($o->price).' '.esc_html($currency).'</td>';
|
||
echo '<td style="color:'.$status_color.';font-weight:bold;">'.esc_html($status_label).'</td>';
|
||
echo '<td>';
|
||
echo '<a href="'.$details_url.'" class="button button-small">👁️ Details</a> ';
|
||
echo '<a href="'.$delete_url.'" class="button button-small" style="color:red;" onclick="return confirm(\'Wirklich löschen?\');">🗑️ Löschen</a>';
|
||
echo '</td>';
|
||
echo '</tr>';
|
||
}
|
||
echo '</tbody></table></div>';
|
||
}
|
||
}
|
||
|
||
// ===========================================================
|
||
// 4. API - AUTOMATION & CONFIRMATION
|
||
// ===========================================================
|
||
class WIS_API {
|
||
public static function register_routes() {
|
||
register_rest_route('wis/v1','/order', ['methods'=>'POST','callback'=>[self::class,'create_order'],'permission_callback'=> '__return_true']);
|
||
register_rest_route('wis/v1','/pending_orders', ['methods'=>'GET','callback'=>[self::class,'get_pending_orders'],'permission_callback'=> '__return_true']);
|
||
register_rest_route('wis/v1','/execute_order', ['methods'=>'POST','callback'=>[self::class,'execute_order'],'permission_callback'=> '__return_true']);
|
||
register_rest_route('wis/v1','/complete_order', ['methods'=>'POST','callback'=>[self::class,'complete_order'],'permission_callback'=> '__return_true']);
|
||
register_rest_route('wis/v1','/cancel_order', ['methods'=>'POST','callback'=>[self::class,'cancel_order'],'permission_callback'=> '__return_true']);
|
||
register_rest_route('wis/v1','/fetch_remote_data', ['methods'=>'POST','callback'=>[self::class,'fetch_remote_data'],'permission_callback'=> '__return_true']);
|
||
register_rest_route('wis/v1','/import_batch', ['methods'=>'POST','callback'=>[self::class,'import_batch'],'permission_callback'=> '__return_true']);
|
||
register_rest_route('wis/v1','/validate_coupon', ['methods'=>'POST','callback'=>[self::class,'validate_coupon'],'permission_callback'=> '__return_true']);
|
||
}
|
||
|
||
public static function create_order($request) {
|
||
global $wpdb;
|
||
$data = $request->get_json_params();
|
||
$player = sanitize_text_field($data['player'] ?? '');
|
||
$cart = $data['cart'] ?? [];
|
||
$server_slug = sanitize_text_field($data['server'] ?? '');
|
||
$coupon_code = isset($data['coupon_code']) ? sanitize_text_field(strtoupper($data['coupon_code'])) : '';
|
||
|
||
if(!$player || empty($cart) || !$server_slug) { return new WP_REST_Response(['success'=>false,'message'=>'Fehlende Eingabedaten'], 400); }
|
||
|
||
$exclude_offers_setting = get_option('wis_coupon_exclude_offers', '0');
|
||
|
||
$valid_cart = [];
|
||
$total_normal = 0;
|
||
$total_offer = 0;
|
||
|
||
foreach($cart as $item) {
|
||
$post_id = intval($item['id'] ?? 0);
|
||
$qty = intval($item['quantity'] ?? 1);
|
||
if($post_id <= 0 || $qty <= 0) continue;
|
||
|
||
$price = intval(get_post_meta($post_id, '_wis_price', true));
|
||
$item_servers = get_post_meta($post_id, '_wis_servers', true);
|
||
$is_offer = get_post_meta($post_id, '_wis_is_offer', true);
|
||
|
||
if(!is_array($item_servers)) $item_servers = [];
|
||
if($price <= 0) continue;
|
||
if(!in_array($server_slug, $item_servers)) continue;
|
||
|
||
$offer_price = intval(get_post_meta($post_id, '_wis_offer_price', true));
|
||
$display_price = ($offer_price > 0) ? $offer_price : $price;
|
||
|
||
$real_item_id = get_post_meta($post_id, '_wis_item_id', true);
|
||
|
||
$valid_cart[] = [
|
||
'id' => $post_id,
|
||
'title' => get_the_title($post_id),
|
||
'price' => $display_price,
|
||
'qty' => $qty,
|
||
'is_offer' => $is_offer,
|
||
'real_item_id' => $real_item_id
|
||
];
|
||
|
||
$item_total = $display_price * $qty;
|
||
|
||
if($is_offer && $exclude_offers_setting) {
|
||
$total_offer += $item_total;
|
||
} else {
|
||
$total_normal += $item_total;
|
||
}
|
||
}
|
||
|
||
if(empty($valid_cart)) return new WP_REST_Response(['success'=>false,'message'=>'Keine gültigen Items im Warenkorb.'], 400);
|
||
|
||
// Gutschein Logik
|
||
$coupon_discount = 0;
|
||
$coupon_msg = '';
|
||
$coupon_applied = false;
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
|
||
if(!empty($coupon_code)) {
|
||
$coupon = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wis_coupons WHERE code = %s", $coupon_code));
|
||
|
||
if($coupon) {
|
||
// Basis Checks
|
||
if($coupon->expiry && date('Y-m-d') > $coupon->expiry) {
|
||
$coupon_msg = 'Gutschein abgelaufen.';
|
||
} elseif($coupon->used_count >= $coupon->usage_limit) {
|
||
$coupon_msg = 'Gutschein wurde zu oft verwendet.';
|
||
} else {
|
||
// Prüfen ob Gutschein angewendet werden darf
|
||
$apply_coupon = false;
|
||
$calculate_discount = false;
|
||
|
||
if($exclude_offers_setting === '1') {
|
||
if($total_normal > 0) {
|
||
$apply_coupon = true;
|
||
}
|
||
} else {
|
||
if($total_normal > 0 || $total_offer > 0) {
|
||
$apply_coupon = true;
|
||
}
|
||
}
|
||
|
||
if($apply_coupon) {
|
||
$calculate_discount = true;
|
||
}
|
||
}
|
||
} else {
|
||
$coupon_msg = 'Ungültiger Gutscheincode.';
|
||
}
|
||
}
|
||
|
||
// Wenn angewendet, berechnen
|
||
if($calculate_discount) {
|
||
$coupon_type = $coupon->type;
|
||
if($coupon_type === 'percent') {
|
||
$coupon_discount = $total_normal * ($coupon->value / 100);
|
||
$coupon_msg = "Gutschein eingelöst: -{$coupon->value}% (-{$coupon_discount} {$currency})";
|
||
} else {
|
||
$coupon_discount = $coupon->value;
|
||
$coupon_msg = "Gutschein eingelöst: -{$coupon_discount} {$currency}";
|
||
}
|
||
|
||
// Aufrunden
|
||
$coupon_discount = floor($coupon_discount);
|
||
|
||
$wpdb->update($wpdb->prefix.'wis_coupons', ['used_count' => $coupon->used_count + 1], ['id' => $coupon->id]);
|
||
$coupon_applied = true;
|
||
}
|
||
|
||
$final_normal = max(0, $total_normal - $coupon_discount);
|
||
$final_price = $final_normal + $total_offer;
|
||
|
||
// Speichern in DB
|
||
$items_payload = [];
|
||
$title_parts = [];
|
||
|
||
foreach($valid_cart as $item) {
|
||
$items_payload[] = [
|
||
'id' => $item['real_item_id'],
|
||
'amount' => $item['qty']
|
||
];
|
||
$title_parts[] = $item['qty'] . 'x ' . $item['title'];
|
||
}
|
||
|
||
$readable_title = "Warenkorb: " . implode(', ', $title_parts);
|
||
if (strlen($readable_title) > 240) $readable_title = substr($readable_title, 0, 237) . '...';
|
||
|
||
$coupon_data = [];
|
||
if($coupon_applied) {
|
||
$coupon_data = [
|
||
'code' => $coupon_code,
|
||
'discount' => $coupon_discount
|
||
];
|
||
}
|
||
|
||
$json_payload = json_encode([
|
||
'items' => $items_payload,
|
||
'coupon' => $coupon_data
|
||
]);
|
||
|
||
$inserted = $wpdb->insert($wpdb->prefix.'wis_orders', [
|
||
'player_name' => $player,
|
||
'server' => $server_slug,
|
||
'item_id' => 'multi_item_cart',
|
||
'item_title' => $readable_title,
|
||
'price' => $final_price,
|
||
'quantity' => count($valid_cart),
|
||
'status' => 'pending',
|
||
'response' => $json_payload,
|
||
'created_at' => current_time('mysql')
|
||
]);
|
||
|
||
if($inserted) {
|
||
$msg = '✅ Bestellung erfolgreich erstellt!';
|
||
if($coupon_msg) $msg .= ' ('.$coupon_msg.')';
|
||
return new WP_REST_Response(['success'=>true,'message'=>$msg], 200);
|
||
} else {
|
||
return new WP_REST_Response(['success'=>false,'message'=>'Fehler beim Speichern.'], 500);
|
||
}
|
||
}
|
||
|
||
public static function validate_coupon($request) {
|
||
global $wpdb;
|
||
$data = $request->get_json_params();
|
||
$code = sanitize_text_field(strtoupper($data['code'] ?? ''));
|
||
$cart = $data['cart'] ?? [];
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
|
||
if(!$code) {
|
||
return new WP_REST_Response(['success'=>false, 'message'=>'Kein Code angegeben']);
|
||
}
|
||
$coupon = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wis_coupons WHERE code = %s", $code));
|
||
|
||
if(!$coupon) {
|
||
return new WP_REST_Response(['success'=>false, 'message'=>'Gutschein nicht gefunden']);
|
||
}
|
||
if($coupon->expiry && date('Y-m-d') > $coupon->expiry) {
|
||
return new WP_REST_Response(['success'=>false, 'message'=>'Gutschein abgelaufen']);
|
||
}
|
||
if($coupon->used_count >= $coupon->usage_limit) {
|
||
return new WP_REST_Response(['success'=>false, 'message'=>'Dieser Gutschein wurde bereits maximal eingelöst']);
|
||
}
|
||
|
||
$exclude_offers_setting = get_option('wis_coupon_exclude_offers', '0');
|
||
|
||
if($exclude_offers_setting === '1' && !empty($cart)) {
|
||
$has_normal_items = false;
|
||
foreach($cart as $item) {
|
||
$post_id = intval($item['id'] ?? 0);
|
||
$is_offer = get_post_meta($post_id, '_wis_is_offer', true);
|
||
if(empty($is_offer) || $is_offer == 0) {
|
||
$has_normal_items = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if(!$has_normal_items) {
|
||
return new WP_REST_Response(['success'=>false, 'message'=>'⚠️ Dieser Gutschein gilt nicht für Angebote.']);
|
||
}
|
||
}
|
||
|
||
$message = '';
|
||
if($coupon->type === 'percent') {
|
||
$message = "Gutschein gültig (-{$coupon->value}%)";
|
||
} else {
|
||
$message = "Gutschein gültig (-{$coupon->value} {$currency})";
|
||
}
|
||
|
||
return new WP_REST_Response(['success'=>true, 'type'=>$coupon->type, 'value'=>$coupon->value, 'message'=>$message]);
|
||
}
|
||
|
||
public static function get_pending_orders($request) {
|
||
global $wpdb;
|
||
$player = sanitize_text_field($request->get_param('player'));
|
||
if(!$player) return new WP_REST_Response(['orders'=>[]]);
|
||
$results = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wis_orders WHERE player_name = %s AND status = 'pending' ORDER BY created_at ASC LIMIT 10", $player));
|
||
return new WP_REST_Response(['orders' => $results]);
|
||
}
|
||
|
||
public static function execute_order($request) {
|
||
global $wpdb;
|
||
$data = $request->get_json_params();
|
||
$id = intval($data['id'] ?? 0);
|
||
if(!$id) return new WP_REST_Response(['success'=>false], 400);
|
||
|
||
$order = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wis_orders WHERE id = %d", $id));
|
||
if($order) {
|
||
$wpdb->update($wpdb->prefix.'wis_orders', ['status' => 'processing'], ['id' => $id]);
|
||
return new WP_REST_Response(['success'=>true]);
|
||
}
|
||
return new WP_REST_Response(['success'=>false], 400);
|
||
}
|
||
|
||
public static function complete_order($request) {
|
||
global $wpdb;
|
||
$data = $request->get_json_params();
|
||
$id = intval($data['id'] ?? 0);
|
||
if(!$id) return new WP_REST_Response(['success'=>false], 400);
|
||
|
||
$wpdb->update($wpdb->prefix.'wis_orders', ['status' => 'completed'], ['id' => $id]);
|
||
return new WP_REST_Response(['success'=>true]);
|
||
}
|
||
|
||
public static function cancel_order($request) {
|
||
global $wpdb;
|
||
$data = $request->get_json_params();
|
||
$id = intval($data['id'] ?? 0);
|
||
if(!$id) return new WP_REST_Response(['success'=>false], 400);
|
||
|
||
$updated = $wpdb->update($wpdb->prefix.'wis_orders', ['status' => 'cancelled'], ['id' => $id]);
|
||
|
||
if($updated !== false) {
|
||
return new WP_REST_Response(['success'=>true]);
|
||
}
|
||
return new WP_REST_Response(['success'=>false], 400);
|
||
}
|
||
|
||
public static function fetch_remote_data($request) {
|
||
$url = esc_url_raw($request->get_json_params()['url']);
|
||
$response = wp_remote_get($url, ['timeout' => 30]);
|
||
if(is_wp_error($response)) return new WP_REST_Response(['success'=>false, 'message'=>$response->get_error_message()], 400);
|
||
$body = wp_remote_retrieve_body($response);
|
||
$data = json_decode($body, true);
|
||
$import_data = [];
|
||
if (isset($data['items']) && is_array($data['items'])) { $import_data = $data['items']; } elseif (is_array($data)) { $import_data = $data; }
|
||
if(empty($import_data)) return new WP_REST_Response(['success'=>false, 'message'=>'Keine gültigen Daten gefunden'], 400);
|
||
return new WP_REST_Response(['success'=>true, 'items' => $import_data]);
|
||
}
|
||
|
||
public static function import_batch($request) {
|
||
$items = $request->get_json_params()['items'] ?? [];
|
||
if(!is_array($items) || empty($items)) return new WP_REST_Response(['success'=>false], 400);
|
||
set_time_limit(300);
|
||
$processed = 0; $skipped = 0;
|
||
foreach($items as $item) {
|
||
$item_id_val = $item['id'] ?? ''; $title_val = $item['name'] ?? 'Unbekannt';
|
||
if(empty($item_id_val)) continue;
|
||
$exists = get_posts(['post_type' => 'wis_item', 'meta_key' => '_wis_item_id', 'meta_value' => $item_id_val, 'posts_per_page' => 1]);
|
||
if($exists) { $skipped++; continue; }
|
||
$new_post_id = wp_insert_post(['post_title' => $title_val, 'post_content' => '', 'post_type' => 'wis_item', 'post_status' => 'draft', 'meta_input' => ['_wis_item_id' => $item_id_val, '_wis_price' => 0]]);
|
||
if(!is_wp_error($new_post_id)) $processed++;
|
||
}
|
||
return new WP_REST_Response(['success'=>true, 'processed' => $processed, 'skipped' => $skipped]);
|
||
}
|
||
}
|
||
|
||
// ===========================================================
|
||
// 5. SHORTCODE
|
||
// ===========================================================
|
||
class WIS_Shortcode {
|
||
public static function register_shortcode() {
|
||
add_shortcode('ingame_shop_form', [self::class, 'render_form']);
|
||
}
|
||
|
||
public static function render_form() {
|
||
$servers = get_posts(['post_type'=>'wis_server','numberposts'=>-1]);
|
||
$items = get_posts(['post_type'=>'wis_item','numberposts'=>-1]);
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
$img_base = get_option('wis_image_base_url', 'https://assets.minecraft-ids.com/1_21_10/');
|
||
$header_text = get_option('wis_header_text', '✅ Auto-Bilder | 💰 Sicherer Checkout');
|
||
$exclude_offers = get_option('wis_coupon_exclude_offers', '0');
|
||
|
||
// NEU: Kategorien
|
||
$categories = get_terms(['taxonomy'=>'wis_category', 'hide_empty'=>true]);
|
||
|
||
ob_start();
|
||
?>
|
||
<style>
|
||
.wis-pro-shop { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; max-width: 1400px; margin: 40px auto; background: #f4f6f8; padding: 20px; border-radius: 10px; }
|
||
.wis-header { text-align: center; margin-bottom: 30px; }
|
||
.wis-header h2 { color: #333; margin-bottom: 10px; }
|
||
.wis-status { background: #d4edda; color: #155724; padding: 15px; border-radius: 8px; border-left: 4px solid #c3e6cb; margin-bottom: 20px; font-weight: 500; }
|
||
.wis-control-bar { display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 30px; align-items: center; justify-content: space-between; background: #fff; padding: 15px; border-radius: 8px; border: 1px solid #ddd; }
|
||
.wis-search-input { padding: 10px 15px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; width: 100%; max-width: 300px; }
|
||
.wis-offer-filter label { cursor: pointer; display: flex; align-items: center; gap: 5px; font-weight: bold; color: #e67e22; }
|
||
.wis-filter-select { padding: 12px 20px; font-size: 16px; border: 2px solid #ddd; border-radius: 8px; background: white; cursor: pointer; min-width: 200px; }
|
||
.wis-cart-btn { padding: 12px 30px; background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; border: none; border-radius: 8px; font-weight: bold; font-size: 1rem; cursor: pointer; transition: all 0.3s; position: relative; display: flex; align-items: center; gap: 10px; }
|
||
.wis-cart-btn:hover { transform: scale(1.05); box-shadow: 0 5px 15px rgba(40, 167, 69, 0.4); }
|
||
.wis-cart-badge { position: absolute; top: -8px; right: -8px; background: #dc3545; color: white; border-radius: 50%; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 0.8rem; font-weight: bold; }
|
||
.wis-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; }
|
||
.wis-card { background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 6px rgba(0,0,0,0.05); transition: transform 0.2s, box-shadow 0.2s; display: flex; flex-direction: column; border: 1px solid #eee; position: relative; }
|
||
.wis-card.offer { border: 2px solid #ffc107; }
|
||
.wis-card:hover { transform: translateY(-5px); box-shadow: 0 10px 20px rgba(0,0,0,0.1); border-color: #0073aa; }
|
||
.wis-card-img { width: 100%; height: 180px; background: #222; display: flex; align-items: center; justify-content: center; position: relative; }
|
||
.wis-card-img img { max-width: 80%; max-height: 80%; object-fit: contain; filter: drop-shadow(0 4px 4px rgba(0,0,0,0.5)); transition: transform 0.3s ease; }
|
||
.wis-card:hover .wis-card-img img { transform: scale(1.15) rotate(3deg); }
|
||
.wis-offer-badge { position: absolute; top: 10px; left: 10px; background: linear-gradient(135deg, #ff416c, #ff4b2b); color: white; padding: 4px 10px; border-radius: 20px; font-size: 0.75rem; font-weight: bold; box-shadow: 0 2px 5px rgba(0,0,0,0.2); z-index: 2; }
|
||
.wis-daily-badge { position: absolute; top: 10px; left: 10px; background: linear-gradient(135deg, #6f42c1, #8e44ad); color: white; padding: 4px 10px; border-radius: 20px; font-size: 0.75rem; font-weight: bold; box-shadow: 0 2px 5px rgba(0,0,0,0.2); z-index: 2; border: 1px solid #fff; }
|
||
.wis-card-body { padding: 15px; flex-grow: 1; display: flex; flex-direction: column; }
|
||
.wis-card-title { font-size: 1.1rem; font-weight: 700; margin: 0 0 10px 0; color: #333; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
.wis-card-desc { font-size: 0.85rem; color: #666; margin-bottom: 10px; line-height: 1.4; height: 40px; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
|
||
.wis-card-price-container { display: flex; align-items: baseline; gap: 10px; margin-bottom: 10px; }
|
||
.wis-card-price { font-size: 1.2rem; color: #e67e22; font-weight: 800; }
|
||
.wis-card-price-old { font-size: 0.9rem; color: #999; text-decoration: line-through; font-style: italic; }
|
||
.wis-card-servers { font-size: 0.85rem; color: #666; margin-bottom: 15px; line-height: 1.4; }
|
||
.wis-hint { font-size: 0.75rem; color: #dc3545; margin-top: 5px; font-weight: bold; }
|
||
.wis-quantity-control { display: flex; align-items: center; gap: 10px; margin-bottom: 15px; }
|
||
.wis-quantity-btn { width: 35px; height: 35px; border: 2px solid #667eea; background: white; color: #667eea; border-radius: 6px; font-weight: bold; cursor: pointer; transition: all 0.2s; }
|
||
.wis-quantity-btn:hover { background: #667eea; color: white; }
|
||
.wis-quantity-input { width: 60px; text-align: center; border: 2px solid #ddd; border-radius: 6px; padding: 8px; font-weight: bold; }
|
||
.wis-btn-add { margin-top: auto; width: 100%; padding: 12px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 6px; font-weight: bold; font-size: 0.9rem; cursor: pointer; transition: all 0.3s; }
|
||
.wis-btn-add:hover { transform: scale(1.05); box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); }
|
||
.wis-coupon-input-group { display: flex; gap: 10px; margin-bottom: 20px; }
|
||
.wis-coupon-input { flex-grow: 1; padding: 10px; border: 1px solid #ddd; border-radius: 6px; }
|
||
.wis-coupon-btn { padding: 10px 15px; background: #6f42c1; color: white; border: none; border-radius: 6px; font-weight: bold; cursor: pointer; }
|
||
.wis-coupon-msg { font-size: 0.85rem; color: #e67e22; margin-top: 5px; min-height: 20px; }
|
||
.wis-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); z-index: 9999; display: none; align-items: center; justify-content: center; backdrop-filter: blur(3px); }
|
||
.wis-modal { background: white; width: 90%; max-width: 600px; max-height: 80vh; padding: 30px; border-radius: 15px; box-shadow: 0 15px 30px rgba(0,0,0,0.3); animation: popIn 0.3s ease; overflow-y: auto; }
|
||
@keyframes popIn { from { transform: scale(0.8); opacity: 0; } to { transform: scale(1); opacity: 1; } }
|
||
.wis-modal h2 { margin-top: 0; color: #333; text-align: center; }
|
||
.wis-cart-empty { text-align: center; padding: 40px; color: #999; }
|
||
.wis-cart-item { display: flex; justify-content: space-between; align-items: center; padding: 15px; border-bottom: 1px solid #eee; }
|
||
.wis-cart-item-info { flex-grow: 1; }
|
||
.wis-cart-item-title { font-weight: bold; color: #333; margin-bottom: 5px; }
|
||
.wis-cart-item-price { color: #e67e22; font-weight: 600; }
|
||
.wis-cart-item-remove { background: #dc3545; color: white; border: none; padding: 8px 15px; border-radius: 6px; cursor: pointer; font-weight: bold; transition: all 0.2s; }
|
||
.wis-cart-item-remove:hover { background: #c82333; }
|
||
.wis-cart-total { border-top: 2px solid #333; padding: 20px 0; margin-top: 15px; }
|
||
.wis-cart-total-row { display: flex; justify-content: space-between; font-size: 1.3rem; font-weight: bold; color: #333; }
|
||
.wis-modal-input { width: 100%; padding: 15px; margin: 20px 0; border: 2px solid #ddd; border-radius: 8px; font-size: 1.1rem; box-sizing: border-box; }
|
||
.wis-modal-input:focus { border-color: #667eea; outline: none; }
|
||
.wis-modal-actions { display: flex; gap: 10px; }
|
||
.wis-modal-btn { flex: 1; padding: 12px; border: none; border-radius: 8px; font-weight: bold; cursor: pointer; font-size: 1rem; transition: all 0.3s; }
|
||
.wis-btn-confirm { background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; }
|
||
.wis-btn-confirm:hover { transform: scale(1.05); }
|
||
.wis-btn-cancel { background: #6c757d; color: white; }
|
||
.wis-btn-cancel:hover { background: #5a6268; }
|
||
.wis-alert { margin-top: 15px; padding: 12px; border-radius: 5px; display: none; }
|
||
.wis-alert.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
|
||
.wis-alert.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
|
||
/* NEU: Kategorien Tabs */
|
||
.wis-cat-tabs { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 20px; justify-content: center; }
|
||
.wis-cat-btn { padding: 8px 16px; border: 1px solid #ddd; background: #fff; border-radius: 20px; cursor: pointer; transition: all 0.2s; }
|
||
.wis-cat-btn:hover, .wis-cat-btn.active { background: #667eea; color: white; border-color: #667eea; }
|
||
.wis-hidden { display: none; }
|
||
</style>
|
||
<div class="wis-pro-shop">
|
||
<div class="wis-header">
|
||
<h2>🛒 Ingame Shop Pro</h2>
|
||
<?php if (!empty(trim($header_text))) : ?>
|
||
<div class="wis-status"><?=esc_html($header_text)?></div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<div class="wis-control-bar">
|
||
<div style="flex-grow: 1;">
|
||
<input type="text" id="wis-search" class="wis-search-input" placeholder="🔍 Suche Item...">
|
||
</div>
|
||
<div class="wis-offer-filter">
|
||
<label><input type="checkbox" id="wis-offer-filter"> Nur Angebote</label>
|
||
</div>
|
||
<select id="server-filter" class="wis-filter-select">
|
||
<option value="">Alle Server</option>
|
||
<?php foreach($servers as $s): ?>
|
||
<option value="<?=esc_attr($s->post_name)?>"><?=esc_html($s->post_title)?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<button class="wis-cart-btn" onclick="openCart()"> 🛒 Warenkorb <span class="wis-cart-badge" id="cart-count">0</span></button>
|
||
</div>
|
||
|
||
<!-- NEU: Kategorien Tabs -->
|
||
<div class="wis-cat-tabs" id="wis-cat-container">
|
||
<button class="wis-cat-btn active" onclick="filterCategory(0)">Alle</button>
|
||
<?php foreach($categories as $cat): ?>
|
||
<button class="wis-cat-btn" onclick="filterCategory(<?=esc_attr($cat->term_id)?>)"><?=esc_html($cat->name)?></button>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
|
||
<div class="wis-grid" id="shop-grid">
|
||
<?php if(empty($items)){ ?>
|
||
<div style="grid-column: 1 / -1; text-align: center; padding: 20px; background: #fff; border-radius: 10px;">Keine Items im Shop verfügbar.</div>
|
||
<?php } ?>
|
||
<?php foreach($items as $item):
|
||
$item_servers = get_post_meta($item->ID, '_wis_servers', true);
|
||
if(!is_array($item_servers)) $item_servers = [];
|
||
$price = get_post_meta($item->ID, '_wis_price', true);
|
||
$item_id_code = get_post_meta($item->ID, '_wis_item_id', true);
|
||
$item_desc = $item->post_content;
|
||
|
||
$is_offer = get_post_meta($item->ID, '_wis_is_offer', true);
|
||
$is_daily_deal = get_post_meta($item->ID, '_wis_daily_deal', true);
|
||
$offer_price = get_post_meta($item->ID, '_wis_offer_price', true);
|
||
$display_price = ($offer_price > 0) ? $offer_price : $price;
|
||
$show_old_price = ($offer_price > 0 && $offer_price != $price);
|
||
|
||
// NEU: Kategorien
|
||
$item_cats = wp_get_post_terms($item->ID, 'wis_category');
|
||
$cat_ids = array_map(function($t) { return $t->term_id; }, $item_cats);
|
||
$cat_data = json_encode($cat_ids);
|
||
|
||
$img_name = str_replace(':', '_', $item_id_code) . '.png';
|
||
$full_img_url = $img_base . $img_name;
|
||
|
||
$server_names = [];
|
||
foreach($item_servers as $slug) {
|
||
$srv = get_page_by_path($slug, OBJECT, 'wis_server');
|
||
if($srv) $server_names[] = $srv->post_title;
|
||
}
|
||
$servers_display = !empty($server_names) ? implode(', ', $server_names) : 'Kein Server';
|
||
$servers_data = json_encode($item_servers);
|
||
?>
|
||
<div class="wis-card <?=esc_attr($is_offer?'offer':'')?>" data-cats='<?=esc_attr($cat_data)?>' data-servers='<?=esc_attr($servers_data)?>' data-title="<?=esc_attr(strtolower($item->post_title))?>" data-offer="<?=esc_attr($is_offer?'1':'0')?>">
|
||
<!-- NEU: Badges Logic -->
|
||
<?php if($is_daily_deal): ?><div class="wis-daily-badge">🎁 Angebot des Tages</div>
|
||
<?php elseif($is_offer): ?><div class="wis-offer-badge">🔥 Angebot</div><?php endif; ?>
|
||
|
||
<div class="wis-card-img">
|
||
<img src="<?=esc_url($full_img_url)?>" alt="<?=esc_attr($item->post_title)?>" loading="lazy" onerror="this.onerror=null; this.src='https://via.placeholder.com/128/333/fff?text=?';">
|
||
</div>
|
||
<div class="wis-card-body">
|
||
<h3 class="wis-card-title" title="<?=esc_attr($item->post_title)?>"><?=esc_html($item->post_title)?></h3>
|
||
|
||
<div class="wis-card-price-container">
|
||
<?php if($show_old_price): ?><div class="wis-card-price-old"><?=esc_html($price)?> <?=esc_html($currency)?></div><?php endif; ?>
|
||
<div class="wis-card-price"><?=esc_html($display_price)?> <?=esc_html($currency)?></div>
|
||
</div>
|
||
|
||
<div class="wis-card-servers">📡 <?=esc_html($servers_display)?></div>
|
||
|
||
<?php if($is_offer && $exclude_offers): ?>
|
||
<div class="wis-hint">⚠️ Gutschein ungültig</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="wis-quantity-control">
|
||
<button class="wis-quantity-btn" onclick="changeQuantity(this, -1)">-</button>
|
||
<input type="number" class="wis-quantity-input" value="1" min="1" max="999" onchange="validateQuantity(this)">
|
||
<button class="wis-quantity-btn" onclick="changeQuantity(this, 1)">+</button>
|
||
</div>
|
||
|
||
<button class="wis-btn-add" onclick="addToCart(<?=esc_attr($item->ID)?>, '<?=esc_js($item->post_title)?>', <?=esc_attr($display_price)?>, this, <?=esc_attr($is_offer?1:0)?>)">
|
||
➕ In den Warenkorb
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Warenkorb Modal -->
|
||
<div class="wis-modal-overlay" id="cart-modal">
|
||
<div class="wis-modal">
|
||
<h2>🛒 Dein Warenkorb</h2>
|
||
<div id="cart-content">
|
||
<div class="wis-cart-empty">Dein Warenkorb ist leer</div>
|
||
</div>
|
||
<div id="cart-checkout" style="display:none;">
|
||
|
||
<div style="margin-bottom: 20px; background: #f8f9fa; padding: 15px; border-radius: 8px; border: 1px solid #e9ecef;">
|
||
<label style="display:block; font-weight:bold; margin-bottom: 5px;">🎫 Gutscheincode (Optional)</label>
|
||
<div class="wis-coupon-input-group">
|
||
<input type="text" id="coupon-code-input" class="wis-coupon-input" placeholder="CODE EINGEBEN">
|
||
<button type="button" class="wis-coupon-btn" onclick="validateCoupon()">Einlösen</button>
|
||
</div>
|
||
<div id="coupon-message" class="wis-coupon-msg"></div>
|
||
</div>
|
||
|
||
<div class="wis-cart-total">
|
||
<div class="wis-cart-total-row">
|
||
<span>Gesamt:</span>
|
||
<span id="cart-total">0 <?=esc_html($currency)?></span>
|
||
</div>
|
||
</div>
|
||
|
||
<select id="checkout-server" class="wis-modal-input" style="margin-top:20px;">
|
||
<option value="">-- Server wählen --</option>
|
||
<?php foreach($servers as $s): ?>
|
||
<option value="<?=esc_attr($s->post_name)?>"><?=esc_html($s->post_title)?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
|
||
<input type="text" class="wis-modal-input" id="checkout-playername" placeholder="Dein exakter Spielername">
|
||
|
||
<div class="wis-modal-actions">
|
||
<button class="wis-modal-btn wis-btn-confirm" onclick="checkout()">💰 Kauf abschließen</button>
|
||
<button class="wis-modal-btn wis-btn-cancel" onclick="closeCart()">Abbrechen</button>
|
||
</div>
|
||
<div id="cart-alert" class="wis-alert"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const shopCurrency = "<?=esc_js($currency)?>";
|
||
const shopExcludeOffers = "<?=esc_js($exclude_offers)?>" === "1";
|
||
let cart = [];
|
||
let couponData = {}; // Speichert {type, value}
|
||
let activeCategory = 0; // 0 = Alle
|
||
|
||
function changeQuantity(btn, delta) {
|
||
const input = btn.parentElement.querySelector('.wis-quantity-input');
|
||
let val = parseInt(input.value) ||1;
|
||
val = Math.max(1, Math.min(999, val + delta));
|
||
input.value = val;
|
||
}
|
||
|
||
function validateQuantity(input) {
|
||
let val = parseInt(input.value) ||1;
|
||
input.value = Math.max(1, Math.min(999, val));
|
||
}
|
||
|
||
function filterCategory(catId) {
|
||
activeCategory = catId;
|
||
document.querySelectorAll('.wis-cat-btn').forEach(btn => {
|
||
btn.classList.remove('active');
|
||
});
|
||
const btn = event.target;
|
||
btn.classList.add('active');
|
||
updateGrid();
|
||
}
|
||
|
||
function updateGrid() {
|
||
const term = document.getElementById('wis-search').value.toLowerCase();
|
||
const isOffersOnly = document.getElementById('wis-offer-filter').checked;
|
||
const serverFilter = document.getElementById('server-filter').value.toLowerCase();
|
||
const searchTerm = document.getElementById('wis-search').value.toLowerCase();
|
||
|
||
document.querySelectorAll('.wis-card').forEach(card => {
|
||
const cats = JSON.parse(card.dataset.cats || '[]');
|
||
const isOffer = card.getAttribute('data-offer') === '1';
|
||
const servers = JSON.parse(card.dataset.servers || '[]');
|
||
const title = card.getAttribute('data-title').toLowerCase();
|
||
|
||
let visible = true;
|
||
|
||
if(term && !title.includes(term)) visible = false;
|
||
if(isOffersOnly && !isOffer) visible = false;
|
||
if(activeCategory > 0 && !cats.includes(activeCategory)) visible = false;
|
||
if(serverFilter && !servers.includes(serverFilter)) visible = false;
|
||
|
||
card.style.display = visible ? 'flex' : 'none';
|
||
});
|
||
}
|
||
|
||
document.getElementById('wis-search').addEventListener('input', updateGrid);
|
||
document.getElementById('wis-offer-filter').addEventListener('change', updateGrid);
|
||
document.getElementById('server-filter').addEventListener('change', updateGrid);
|
||
|
||
function addToCart(itemId, itemTitle, price, btn, isOffer) {
|
||
const card = btn.closest('.wis-card');
|
||
const quantity = parseInt(card.querySelector('.wis-quantity-input').value) || 1;
|
||
|
||
const existing = cart.find(i => i.id === itemId);
|
||
if (existing) {
|
||
existing.quantity += quantity;
|
||
} else {
|
||
const serversData = JSON.parse(card.dataset.servers || '[]');
|
||
const catsData = JSON.parse(card.dataset.cats || '[]');
|
||
|
||
cart.push({
|
||
id: itemId,
|
||
title: itemTitle,
|
||
price: price,
|
||
quantity: quantity,
|
||
servers: serversData,
|
||
cats: catsData,
|
||
is_offer: !!isOffer
|
||
});
|
||
}
|
||
|
||
updateCartBadge();
|
||
|
||
btn.innerHTML = '✅ Hinzugefügt';
|
||
btn.style.background = '#28a745';
|
||
setTimeout(() => {
|
||
btn.innerHTML = '➕ In den Warenkorb';
|
||
btn.style.background = '';
|
||
}, 1500);
|
||
|
||
card.querySelector('.wis-quantity-input').value = 1;
|
||
}
|
||
|
||
function updateCartBadge() {
|
||
const totalItems = cart.reduce((sum, item) => sum + item.quantity, 0);
|
||
document.getElementById('cart-count').textContent = totalItems;
|
||
}
|
||
|
||
function openCart() {
|
||
renderCart();
|
||
document.getElementById('cart-modal').style.display = 'flex';
|
||
}
|
||
|
||
function closeCart() {
|
||
document.getElementById('cart-modal').style.display = 'none';
|
||
document.getElementById('cart-alert').style.display = 'none';
|
||
}
|
||
|
||
function calculateTotal() {
|
||
let normalSum = 0;
|
||
let offerSum = 0;
|
||
|
||
cart.forEach(item => {
|
||
const itemTotal = item.price * item.quantity;
|
||
if(shopExcludeOffers && item.is_offer) {
|
||
offerSum += itemTotal;
|
||
} else {
|
||
normalSum += itemTotal;
|
||
}
|
||
});
|
||
|
||
// FIX: Prüfen ob Gutschein existiert, um NaN zu vermeiden
|
||
let discount = 0;
|
||
if (couponData && typeof couponData.value !== 'undefined') {
|
||
if(couponData.type === 'percent') {
|
||
discount = normalSum * (couponData.value / 100);
|
||
} else {
|
||
discount = couponData.value;
|
||
}
|
||
}
|
||
|
||
let discountedNormal = Math.max(0, normalSum - discount);
|
||
discountedNormal = Math.floor(discountedNormal);
|
||
|
||
return discountedNormal + offerSum;
|
||
}
|
||
|
||
function renderCart() {
|
||
const content = document.getElementById('cart-content');
|
||
const checkout = document.getElementById('cart-checkout');
|
||
|
||
if (cart.length === 0) {
|
||
content.innerHTML = '<div class="wis-cart-empty">Dein Warenkorb ist leer</div>';
|
||
checkout.style.display = 'none';
|
||
return;
|
||
}
|
||
|
||
let html = '';
|
||
|
||
cart.forEach((item, index) => {
|
||
const itemTotal = item.price * item.quantity;
|
||
html += `
|
||
<div class="wis-cart-item">
|
||
<div class="wis-cart-item-info">
|
||
<div class="wis-cart-item-title">${escapeHtml(item.title)}</div>
|
||
<div class="wis-cart-item-price">${item.quantity}x × ${item.price} = ${itemTotal} ${shopCurrency}</div>
|
||
</div>
|
||
<button class="wis-cart-item-remove" onclick="removeFromCart(${index})">🗑️</button>
|
||
</div>
|
||
`;
|
||
});
|
||
|
||
content.innerHTML = html;
|
||
|
||
let finalTotal = calculateTotal();
|
||
document.getElementById('cart-total').textContent = finalTotal + ' ' + shopCurrency;
|
||
checkout.style.display = 'block';
|
||
}
|
||
|
||
function removeFromCart(index) {
|
||
cart.splice(index, 1);
|
||
updateCartBadge();
|
||
renderCart();
|
||
}
|
||
|
||
function validateCoupon() {
|
||
const codeInput = document.getElementById('coupon-code-input');
|
||
const msgBox = document.getElementById('coupon-message');
|
||
const code = codeInput.value.trim().toUpperCase();
|
||
|
||
// FIX: Wenn Feld leer ist, Gutschein löschen und Preiss neu berechnen
|
||
if(!code) {
|
||
couponData = {};
|
||
msgBox.textContent = '';
|
||
renderCart();
|
||
return;
|
||
}
|
||
|
||
msgBox.textContent = 'Prüfe...';
|
||
msgBox.style.color = '#0073aa';
|
||
msgBox.style.fontWeight = 'bold';
|
||
|
||
fetch('<?php echo rest_url('wis/v1/validate_coupon'); ?>', {
|
||
method: 'POST',
|
||
headers: {'Content-Type':'application/json'},
|
||
body: JSON.stringify({code: code, cart: cart})
|
||
})
|
||
.then(r => {
|
||
if(!r.ok) throw new Error('Fehler: ' + r.statusText);
|
||
return r.json();
|
||
})
|
||
.then(data => {
|
||
if(data.success) {
|
||
couponData = { type: data.type, value: data.value };
|
||
msgBox.textContent = '✅ ' + data.message;
|
||
msgBox.style.color = 'green';
|
||
msgBox.style.fontWeight = 'bold';
|
||
renderCart();
|
||
} else {
|
||
// FIX: Bei Fehlern, Gutschein Daten leeren, damit Preis stimmt
|
||
couponData = {};
|
||
msgBox.textContent = '❌ ' + data.message;
|
||
msgBox.style.color = 'red';
|
||
renderCart();
|
||
}
|
||
})
|
||
.catch(e => {
|
||
console.error(e);
|
||
// FIX: Auch bei Netzwerkfehler leeren
|
||
couponData = {};
|
||
msgBox.textContent = 'Fehler beim Prüfen (Server?)';
|
||
msgBox.style.color = 'red';
|
||
renderCart();
|
||
});
|
||
}
|
||
|
||
function checkout() {
|
||
const playerName = document.getElementById('checkout-playername').value.trim();
|
||
const server = document.getElementById('checkout-server').value;
|
||
const codeInput = document.getElementById('coupon-code-input').value.trim();
|
||
|
||
if (!playerName) { showCartAlert('Bitte Spielername eingeben!', 'error'); return; }
|
||
if (!server) { showCartAlert('Bitte Server auswählen!', 'error'); return; }
|
||
|
||
const invalidItems = cart.filter(item => !item.servers.includes(server));
|
||
if (invalidItems.length > 0) { showCartAlert('Einige Items sind nicht für den gewählten Server verfügbar!', 'error'); return; }
|
||
|
||
const btn = document.querySelector('.wis-btn-confirm');
|
||
btn.innerHTML = '⏳ Speichere...';
|
||
btn.disabled = true;
|
||
|
||
fetch('<?php echo rest_url('wis/v1/order')?>', {
|
||
method: 'POST',
|
||
headers: {'Content-Type':'application/json'},
|
||
body: JSON.stringify({
|
||
player: playerName,
|
||
cart: cart,
|
||
server: server,
|
||
coupon_code: codeInput
|
||
})
|
||
})
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
showCartAlert(data.message, 'success');
|
||
cart = []; couponData = {}; document.getElementById('coupon-code-input').value = ''; document.getElementById('coupon-message').textContent = '';
|
||
updateCartBadge();
|
||
setTimeout(() => { closeCart(); location.reload(); },2000);
|
||
} else {
|
||
showCartAlert(data.message, 'error');
|
||
}
|
||
})
|
||
.catch(e => { showCartAlert('Netzwerkfehler: ' + e.message, 'error'); })
|
||
.finally(() => { btn.innerHTML = '💰 Kauf abschließen'; btn.disabled = false; });
|
||
}
|
||
|
||
function showCartAlert(msg, type) {
|
||
const alert = document.getElementById('cart-alert');
|
||
alert.textContent = msg;
|
||
alert.className = 'wis-alert ' + type;
|
||
alert.style.display = 'block';
|
||
}
|
||
|
||
function escapeHtml(text) {
|
||
const map = {'&': '&', '<': '<', '>': '>', '"': '"', "'": '''};
|
||
return text.replace(/[&<>"']/g, m => map[m]);
|
||
}
|
||
|
||
document.getElementById('cart-modal').addEventListener('click', function(e) { if (e.target === this) closeCart(); });
|
||
</script>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
}
|
||
|
||
// ===========================================================
|
||
// INIT + Bulk Delete Hooks + Cron
|
||
// ===========================================================
|
||
register_activation_hook(__FILE__, [WIS_Activator::class, 'activate']);
|
||
register_deactivation_hook(__FILE__, [WIS_Activator::class, 'deactivate']);
|
||
add_action('init', [WIS_CPT::class, 'register_post_types']);
|
||
add_action('add_meta_boxes', [WIS_CPT::class, 'add_meta_boxes']);
|
||
add_action('save_post', [WIS_CPT::class, 'save_meta']);
|
||
add_action('save_post', [WIS_CPT::class, 'auto_update_status'], 10);
|
||
add_action('delete_post', [WIS_CPT::class, 'delete_coupon_from_table']);
|
||
add_action('admin_menu', [WIS_Admin::class, 'register_menu']);
|
||
add_action('rest_api_init', [WIS_API::class, 'register_routes']);
|
||
add_action('init', [WIS_Shortcode::class, 'register_shortcode']);
|
||
|
||
// NEU: Cron Hook
|
||
add_action('wis_daily_deal_event', [WIS_Activator::class, 'run_daily_deal']);
|
||
|
||
// Bulk Delete aktivieren
|
||
add_filter('bulk_actions-edit-wis_item', [WIS_Bulk_Actions::class, 'register_bulk_actions']);
|
||
add_filter('bulk_actions-edit-wis_coupon', [WIS_Bulk_Actions::class, 'register_bulk_actions']);
|
||
add_filter('bulk_actions-edit-wis_server', [WIS_Bulk_Actions::class, 'register_bulk_actions']);
|
||
|
||
add_filter('handle_bulk_actions-edit-wis_item', [WIS_Bulk_Actions::class, 'handle_bulk_actions'], 10, 3);
|
||
add_filter('handle_bulk_actions-edit-wis_coupon', [WIS_Bulk_Actions::class, 'handle_bulk_actions'], 10, 3);
|
||
add_filter('handle_bulk_actions-edit-wis_server', [WIS_Bulk_Actions::class, 'handle_bulk_actions'], 10, 3);
|
||
|
||
add_action('admin_notices', [WIS_Bulk_Actions::class, 'admin_notices']);
|
||
?>
|