2473 lines
118 KiB
PHP
2473 lines
118 KiB
PHP
<?php
|
||
/*
|
||
Plugin Name: WP Ingame Shop Pro
|
||
Plugin URI: https://git.viper.ipv64.net/M_Viper/WP-Ingame-Shop-Pro
|
||
Description: Vollautomatischer Shop mit Warenkorb + eigener DB-Struktur (SPIGOT COMPATIBLE)
|
||
Version: 2.1.0
|
||
Author: M_Viper
|
||
Author URI: https://m-viper.de
|
||
Requires at least: 6.9.1
|
||
Tested up to: 6.9.1
|
||
Requires PHP: 7.4
|
||
License: GPL-2.0-or-later
|
||
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||
Text Domain: WP-Ingame-Shop-Pro
|
||
Tags: shop, items, minecraft, coupons, deals, categories
|
||
*/
|
||
|
||
if (!defined('ABSPATH')) exit;
|
||
|
||
// Plugin Constants
|
||
define('WIS_VERSION', '2.1.0');
|
||
define('WIS_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||
define('WIS_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||
|
||
// ===========================================================
|
||
// ACTIVATION & DATABASE
|
||
// ===========================================================
|
||
class WIS_Activator {
|
||
public static function activate() {
|
||
self::create_tables();
|
||
self::set_default_options();
|
||
|
||
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;
|
||
$charset_collate = $wpdb->get_charset_collate();
|
||
|
||
$tables = [];
|
||
|
||
// Items
|
||
$tables[] = "CREATE TABLE {$wpdb->prefix}wis_items (
|
||
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
||
item_id varchar(100) NOT NULL,
|
||
name varchar(255) NOT NULL,
|
||
description text,
|
||
price int(11) DEFAULT 0,
|
||
offer_price int(11) DEFAULT 0,
|
||
is_offer tinyint(1) DEFAULT 0,
|
||
is_daily_deal tinyint(1) DEFAULT 0,
|
||
servers text,
|
||
categories text,
|
||
status varchar(20) DEFAULT 'draft',
|
||
created_at datetime DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY item_id (item_id),
|
||
KEY status (status)
|
||
) $charset_collate;";
|
||
|
||
// Orders
|
||
$tables[] = "CREATE TABLE {$wpdb->prefix}wis_orders (
|
||
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,
|
||
PRIMARY KEY (id),
|
||
KEY player_name (player_name),
|
||
KEY status (status)
|
||
) $charset_collate;";
|
||
|
||
// Coupons
|
||
$tables[] = "CREATE TABLE {$wpdb->prefix}wis_coupons (
|
||
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,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY code (code)
|
||
) $charset_collate;";
|
||
|
||
// Servers
|
||
$tables[] = "CREATE TABLE {$wpdb->prefix}wis_servers (
|
||
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
||
slug varchar(100) NOT NULL,
|
||
name varchar(255) NOT NULL,
|
||
created_at datetime DEFAULT CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY slug (slug)
|
||
) $charset_collate;";
|
||
|
||
// Categories
|
||
$tables[] = "CREATE TABLE {$wpdb->prefix}wis_categories (
|
||
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
||
name varchar(255) NOT NULL,
|
||
slug varchar(100) NOT NULL,
|
||
created_at datetime DEFAULT CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY slug (slug)
|
||
) $charset_collate;";
|
||
|
||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||
foreach ($tables as $sql) {
|
||
dbDelta($sql);
|
||
}
|
||
}
|
||
|
||
private static function set_default_options() {
|
||
$defaults = [
|
||
'wis_currency_name' => 'Coins',
|
||
'wis_image_base_url' => 'https://git.viper.ipv64.net/M_Viper/minecraft-items/raw/branch/main/images/',
|
||
'wis_header_text' => '✅ Auto-Bilder | 💰 Sicherer Checkout | 🎮 Ingame-Bestätigung',
|
||
'wis_coupon_exclude_offers' => '0',
|
||
'wis_daily_deal_enabled' => '0',
|
||
'wis_daily_deal_discount' => '20'
|
||
];
|
||
|
||
foreach ($defaults as $key => $value) {
|
||
if (get_option($key) === false) {
|
||
add_option($key, $value);
|
||
}
|
||
}
|
||
}
|
||
|
||
public static function run_daily_deal() {
|
||
if (get_option('wis_daily_deal_enabled') !== '1') return;
|
||
|
||
global $wpdb;
|
||
$table = $wpdb->prefix . 'wis_items';
|
||
$discount = intval(get_option('wis_daily_deal_discount', 20));
|
||
|
||
// Reset old daily deal
|
||
$wpdb->update($table, ['is_daily_deal' => 0], ['is_daily_deal' => 1]);
|
||
|
||
// Select random item
|
||
$item = $wpdb->get_row("SELECT * FROM $table WHERE status = 'publish' AND price > 0 ORDER BY RAND() LIMIT 1");
|
||
|
||
if ($item) {
|
||
// Syntax Error Fix here: removed extra parent
|
||
$offer_price = max(0, floor($item->price - ($item->price * ($discount / 100))));
|
||
$wpdb->update($table, [
|
||
'is_daily_deal' => 1,
|
||
'is_offer' => 1,
|
||
'offer_price' => $offer_price
|
||
], ['id' => $item->id]);
|
||
}
|
||
}
|
||
|
||
public static function reset_shop() {
|
||
global $wpdb;
|
||
|
||
$tables = [
|
||
'wis_items', 'wis_orders', 'wis_coupons',
|
||
'wis_servers', 'wis_categories'
|
||
];
|
||
|
||
foreach ($tables as $table) {
|
||
$wpdb->query("TRUNCATE TABLE {$wpdb->prefix}$table");
|
||
}
|
||
|
||
self::set_default_options();
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// ===========================================================
|
||
// DATABASE HELPER
|
||
// ===========================================================
|
||
class WIS_DB {
|
||
public static function get_items($args = []) {
|
||
global $wpdb;
|
||
$table = $wpdb->prefix . 'wis_items';
|
||
|
||
$where = isset($args['status']) ? $wpdb->prepare("WHERE status = %s", $args['status']) : "WHERE 1=1";
|
||
$limit = isset($args['limit']) ? "LIMIT " . intval($args['limit']) : "";
|
||
$orderby = isset($args['orderby']) ? "ORDER BY " . sanitize_text_field($args['orderby']) : "ORDER BY name ASC";
|
||
|
||
return $wpdb->get_results("SELECT * FROM $table $where $orderby $limit");
|
||
}
|
||
|
||
public static function get_item($id) {
|
||
global $wpdb;
|
||
return $wpdb->get_row($wpdb->prepare(
|
||
"SELECT * FROM {$wpdb->prefix}wis_items WHERE id = %d",
|
||
$id
|
||
));
|
||
}
|
||
|
||
public static function get_item_by_item_id($item_id) {
|
||
global $wpdb;
|
||
return $wpdb->get_row($wpdb->prepare(
|
||
"SELECT * FROM {$wpdb->prefix}wis_items WHERE item_id = %s",
|
||
$item_id
|
||
));
|
||
}
|
||
|
||
public static function insert_item($data) {
|
||
global $wpdb;
|
||
return $wpdb->insert($wpdb->prefix . 'wis_items', $data);
|
||
}
|
||
|
||
public static function update_item($id, $data) {
|
||
global $wpdb;
|
||
return $wpdb->update($wpdb->prefix . 'wis_items', $data, ['id' => $id]);
|
||
}
|
||
|
||
public static function delete_item($id) {
|
||
global $wpdb;
|
||
return $wpdb->delete($wpdb->prefix . 'wis_items', ['id' => $id]);
|
||
}
|
||
|
||
// Servers
|
||
public static function get_servers() {
|
||
global $wpdb;
|
||
return $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wis_servers ORDER BY name ASC");
|
||
}
|
||
|
||
public static function insert_server($slug, $name) {
|
||
global $wpdb;
|
||
return $wpdb->insert($wpdb->prefix . 'wis_servers', [
|
||
'slug' => sanitize_title($slug),
|
||
'name' => sanitize_text_field($name)
|
||
]);
|
||
}
|
||
|
||
public static function delete_server($id) {
|
||
global $wpdb;
|
||
return $wpdb->delete($wpdb->prefix . 'wis_servers', ['id' => $id]);
|
||
}
|
||
|
||
// Categories
|
||
public static function get_categories() {
|
||
global $wpdb;
|
||
return $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wis_categories ORDER BY name ASC");
|
||
}
|
||
|
||
public static function insert_category($name) {
|
||
global $wpdb;
|
||
return $wpdb->insert($wpdb->prefix . 'wis_categories', [
|
||
'name' => sanitize_text_field($name),
|
||
'slug' => sanitize_title($name)
|
||
]);
|
||
}
|
||
|
||
public static function delete_category($id) {
|
||
global $wpdb;
|
||
return $wpdb->delete($wpdb->prefix . 'wis_categories', ['id' => $id]);
|
||
}
|
||
|
||
// Coupons
|
||
public static function get_coupons() {
|
||
global $wpdb;
|
||
return $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wis_coupons ORDER BY created_at DESC");
|
||
}
|
||
|
||
public static function get_coupon_by_code($code) {
|
||
global $wpdb;
|
||
return $wpdb->get_row($wpdb->prepare(
|
||
"SELECT * FROM {$wpdb->prefix}wis_coupons WHERE code = %s",
|
||
strtoupper($code)
|
||
));
|
||
}
|
||
|
||
public static function insert_coupon($data) {
|
||
global $wpdb;
|
||
$data['code'] = strtoupper($data['code']);
|
||
return $wpdb->insert($wpdb->prefix . 'wis_coupons', $data);
|
||
}
|
||
|
||
public static function update_coupon($id, $data) {
|
||
global $wpdb;
|
||
if (isset($data['code'])) {
|
||
$data['code'] = strtoupper($data['code']);
|
||
}
|
||
return $wpdb->update($wpdb->prefix . 'wis_coupons', $data, ['id' => $id]);
|
||
}
|
||
|
||
public static function delete_coupon($id) {
|
||
global $wpdb;
|
||
return $wpdb->delete($wpdb->prefix . 'wis_coupons', ['id' => $id]);
|
||
}
|
||
|
||
// Orders
|
||
public static function get_orders($limit = 100) {
|
||
global $wpdb;
|
||
return $wpdb->get_results($wpdb->prepare(
|
||
"SELECT * FROM {$wpdb->prefix}wis_orders ORDER BY created_at DESC LIMIT %d",
|
||
$limit
|
||
));
|
||
}
|
||
|
||
public static function get_order($id) {
|
||
global $wpdb;
|
||
return $wpdb->get_row($wpdb->prepare(
|
||
"SELECT * FROM {$wpdb->prefix}wis_orders WHERE id = %d",
|
||
$id
|
||
));
|
||
}
|
||
|
||
public static function insert_order($data) {
|
||
global $wpdb;
|
||
return $wpdb->insert($wpdb->prefix . 'wis_orders', $data);
|
||
}
|
||
|
||
public static function update_order_status($id, $status) {
|
||
global $wpdb;
|
||
return $wpdb->update(
|
||
$wpdb->prefix . 'wis_orders',
|
||
['status' => $status],
|
||
['id' => $id]
|
||
);
|
||
}
|
||
|
||
public static function delete_order($id) {
|
||
global $wpdb;
|
||
return $wpdb->delete($wpdb->prefix . 'wis_orders', ['id' => $id]);
|
||
}
|
||
}
|
||
|
||
// ===========================================================
|
||
// ADMIN PAGES
|
||
// ===========================================================
|
||
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', 'Einstellungen', 'Einstellungen', 'manage_options', 'wis_shop');
|
||
add_submenu_page('wis_shop', 'Items', 'Items', 'manage_options', 'wis_items', [self::class, 'page_items']);
|
||
add_submenu_page('wis_shop', 'Bestellungen', 'Bestellungen', 'manage_options', 'wis_orders', [self::class, 'page_orders']);
|
||
add_submenu_page('wis_shop', 'Server', 'Server', 'manage_options', 'wis_servers', [self::class, 'page_servers']);
|
||
add_submenu_page('wis_shop', 'Kategorien', 'Kategorien', 'manage_options', 'wis_categories', [self::class, 'page_categories']);
|
||
add_submenu_page('wis_shop', 'Gutscheine', 'Gutscheine', 'manage_options', 'wis_coupons', [self::class, 'page_coupons']);
|
||
add_submenu_page('wis_shop', 'JSON Export/Import', 'JSON Tools', 'manage_options', 'wis_json', [self::class, 'page_json']);
|
||
add_submenu_page('wis_shop', 'Shop Reset', '🔄 Reset', 'manage_options', 'wis_reset', [self::class, 'page_reset']);
|
||
add_submenu_page('wis_shop', 'Top Spender', 'Top Spender', 'manage_options', 'wis_top_spenders', [self::class, 'page_top_spenders']);
|
||
}
|
||
|
||
public static function page_overview() {
|
||
// Handle settings save
|
||
if (isset($_POST['wis_save_settings']) && check_admin_referer('wis_settings')) {
|
||
update_option('wis_currency_name', sanitize_text_field($_POST['wis_currency_name']));
|
||
update_option('wis_image_base_url', esc_url_raw($_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');
|
||
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>';
|
||
}
|
||
|
||
global $wpdb;
|
||
$stats = [
|
||
'items' => $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wis_items"),
|
||
'orders' => $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wis_orders"),
|
||
'servers' => $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wis_servers"),
|
||
'coupons' => $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wis_coupons")
|
||
];
|
||
|
||
?>
|
||
<div class="wrap">
|
||
<h1>🛒 WP Ingame Shop Pro <span style="color:#28a745;">v<?php echo WIS_VERSION; ?></span></h1>
|
||
|
||
<div class="card" style="max-width:800px; margin-top:20px;">
|
||
<h2>📊 Statistiken</h2>
|
||
<table class="widefat">
|
||
<tr><th>Items im Shop:</th><td><strong><?php echo $stats['items']; ?></strong></td></tr>
|
||
<tr><th>Bestellungen:</th><td><strong><?php echo $stats['orders']; ?></strong></td></tr>
|
||
<tr><th>Server:</th><td><strong><?php echo $stats['servers']; ?></strong></td></tr>
|
||
<tr><th>Gutscheine:</th><td><strong><?php echo $stats['coupons']; ?></strong></td></tr>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="card" style="max-width:800px; margin-top:20px; padding:20px;">
|
||
<h2>⚙️ Einstellungen</h2>
|
||
<form method="post">
|
||
<?php wp_nonce_field('wis_settings'); ?>
|
||
<table class="form-table">
|
||
<tr>
|
||
<th><label for="header_text">Shop Header Text</label></th>
|
||
<td>
|
||
<textarea id="header_text" name="wis_header_text" class="large-text" rows="2"><?php echo esc_textarea(get_option('wis_header_text')); ?></textarea>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th><label for="currency">Währungsname</label></th>
|
||
<td>
|
||
<input type="text" id="currency" name="wis_currency_name" value="<?php echo esc_attr(get_option('wis_currency_name')); ?>" class="regular-text">
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th><label for="image_url">Bilder Basis-URL</label></th>
|
||
<td>
|
||
<input type="text" id="image_url" name="wis_image_base_url" value="<?php echo esc_attr(get_option('wis_image_base_url')); ?>" class="large-text code">
|
||
<p class="description">Muss mit / enden! Z.B.: https://git.viper.ipv64.net/.../images/</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Gutscheine bei Angeboten</th>
|
||
<td>
|
||
<label>
|
||
<input type="checkbox" name="wis_coupon_exclude_offers" value="1" <?php checked(get_option('wis_coupon_exclude_offers'), '1'); ?>>
|
||
Gutscheine NICHT auf Angebote anwenden
|
||
</label>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Daily Deal</th>
|
||
<td>
|
||
<label>
|
||
<input type="checkbox" name="wis_daily_deal_enabled" value="1" <?php checked(get_option('wis_daily_deal_enabled'), '1'); ?>>
|
||
Automatisches Tagesangebot aktivieren
|
||
</label>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th><label for="discount">Daily Deal Rabatt (%)</label></th>
|
||
<td>
|
||
<input type="number" id="discount" name="wis_daily_deal_discount" value="<?php echo esc_attr(get_option('wis_daily_deal_discount', 20)); ?>" min="1" max="99">
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
<p class="submit">
|
||
<input type="submit" name="wis_save_settings" class="button button-primary" value="Einstellungen speichern">
|
||
</p>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
public static function page_items() {
|
||
// ===========================================================
|
||
// HANDLE BULK ACTIONS (NEU)
|
||
// ===========================================================
|
||
|
||
// 1. Bulk Save: Die eigentliche Änderung in der Datenbank
|
||
if (isset($_POST['wis_bulk_save']) && check_admin_referer('wis_bulk_edit')) {
|
||
$ids = isset($_POST['item_ids']) ? array_map('intval', $_POST['item_ids']) : [];
|
||
$action = sanitize_text_field($_POST['bulk_action_type']);
|
||
|
||
if (!empty($ids) && !empty($action)) {
|
||
$count = 0;
|
||
foreach ($ids as $id) {
|
||
$data = [];
|
||
|
||
switch ($action) {
|
||
case 'price':
|
||
$new_price = intval($_POST['bulk_price']);
|
||
if ($new_price >= 0) {
|
||
$data['price'] = $new_price;
|
||
}
|
||
break;
|
||
|
||
case 'offer':
|
||
$is_offer = isset($_POST['bulk_is_offer']) ? 1 : 0;
|
||
$data['is_offer'] = $is_offer;
|
||
if (!$is_offer) {
|
||
$data['offer_price'] = 0;
|
||
}
|
||
break;
|
||
|
||
case 'status':
|
||
$new_status = sanitize_text_field($_POST['bulk_status']);
|
||
if (in_array($new_status, ['publish', 'draft'])) {
|
||
$data['status'] = $new_status;
|
||
}
|
||
break;
|
||
|
||
case 'server':
|
||
$servers = isset($_POST['bulk_servers']) ? $_POST['bulk_servers'] : [];
|
||
$data['servers'] = json_encode($servers);
|
||
break;
|
||
|
||
case 'category':
|
||
$categories = isset($_POST['bulk_categories']) ? $_POST['bulk_categories'] : [];
|
||
$data['categories'] = json_encode($categories);
|
||
break;
|
||
}
|
||
|
||
if (!empty($data)) {
|
||
WIS_DB::update_item($id, $data);
|
||
$count++;
|
||
}
|
||
}
|
||
echo '<div class="updated"><p>✅ ' . $count . ' Items erfolgreich aktualisiert!</p></div>';
|
||
}
|
||
}
|
||
|
||
// 2. Bulk Apply: Zeigt das Formular für die gewählte Aktion an
|
||
if (isset($_POST['wis_bulk_apply']) && !empty($_POST['wis_bulk_action']) && isset($_POST['item_ids'])) {
|
||
$action = sanitize_text_field($_POST['wis_bulk_action']);
|
||
$selected_ids = array_map('intval', $_POST['item_ids']);
|
||
|
||
// Daten für Dropdowns laden
|
||
$all_servers = WIS_DB::get_servers();
|
||
$all_categories = WIS_DB::get_categories();
|
||
|
||
?>
|
||
<div class="wrap">
|
||
<h1>📦 Mehrfachbearbeitung</h1>
|
||
<p><strong>Aktion:</strong> <?php echo esc_html(ucfirst($action)); ?> für <?php echo count($selected_ids); ?> Items.</p>
|
||
|
||
<div class="card" style="max-width:800px; padding:20px; margin-top:20px;">
|
||
<form method="post">
|
||
<?php wp_nonce_field('wis_bulk_edit'); ?>
|
||
<input type="hidden" name="bulk_action_type" value="<?php echo esc_attr($action); ?>">
|
||
|
||
<!-- IDs als Hidden Fields mitschicken -->
|
||
<?php foreach ($selected_ids as $id): ?>
|
||
<input type="hidden" name="item_ids[]" value="<?php echo $id; ?>">
|
||
<?php endforeach; ?>
|
||
|
||
<table class="form-table">
|
||
<?php if ($action === 'price'): ?>
|
||
<tr>
|
||
<th><label>Neuer Preis</label></th>
|
||
<td>
|
||
<input type="number" name="bulk_price" class="regular-text" min="0" required>
|
||
</td>
|
||
</tr>
|
||
<?php elseif ($action === 'offer'): ?>
|
||
<tr>
|
||
<th><label>Als Angebot markieren?</label></th>
|
||
<td>
|
||
<label>
|
||
<input type="checkbox" name="bulk_is_offer" value="1">
|
||
Ja, als Angebot aktivieren
|
||
</label>
|
||
</td>
|
||
</tr>
|
||
<?php elseif ($action === 'status'): ?>
|
||
<tr>
|
||
<th><label>Neuer Status</label></th>
|
||
<td>
|
||
<select name="bulk_status">
|
||
<option value="publish">Aktivieren (Publish)</option>
|
||
<option value="draft">Deaktivieren (Draft)</option>
|
||
</select>
|
||
</td>
|
||
</tr>
|
||
<?php elseif ($action === 'server'): ?>
|
||
<tr>
|
||
<th><label>Server zuweisen</label></th>
|
||
<td>
|
||
<?php if (empty($all_servers)): ?>
|
||
<p>Keine Server vorhanden.</p>
|
||
<?php else: ?>
|
||
<?php foreach ($all_servers as $s): ?>
|
||
<label style="display:block; margin-bottom:5px;">
|
||
<input type="checkbox" name="bulk_servers[]" value="<?php echo esc_attr($s->slug); ?>">
|
||
<?php echo esc_html($s->name); ?>
|
||
</label>
|
||
<?php endforeach; ?>
|
||
<p class="description">Hake die Server an, denen die Items zugewiesen werden sollen (überschreibt aktuelle Zuweisung).</p>
|
||
<?php endif; ?>
|
||
</td>
|
||
</tr>
|
||
<?php elseif ($action === 'category'): ?>
|
||
<tr>
|
||
<th><label>Kategorie zuweisen</label></th>
|
||
<td>
|
||
<?php if (empty($all_categories)): ?>
|
||
<p>Keine Kategorien vorhanden.</p>
|
||
<?php else: ?>
|
||
<?php foreach ($all_categories as $c): ?>
|
||
<label style="display:block; margin-bottom:5px;">
|
||
<input type="checkbox" name="bulk_categories[]" value="<?php echo esc_attr($c->slug); ?>">
|
||
<?php echo esc_html($c->name); ?>
|
||
</label>
|
||
<?php endforeach; ?>
|
||
<p class="description">Hake die Kategorien an (überschreibt aktuelle Zuweisung).</p>
|
||
<?php endif; ?>
|
||
</td>
|
||
</tr>
|
||
<?php endif; ?>
|
||
</table>
|
||
|
||
<p class="submit">
|
||
<a href="<?php echo admin_url('admin.php?page=wis_items'); ?>" class="button">Abbrechen</a>
|
||
<input type="submit" name="wis_bulk_save" class="button button-primary" value="Jetzt ändern">
|
||
</p>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
return; // WICHTIG: Hier stoppen, damit die normale Liste nicht geladen wird
|
||
}
|
||
|
||
// ===========================================================
|
||
// BESTEHENDER CODE (Single Actions & Edit Form)
|
||
// ===========================================================
|
||
|
||
// Handle actions
|
||
if (isset($_GET['action'], $_GET['id'], $_GET['_wpnonce'])) {
|
||
if (!wp_verify_nonce($_GET['_wpnonce'], 'wis_item_action')) {
|
||
wp_die('Security check failed');
|
||
}
|
||
|
||
$id = intval($_GET['id']);
|
||
|
||
if ($_GET['action'] === 'delete') {
|
||
WIS_DB::delete_item($id);
|
||
echo '<div class="updated"><p>✅ Item gelöscht!</p></div>';
|
||
} elseif ($_GET['action'] === 'toggle_status') {
|
||
$item = WIS_DB::get_item($id);
|
||
$new_status = ($item->status === 'publish') ? 'draft' : 'publish';
|
||
WIS_DB::update_item($id, ['status' => $new_status]);
|
||
echo '<div class="updated"><p>✅ Status geändert!</p></div>';
|
||
}
|
||
}
|
||
|
||
// Handle edit/add
|
||
if (isset($_POST['wis_save_item'])) {
|
||
check_admin_referer('wis_item_form');
|
||
|
||
$data = [
|
||
'item_id' => sanitize_text_field($_POST['item_id']),
|
||
'name' => sanitize_text_field($_POST['name']),
|
||
'description' => sanitize_textarea_field($_POST['description']),
|
||
'price' => intval($_POST['price']),
|
||
'offer_price' => intval($_POST['offer_price']),
|
||
'is_offer' => isset($_POST['is_offer']) ? 1 : 0,
|
||
'servers' => isset($_POST['servers']) ? json_encode($_POST['servers']) : '[]',
|
||
'categories' => isset($_POST['categories']) ? json_encode($_POST['categories']) : '[]',
|
||
'status' => intval($_POST['price']) > 0 ? 'publish' : 'draft'
|
||
];
|
||
|
||
if (isset($_GET['edit'])) {
|
||
WIS_DB::update_item(intval($_GET['edit']), $data);
|
||
echo '<div class="updated"><p>✅ Item gespeichert!</p></div>';
|
||
} else {
|
||
WIS_DB::insert_item($data);
|
||
echo '<div class="updated"><p>✅ Item erstellt!</p></div>';
|
||
}
|
||
}
|
||
|
||
// Show form if edit or add
|
||
if (isset($_GET['edit']) || isset($_GET['add'])) {
|
||
$item = isset($_GET['edit']) ? WIS_DB::get_item(intval($_GET['edit'])) : null;
|
||
$servers = WIS_DB::get_servers();
|
||
$categories = WIS_DB::get_categories();
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
|
||
$item_servers = $item ? json_decode($item->servers, true) : [];
|
||
$item_cats = $item ? json_decode($item->categories, true) : [];
|
||
?>
|
||
<div class="wrap">
|
||
<h1><?php echo $item ? 'Item bearbeiten' : 'Neues Item'; ?></h1>
|
||
<a href="<?php echo admin_url('admin.php?page=wis_items'); ?>" class="button">← Zurück zur Liste</a>
|
||
|
||
<form method="post" style="max-width:800px; margin-top:20px;">
|
||
<?php wp_nonce_field('wis_item_form'); ?>
|
||
|
||
<table class="form-table">
|
||
<tr>
|
||
<th><label for="name">Name *</label></th>
|
||
<td><input type="text" id="name" name="name" value="<?php echo $item ? esc_attr($item->name) : ''; ?>" class="regular-text" required></td>
|
||
</tr>
|
||
<tr>
|
||
<th><label for="item_id">Item ID *</label></th>
|
||
<td>
|
||
<input type="text" id="item_id" name="item_id" value="<?php echo $item ? esc_attr($item->item_id) : ''; ?>" class="regular-text" placeholder="minecraft:diamond" required>
|
||
<p class="description">Z.B.: minecraft:diamond</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th><label for="description">Beschreibung</label></th>
|
||
<td><textarea id="description" name="description" rows="3" class="large-text"><?php echo $item ? esc_textarea($item->description) : ''; ?></textarea></td>
|
||
</tr>
|
||
<tr>
|
||
<th><label for="price">Preis (<?php echo esc_html($currency); ?>) *</label></th>
|
||
<td><input type="number" id="price" name="price" value="<?php echo $item ? esc_attr($item->price) : '0'; ?>" min="0" required></td>
|
||
</tr>
|
||
<tr>
|
||
<th><label for="offer_price">Angebotspreis</label></th>
|
||
<td>
|
||
<input type="number" id="offer_price" name="offer_price" value="<?php echo $item ? esc_attr($item->offer_price) : '0'; ?>" min="0">
|
||
<p class="description">Optional: Wenn gesetzt, wird der normale Preis durchgestrichen</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Markierungen</th>
|
||
<td>
|
||
<label>
|
||
<input type="checkbox" name="is_offer" value="1" <?php echo ($item && $item->is_offer) ? 'checked' : ''; ?>>
|
||
Als Angebot markieren
|
||
</label>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Server</th>
|
||
<td>
|
||
<?php if (empty($servers)): ?>
|
||
<p>Keine Server vorhanden. <a href="<?php echo admin_url('admin.php?page=wis_servers'); ?>">Server erstellen</a></p>
|
||
<?php else: ?>
|
||
<?php foreach ($servers as $server): ?>
|
||
<label style="display:block; margin:5px 0;">
|
||
<input type="checkbox" name="servers[]" value="<?php echo esc_attr($server->slug); ?>" <?php echo in_array($server->slug, $item_servers ?: []) ? 'checked' : ''; ?>>
|
||
<?php echo esc_html($server->name); ?>
|
||
</label>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Kategorien</th>
|
||
<td>
|
||
<?php if (empty($categories)): ?>
|
||
<p>Keine Kategorien vorhanden. <a href="<?php echo admin_url('admin.php?page=wis_categories'); ?>">Kategorien erstellen</a></p>
|
||
<?php else: ?>
|
||
<?php foreach ($categories as $cat): ?>
|
||
<label style="display:block; margin:5px 0;">
|
||
<input type="checkbox" name="categories[]" value="<?php echo esc_attr($cat->slug); ?>" <?php echo in_array($cat->slug, $item_cats ?: []) ? 'checked' : ''; ?>>
|
||
<?php echo esc_html($cat->name); ?>
|
||
</label>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
<p class="submit">
|
||
<input type="submit" name="wis_save_item" class="button button-primary" value="Speichern">
|
||
</p>
|
||
</form>
|
||
</div>
|
||
<?php
|
||
return;
|
||
}
|
||
|
||
// ===========================================================
|
||
// LISTE ANZEIGEN (MIT BULK ACTIONS)
|
||
// ===========================================================
|
||
$items = WIS_DB::get_items();
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
?>
|
||
<div class="wrap">
|
||
<h1>Items <a href="<?php echo admin_url('admin.php?page=wis_items&add=1'); ?>" class="page-title-action">Neu erstellen</a></h1>
|
||
|
||
<!-- Bulk Actions Form Start -->
|
||
<form method="post">
|
||
|
||
<!-- Toolbar -->
|
||
<div class="tablenav top">
|
||
<div class="alignleft actions bulkactions">
|
||
<label for="bulk-action-selector-top" class="screen-reader-text">Massenaktionen wählen</label>
|
||
<select name="wis_bulk_action" id="bulk-action-selector-top">
|
||
<option value="">Massenaktionen</option>
|
||
<option value="server">Server zuweisen</option>
|
||
<option value="category">Kategorie zuweisen</option>
|
||
<option value="price">Preis ändern</option>
|
||
<option value="offer">Angebot ändern</option>
|
||
<option value="status">Aktivieren/Deaktivieren</option>
|
||
</select>
|
||
<input type="submit" name="wis_bulk_apply" class="button action" value="Anwenden">
|
||
</div>
|
||
<br class="clear">
|
||
</div>
|
||
|
||
<table class="widefat fixed striped">
|
||
<thead>
|
||
<tr>
|
||
<th style="width:40px;" class="manage-column column-cb check-column">
|
||
<input type="checkbox" id="cb-select-all-1">
|
||
</th>
|
||
<th>ID</th>
|
||
<th>Name</th>
|
||
<th>Item ID</th>
|
||
<th>Preis</th>
|
||
<th>Status</th>
|
||
<th>Aktionen</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php if (empty($items)): ?>
|
||
<tr><td colspan="7" style="text-align:center; padding:40px;">Noch keine Items vorhanden.</td></tr>
|
||
<?php else: ?>
|
||
<?php foreach ($items as $item): ?>
|
||
<tr>
|
||
<th class="check-column">
|
||
<input type="checkbox" name="item_ids[]" id="cb-select-<?php echo $item->id; ?>" value="<?php echo $item->id; ?>">
|
||
</th>
|
||
<td><?php echo esc_html($item->id); ?></td>
|
||
<td><strong><?php echo esc_html($item->name); ?></strong></td>
|
||
<td><code><?php echo esc_html($item->item_id); ?></code></td>
|
||
<td><?php echo esc_html($item->price); ?> <?php echo esc_html($currency); ?></td>
|
||
<td>
|
||
<?php if ($item->status === 'publish'): ?>
|
||
<span style="color:green;">✅ Aktiv</span>
|
||
<?php else: ?>
|
||
<span style="color:orange;">📝 Entwurf</span>
|
||
<?php endif; ?>
|
||
</td>
|
||
<td>
|
||
<a href="<?php echo wp_nonce_url(admin_url('admin.php?page=wis_items&edit=' . $item->id), 'wis_item_action', '_wpnonce'); ?>" class="button button-small">Bearbeiten</a>
|
||
<a href="<?php echo wp_nonce_url(admin_url('admin.php?page=wis_items&action=delete&id=' . $item->id), 'wis_item_action', '_wpnonce'); ?>" class="button button-small" onclick="return confirm('Wirklich löschen?');" style="color:red;">Löschen</a>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</form>
|
||
<!-- Bulk Actions Form End -->
|
||
</div>
|
||
|
||
<script>
|
||
// Einfaches Skript für "Alle auswählen" Checkbox
|
||
(function() {
|
||
const mainCb = document.getElementById('cb-select-all-1');
|
||
if(mainCb) {
|
||
mainCb.addEventListener('change', function(e) {
|
||
const cbs = document.querySelectorAll('input[name="item_ids[]"]');
|
||
cbs.forEach(cb => cb.checked = e.target.checked);
|
||
});
|
||
}
|
||
})();
|
||
</script>
|
||
<?php
|
||
}
|
||
|
||
public static function page_servers() {
|
||
// Handle add
|
||
if (isset($_POST['wis_add_server'])) {
|
||
check_admin_referer('wis_server_form');
|
||
WIS_DB::insert_server($_POST['slug'], $_POST['name']);
|
||
echo '<div class="updated"><p>✅ Server erstellt!</p></div>';
|
||
}
|
||
|
||
// Handle delete
|
||
if (isset($_GET['action'], $_GET['id']) && $_GET['action'] === 'delete') {
|
||
check_admin_referer('wis_server_action', '_wpnonce');
|
||
WIS_DB::delete_server(intval($_GET['id']));
|
||
echo '<div class="updated"><p>✅ Server gelöscht!</p></div>';
|
||
}
|
||
|
||
$servers = WIS_DB::get_servers();
|
||
?>
|
||
<div class="wrap">
|
||
<h1>Server</h1>
|
||
|
||
<div class="card" style="max-width:600px; padding:20px; margin-top:20px;">
|
||
<h2>Neuen Server erstellen</h2>
|
||
<form method="post">
|
||
<?php wp_nonce_field('wis_server_form'); ?>
|
||
<table class="form-table">
|
||
<tr>
|
||
<th><label for="name">Name *</label></th>
|
||
<td><input type="text" id="name" name="name" class="regular-text" required></td>
|
||
</tr>
|
||
<tr>
|
||
<th><label for="slug">Slug *</label></th>
|
||
<td>
|
||
<input type="text" id="slug" name="slug" class="regular-text" placeholder="survival" required>
|
||
<p class="description">Kleinbuchstaben ohne Leerzeichen</p>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
<p class="submit">
|
||
<input type="submit" name="wis_add_server" class="button button-primary" value="Server erstellen">
|
||
</p>
|
||
</form>
|
||
</div>
|
||
|
||
<h2 style="margin-top:40px;">Vorhandene Server</h2>
|
||
<table class="widefat fixed striped">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Name</th>
|
||
<th>Slug</th>
|
||
<th>Aktionen</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php if (empty($servers)): ?>
|
||
<tr><td colspan="4" style="text-align:center; padding:40px;">Noch keine Server vorhanden.</td></tr>
|
||
<?php else: ?>
|
||
<?php foreach ($servers as $server): ?>
|
||
<tr>
|
||
<td><?php echo esc_html($server->id); ?></td>
|
||
<td><strong><?php echo esc_html($server->name); ?></strong></td>
|
||
<td><code><?php echo esc_html($server->slug); ?></code></td>
|
||
<td>
|
||
<a href="<?php echo wp_nonce_url(admin_url('admin.php?page=wis_servers&action=delete&id=' . $server->id), 'wis_server_action', '_wpnonce'); ?>" class="button button-small" onclick="return confirm('Wirklich löschen?');" style="color:red;">Löschen</a>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
public static function page_categories() {
|
||
// Handle add
|
||
if (isset($_POST['wis_add_category'])) {
|
||
check_admin_referer('wis_category_form');
|
||
WIS_DB::insert_category($_POST['name']);
|
||
echo '<div class="updated"><p>✅ Kategorie erstellt!</p></div>';
|
||
}
|
||
|
||
// Handle delete
|
||
if (isset($_GET['action'], $_GET['id']) && $_GET['action'] === 'delete') {
|
||
check_admin_referer('wis_category_action', '_wpnonce');
|
||
WIS_DB::delete_category(intval($_GET['id']));
|
||
echo '<div class="updated"><p>✅ Kategorie gelöscht!</p></div>';
|
||
}
|
||
|
||
$categories = WIS_DB::get_categories();
|
||
?>
|
||
<div class="wrap">
|
||
<h1>Kategorien</h1>
|
||
|
||
<div class="card" style="max-width:600px; padding:20px; margin-top:20px;">
|
||
<h2>Neue Kategorie erstellen</h2>
|
||
<form method="post">
|
||
<?php wp_nonce_field('wis_category_form'); ?>
|
||
<table class="form-table">
|
||
<tr>
|
||
<th><label for="name">Name *</label></th>
|
||
<td><input type="text" id="name" name="name" class="regular-text" required></td>
|
||
</tr>
|
||
</table>
|
||
<p class="submit">
|
||
<input type="submit" name="wis_add_category" class="button button-primary" value="Kategorie erstellen">
|
||
</p>
|
||
</form>
|
||
</div>
|
||
|
||
<h2 style="margin-top:40px;">Vorhandene Kategorien</h2>
|
||
<table class="widefat fixed striped">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Name</th>
|
||
<th>Slug</th>
|
||
<th>Aktionen</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php if (empty($categories)): ?>
|
||
<tr><td colspan="4" style="text-align:center; padding:40px;">Noch keine Kategorien vorhanden.</td></tr>
|
||
<?php else: ?>
|
||
<?php foreach ($categories as $cat): ?>
|
||
<tr>
|
||
<td><?php echo esc_html($cat->id); ?></td>
|
||
<td><strong><?php echo esc_html($cat->name); ?></strong></td>
|
||
<td><code><?php echo esc_html($cat->slug); ?></code></td>
|
||
<td>
|
||
<a href="<?php echo wp_nonce_url(admin_url('admin.php?page=wis_categories&action=delete&id=' . $cat->id), 'wis_category_action', '_wpnonce'); ?>" class="button button-small" onclick="return confirm('Wirklich löschen?');" style="color:red;">Löschen</a>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
public static function page_coupons() {
|
||
// Handle add/edit
|
||
if (isset($_POST['wis_save_coupon'])) {
|
||
check_admin_referer('wis_coupon_form');
|
||
|
||
$data = [
|
||
'code' => sanitize_text_field($_POST['code']),
|
||
'value' => intval($_POST['value']),
|
||
'type' => sanitize_text_field($_POST['type']),
|
||
'usage_limit' => intval($_POST['usage_limit']),
|
||
'expiry' => !empty($_POST['expiry']) ? sanitize_text_field($_POST['expiry']) : null
|
||
];
|
||
|
||
if (isset($_GET['edit'])) {
|
||
unset($data['used_count']); // Don't reset usage count
|
||
WIS_DB::update_coupon(intval($_GET['edit']), $data);
|
||
echo '<div class="updated"><p>✅ Gutschein gespeichert!</p></div>';
|
||
} else {
|
||
$data['used_count'] = 0;
|
||
WIS_DB::insert_coupon($data);
|
||
echo '<div class="updated"><p>✅ Gutschein erstellt!</p></div>';
|
||
}
|
||
}
|
||
|
||
// Handle delete
|
||
if (isset($_GET['action'], $_GET['id']) && $_GET['action'] === 'delete') {
|
||
check_admin_referer('wis_coupon_action', '_wpnonce');
|
||
WIS_DB::delete_coupon(intval($_GET['id']));
|
||
echo '<div class="updated"><p>✅ Gutschein gelöscht!</p></div>';
|
||
}
|
||
|
||
// Show form
|
||
if (isset($_GET['add']) || isset($_GET['edit'])) {
|
||
global $wpdb;
|
||
$coupon = isset($_GET['edit']) ? $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wis_coupons WHERE id = %d", intval($_GET['edit']))) : null;
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
?>
|
||
<div class="wrap">
|
||
<h1><?php echo $coupon ? 'Gutschein bearbeiten' : 'Neuer Gutschein'; ?></h1>
|
||
<a href="<?php echo admin_url('admin.php?page=wis_coupons'); ?>" class="button">← Zurück zur Liste</a>
|
||
|
||
<form method="post" style="max-width:600px; margin-top:20px;">
|
||
<?php wp_nonce_field('wis_coupon_form'); ?>
|
||
<table class="form-table">
|
||
<tr>
|
||
<th><label for="code">Code *</label></th>
|
||
<td>
|
||
<input type="text" id="code" name="code" value="<?php echo $coupon ? esc_attr($coupon->code) : ''; ?>" class="regular-text" style="text-transform:uppercase;" required>
|
||
<p class="description">Z.B.: SUMMER20</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th><label for="type">Typ *</label></th>
|
||
<td>
|
||
<select id="type" name="type" required>
|
||
<option value="fixed" <?php echo ($coupon && $coupon->type === 'fixed') ? 'selected' : ''; ?>>Festbetrag (z.B. 100 <?php echo esc_html($currency); ?> Rabatt)</option>
|
||
<option value="percent" <?php echo ($coupon && $coupon->type === 'percent') ? 'selected' : ''; ?>>Prozentual (z.B. 20% Rabatt)</option>
|
||
</select>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th><label for="value">Wert *</label></th>
|
||
<td>
|
||
<input type="number" id="value" name="value" value="<?php echo $coupon ? esc_attr($coupon->value) : ''; ?>" min="1" required>
|
||
<p class="description">Bei Festbetrag: Betrag in <?php echo esc_html($currency); ?>. Bei Prozent: Zahl ohne %</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th><label for="usage_limit">Nutzungslimit *</label></th>
|
||
<td>
|
||
<input type="number" id="usage_limit" name="usage_limit" value="<?php echo $coupon ? esc_attr($coupon->usage_limit) : '1'; ?>" min="1" required>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th><label for="expiry">Ablaufdatum</label></th>
|
||
<td>
|
||
<input type="date" id="expiry" name="expiry" value="<?php echo $coupon && $coupon->expiry ? esc_attr($coupon->expiry) : ''; ?>">
|
||
<p class="description">Optional</p>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
<p class="submit">
|
||
<input type="submit" name="wis_save_coupon" class="button button-primary" value="Speichern">
|
||
</p>
|
||
</form>
|
||
</div>
|
||
<?php
|
||
return;
|
||
}
|
||
|
||
// List coupons
|
||
$coupons = WIS_DB::get_coupons();
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
?>
|
||
<div class="wrap">
|
||
<h1>Gutscheine <a href="<?php echo admin_url('admin.php?page=wis_coupons&add=1'); ?>" class="page-title-action">Neu erstellen</a></h1>
|
||
|
||
<table class="widefat fixed striped">
|
||
<thead>
|
||
<tr>
|
||
<th>Code</th>
|
||
<th>Rabatt</th>
|
||
<th>Genutzt</th>
|
||
<th>Gültig bis</th>
|
||
<th>Aktionen</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php if (empty($coupons)): ?>
|
||
<tr><td colspan="5" style="text-align:center; padding:40px;">Noch keine Gutscheine vorhanden.</td></tr>
|
||
<?php else: ?>
|
||
<?php foreach ($coupons as $coupon): ?>
|
||
<tr>
|
||
<td><strong><?php echo esc_html($coupon->code); ?></strong></td>
|
||
<td>
|
||
<?php if ($coupon->type === 'percent'): ?>
|
||
<?php echo esc_html($coupon->value); ?>%
|
||
<?php else: ?>
|
||
<?php echo esc_html($coupon->value); ?> <?php echo esc_html($currency); ?>
|
||
<?php endif; ?>
|
||
</td>
|
||
<td><?php echo esc_html($coupon->used_count); ?> / <?php echo esc_html($coupon->usage_limit); ?></td>
|
||
<td><?php echo $coupon->expiry ? esc_html(date('d.m.Y', strtotime($coupon->expiry))) : '∞'; ?></td>
|
||
<td>
|
||
<a href="<?php echo admin_url('admin.php?page=wis_coupons&edit=' . $coupon->id); ?>" class="button button-small">Bearbeiten</a>
|
||
<a href="<?php echo wp_nonce_url(admin_url('admin.php?page=wis_coupons&action=delete&id=' . $coupon->id), 'wis_coupon_action', '_wpnonce'); ?>" class="button button-small" onclick="return confirm('Wirklich löschen?');" style="color:red;">Löschen</a>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
public static function page_orders() {
|
||
// Handle delete
|
||
if (isset($_GET['action'], $_GET['id']) && $_GET['action'] === 'delete') {
|
||
check_admin_referer('wis_order_action', '_wpnonce');
|
||
WIS_DB::delete_order(intval($_GET['id']));
|
||
echo '<div class="updated"><p>✅ Bestellung gelöscht!</p></div>';
|
||
}
|
||
|
||
// Handle status change
|
||
if (isset($_GET['action'], $_GET['id']) && $_GET['action'] === 'complete') {
|
||
check_admin_referer('wis_order_action', '_wpnonce');
|
||
WIS_DB::update_order_status(intval($_GET['id']), 'completed');
|
||
echo '<div class="updated"><p>✅ Status geändert!</p></div>';
|
||
}
|
||
|
||
// Show details
|
||
if (isset($_GET['view'])) {
|
||
$order = WIS_DB::get_order(intval($_GET['view']));
|
||
if (!$order) {
|
||
echo '<div class="error"><p>Bestellung nicht gefunden.</p></div>';
|
||
return;
|
||
}
|
||
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
$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'
|
||
];
|
||
|
||
$decoded = json_decode($order->response, true);
|
||
?>
|
||
<div class="wrap">
|
||
<h1>Bestellung #<?php echo $order->id; ?> - Details</h1>
|
||
<a href="<?php echo admin_url('admin.php?page=wis_orders'); ?>" class="button">← Zurück</a>
|
||
|
||
<table class="widefat" style="max-width:800px; margin-top:20px;">
|
||
<tr><th>ID</th><td><?php echo esc_html($order->id); ?></td></tr>
|
||
<tr><th>Datum</th><td><?php echo esc_html(date('d.m.Y H:i', strtotime($order->created_at))); ?></td></tr>
|
||
<tr><th>Spieler</th><td><strong><?php echo esc_html($order->player_name); ?></strong></td></tr>
|
||
<tr><th>Server</th><td><?php echo esc_html($order->server); ?></td></tr>
|
||
<tr><th>Zusammenfassung</th><td><?php echo esc_html($order->item_title); ?></td></tr>
|
||
<tr><th>Preis</th><td><?php echo esc_html($order->price); ?> <?php echo esc_html($currency); ?></td></tr>
|
||
<tr><th>Status</th><td style="color:<?php echo $status_colors[$order->status] ?? 'black'; ?>; font-weight:bold;"><?php echo $status_labels[$order->status] ?? $order->status; ?></td></tr>
|
||
<tr>
|
||
<th>Details (JSON)</th>
|
||
<td><code style="display:block; background:#eee; padding:10px; font-size:11px; overflow-x:auto;"><?php echo esc_html($order->response); ?></code></td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<?php
|
||
return;
|
||
}
|
||
|
||
// List orders
|
||
$orders = WIS_DB::get_orders();
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
?>
|
||
<div class="wrap">
|
||
<h1>Bestellungen</h1>
|
||
|
||
<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;">Aktionen</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php if (empty($orders)): ?>
|
||
<tr><td colspan="6" style="text-align:center; padding:40px;">Noch keine Bestellungen vorhanden.</td></tr>
|
||
<?php else: ?>
|
||
<?php foreach ($orders as $order): ?>
|
||
<?php
|
||
$status_map = [
|
||
'pending' => 'Warte',
|
||
'processing' => 'Geben...',
|
||
'completed' => 'Fertig',
|
||
'cancelled' => 'Abgebrochen',
|
||
'failed' => 'Fehler'
|
||
];
|
||
$status_colors = [
|
||
'pending' => '#ffc107',
|
||
'processing' => '#0073aa',
|
||
'completed' => 'green',
|
||
'cancelled' => 'red',
|
||
'failed' => 'red'
|
||
];
|
||
?>
|
||
<tr>
|
||
<td><?php echo esc_html(date('d.m.Y H:i', strtotime($order->created_at))); ?></td>
|
||
<td><strong><?php echo esc_html($order->player_name); ?></strong></td>
|
||
<td><?php echo esc_html(substr($order->item_title, 0, 50)) . (strlen($order->item_title) > 50 ? '...' : ''); ?></td>
|
||
<td><?php echo esc_html($order->price); ?> <?php echo esc_html($currency); ?></td>
|
||
<td style="color:<?php echo $status_colors[$order->status] ?? 'black'; ?>; font-weight:bold;"><?php echo $status_map[$order->status] ?? $order->status; ?></td>
|
||
<td>
|
||
<a href="<?php echo admin_url('admin.php?page=wis_orders&view=' . $order->id); ?>" class="button button-small">👁️ Details</a>
|
||
<a href="<?php echo wp_nonce_url(admin_url('admin.php?page=wis_orders&action=delete&id=' . $order->id), 'wis_order_action', '_wpnonce'); ?>" class="button button-small" onclick="return confirm('Wirklich löschen?');" style="color:red;">🗑️ Löschen</a>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
public static function page_json() {
|
||
// Handle export
|
||
if (isset($_POST['wis_generate_json'])) {
|
||
check_admin_referer('wis_json_export');
|
||
|
||
$items = WIS_DB::get_items(['status' => 'publish']);
|
||
$img_base = get_option('wis_image_base_url', '');
|
||
|
||
$json_data = ['items' => []];
|
||
|
||
foreach ($items as $item) {
|
||
$img_name = str_replace(':', '_', $item->item_id) . '.png';
|
||
$json_data['items'][] = [
|
||
'id' => $item->item_id,
|
||
'name' => $item->name,
|
||
'description' => $item->description,
|
||
'price' => intval($item->price),
|
||
'image' => $img_base . $img_name
|
||
];
|
||
}
|
||
|
||
$json_output = json_encode($json_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||
|
||
echo '<div class="wrap"><h1>📦 JSON Export</h1>';
|
||
echo '<div class="updated"><p>✅ JSON erfolgreich generiert!</p></div>';
|
||
echo '<textarea style="width:100%; height:400px; font-family:monospace; font-size:12px;">'.esc_textarea($json_output).'</textarea>';
|
||
echo '<p><button onclick="downloadJSON()" class="button button-primary">💾 Als items.json herunterladen</button></p>';
|
||
echo '<script>
|
||
function downloadJSON() {
|
||
const text = document.querySelector("textarea").value;
|
||
const blob = new Blob([text], {type: "application/json"});
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement("a");
|
||
a.href = url;
|
||
a.download = "items.json";
|
||
a.click();
|
||
}
|
||
</script>';
|
||
echo '<h3>📤 Nächste Schritte:</h3>';
|
||
echo '<ol>';
|
||
echo '<li>Lade die JSON-Datei herunter</li>';
|
||
echo '<li>Gehe zu deinem Gitea Repository</li>';
|
||
echo '<li>Lade die <code>items.json</code> hoch unter: <code>https://git.viper.ipv64.net/M_Viper/WP-Ingame-Shop-Pro</code></li>';
|
||
echo '<li>Klicke dann auf den <strong>Quick-Import</strong> Button unten!</li>';
|
||
echo '</ol>';
|
||
echo '</div>';
|
||
return;
|
||
}
|
||
|
||
// Default Gitea URL
|
||
$default_url = 'https://git.viper.ipv64.net/M_Viper/WP-Ingame-Shop-Pro/raw/branch/main/items.json';
|
||
?>
|
||
<div class="wrap">
|
||
<h1>📦 JSON Export/Import</h1>
|
||
|
||
<div class="card" style="max-width:800px; padding:20px; margin-top:20px;">
|
||
<h2>📤 JSON Export</h2>
|
||
<p>Generiere eine JSON-Datei mit allen deinen Items für Gitea.</p>
|
||
<form method="post">
|
||
<?php wp_nonce_field('wis_json_export'); ?>
|
||
<p class="submit">
|
||
<input type="submit" name="wis_generate_json" class="button button-primary button-large" value="📦 JSON Generieren">
|
||
</p>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="card" style="max-width:800px; padding:20px; margin-top:20px; background:#e8f5e9;">
|
||
<h2>⚡ Quick-Import von Gitea</h2>
|
||
<p><strong>Importiert direkt von deinem Gitea Repository!</strong></p>
|
||
<p style="margin:10px 0; padding:10px; background:#fff; border-left:4px solid #28a745; font-family:monospace; font-size:12px; word-break:break-all;">
|
||
<?php echo esc_html($default_url); ?>
|
||
</p>
|
||
<button type="button" id="btn-quick-import" class="button button-primary button-large" style="background:#28a745; border-color:#28a745;">
|
||
⚡ Quick-Import starten
|
||
</button>
|
||
<div id="quick-import-result" style="margin-top:15px;"></div>
|
||
</div>
|
||
|
||
<div class="card" style="max-width:800px; padding:20px; margin-top:20px;">
|
||
<h2>📥 JSON Import (Manuelle URL)</h2>
|
||
<p>Importiere Items aus einer beliebigen JSON-URL.</p>
|
||
|
||
<div id="wis-import-form">
|
||
<input type="text" id="import-url" class="large-text code" value="<?php echo esc_attr($default_url); ?>" style="margin-bottom:15px;">
|
||
<br>
|
||
<button type="button" id="btn-import" class="button button-primary button-large">📥 Importieren</button>
|
||
<div id="import-result" style="margin-top:15px;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Quick-Import Function
|
||
document.getElementById('btn-quick-import').addEventListener('click', function() {
|
||
const url = '<?php echo esc_js($default_url); ?>';
|
||
const resultDiv = document.getElementById('quick-import-result');
|
||
const btn = this;
|
||
|
||
btn.disabled = true;
|
||
btn.textContent = '⏳ Importiere von Gitea...';
|
||
resultDiv.innerHTML = '<div style="padding:10px; background:#fff3cd; border-left:4px solid:#ffc107; margin-top:10px;">⏳ Lade Items von Gitea...</div>';
|
||
|
||
fetch('<?php echo rest_url('wis/v1/import_json'); ?>', {
|
||
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(data => {
|
||
if (data.success) {
|
||
resultDiv.innerHTML = '<div class="updated" style="margin-top:10px;"><p><strong>✅ Erfolgreich!</strong><br>' + data.imported + ' Items importiert! (' + data.skipped + ' übersprungen)</p></div>';
|
||
setTimeout(() => {
|
||
window.location.href = '<?php echo admin_url('admin.php?page=wis_items'); ?>';
|
||
}, 2000);
|
||
} else {
|
||
resultDiv.innerHTML = '<div class="error" style="margin-top:10px;"><p><strong>❌ Fehler:</strong><br>' + data.message + '</p></div>';
|
||
}
|
||
})
|
||
.catch(e => {
|
||
resultDiv.innerHTML = '<div class="error" style="margin-top:10px;"><p><strong>❌ Netzwerkfehler:</strong><br>' + e.message + '</p></div>';
|
||
})
|
||
.finally(() => {
|
||
btn.disabled = false;
|
||
btn.textContent = '⚡ Quick-Import starten';
|
||
});
|
||
});
|
||
|
||
// Manual Import Function
|
||
document.getElementById('btn-import').addEventListener('click', function() {
|
||
const url = document.getElementById('import-url').value.trim();
|
||
const resultDiv = document.getElementById('import-result');
|
||
|
||
if (!url) {
|
||
resultDiv.innerHTML = '<div class="error"><p>Bitte URL eingeben!</p></div>';
|
||
return;
|
||
}
|
||
|
||
this.disabled = true;
|
||
this.textContent = '⏳ Importiere...';
|
||
|
||
fetch('<?php echo rest_url('wis/v1/import_json'); ?>', {
|
||
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(data => {
|
||
if (data.success) {
|
||
resultDiv.innerHTML = '<div class="updated"><p>✅ ' + data.imported + ' Items importiert! (' + data.skipped + ' übersprungen)</p></div>';
|
||
setTimeout(() => {
|
||
window.location.href = '<?php echo admin_url('admin.php?page=wis_items'); ?>';
|
||
}, 2000);
|
||
} else {
|
||
resultDiv.innerHTML = '<div class="error"><p>❌ Fehler: ' + data.message + '</p></div>';
|
||
}
|
||
})
|
||
.catch(e => {
|
||
resultDiv.innerHTML = '<div class="error"><p>❌ Netzwerkfehler: ' + e.message + '</p></div>';
|
||
})
|
||
.finally(() => {
|
||
this.disabled = false;
|
||
this.textContent = '📥 Importieren';
|
||
});
|
||
});
|
||
</script>
|
||
<?php
|
||
}
|
||
|
||
public static function page_reset() {
|
||
if (isset($_POST['wis_confirm_reset'])) {
|
||
check_admin_referer('wis_reset');
|
||
WIS_Activator::reset_shop();
|
||
echo '<div class="updated"><p>✅ Shop wurde komplett zurückgesetzt!</p></div>';
|
||
}
|
||
|
||
?>
|
||
<div class="wrap">
|
||
<h1>🔄 Shop Reset</h1>
|
||
|
||
<div class="card" style="max-width:800px; padding:20px; background:#fff3cd;">
|
||
<h2 style="color:#856404;">⚠️ WARNUNG</h2>
|
||
<p><strong>Diese Aktion löscht ALLE Daten:</strong></p>
|
||
<ul>
|
||
<li>❌ Alle Items</li>
|
||
<li>❌ Alle Bestellungen</li>
|
||
<li>❌ Alle Gutscheine</li>
|
||
<li>❌ Alle Server</li>
|
||
<li>❌ Alle Kategorien</li>
|
||
</ul>
|
||
<p style="color:red; font-weight:bold;">Diese Aktion kann NICHT rückgängig gemacht werden!</p>
|
||
|
||
<form method="post" onsubmit="return confirm('WIRKLICH ALLE DATEN LÖSCHEN? Dies kann nicht rückgängig gemacht werden!');">
|
||
<?php wp_nonce_field('wis_reset'); ?>
|
||
<p class="submit">
|
||
<input type="submit" name="wis_confirm_reset" class="button button-secondary button-large" value="🗑️ Shop jetzt zurücksetzen">
|
||
</p>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
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'
|
||
GROUP BY player_name
|
||
ORDER BY total_spent DESC
|
||
LIMIT 50
|
||
");
|
||
|
||
?>
|
||
<div class="wrap">
|
||
<h1>🏆 Top Spender</h1>
|
||
<p>Spieler mit den höchsten Gesamtausgaben</p>
|
||
|
||
<table class="widefat fixed striped">
|
||
<thead>
|
||
<tr>
|
||
<th style="width:80px;">Rang</th>
|
||
<th>Spieler</th>
|
||
<th style="width:150px;">Ausgegeben</th>
|
||
<th style="width:150px;">Bestellungen</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php if (empty($results)): ?>
|
||
<tr><td colspan="4" style="text-align:center; padding:40px;">Noch keine Statistiken vorhanden.</td></tr>
|
||
<?php else: ?>
|
||
<?php $rank = 1; foreach ($results as $row): ?>
|
||
<tr>
|
||
<td><strong>#<?php echo $rank++; ?></strong></td>
|
||
<td><?php echo esc_html($row->player_name); ?></td>
|
||
<td><?php echo esc_html(number_format($row->total_spent)); ?> <?php echo esc_html($currency); ?></td>
|
||
<td><?php echo esc_html($row->order_count); ?></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php
|
||
}
|
||
}
|
||
|
||
// ===========================================================
|
||
// REST API
|
||
// ===========================================================
|
||
class WIS_API {
|
||
public static function register_routes() {
|
||
register_rest_route('wis/v1', '/import_json', [
|
||
'methods' => 'POST',
|
||
'callback' => [self::class, 'import_json'],
|
||
'permission_callback' => '__return_true'
|
||
]);
|
||
|
||
register_rest_route('wis/v1', '/order', [
|
||
'methods' => 'POST',
|
||
'callback' => [self::class, 'create_order'],
|
||
'permission_callback' => '__return_true'
|
||
]);
|
||
|
||
register_rest_route('wis/v1', '/validate_coupon', [
|
||
'methods' => 'POST',
|
||
'callback' => [self::class, 'validate_coupon'],
|
||
'permission_callback' => '__return_true'
|
||
]);
|
||
|
||
// =================== SPIGOT COMPATIBILITY ROUTES ===================
|
||
|
||
// Get pending orders for a specific player (called by Spigot plugin)
|
||
register_rest_route('wis/v1', '/pending_orders', [
|
||
'methods' => 'GET',
|
||
'callback' => [self::class, 'get_pending_orders'],
|
||
'permission_callback' => '__return_true'
|
||
]);
|
||
|
||
// Execute order (called when player clicks Yes in Spigot GUI)
|
||
register_rest_route('wis/v1', '/execute_order', [
|
||
'methods' => 'POST',
|
||
'callback' => [self::class, 'execute_order'],
|
||
'permission_callback' => '__return_true'
|
||
]);
|
||
|
||
// Complete order (called after items given in Spigot)
|
||
register_rest_route('wis/v1', '/complete_order', [
|
||
'methods' => 'POST',
|
||
'callback' => [self::class, 'complete_order'],
|
||
'permission_callback' => '__return_true'
|
||
]);
|
||
|
||
// Cancel order (called when player clicks No in Spigot GUI)
|
||
register_rest_route('wis/v1', '/cancel_order', [
|
||
'methods' => 'POST',
|
||
'callback' => [self::class, 'cancel_order'],
|
||
'permission_callback' => '__return_true'
|
||
]);
|
||
}
|
||
|
||
// --- SPIGOT ENDPOINTS ---
|
||
|
||
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);
|
||
|
||
$wpdb->update(
|
||
$wpdb->prefix . 'wis_orders',
|
||
['status' => 'processing'],
|
||
['id' => $id]
|
||
);
|
||
|
||
return new WP_REST_Response(['success' => true]);
|
||
}
|
||
|
||
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);
|
||
|
||
$wpdb->update(
|
||
$wpdb->prefix . 'wis_orders',
|
||
['status' => 'cancelled'],
|
||
['id' => $id]
|
||
);
|
||
|
||
return new WP_REST_Response(['success' => true]);
|
||
}
|
||
|
||
// --- EXISTING ENDPOINTS ---
|
||
|
||
public static function import_json($request) {
|
||
$data = $request->get_json_params();
|
||
$url = esc_url_raw($data['url'] ?? '');
|
||
|
||
if (!$url) {
|
||
return new WP_REST_Response(['success' => false, 'message' => 'Keine URL angegeben'], 400);
|
||
}
|
||
|
||
$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);
|
||
$json = json_decode($body, true);
|
||
|
||
if (!isset($json['items']) || !is_array($json['items'])) {
|
||
return new WP_REST_Response(['success' => false, 'message' => 'Ungültiges JSON Format'], 400);
|
||
}
|
||
|
||
$imported = 0;
|
||
$skipped = 0;
|
||
|
||
foreach ($json['items'] as $item) {
|
||
$item_id = sanitize_text_field($item['id'] ?? '');
|
||
$name = sanitize_text_field($item['name'] ?? 'Unbekannt');
|
||
|
||
if (empty($item_id)) continue;
|
||
|
||
$exists = WIS_DB::get_item_by_item_id($item_id);
|
||
|
||
if ($exists) {
|
||
$skipped++;
|
||
continue;
|
||
}
|
||
|
||
WIS_DB::insert_item([
|
||
'item_id' => $item_id,
|
||
'name' => $name,
|
||
'description' => sanitize_textarea_field($item['description'] ?? ''),
|
||
'price' => intval($item['price'] ?? 0),
|
||
'status' => intval($item['price'] ?? 0) > 0 ? 'publish' : 'draft',
|
||
'servers' => '[]',
|
||
'categories' => '[]'
|
||
]);
|
||
|
||
$imported++;
|
||
}
|
||
|
||
return new WP_REST_Response(['success' => true, 'imported' => $imported, 'skipped' => $skipped]);
|
||
}
|
||
|
||
public static function create_order($request) {
|
||
global $wpdb;
|
||
$data = $request->get_json_params();
|
||
|
||
$player = sanitize_text_field($data['player'] ?? '');
|
||
$cart = $data['cart'] ?? [];
|
||
$server = sanitize_text_field($data['server'] ?? '');
|
||
$coupon_code = isset($data['coupon_code']) ? sanitize_text_field(strtoupper($data['coupon_code'])) : '';
|
||
|
||
if (!$player || empty($cart) || !$server) {
|
||
return new WP_REST_Response(['success' => false, 'message' => 'Fehlende Daten'], 400);
|
||
}
|
||
|
||
$exclude_offers = get_option('wis_coupon_exclude_offers', '0');
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
|
||
$valid_cart = [];
|
||
$total_normal = 0;
|
||
$total_offer = 0;
|
||
|
||
foreach ($cart as $item_data) {
|
||
$item = WIS_DB::get_item(intval($item_data['id'] ?? 0));
|
||
if (!$item || $item->status !== 'publish') continue;
|
||
|
||
$qty = intval($item_data['quantity'] ?? 1);
|
||
if ($qty <= 0) continue;
|
||
|
||
$servers = json_decode($item->servers, true);
|
||
if (!in_array($server, $servers ?: [])) continue;
|
||
|
||
$price = $item->offer_price > 0 ? $item->offer_price : $item->price;
|
||
|
||
$valid_cart[] = [
|
||
'id' => $item->item_id, // Important: Spigot needs the item ID (e.g., minecraft:diamond)
|
||
'title' => $item->name,
|
||
'price' => $price,
|
||
'qty' => $qty,
|
||
'is_offer' => $item->is_offer
|
||
];
|
||
|
||
$item_total = $price * $qty;
|
||
|
||
if ($item->is_offer && $exclude_offers === '1') {
|
||
$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'], 400);
|
||
}
|
||
|
||
// Coupon validation
|
||
$coupon_discount = 0;
|
||
$coupon_msg = '';
|
||
$coupon_applied = false;
|
||
|
||
if (!empty($coupon_code)) {
|
||
$coupon = WIS_DB::get_coupon_by_code($coupon_code);
|
||
|
||
if ($coupon) {
|
||
if ($coupon->expiry && date('Y-m-d') > $coupon->expiry) {
|
||
$coupon_msg = 'Gutschein abgelaufen';
|
||
} elseif ($coupon->used_count >= $coupon->usage_limit) {
|
||
$coupon_msg = 'Gutschein bereits aufgebraucht';
|
||
} else {
|
||
if ($exclude_offers === '1' && $total_normal <= 0) {
|
||
$coupon_msg = 'Gutschein gilt nicht für Angebote';
|
||
} else {
|
||
if ($coupon->type === 'percent') {
|
||
$coupon_discount = floor($total_normal * ($coupon->value / 100));
|
||
} else {
|
||
$coupon_discount = $coupon->value;
|
||
}
|
||
|
||
WIS_DB::update_coupon($coupon->id, ['used_count' => $coupon->used_count + 1]);
|
||
$coupon_applied = true;
|
||
$coupon_msg = "Gutschein eingelöst: -{$coupon_discount} {$currency}";
|
||
}
|
||
}
|
||
} else {
|
||
$coupon_msg = 'Ungültiger Code';
|
||
}
|
||
}
|
||
|
||
$final_price = max(0, $total_normal - $coupon_discount) + $total_offer;
|
||
|
||
// Build order
|
||
$items_payload = [];
|
||
$title_parts = [];
|
||
|
||
foreach ($valid_cart as $item) {
|
||
$items_payload[] = [
|
||
'id' => $item['id'], // minecraft:diamond
|
||
'amount' => $item['qty']
|
||
];
|
||
$title_parts[] = $item['qty'] . 'x ' . $item['title'];
|
||
}
|
||
|
||
$title = "Warenkorb: " . implode(', ', $title_parts);
|
||
if (strlen($title) > 240) $title = substr($title, 0, 237) . '...';
|
||
|
||
$payload = [
|
||
'items' => $items_payload,
|
||
'coupon' => $coupon_applied ? ['code' => $coupon_code, 'discount' => $coupon_discount] : []
|
||
];
|
||
|
||
WIS_DB::insert_order([
|
||
'player_name' => $player,
|
||
'server' => $server,
|
||
'item_id' => 'cart',
|
||
'item_title' => $title,
|
||
'price' => $final_price,
|
||
'quantity' => count($valid_cart),
|
||
'status' => 'pending',
|
||
'response' => json_encode($payload)
|
||
]);
|
||
|
||
$msg = '✅ Bestellung erfolgreich!';
|
||
if ($coupon_msg) $msg .= ' (' . $coupon_msg . ')';
|
||
|
||
return new WP_REST_Response(['success' => true, 'message' => $msg]);
|
||
}
|
||
|
||
public static function validate_coupon($request) {
|
||
$data = $request->get_json_params();
|
||
$code = sanitize_text_field(strtoupper($data['code'] ?? ''));
|
||
$cart = $data['cart'] ?? [];
|
||
|
||
if (!$code) {
|
||
return new WP_REST_Response(['success' => false, 'message' => 'Kein Code']);
|
||
}
|
||
|
||
$coupon = WIS_DB::get_coupon_by_code($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' => 'Bereits aufgebraucht']);
|
||
}
|
||
|
||
$exclude_offers = get_option('wis_coupon_exclude_offers', '0');
|
||
|
||
if ($exclude_offers === '1' && !empty($cart)) {
|
||
$has_normal = false;
|
||
foreach ($cart as $item_data) {
|
||
$item = WIS_DB::get_item(intval($item_data['id'] ?? 0));
|
||
if ($item && !$item->is_offer) {
|
||
$has_normal = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!$has_normal) {
|
||
return new WP_REST_Response(['success' => false, 'message' => 'Gutschein gilt nicht für Angebote']);
|
||
}
|
||
}
|
||
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
$msg = $coupon->type === 'percent'
|
||
? "Gutschein gültig (-{$coupon->value}%)"
|
||
: "Gutschein gültig (-{$coupon->value} {$currency})";
|
||
|
||
return new WP_REST_Response([
|
||
'success' => true,
|
||
'type' => $coupon->type,
|
||
'value' => $coupon->value,
|
||
'message' => $msg
|
||
]);
|
||
}
|
||
}
|
||
|
||
// ===========================================================
|
||
// SHORTCODE - FRONTEND SHOP
|
||
// ===========================================================
|
||
class WIS_Shortcode {
|
||
public static function register() {
|
||
add_shortcode('ingame_shop_form', [self::class, 'render']);
|
||
}
|
||
|
||
public static function render() {
|
||
$servers = WIS_DB::get_servers();
|
||
$items = WIS_DB::get_items(['status' => 'publish']);
|
||
$categories = WIS_DB::get_categories();
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
$img_base = get_option('wis_image_base_url', '');
|
||
$header_text = get_option('wis_header_text', '');
|
||
$exclude_offers = get_option('wis_coupon_exclude_offers', '0');
|
||
|
||
ob_start();
|
||
?>
|
||
<style>
|
||
.wis-shop { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; max-width: 1400px; margin: 40px auto; background: #f4f6f8; padding: 20px; border-radius: 10px; position: relative; }
|
||
.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-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-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-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 320px)); gap: 15px; justify-content: center; margin: 0 auto; }
|
||
.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; width: 100%; }
|
||
.wis-card.offer { border: 2px solid #ffc107; }
|
||
.wis-card:hover { transform: translateY(-5px); box-shadow: 0 10px 20px rgba(0,0,0,0.1); }
|
||
.wis-card-img { width: 100%; height: 180px; background: #2d2d2d; display: flex; align-items: center; justify-content: center; position: relative; padding: 20px; }
|
||
.wis-card-img img { width: 100px !important; height: 100px !important; object-fit: contain; filter: drop-shadow(0 6px 8px rgba(0,0,0,0.7)); transition: transform 0.3s; image-rendering: pixelated; image-rendering: -moz-crisp-edges; image-rendering: crisp-edges; }
|
||
.wis-card:hover .wis-card-img img { transform: scale(1.15) rotate(5deg); }
|
||
.wis-offer-badge, .wis-daily-badge { position: absolute; top: 10px; left: 10px; background: linear-gradient(135deg, #ff416c, #ff4b2b); color: white; padding: 6px 12px; border-radius: 20px; font-size: 0.75rem; font-weight: bold; box-shadow: 0 2px 5px rgba(0,0,0,0.3); z-index: 2; }
|
||
.wis-daily-badge { background: linear-gradient(135deg, #6f42c1, #8e44ad); 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; }
|
||
.wis-card-servers { font-size: 0.85rem; color: #666; margin-bottom: 15px; }
|
||
.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; 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); }
|
||
|
||
/* FIX: HOHER Z-INDEX UND ISOLATION */
|
||
.wis-modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0,0,0,0.6);
|
||
z-index: 2147483647; /* Maximaler 32-bit Integer */
|
||
display: none;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transform: translateZ(0); /* Erzwingt neue GPU Ebene */
|
||
}
|
||
|
||
.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); overflow-y: auto; position: relative; z-index: 2147483648; }
|
||
.wis-cart-item { display: flex; justify-content: space-between; align-items: center; padding: 15px; border-bottom: 1px solid #eee; }
|
||
.wis-cart-item-title { font-weight: bold; color: #333; }
|
||
.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; }
|
||
.wis-cart-total { border-top: 2px solid #333; padding: 20px 0; margin-top: 15px; font-size: 1.3rem; font-weight: bold; display: flex; justify-content: space-between; }
|
||
.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-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; }
|
||
.wis-btn-confirm { background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; }
|
||
.wis-btn-cancel { background: #6c757d; color: white; }
|
||
.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-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; }
|
||
</style>
|
||
|
||
<div class="wis-shop">
|
||
<div class="wis-header">
|
||
<h2>🛒 Ingame Shop</h2>
|
||
<?php if (!empty(trim($header_text))): ?>
|
||
<div class="wis-status"><?php echo esc_html($header_text); ?></div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<div class="wis-control-bar">
|
||
<input type="text" id="wis-search" class="wis-search-input" placeholder="🔍 Suche Item...">
|
||
|
||
<select id="server-filter" class="wis-filter-select">
|
||
<option value="">Alle Server</option>
|
||
<?php foreach ($servers as $s): ?>
|
||
<option value="<?php echo esc_attr($s->slug); ?>"><?php echo esc_html($s->name); ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
|
||
<button class="wis-cart-btn" onclick="openCart()">
|
||
🛒 Warenkorb
|
||
<span class="wis-cart-badge" id="cart-count">0</span>
|
||
</button>
|
||
</div>
|
||
|
||
<?php if (!empty($categories)): ?>
|
||
<div class="wis-cat-tabs">
|
||
<button class="wis-cat-btn active" onclick="filterCategory(0)">Alle</button>
|
||
<?php foreach ($categories as $cat): ?>
|
||
<button class="wis-cat-btn" onclick="filterCategory('<?php echo esc_js($cat->slug); ?>')"><?php echo esc_html($cat->name); ?></button>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="wis-grid">
|
||
<?php if (empty($items)): ?>
|
||
<div style="grid-column: 1 / -1; text-align: center; padding: 40px; background: #fff; border-radius: 10px;">
|
||
Keine Items im Shop verfügbar.
|
||
</div>
|
||
<?php else: ?>
|
||
<?php foreach ($items as $item):
|
||
$servers_arr = json_decode($item->servers, true) ?: [];
|
||
$cats_arr = json_decode($item->categories, true) ?: [];
|
||
$price = $item->offer_price > 0 ? $item->offer_price : $item->price;
|
||
$show_old = $item->offer_price > 0 && $item->offer_price != $item->price;
|
||
|
||
$img_name = str_replace(':', '_', $item->item_id) . '.png';
|
||
$img_url = $img_base . $img_name;
|
||
|
||
$server_names = [];
|
||
foreach ($servers_arr as $slug) {
|
||
foreach ($servers as $s) {
|
||
if ($s->slug === $slug) {
|
||
$server_names[] = $s->name;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
$servers_display = !empty($server_names) ? implode(', ', $server_names) : 'Kein Server';
|
||
?>
|
||
<div class="wis-card <?php echo $item->is_offer ? 'offer' : ''; ?>"
|
||
data-servers='<?php echo esc_attr(json_encode($servers_arr)); ?>'
|
||
data-cats='<?php echo esc_attr(json_encode($cats_arr)); ?>'
|
||
data-title="<?php echo esc_attr(strtolower($item->name)); ?>"
|
||
data-offer="<?php echo $item->is_offer ? '1' : '0'; ?>">
|
||
|
||
<?php if ($item->is_daily_deal): ?>
|
||
<div class="wis-daily-badge">🎁 Angebot des Tages</div>
|
||
<?php elseif ($item->is_offer): ?>
|
||
<div class="wis-offer-badge">🔥 Angebot</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="wis-card-img">
|
||
<img src="<?php echo esc_url($img_url); ?>"
|
||
alt="<?php echo esc_attr($item->name); ?>"
|
||
loading="lazy"
|
||
onerror="this.onerror=null; this.src='https://via.placeholder.com/100/333/fff?text=?';">
|
||
</div>
|
||
|
||
<div class="wis-card-body">
|
||
<h3 class="wis-card-title" title="<?php echo esc_attr($item->name); ?>">
|
||
<?php echo esc_html($item->name); ?>
|
||
</h3>
|
||
|
||
<?php if ($item->description): ?>
|
||
<div class="wis-card-desc"><?php echo esc_html($item->description); ?></div>
|
||
<?php endif; ?>
|
||
|
||
<div class="wis-card-price-container">
|
||
<?php if ($show_old): ?>
|
||
<div class="wis-card-price-old"><?php echo esc_html($item->price); ?> <?php echo esc_html($currency); ?></div>
|
||
<?php endif; ?>
|
||
<div class="wis-card-price"><?php echo esc_html($price); ?> <?php echo esc_html($currency); ?></div>
|
||
</div>
|
||
|
||
<div class="wis-card-servers">📡 <?php echo esc_html($servers_display); ?></div>
|
||
|
||
<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">
|
||
<button class="wis-quantity-btn" onclick="changeQuantity(this, 1)">+</button>
|
||
</div>
|
||
|
||
<button class="wis-btn-add" onclick="addToCart(<?php echo $item->id; ?>, '<?php echo esc_js($item->name); ?>', <?php echo $price; ?>, this, <?php echo $item->is_offer ? 1 : 0; ?>)">
|
||
➕ In den Warenkorb
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Cart Modal -->
|
||
<div class="wis-modal-overlay" id="cart-modal">
|
||
<div class="wis-modal">
|
||
<h2>🛒 Dein Warenkorb</h2>
|
||
<div id="cart-content">
|
||
<div style="text-align:center; padding:40px; color:#999;">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;">
|
||
<label style="display:block; font-weight:bold; margin-bottom:5px;">🎫 Gutscheincode</label>
|
||
<div class="wis-coupon-input-group">
|
||
<input type="text" id="coupon-code" class="wis-coupon-input" placeholder="CODE">
|
||
<button class="wis-coupon-btn" onclick="validateCoupon()">Einlösen</button>
|
||
</div>
|
||
<div id="coupon-msg" class="wis-coupon-msg"></div>
|
||
</div>
|
||
|
||
<div class="wis-cart-total">
|
||
<span>Gesamt:</span>
|
||
<span id="cart-total">0 <?php echo esc_html($currency); ?></span>
|
||
</div>
|
||
|
||
<select id="checkout-server" class="wis-modal-input">
|
||
<option value="">-- Server wählen --</option>
|
||
<?php foreach ($servers as $s): ?>
|
||
<option value="<?php echo esc_attr($s->slug); ?>"><?php echo esc_html($s->name); ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
|
||
<input type="text" id="checkout-player" class="wis-modal-input" placeholder="Dein 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 = "<?php echo esc_js($currency); ?>";
|
||
const shopExcludeOffers = <?php echo $exclude_offers === '1' ? 'true' : 'false'; ?>;
|
||
let cart = [];
|
||
let couponData = {};
|
||
let activeCategory = 0;
|
||
|
||
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 filterCategory(cat) {
|
||
activeCategory = cat;
|
||
document.querySelectorAll('.wis-cat-btn').forEach(b => b.classList.remove('active'));
|
||
event.target.classList.add('active');
|
||
updateGrid();
|
||
}
|
||
|
||
function updateGrid() {
|
||
const search = document.getElementById('wis-search').value.toLowerCase();
|
||
const server = document.getElementById('server-filter').value;
|
||
|
||
document.querySelectorAll('.wis-card').forEach(card => {
|
||
const title = card.dataset.title;
|
||
const servers = JSON.parse(card.dataset.servers || '[]');
|
||
const cats = JSON.parse(card.dataset.cats || '[]');
|
||
|
||
let visible = true;
|
||
|
||
if (search && !title.includes(search)) visible = false;
|
||
if (server && !servers.includes(server)) visible = false;
|
||
if (activeCategory && !cats.includes(activeCategory)) visible = false;
|
||
|
||
card.style.display = visible ? 'flex' : 'none';
|
||
});
|
||
}
|
||
|
||
document.getElementById('wis-search').addEventListener('input', updateGrid);
|
||
document.getElementById('server-filter').addEventListener('change', updateGrid);
|
||
|
||
function addToCart(id, name, price, btn, isOffer) {
|
||
const card = btn.closest('.wis-card');
|
||
const qty = parseInt(card.querySelector('.wis-quantity-input').value) || 1;
|
||
const servers = JSON.parse(card.dataset.servers || '[]');
|
||
|
||
const existing = cart.find(i => i.id === id);
|
||
if (existing) {
|
||
existing.quantity += qty;
|
||
} else {
|
||
cart.push({id, name, price, quantity: qty, servers, is_offer: !!isOffer});
|
||
}
|
||
|
||
updateCartBadge();
|
||
|
||
btn.textContent = '✅ Hinzugefügt';
|
||
btn.style.background = '#28a745';
|
||
setTimeout(() => {
|
||
btn.textContent = '➕ In den Warenkorb';
|
||
btn.style.background = '';
|
||
}, 1500);
|
||
|
||
card.querySelector('.wis-quantity-input').value = 1;
|
||
}
|
||
|
||
function updateCartBadge() {
|
||
const total = cart.reduce((sum, i) => sum + i.quantity, 0);
|
||
document.getElementById('cart-count').textContent = total;
|
||
}
|
||
|
||
function openCart() {
|
||
renderCart();
|
||
document.getElementById('cart-modal').style.display = 'flex';
|
||
}
|
||
|
||
function closeCart() {
|
||
document.getElementById('cart-modal').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;
|
||
}
|
||
});
|
||
|
||
let discount = 0;
|
||
if (couponData && typeof couponData.value !== 'undefined') {
|
||
if (couponData.type === 'percent') {
|
||
discount = normalSum * (couponData.value / 100);
|
||
} else {
|
||
discount = couponData.value;
|
||
}
|
||
}
|
||
|
||
return Math.max(0, normalSum - discount) + offerSum;
|
||
}
|
||
|
||
function renderCart() {
|
||
const content = document.getElementById('cart-content');
|
||
const checkout = document.getElementById('cart-checkout');
|
||
|
||
if (cart.length === 0) {
|
||
content.innerHTML = '<div style="text-align:center; padding:40px; color:#999;">Dein Warenkorb ist leer</div>';
|
||
checkout.style.display = 'none';
|
||
return;
|
||
}
|
||
|
||
let html = '';
|
||
cart.forEach((item, idx) => {
|
||
html += `
|
||
<div class="wis-cart-item">
|
||
<div>
|
||
<div class="wis-cart-item-title">${item.name}</div>
|
||
<div class="wis-cart-item-price">${item.quantity}x × ${item.price} = ${item.price * item.quantity} ${shopCurrency}</div>
|
||
</div>
|
||
<button class="wis-cart-item-remove" onclick="removeFromCart(${idx})">🗑️</button>
|
||
</div>
|
||
`;
|
||
});
|
||
|
||
content.innerHTML = html;
|
||
document.getElementById('cart-total').textContent = calculateTotal() + ' ' + shopCurrency;
|
||
checkout.style.display = 'block';
|
||
}
|
||
|
||
function removeFromCart(idx) {
|
||
cart.splice(idx, 1);
|
||
updateCartBadge();
|
||
renderCart();
|
||
}
|
||
|
||
function validateCoupon() {
|
||
const code = document.getElementById('coupon-code').value.trim().toUpperCase();
|
||
const msg = document.getElementById('coupon-msg');
|
||
|
||
if (!code) {
|
||
couponData = {};
|
||
msg.textContent = '';
|
||
renderCart();
|
||
return;
|
||
}
|
||
|
||
msg.textContent = 'Prüfe...';
|
||
msg.style.color = '#0073aa';
|
||
|
||
fetch('<?php echo rest_url('wis/v1/validate_coupon'); ?>', {
|
||
method: 'POST',
|
||
headers: {'Content-Type':'application/json'},
|
||
body: JSON.stringify({code, cart})
|
||
})
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
couponData = {type: data.type, value: data.value};
|
||
msg.textContent = '✅ ' + data.message;
|
||
msg.style.color = 'green';
|
||
} else {
|
||
couponData = {};
|
||
msg.textContent = '❌ ' + data.message;
|
||
msg.style.color = 'red';
|
||
}
|
||
renderCart();
|
||
})
|
||
.catch(e => {
|
||
couponData = {};
|
||
msg.textContent = 'Fehler: ' + e.message;
|
||
msg.style.color = 'red';
|
||
renderCart();
|
||
});
|
||
}
|
||
|
||
function checkout() {
|
||
const player = document.getElementById('checkout-player').value.trim();
|
||
const server = document.getElementById('checkout-server').value;
|
||
const coupon_code = document.getElementById('coupon-code').value.trim();
|
||
const alert = document.getElementById('cart-alert');
|
||
|
||
if (!player) {
|
||
alert.textContent = 'Bitte Spielername eingeben!';
|
||
alert.className = 'wis-alert error';
|
||
alert.style.display = 'block';
|
||
return;
|
||
}
|
||
|
||
if (!server) {
|
||
alert.textContent = 'Bitte Server auswählen!';
|
||
alert.className = 'wis-alert error';
|
||
alert.style.display = 'block';
|
||
return;
|
||
}
|
||
|
||
const invalidItems = cart.filter(item => !item.servers.includes(server));
|
||
if (invalidItems.length > 0) {
|
||
alert.textContent = 'Einige Items sind nicht für diesen Server verfügbar!';
|
||
alert.className = 'wis-alert error';
|
||
alert.style.display = 'block';
|
||
return;
|
||
}
|
||
|
||
const btn = document.querySelector('.wis-btn-confirm');
|
||
btn.disabled = true;
|
||
btn.textContent = '⏳ Speichere...';
|
||
|
||
fetch('<?php echo rest_url('wis/v1/order'); ?>', {
|
||
method: 'POST',
|
||
headers: {'Content-Type':'application/json'},
|
||
body: JSON.stringify({player, cart, server, coupon_code})
|
||
})
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
alert.textContent = data.message;
|
||
alert.className = 'wis-alert success';
|
||
alert.style.display = 'block';
|
||
cart = [];
|
||
couponData = {};
|
||
updateCartBadge();
|
||
setTimeout(() => {
|
||
closeCart();
|
||
location.reload();
|
||
},2000);
|
||
} else {
|
||
alert.textContent = data.message;
|
||
alert.className = 'wis-alert error';
|
||
alert.style.display = 'block';
|
||
}
|
||
})
|
||
.catch(e => {
|
||
alert.textContent = 'Fehler: ' + e.message;
|
||
alert.className = 'wis-alert error';
|
||
alert.style.display = 'block';
|
||
})
|
||
.finally(() => {
|
||
btn.disabled = false;
|
||
btn.textContent = '💰 Kauf abschließen';
|
||
});
|
||
}
|
||
|
||
document.getElementById('cart-modal').addEventListener('click', function(e) {
|
||
if (e.target === this) closeCart();
|
||
});
|
||
|
||
// FIX: Verschiebt das Modal ans Ende des Body Tags, damit es über allem liegt (Sticky Header, etc.)
|
||
document.addEventListener("DOMContentLoaded", function() {
|
||
const modal = document.getElementById('cart-modal');
|
||
if (modal && document.body) {
|
||
document.body.appendChild(modal);
|
||
}
|
||
});
|
||
</script>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
}
|
||
|
||
// ===========================================================
|
||
// SIDEBAR WIDGET
|
||
// ===========================================================
|
||
class WIS_Sidebar_Widget extends WP_Widget {
|
||
public function __construct() {
|
||
parent::__construct(
|
||
'wis_sidebar_offer',
|
||
'WIS Shop Angebot',
|
||
['description' => 'Zeigt Daily Deal oder Angebot an']
|
||
);
|
||
}
|
||
|
||
public function widget($args, $instance) {
|
||
echo $args['before_widget'];
|
||
|
||
if (!empty($instance['title'])) {
|
||
echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title'];
|
||
}
|
||
|
||
global $wpdb;
|
||
$currency = get_option('wis_currency_name', 'Coins');
|
||
$img_base = get_option('wis_image_base_url', '');
|
||
|
||
// Try daily deal first
|
||
$item = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wis_items WHERE is_daily_deal = 1 AND status = 'publish' LIMIT 1");
|
||
|
||
// Fallback to regular offer
|
||
if (!$item) {
|
||
$item = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wis_items WHERE is_offer = 1 AND status = 'publish' ORDER BY updated_at DESC LIMIT 1");
|
||
}
|
||
|
||
if ($item) {
|
||
$price = $item->offer_price > 0 ? $item->offer_price : $item->price;
|
||
$show_old = $item->offer_price > 0 && $item->offer_price != $item->price;
|
||
$img_name = str_replace(':', '_', $item->item_id) . '.png';
|
||
$img_url = $img_base . $img_name;
|
||
$target_url = !empty($instance['shop_url']) ? $instance['shop_url'] : home_url();
|
||
|
||
?>
|
||
<div style="background:#fff; border-radius:10px; padding:0; overflow:hidden; box-shadow:0 4px 10px rgba(0,0,0,0.05); text-align:center;">
|
||
<div style="position:relative; background:#2d2d2d; padding:15px; height:160px; display:flex; align-items:center; justify-content:center;">
|
||
<?php if ($item->is_daily_deal): ?>
|
||
<div style="position:absolute; top:10px; left:10px; background:linear-gradient(135deg, #6f42c1, #8e44ad); color:#fff; padding:4px 10px; font-size:10px; border-radius:20px; font-weight:bold; z-index:2;">🎁 DAILY DEAL</div>
|
||
<?php elseif ($item->is_offer): ?>
|
||
<div style="position:absolute; top:10px; left:10px; background:linear-gradient(135deg, #ff416c, #ff4b2b); color:#fff; padding:4px 10px; font-size:10px; border-radius:20px; font-weight:bold; z-index:2;">🔥 ANGEBOT</div>
|
||
<?php endif; ?>
|
||
<img src="<?php echo esc_url($img_url); ?>" alt="<?php echo esc_attr($item->name); ?>" style="max-width:90%; max-height:90%; object-fit:contain; filter:drop-shadow(0 4px 8px rgba(0,0,0,0.6));">
|
||
</div>
|
||
<div style="padding:15px;">
|
||
<h4 style="margin:0 0 8px 0; font-size:15px; color:#333; font-weight:700;"><?php echo esc_html($item->name); ?></h4>
|
||
<div style="margin-bottom:12px;">
|
||
<?php if ($show_old): ?>
|
||
<span style="text-decoration:line-through; color:#999; font-size:11px;"><?php echo esc_html($item->price); ?> <?php echo esc_html($currency); ?></span>
|
||
<?php endif; ?>
|
||
<span style="font-size:20px; font-weight:800; color:#28a745;"><?php echo esc_html($price); ?> <span style="font-size:12px; font-weight:400; color:#666;"><?php echo esc_html($currency); ?></span></span>
|
||
</div>
|
||
<a href="<?php echo esc_url($target_url); ?>" style="display:block; padding:10px 0; background:linear-gradient(135deg, #667eea 0%, #764ba2 100%); color:#fff; text-decoration:none; border-radius:6px; font-weight:bold; font-size:13px;">
|
||
<?php echo esc_html($instance['btn_text'] ?? 'Zum Shop'); ?> 🛒
|
||
</a>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
} else {
|
||
echo '<div style="background:#f8f9fa; border:1px dashed #ddd; border-radius:8px; padding:20px; text-align:center;"><p style="margin:0; color:#888; font-size:13px;">Kein Angebot verfügbar.</p></div>';
|
||
}
|
||
|
||
echo $args['after_widget'];
|
||
}
|
||
|
||
public function form($instance) {
|
||
$title = !empty($instance['title']) ? $instance['title'] : '🔥 Angebot des Tages';
|
||
$btn_text = !empty($instance['btn_text']) ? $instance['btn_text'] : 'Zum Shop';
|
||
$shop_url = !empty($instance['shop_url']) ? $instance['shop_url'] : '';
|
||
?>
|
||
<p>
|
||
<label for="<?php echo $this->get_field_id('title'); ?>">Titel:</label>
|
||
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>">
|
||
</p>
|
||
<p>
|
||
<label for="<?php echo $this->get_field_id('btn_text'); ?>">Button Text:</label>
|
||
<input class="widefat" id="<?php echo $this->get_field_id('btn_text'); ?>" name="<?php echo $this->get_field_name('btn_text'); ?>" type="text" value="<?php echo esc_attr($btn_text); ?>">
|
||
</p>
|
||
<p>
|
||
<label for="<?php echo $this->get_field_id('shop_url'); ?>">Shop URL:</label>
|
||
<input class="widefat" id="<?php echo $this->get_field_id('shop_url'); ?>" name="<?php echo $this->get_field_name('shop_url'); ?>" type="text" value="<?php echo esc_attr($shop_url); ?>">
|
||
</p>
|
||
<?php
|
||
}
|
||
|
||
public function update($new_instance, $old_instance) {
|
||
$instance = [];
|
||
$instance['title'] = !empty($new_instance['title']) ? sanitize_text_field($new_instance['title']) : '';
|
||
$instance['btn_text'] = !empty($new_instance['btn_text']) ? sanitize_text_field($new_instance['btn_text']) : 'Zum Shop';
|
||
$instance['shop_url'] = !empty($new_instance['shop_url']) ? esc_url_raw($new_instance['shop_url']) : '';
|
||
return $instance;
|
||
}
|
||
}
|
||
|
||
// ===========================================================
|
||
// INITIALIZATION
|
||
// ===========================================================
|
||
register_activation_hook(__FILE__, [WIS_Activator::class, 'activate']);
|
||
register_deactivation_hook(__FILE__, [WIS_Activator::class, 'deactivate']);
|
||
|
||
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']);
|
||
add_action('widgets_init', function() {
|
||
register_widget('WIS_Sidebar_Widget');
|
||
});
|
||
add_action('wis_daily_deal_event', [WIS_Activator::class, 'run_daily_deal']);
|