Upload via Git Manager GUI

This commit is contained in:
Git Manager GUI
2026-04-24 09:12:43 +02:00
parent 25a3f62b06
commit 3ba519b53d

View File

@@ -227,6 +227,14 @@ add_action('admin_notices', [WIS_Dashboard::class, 'show_update_notice']);
// =========================================================== // ===========================================================
class WIS_Activator { class WIS_Activator {
public static function activate() { public static function activate() {
// Spalte custom_image_url nachrüsten (für bestehende Installationen)
global $wpdb;
$table = $wpdb->prefix . 'wis_items';
$col = $wpdb->get_var("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '$table' AND COLUMN_NAME = 'custom_image_url'");
if (!$col) {
$wpdb->query("ALTER TABLE $table ADD COLUMN custom_image_url varchar(500) DEFAULT NULL AFTER categories");
}
self::create_tables(); self::create_tables();
self::set_default_options(); self::set_default_options();
self::create_default_categories(); self::create_default_categories();
@@ -260,6 +268,7 @@ class WIS_Activator {
is_daily_deal tinyint(1) DEFAULT 0, is_daily_deal tinyint(1) DEFAULT 0,
servers text, servers text,
categories text, categories text,
custom_image_url varchar(500) DEFAULT NULL,
status varchar(20) DEFAULT 'draft', status varchar(20) DEFAULT 'draft',
created_at datetime DEFAULT CURRENT_TIMESTAMP, created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
@@ -775,6 +784,20 @@ class WIS_Item_Categorizer {
// DATABASE HELPER // DATABASE HELPER
// =========================================================== // ===========================================================
class WIS_DB { class WIS_DB {
/**
* Gibt die Bild-URL eines Items zurück.
* Priorität: custom_image_url > automatisch generierte URL aus item_id.
*/
public static function get_item_image($item) {
if (!empty($item->custom_image_url)) {
return esc_url($item->custom_image_url);
}
$img_base = get_option('wis_image_base_url', '');
$img_name = str_replace(':', '_', $item->item_id) . '.png';
return $img_base . $img_name;
}
public static function get_items($args = []) { public static function get_items($args = []) {
global $wpdb; global $wpdb;
$table = $wpdb->prefix . 'wis_items'; $table = $wpdb->prefix . 'wis_items';
@@ -1448,26 +1471,77 @@ class WIS_Admin {
if (isset($_POST['wis_save_item'])) { if (isset($_POST['wis_save_item'])) {
check_admin_referer('wis_item_form'); check_admin_referer('wis_item_form');
global $wpdb;
$data = [ // item_id je nach Typ ermitteln
'item_id' => sanitize_text_field($_POST['item_id']), $item_type = sanitize_text_field($_POST['item_type'] ?? 'minecraft');
'name' => sanitize_text_field($_POST['name']), if ($item_type === 'fly') {
'description' => sanitize_textarea_field($_POST['description']), $resolved_item_id = sanitize_text_field($_POST['fly_duration'] ?? 'fly_5min');
'price' => intval($_POST['price']), } elseif ($item_type === 'rank') {
'offer_price' => intval($_POST['offer_price']), $rank_id = preg_replace('/[^a-z0-9_\-]/', '', strtolower($_POST['rank_id'] ?? 'vip'));
'is_offer' => isset($_POST['is_offer']) ? 1 : 0, $lp_group = preg_replace('/[^a-zA-Z0-9_\-]/', '', $_POST['lp_group'] ?? $rank_id);
'servers' => isset($_POST['servers']) ? json_encode($_POST['servers']) : '[]', $default_group = preg_replace('/[^a-zA-Z0-9_\-]/', '', $_POST['default_group'] ?? 'default');
'categories' => isset($_POST['categories']) ? json_encode($_POST['categories']) : '[]', $rank_days = max(0, intval($_POST['rank_days'] ?? 30));
'status' => intval($_POST['price']) > 0 ? 'publish' : 'draft' if (empty($lp_group)) $lp_group = $rank_id;
]; if (empty($default_group)) $default_group = 'default';
// Format: rank_{rank_id}_{lp_group}_{default_group}_{days}
if (isset($_GET['edit'])) { $resolved_item_id = 'rank_' . $rank_id . '_' . $lp_group . '_' . $default_group . '_' . $rank_days;
WIS_DB::update_item(intval($_GET['edit']), $data);
echo '<div class="updated"><p>✅ Item gespeichert!</p></div>';
} else { } else {
WIS_DB::insert_item($data); $resolved_item_id = sanitize_text_field($_POST['item_id'] ?? '');
echo '<div class="updated"><p>✅ Item erstellt!</p></div>';
} }
// Pflichtfeld-Prüfung
if (empty($resolved_item_id) || empty(trim($_POST['name'] ?? ''))) {
echo '<div class="error"><p>❌ Name und Item-ID sind Pflichtfelder.</p></div>';
} else {
$data = [
'item_id' => $resolved_item_id,
'name' => sanitize_text_field($_POST['name']),
'description' => sanitize_textarea_field($_POST['description'] ?? ''),
'price' => intval($_POST['price'] ?? 0),
'offer_price' => intval($_POST['offer_price'] ?? 0),
'is_offer' => isset($_POST['is_offer']) ? 1 : 0,
'servers' => isset($_POST['servers']) ? json_encode(array_map('sanitize_text_field', $_POST['servers'])) : '[]',
'categories' => isset($_POST['categories']) ? json_encode(array_map('sanitize_text_field', $_POST['categories'])) : '[]',
'custom_image_url' => esc_url_raw($_POST['custom_image_url'] ?? ''),
'status' => (intval($_POST['price'] ?? 0) > 0 || $item_type === 'fly' || $item_type === 'rank') ? 'publish' : 'draft',
];
if (isset($_GET['edit'])) {
$result = WIS_DB::update_item(intval($_GET['edit']), $data);
if ($result !== false) {
wp_redirect(admin_url('admin.php?page=wis_items&edit=' . intval($_GET['edit']) . '&saved=1'));
exit;
} else {
echo '<div class="error"><p>❌ Fehler beim Speichern: ' . esc_html($wpdb->last_error) . '</p></div>';
}
} else {
// Prüfen ob item_id bereits existiert → direkt zum Edit weiterleiten
$existing = WIS_DB::get_item_by_item_id($resolved_item_id);
if ($existing) {
// Bestehenden Artikel aktualisieren statt Duplikat-Fehler
WIS_DB::update_item($existing->id, $data);
wp_redirect(admin_url('admin.php?page=wis_items&edit=' . $existing->id . '&saved=1'));
exit;
}
$result = WIS_DB::insert_item($data);
if ($result) {
$new_id = $wpdb->insert_id;
wp_redirect(admin_url('admin.php?page=wis_items&edit=' . $new_id . '&created=1'));
exit;
} else {
$err = $wpdb->last_error ?: 'Unbekannter Fehler.';
echo '<div class="error"><p>❌ Fehler beim Erstellen: ' . esc_html($err) . '</p></div>';
}
}
}
}
if (isset($_GET['created'])) {
echo '<div class="updated"><p>✅ Item erfolgreich erstellt!</p></div>';
}
if (isset($_GET['saved'])) {
echo '<div class="updated"><p>✅ Item gespeichert!</p></div>';
} }
if (isset($_GET['edit']) || isset($_GET['add'])) { if (isset($_GET['edit']) || isset($_GET['add'])) {
@@ -1497,10 +1571,120 @@ class WIS_Admin {
<td><input type="text" id="name" name="name" value="<?php echo $item ? esc_attr($item->name) : ''; ?>" class="regular-text" required></td> <td><input type="text" id="name" name="name" value="<?php echo $item ? esc_attr($item->name) : ''; ?>" class="regular-text" required></td>
</tr> </tr>
<tr> <tr>
<th><label for="item_id">Item ID *</label></th> <th><label for="item_id">Item ID / Typ *</label></th>
<td> <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> <?php
<p class="description">Z.B.: minecraft:diamond (Kategorien werden automatisch zugewiesen)</p> $fly_ids = ['fly_5min','fly_15min','fly_30min','fly_1h','fly_2h','fly_3h'];
$is_fly = $item && in_array($item->item_id, $fly_ids);
$is_rank = $item && preg_match('/^rank_([^_]+(?:_[^_]+)*)_([a-zA-Z0-9_\-]+)_([a-zA-Z0-9_\-]+)_(\d+)$/', $item->item_id, $rm);
// Format: rank_{rank_id}_{lp_group}_{default_group}_{days}
// Fallback für altes Format rank_{rank_id}_{days}
$is_rank_old = !$is_rank && $item && preg_match('/^rank_(.+)_(\d+)$/', $item->item_id, $rm_old);
$cur_rank_id = $is_rank ? $rm[1] : ($is_rank_old ? $rm_old[1] : 'vip');
$cur_lp_group = $is_rank ? $rm[2] : $cur_rank_id;
$cur_default_group = $is_rank ? $rm[3] : 'default';
$cur_rank_days = $is_rank ? intval($rm[4]) : ($is_rank_old ? intval($rm_old[2]) : 30);
$cur_label = $item ? esc_attr($item->name) : '';
$detected_type = $is_fly ? 'fly' : (($is_rank || $is_rank_old) ? 'rank' : 'minecraft');
?>
<select id="item_type" name="item_type" onchange="wisToggleItemType(this.value)" style="margin-bottom:8px;">
<option value="minecraft" <?php echo $detected_type === 'minecraft' ? 'selected' : ''; ?>>Minecraft Item</option>
<option value="fly" <?php echo $detected_type === 'fly' ? 'selected' : ''; ?>>✈ Fly-Gutschein</option>
<option value="rank" <?php echo $detected_type === 'rank' ? 'selected' : ''; ?>>👑 Rang (LuckPerms)</option>
</select>
<div id="wis_item_minecraft">
<input type="text" id="item_id" name="item_id" value="<?php echo ($item && !$is_fly && !$is_rank) ? esc_attr($item->item_id) : ''; ?>" class="regular-text" placeholder="minecraft:diamond">
<p class="description">Z.B.: minecraft:diamond (Kategorien werden automatisch zugewiesen)</p>
</div>
<div id="wis_item_fly" style="display:none;">
<select name="fly_duration" id="fly_duration">
<option value="fly_5min" <?php echo ($item && $item->item_id === 'fly_5min') ? 'selected' : ''; ?>>✈ 5 Minuten Fly</option>
<option value="fly_15min" <?php echo ($item && $item->item_id === 'fly_15min') ? 'selected' : ''; ?>>✈ 15 Minuten Fly</option>
<option value="fly_30min" <?php echo ($item && $item->item_id === 'fly_30min') ? 'selected' : ''; ?>>✈ 30 Minuten Fly</option>
<option value="fly_1h" <?php echo ($item && $item->item_id === 'fly_1h') ? 'selected' : ''; ?>>✈ 1 Stunde Fly</option>
<option value="fly_2h" <?php echo ($item && $item->item_id === 'fly_2h') ? 'selected' : ''; ?>>✈ 2 Stunden Fly</option>
<option value="fly_3h" <?php echo ($item && $item->item_id === 'fly_3h') ? 'selected' : ''; ?>>✈ 3 Stunden Fly</option>
</select>
<p class="description">Der Spieler bekommt nach dem Kauf Fly für die gewählte Dauer.</p>
</div>
<div id="wis_item_rank" style="display:none;">
<table style="border-collapse:collapse;">
<tr>
<td style="padding:4px 10px 4px 0;"><label for="rank_id"><strong>Rang-ID:</strong></label></td>
<td><input type="text" id="rank_id" name="rank_id" value="<?php echo esc_attr($cur_rank_id); ?>" class="regular-text" placeholder="vip" style="width:160px;"></td>
<td style="padding:4px 0 4px 12px; color:#666; font-size:12px;">Interner Name (frei wählbar, z.&nbsp;B. <code>vip</code>)</td>
</tr>
<tr>
<td style="padding:4px 10px 4px 0;"><label for="lp_group"><strong>LuckPerms-Gruppe:</strong></label></td>
<td><input type="text" id="lp_group" name="lp_group" value="<?php echo esc_attr($cur_lp_group); ?>" class="regular-text" placeholder="vip" style="width:160px;"></td>
<td style="padding:4px 0 4px 12px; color:#666; font-size:12px;">Exakter Gruppenname in LuckPerms</td>
</tr>
<tr>
<td style="padding:4px 10px 4px 0;"><label for="default_group"><strong>Standard-Gruppe (nach Ablauf):</strong></label></td>
<td><input type="text" id="default_group" name="default_group" value="<?php echo esc_attr($cur_default_group); ?>" class="regular-text" placeholder="default" style="width:160px;"></td>
<td style="padding:4px 0 4px 12px; color:#666; font-size:12px;">Gruppe nach Rang-Ablauf (z.&nbsp;B. <code>default</code>)</td>
</tr>
<tr>
<td style="padding:4px 10px 4px 0;"><label for="rank_days"><strong>Laufzeit (Tage):</strong></label></td>
<td>
<input type="number" id="rank_days" name="rank_days" value="<?php echo esc_attr($cur_rank_days); ?>" min="0" style="width:80px;">
<span class="description" style="margin-left:8px;">0 = dauerhaft</span>
</td>
</tr>
</table>
<p class="description" style="margin-top:6px;">
Der Spielername im Shop (Feld <em>Name</em> oben) wird dem Spieler als Rang-Label angezeigt.<br>
Gespeicherte Item-ID: <code>rank_{rang-id}_{lp-gruppe}_{standard-gruppe}_{tage}</code>
</p>
</div>
<script>
function wisToggleItemType(val) {
document.getElementById('wis_item_minecraft').style.display = (val === 'minecraft') ? '' : 'none';
document.getElementById('wis_item_fly').style.display = (val === 'fly') ? '' : 'none';
document.getElementById('wis_item_rank').style.display = (val === 'rank') ? '' : 'none';
// item_id Pflichtfeld nur bei Minecraft-Items
document.getElementById('item_id').required = (val === 'minecraft');
}
// Initial state
wisToggleItemType(document.getElementById('item_type').value);
</script>
</td>
</tr>
<tr>
<th><label for="custom_image_url">Bild-URL (optional)</label></th>
<td>
<input type="url" id="custom_image_url" name="custom_image_url"
value="<?php echo ($item && !empty($item->custom_image_url)) ? esc_attr($item->custom_image_url) : ''; ?>"
class="large-text" placeholder="https://example.com/mein-fly-bild.png"
oninput="wisPreviewImage(this.value)">
<p class="description">
Eigene Bild-URL überschreibt die automatische Minecraft-Item-ID-URL.<br>
Ideal für Fly-Gutscheine, VIP-Pakete etc. Leer lassen = Standard.
</p>
<div id="wis_img_preview" style="margin-top:8px; <?php echo ($item && !empty($item->custom_image_url)) ? '' : 'display:none;'; ?>">
<img id="wis_img_preview_img"
src="<?php echo ($item && !empty($item->custom_image_url)) ? esc_url($item->custom_image_url) : ''; ?>"
style="max-height:80px; border-radius:6px; border:1px solid #ddd; padding:4px; background:#2d2d2d;"
alt="Vorschau"
onerror="document.getElementById('wis_img_preview').style.display='none';">
<span style="margin-left:8px; color:#666; font-size:12px;">Vorschau</span>
</div>
<script>
function wisPreviewImage(url) {
var box = document.getElementById('wis_img_preview');
var img = document.getElementById('wis_img_preview_img');
if (url) {
img.src = url;
box.style.display = '';
} else {
box.style.display = 'none';
}
}
</script>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -1576,7 +1760,7 @@ class WIS_Admin {
$per_page = 24; $per_page = 24;
$current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1; $current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
$fetch_args = ['status' => 'publish']; $fetch_args = []; // kein Status-Filter → alle Items zählen
if (!empty($current_category)) $fetch_args['category_slug'] = $current_category; if (!empty($current_category)) $fetch_args['category_slug'] = $current_category;
if (!empty($search_query)) $fetch_args['search'] = $search_query; if (!empty($search_query)) $fetch_args['search'] = $search_query;
@@ -1587,21 +1771,28 @@ class WIS_Admin {
global $wpdb; global $wpdb;
$table_items = $wpdb->prefix . 'wis_items'; $table_items = $wpdb->prefix . 'wis_items';
$where_parts = ["status = 'publish'"]; // Admin-Liste: alle Items anzeigen (publish + draft), Query ohne doppeltes prepare()
$where_parts = [];
$where_vals = [];
if (!empty($current_category)) { if (!empty($current_category)) {
$search_pattern = '%"' . $current_category . '"%'; $where_parts[] = "categories LIKE %s";
$where_parts[] = $wpdb->prepare("categories LIKE %s", $search_pattern); $where_vals[] = '%"' . $wpdb->esc_like($current_category) . '"%';
} }
if (!empty($search_query)) { if (!empty($search_query)) {
$search_like = '%' . $wpdb->esc_like($search_query) . '%'; $like = '%' . $wpdb->esc_like($search_query) . '%';
$where_parts[] = $wpdb->prepare("(name LIKE %s OR item_id LIKE %s)", $search_like, $search_like); $where_parts[] = "(name LIKE %s OR item_id LIKE %s)";
$where_vals[] = $like;
$where_vals[] = $like;
} }
$where_sql = implode(" AND ", $where_parts); $where_sql = !empty($where_parts) ? 'WHERE ' . implode(' AND ', $where_parts) : '';
$items = $wpdb->get_results($wpdb->prepare( $where_vals[] = $per_page;
"SELECT * FROM $table_items WHERE $where_sql ORDER BY name ASC LIMIT %d OFFSET %d", $where_vals[] = $offset;
$per_page, $items = $wpdb->get_results(
$offset $wpdb->prepare(
)); "SELECT * FROM $table_items $where_sql ORDER BY name ASC LIMIT %d OFFSET %d",
$where_vals
)
);
$currency = get_option('wis_currency_name', 'Coins'); $currency = get_option('wis_currency_name', 'Coins');
@@ -1717,7 +1908,19 @@ class WIS_Admin {
</th> </th>
<td><?php echo esc_html($item->id); ?></td> <td><?php echo esc_html($item->id); ?></td>
<td><strong><?php echo esc_html($item->name); ?></strong></td> <td><strong><?php echo esc_html($item->name); ?></strong></td>
<td><code><?php echo esc_html($item->item_id); ?></code></td> <td>
<?php
if (preg_match('/^rank_([^_]+)_([a-zA-Z0-9_\-]+)_([a-zA-Z0-9_\-]+)_(\d+)$/', $item->item_id, $rm2)) {
$rd = intval($rm2[4]);
echo '<span title="' . esc_attr($item->item_id) . '">👑 LP: <code>' . esc_html($rm2[2]) . '</code> &mdash; ' . ($rd === 0 ? '<em>dauerhaft</em>' : esc_html($rd) . ' Tage') . '</span>';
} elseif (preg_match('/^rank_(.+)_(\d+)$/', $item->item_id, $rm2)) {
$rd = intval($rm2[2]);
echo '<span title="' . esc_attr($item->item_id) . '">👑 <code>' . esc_html($rm2[1]) . '</code> &mdash; ' . ($rd === 0 ? '<em>dauerhaft</em>' : esc_html($rd) . ' Tage') . '</span>';
} else {
echo '<code>' . esc_html($item->item_id) . '</code>';
}
?>
</td>
<td><?php echo esc_html($item->price); ?> <?php echo esc_html($currency); ?></td> <td><?php echo esc_html($item->price); ?> <?php echo esc_html($currency); ?></td>
<td> <td>
<?php if ($item->status === 'publish'): ?> <?php if ($item->status === 'publish'): ?>
@@ -2171,13 +2374,12 @@ class WIS_Admin {
$json_data = ['items' => []]; $json_data = ['items' => []];
foreach ($items as $item) { foreach ($items as $item) {
$img_name = str_replace(':', '_', $item->item_id) . '.png';
$json_data['items'][] = [ $json_data['items'][] = [
'id' => $item->item_id, 'id' => $item->item_id,
'name' => $item->name, 'name' => $item->name,
'description' => $item->description, 'description' => $item->description,
'price' => intval($item->price), 'price' => intval($item->price),
'image' => $img_base . $img_name 'image' => WIS_DB::get_item_image($item)
]; ];
} }
@@ -2501,7 +2703,6 @@ class WIS_API {
$result = []; $result = [];
foreach ($items as $item) { foreach ($items as $item) {
$img_name = str_replace(':', '_', $item->item_id) . '.png';
$result[] = [ $result[] = [
'id' => $item->id, 'id' => $item->id,
'item_id' => $item->item_id, 'item_id' => $item->item_id,
@@ -2513,10 +2714,20 @@ class WIS_API {
'is_daily_deal' => (bool) $item->is_daily_deal, 'is_daily_deal' => (bool) $item->is_daily_deal,
'servers' => json_decode($item->servers, true) ?: [], 'servers' => json_decode($item->servers, true) ?: [],
'categories' => json_decode($item->categories, true) ?: [], 'categories' => json_decode($item->categories, true) ?: [],
'image' => $img_base . $img_name, 'image' => WIS_DB::get_item_image($item),
'has_custom_image' => !empty($item->custom_image_url),
]; ];
} }
// Fly-Items nach Dauer sortieren: 5min→15min→30min→1h→2h→3h
$fly_order = ['fly_5min'=>1,'fly_15min'=>2,'fly_30min'=>3,'fly_1h'=>4,'fly_2h'=>5,'fly_3h'=>6];
usort($result, function($a, $b) use ($fly_order) {
$ap = isset($fly_order[$a['item_id']]) ? $fly_order[$a['item_id']] : 999;
$bp = isset($fly_order[$b['item_id']]) ? $fly_order[$b['item_id']] : 999;
if ($ap !== 999 || $bp !== 999) return $ap - $bp;
return strcmp($a['name'], $b['name']);
});
return new WP_REST_Response([ return new WP_REST_Response([
'items' => $result, 'items' => $result,
'total' => $total, 'total' => $total,
@@ -2787,23 +2998,93 @@ class WIS_API {
$final_price = max(0, $total_normal - $coupon_discount) + $total_offer; $final_price = max(0, $total_normal - $coupon_discount) + $total_offer;
$items_payload = []; // Fly-Dauern (item_id → Sekunden)
$title_parts = []; // Fly-Dauern und lesbares Label (wird dem Spieler auf dem Code angezeigt)
$fly_durations = [
'fly_5min' => 5 * 60,
'fly_15min' => 15 * 60,
'fly_30min' => 30 * 60,
'fly_1h' => 1 * 3600,
'fly_2h' => 2 * 3600,
'fly_3h' => 3 * 3600,
];
$fly_labels = [
'fly_5min' => '5 Minuten Fly',
'fly_15min' => '15 Minuten Fly',
'fly_30min' => '30 Minuten Fly',
'fly_1h' => '1 Stunde Fly',
'fly_2h' => '2 Stunden Fly',
'fly_3h' => '3 Stunden Fly',
];
$items_payload = [];
$commands_payload = [];
$title_parts = [];
foreach ($valid_cart as $item) { foreach ($valid_cart as $item) {
$items_payload[] = [ $item_id = $item['id']; // Das ist der item_id-String aus wis_items
'id' => $item['id'],
'amount' => $item['qty'] if (isset($fly_durations[$item_id])) {
]; // Fly-Gutschein: pro Stueck einen eigenen Code-Eintrag (qty > 1 = mehrere Codes)
$title_parts[] = $item['qty'] . 'x ' . $item['title']; $base_sec = $fly_durations[$item_id];
$base_label = $fly_labels[$item_id];
for ($q = 0; $q < intval($item['qty']); $q++) {
$commands_payload[] = [
'type' => 'fly',
'duration_sec' => $base_sec,
'label' => $base_label,
];
}
$title_parts[] = $item['qty'] . 'x ' . $item['title'];
} elseif (preg_match('/^rank_([^_]+)_([a-zA-Z0-9_\-]+)_([a-zA-Z0-9_\-]+)_(\d+)$/', $item_id, $rm)) {
// Neues Format: rank_{rank_id}_{lp_group}_{default_group}_{days}
$rank_id = $rm[1];
$lp_group = $rm[2];
$default_group = $rm[3];
$rank_days = intval($rm[4]);
for ($q = 0; $q < intval($item['qty']); $q++) {
$commands_payload[] = [
'type' => 'rank',
'rank_id' => $rank_id,
'lp_group' => $lp_group,
'default_group' => $default_group,
'label' => $item['title'],
'days' => $rank_days,
];
}
$title_parts[] = $item['qty'] . 'x ' . $item['title'];
} elseif (preg_match('/^rank_(.+)_(\d+)$/', $item_id, $rm)) {
// Altes Format (Fallback): rank_{rank_id}_{days}
$rank_id = $rm[1];
$rank_days = intval($rm[2]);
for ($q = 0; $q < intval($item['qty']); $q++) {
$commands_payload[] = [
'type' => 'rank',
'rank_id' => $rank_id,
'lp_group'=> $rank_id,
'default_group' => 'default',
'label' => $item['title'],
'days' => $rank_days,
];
}
$title_parts[] = $item['qty'] . 'x ' . $item['title'];
} else {
// Normales Item
$items_payload[] = [
'id' => $item_id,
'amount' => $item['qty'],
];
$title_parts[] = $item['qty'] . 'x ' . $item['title'];
}
} }
$title = "Warenkorb: " . implode(', ', $title_parts); $title = "Warenkorb: " . implode(', ', $title_parts);
if (strlen($title) > 240) $title = substr($title, 0, 237) . '...'; if (strlen($title) > 240) $title = substr($title, 0, 237) . '...';
$payload = [ $payload = [
'items' => $items_payload, 'items' => $items_payload,
'coupon' => $coupon_applied ? ['code' => $coupon_code, 'discount' => $coupon_discount] : [] 'commands' => $commands_payload,
'coupon' => $coupon_applied ? ['code' => $coupon_code, 'discount' => $coupon_discount] : [],
]; ];
WIS_DB::insert_order([ WIS_DB::insert_order([
@@ -2914,8 +3195,11 @@ class WIS_Shortcode {
.wis-card.offer { border: 2px solid #ffc107; } .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: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 { 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-img img { width: 160px !important; height: 160px !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-card:hover .wis-card-img img { transform: scale(1.15) rotate(5deg); }
.wis-card-img--custom { padding: 0 !important; overflow: hidden; }
.wis-card-img--custom img { width: 100% !important; height: 100% !important; object-fit: cover !important; image-rendering: auto !important; filter: none !important; border-radius: 0; }
.wis-card:hover .wis-card-img--custom img { transform: scale(1.05) !important; }
.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-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-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-body { padding: 15px; flex-grow: 1; display: flex; flex-direction: column; }
@@ -3235,7 +3519,7 @@ class WIS_Shortcode {
// Wichtig: data-item-id statt onclick mit JSON // Wichtig: data-item-id statt onclick mit JSON
return '<div class="wis-card' + (item.is_offer ? ' offer' : '') + '">' return '<div class="wis-card' + (item.is_offer ? ' offer' : '') + '">'
+ badge + badge
+ '<div class="wis-card-img">' + '<div class="wis-card-img' + (item.has_custom_image ? ' wis-card-img--custom' : '') + '">'
+ '<img src="' + escHtml(item.image) + '" alt="' + escHtml(item.name) + '" loading="lazy"' + '<img src="' + escHtml(item.image) + '" alt="' + escHtml(item.name) + '" loading="lazy"'
+ ' onerror="this.onerror=null;this.src=\'https://via.placeholder.com/100/333/fff?text=?\';">' + ' onerror="this.onerror=null;this.src=\'https://via.placeholder.com/100/333/fff?text=?\';">'
+ '</div>' + '</div>'
@@ -3506,8 +3790,7 @@ class WIS_Sidebar_Widget extends WP_Widget {
if ($item) { if ($item) {
$price = $item->offer_price > 0 ? $item->offer_price : $item->price; $price = $item->offer_price > 0 ? $item->offer_price : $item->price;
$show_old = $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 = WIS_DB::get_item_image($item);
$img_url = $img_base . $img_name;
$target_url = !empty($instance['shop_url']) ? $instance['shop_url'] : home_url(); $target_url = !empty($instance['shop_url']) ? $instance['shop_url'] : home_url();
?> ?>