"./admin/coupon-test.php" : """ false, 'message' => '', 'tests' => array(), 'working_method' => null ); // Preveri, ali je WooCommerce aktiven if (!class_exists('WooCommerce')) { $results['message'] = 'WooCommerce ni aktiven. Prosim aktivirajte WooCommerce vtičnik.'; return $results; } // Pridobi trenutnega uporabnika za testiranje $user = wp_get_current_user(); if (!$user || !$user->exists()) { $results['message'] = 'Uporabnik ni prijavljen. Prijavite se kot administrator.'; return $results; } // Nastavi testno kodo $test_code = 'TEST-' . strtoupper(substr(bin2hex(random_bytes(4)), 0, 8)); // Test 1: Standardna WooCommerce API metoda $results['tests']['standard_api'] = self::test_standard_api($test_code . '-1', $user); // Test 2: Programsko ustvarjanje kupona $results['tests']['programmatic'] = self::test_programmatic($test_code . '-2', $user); // Test 3: Uporaba WC_Coupon z dodatnimi preverjanji $results['tests']['wc_coupon_extended'] = self::test_wc_coupon_extended($test_code . '-3', $user); // Test 4: Neposredno vstavljanje v podatkovno bazo $results['tests']['direct_db'] = self::test_direct_db($test_code . '-4', $user); // Počisti testne kupone self::cleanup_test_coupons($test_code); // Določi, katera metoda deluje foreach ($results['tests'] as $method => $test_result) { if ($test_result['success']) { $results['working_method'] = $method; $results['success'] = true; break; } } if ($results['success']) { $results['message'] = 'Našli smo delujočo metodo za ustvarjanje kuponov: ' . $results['working_method']; } else { $results['message'] = 'Nobena testirana metoda ne deluje. Preverite dnevnik napak za več informacij.'; } return $results; } /** * Test 1: Standardna WooCommerce API metoda */ private static function test_standard_api($code, $user) { $result = array( 'success' => false, 'message' => '', 'coupon_id' => 0 ); try { $coupon = new WC_Coupon(); $coupon->set_code($code); $coupon->set_description('Test coupon'); $coupon->set_discount_type('percent'); $coupon->set_amount(10); $coupon->set_individual_use(true); $coupon->set_usage_limit(1); $coupon->set_usage_limit_per_user(1); $coupon->set_email_restrictions(array($user->user_email)); $coupon_id = $coupon->save(); if ($coupon_id) { $result['success'] = true; $result['message'] = 'Kupon uspešno ustvarjen z ID: ' . $coupon_id; $result['coupon_id'] = $coupon_id; } else { $result['message'] = 'Napaka: Kupon ni bil ustvarjen (save() je vrnil 0)'; } } catch (Exception $e) { $result['message'] = 'Napaka: ' . $e->getMessage(); } return $result; } /** * Test 2: Programsko ustvarjanje kupona */ private static function test_programmatic($code, $user) { $result = array( 'success' => false, 'message' => '', 'coupon_id' => 0 ); try { // Ustvari nov post tipa shop_coupon $coupon_data = array( 'post_title' => $code, 'post_content' => '', 'post_status' => 'publish', 'post_author' => $user->ID, 'post_type' => 'shop_coupon' ); $coupon_id = wp_insert_post($coupon_data); if (!is_wp_error($coupon_id)) { // Dodaj meta podatke za kupon update_post_meta($coupon_id, 'discount_type', 'percent'); update_post_meta($coupon_id, 'coupon_amount', '10'); update_post_meta($coupon_id, 'individual_use', 'yes'); update_post_meta($coupon_id, 'usage_limit', '1'); update_post_meta($coupon_id, 'usage_limit_per_user', '1'); update_post_meta($coupon_id, 'customer_email', array($user->user_email)); $result['success'] = true; $result['message'] = 'Kupon uspešno ustvarjen z ID: ' . $coupon_id; $result['coupon_id'] = $coupon_id; } else { $result['message'] = 'Napaka: ' . $coupon_id->get_error_message(); } } catch (Exception $e) { $result['message'] = 'Napaka: ' . $e->getMessage(); } return $result; } /** * Test 3: Uporaba WC_Coupon z dodatnimi preverjanji */ private static function test_wc_coupon_extended($code, $user) { $result = array( 'success' => false, 'message' => '', 'coupon_id' => 0 ); try { // Preveri, ali koda že obstaja $existing_id = wc_get_coupon_id_by_code($code); if ($existing_id) { $result['message'] = 'Napaka: Kupon s to kodo že obstaja (ID: ' . $existing_id . ')'; return $result; } // Ustvari nov kupon z dodatnimi preverjanji $coupon = new WC_Coupon(); $coupon->set_code($code); $coupon->set_description('Test coupon with extended checks'); $coupon->set_discount_type('percent'); $coupon->set_amount(10); $coupon->set_individual_use(true); $coupon->set_usage_limit(1); $coupon->set_usage_limit_per_user(1); $coupon->set_email_restrictions(array($user->user_email)); // Dodatna nastavitev $coupon->set_date_expires(strtotime('+7 days')); $coupon_id = $coupon->save(); // Preveri, ali je kupon res ustvarjen if ($coupon_id) { $verification_id = wc_get_coupon_id_by_code($code); if ($verification_id && $verification_id == $coupon_id) { $result['success'] = true; $result['message'] = 'Kupon uspešno ustvarjen in preverjen z ID: ' . $coupon_id; $result['coupon_id'] = $coupon_id; } else { $result['message'] = 'Napaka: Kupon je bil ustvarjen, vendar preverjanje ni uspelo'; } } else { $result['message'] = 'Napaka: Kupon ni bil ustvarjen (save() je vrnil 0)'; } } catch (Exception $e) { $result['message'] = 'Napaka: ' . $e->getMessage(); } return $result; } /** * Test 4: Neposredno vstavljanje v podatkovno bazo */ private static function test_direct_db($code, $user) { $result = array( 'success' => false, 'message' => '', 'coupon_id' => 0 ); try { global $wpdb; // Ustvari nov post tipa shop_coupon $wpdb->insert( $wpdb->posts, array( 'post_title' => $code, 'post_name' => sanitize_title($code), 'post_content' => '', 'post_status' => 'publish', 'post_author' => $user->ID, 'post_type' => 'shop_coupon', 'post_date' => current_time('mysql'), 'post_date_gmt' => current_time('mysql', 1) ) ); $coupon_id = $wpdb->insert_id; if ($coupon_id) { // Dodaj meta podatke za kupon $meta_data = array( 'discount_type' => 'percent', 'coupon_amount' => '10', 'individual_use' => 'yes', 'usage_limit' => '1', 'usage_limit_per_user' => '1', 'customer_email' => serialize(array($user->user_email)) ); foreach ($meta_data as $meta_key => $meta_value) { $wpdb->insert( $wpdb->postmeta, array( 'post_id' => $coupon_id, 'meta_key' => $meta_key, 'meta_value' => $meta_value ) ); } $result['success'] = true; $result['message'] = 'Kupon uspešno ustvarjen neposredno v bazi z ID: ' . $coupon_id; $result['coupon_id'] = $coupon_id; } else { $result['message'] = 'Napaka: Kupon ni bil ustvarjen v bazi'; } } catch (Exception $e) { $result['message'] = 'Napaka: ' . $e->getMessage(); } return $result; } /** * Počisti testne kupone */ private static function cleanup_test_coupons($prefix) { global $wpdb; // Poišči vse testne kupone $test_coupons = $wpdb->get_results( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_title LIKE %s AND post_type = 'shop_coupon'", $prefix . '%' ) ); // Izbriši najdene kupone foreach ($test_coupons as $coupon) { wp_delete_post($coupon->ID, true); } } } /** * Funkcija za obdelavo AJAX zahteve za testiranje kuponov */ function wheel_test_coupons_ajax() { // Preveri varnostno kodo check_ajax_referer('wheel_admin_nonce', 'nonce'); // Preveri pravice if (!current_user_can('manage_options')) { wp_send_json_error(array('message' => 'Nimate pravic za izvedbo tega dejanja.')); return; } // Zaženi teste $results = WheelCouponTester::run_tests(); // Vrni rezultate if ($results['success']) { wp_send_json_success($results); } else { wp_send_json_error($results); } } add_action('wp_ajax_wheel_test_coupons', 'wheel_test_coupons_ajax'); /** * Shrani najboljšo metodo za ustvarjanje kuponov */ function wheel_save_best_coupon_method() { // Preveri varnostno kodo check_ajax_referer('wheel_admin_nonce', 'nonce'); // Preveri pravice if (!current_user_can('manage_options')) { wp_send_json_error(array('message' => 'Nimate pravic za izvedbo tega dejanja.')); return; } // Preveri, ali je metoda podana if (!isset($_POST['method']) || empty($_POST['method'])) { wp_send_json_error(array('message' => 'Metoda ni podana.')); return; } $method = sanitize_text_field($_POST['method']); $valid_methods = array('standard_api', 'programmatic', 'wc_coupon_extended', 'direct_db'); if (!in_array($method, $valid_methods)) { wp_send_json_error(array('message' => 'Neveljavna metoda.')); return; } // Shrani najboljšo metodo update_option('wheel_best_coupon_method', $method); wp_send_json_success(array( 'message' => sprintf(__('Metoda %s je bila uspešno shranjena kot najboljša metoda za ustvarjanje kuponov.', 'wheel-of-fortune'), $method) )); } add_action('wp_ajax_wheel_save_best_coupon_method', 'wheel_save_best_coupon_method'); """ "./admin/edit-wheel-page.php" : """ prefix . 'wheel_prizes'; $wheel_id = isset($_POST['wheel_id']) ? intval($_POST['wheel_id']) : 0; $name = isset($_POST['prize_name']) ? sanitize_text_field($_POST['prize_name']) : ''; $description = isset($_POST['prize_description']) ? sanitize_textarea_field($_POST['prize_description']) : ''; $probability = isset($_POST['prize_probability']) ? floatval($_POST['prize_probability']) : 0; $is_active = isset($_POST['prize_is_active']) ? 1 : 0; $redemption_code = isset($_POST['prize_redemption_code']) ? sanitize_text_field($_POST['prize_redemption_code']) : ''; $is_discount = isset($_POST['prize_is_discount']) ? 1 : 0; $discount_value = isset($_POST['prize_discount_value']) ? floatval($_POST['prize_discount_value']) : 0; $email_subject = isset($_POST['prize_email_subject']) ? sanitize_text_field($_POST['prize_email_subject']) : ''; $email_template = isset($_POST['prize_email_template']) ? wp_kses_post($_POST['prize_email_template']) : ''; $is_try_again = isset($_POST['prize_is_try_again']) ? 1 : 0; if (!empty($name) && $wheel_id > 0) { $result = $wpdb->insert( $prizes_table, [ 'wheel_id' => $wheel_id, 'name' => $name, 'description' => $description, 'probability' => $probability, 'is_active' => $is_active, 'redemption_code' => $redemption_code, 'is_discount' => $is_discount, 'discount_value' => $discount_value, 'email_subject' => $email_subject, 'email_template' => $email_template, 'is_try_again' => $is_try_again ], ['%d', '%s', '%s', '%f', '%d', '%s', '%d', '%f', '%s', '%s', '%d'] ); if ($result !== false) { echo '

' . __('Prize added successfully!', 'wheel-of-fortune') . '

'; } else { echo '

' . __('Error adding prize. Please try again.', 'wheel-of-fortune') . '

'; } } else { echo '

' . __('Please fill in all required fields.', 'wheel-of-fortune') . '

'; } } elseif ($_POST['action'] === 'edit_prize' && check_admin_referer('wheel_prizes_nonce')) { global $wpdb; $prizes_table = $wpdb->prefix . 'wheel_prizes'; $prize_id = isset($_POST['prize_id']) ? intval($_POST['prize_id']) : 0; $wheel_id = isset($_POST['wheel_id']) ? intval($_POST['wheel_id']) : 0; $name = isset($_POST['prize_name']) ? sanitize_text_field($_POST['prize_name']) : ''; $description = isset($_POST['prize_description']) ? sanitize_textarea_field($_POST['prize_description']) : ''; $probability = isset($_POST['prize_probability']) ? floatval($_POST['prize_probability']) : 0; $is_active = isset($_POST['prize_is_active']) ? 1 : 0; $redemption_code = isset($_POST['prize_redemption_code']) ? sanitize_text_field($_POST['prize_redemption_code']) : ''; $is_discount = isset($_POST['prize_is_discount']) ? 1 : 0; $discount_value = isset($_POST['prize_discount_value']) ? floatval($_POST['prize_discount_value']) : 0; $email_subject = isset($_POST['prize_email_subject']) ? sanitize_text_field($_POST['prize_email_subject']) : ''; $email_template = isset($_POST['prize_email_template']) ? wp_kses_post($_POST['prize_email_template']) : ''; $is_try_again = isset($_POST['prize_is_try_again']) ? 1 : 0; if (!empty($name) && $prize_id > 0 && $wheel_id > 0) { $result = $wpdb->update( $prizes_table, [ 'wheel_id' => $wheel_id, 'name' => $name, 'description' => $description, 'probability' => $probability, 'is_active' => $is_active, 'redemption_code' => $redemption_code, 'is_discount' => $is_discount, 'discount_value' => $discount_value, 'email_subject' => $email_subject, 'email_template' => $email_template, 'is_try_again' => $is_try_again ], ['id' => $prize_id], ['%d', '%s', '%s', '%f', '%d', '%s', '%d', '%f', '%s', '%s', '%d'], ['%d'] ); if ($result !== false) { echo '

' . __('Prize updated successfully!', 'wheel-of-fortune') . '

'; } else { echo '

' . __('Error updating prize. Please try again.', 'wheel-of-fortune') . '

'; } } else { echo '

' . __('Please fill in all required fields.', 'wheel-of-fortune') . '

'; } } } if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && in_array($_POST['action'], ['add_wheel_product', 'delete_wheel_product']) ) { global $wpdb; $wheels_table = $wpdb->prefix . 'wof_wheels'; $wheel_products_table = $wpdb->prefix . 'wheel_of_fortune_products'; if ($_POST['action'] === 'add_wheel_product' && check_admin_referer('wheel_products_nonce')) { $wheel_id = isset($_POST['wheel_id']) ? intval($_POST['wheel_id']) : 0; $product_id = isset($_POST['product_id']) ? intval($_POST['product_id']) : 0; $spins_per_purchase = isset($_POST['spins_per_purchase']) ? intval($_POST['spins_per_purchase']) : 1; // Debug informacije error_log("=== WHEEL PRODUCT DEBUG ==="); error_log("POST data: " . print_r($_POST, true)); error_log("Wheel ID: " . $wheel_id); error_log("Product ID: " . $product_id); error_log("Spins per purchase: " . $spins_per_purchase); error_log("Table name: " . $wheel_products_table); // Preveri, ali tabela obstaja $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$wheel_products_table'") == $wheel_products_table; error_log("Table exists: " . ($table_exists ? 'YES' : 'NO')); if (!$table_exists) { error_log("ERROR: Tabela $wheel_products_table ne obstaja!"); echo '

' . __('Napaka: Tabela za produkte ne obstaja. Prosimo, deaktivirajte in ponovno aktivirajte plugin.', 'wheel-of-fortune') . '

'; return; } if ($wheel_id > 0 && $product_id > 0 && $spins_per_purchase > 0) { // Preveri, ali kolo obstaja $wheel_exists = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $wheels_table WHERE id = %d", $wheel_id)); error_log("Wheel exists: " . $wheel_exists); if (!$wheel_exists) { error_log("ERROR: Kolo z ID $wheel_id ne obstaja!"); echo '

' . __('Napaka: Izbrano kolo ne obstaja.', 'wheel-of-fortune') . '

'; return; } // Preveri, ali produkt obstaja if (class_exists('WooCommerce')) { $product = wc_get_product($product_id); if (!$product) { error_log("ERROR: Produkt z ID $product_id ne obstaja!"); echo '

' . __('Napaka: Izbrani produkt ne obstaja.', 'wheel-of-fortune') . '

'; return; } } $data = [ 'wheel_id' => $wheel_id, 'product_id' => $product_id, 'spins_per_purchase' => $spins_per_purchase ]; error_log("Data to insert: " . print_r($data, true)); // Preveri, ali je produkt že povezan s tem kolesom $existing_product = $wpdb->get_var($wpdb->prepare( "SELECT id FROM $wheel_products_table WHERE wheel_id = %d AND product_id = %d", $wheel_id, $product_id )); if ($existing_product) { error_log("Product already exists for this wheel, updating..."); } else { error_log("Adding new product to wheel..."); } $result = $wpdb->replace( $wheel_products_table, $data, ['%d', '%d', '%d'] ); error_log("SQL result: " . $result); error_log("Last SQL query: " . $wpdb->last_query); error_log("Last SQL error: " . $wpdb->last_error); if ($result !== false) { echo '

' . __('Produkt je bil uspešno dodan ali posodobljen.', 'wheel-of-fortune') . '

'; } else { echo '

' . __('Napaka pri dodajanju produkta. Preveri vnos.', 'wheel-of-fortune') . '

'; } } else { error_log("Validation failed - Wheel ID: $wheel_id, Product ID: $product_id, Spins: $spins_per_purchase"); echo '

' . __('Napaka pri dodajanju produkta. Preveri vnos.', 'wheel-of-fortune') . '

'; } } elseif ($_POST['action'] === 'delete_wheel_product' && check_admin_referer('wheel_products_nonce')) { $id = isset($_POST['id']) ? intval($_POST['id']) : 0; if ($id > 0) { $wpdb->delete($wheel_products_table, ['id' => $id], ['%d']); echo '

' . __('Produkt je bil izbrisan.', 'wheel-of-fortune') . '

'; } } } global $wpdb; $wheels_table = $wpdb->prefix . 'wof_wheels'; $prizes_table = $wpdb->prefix . 'wheel_prizes'; // Get the current wheel ID from URL $wheel_id = isset($_GET['wheel_id']) ? intval($_GET['wheel_id']) : 0; // Fetch wheel data $wheel = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wheels_table WHERE id = %d", $wheel_id), ARRAY_A); if (!$wheel) { echo '

' . __('Wheel not found.', 'wheel-of-fortune') . '

'; return; } // Fetch prizes for this specific wheel $prizes = $wpdb->get_results($wpdb->prepare("SELECT * FROM $prizes_table WHERE wheel_id = %d ORDER BY id ASC", $wheel_id), ARRAY_A); $total_probability = array_sum(wp_list_pluck($prizes, 'probability')); // Fetch povezane produkte za to kolo $wheel_products_table = $wpdb->prefix . 'wheel_of_fortune_products'; $products = $wpdb->get_results($wpdb->prepare("SELECT * FROM $wheel_products_table WHERE wheel_id = %d", $wheel_id), ARRAY_A); // Pridobi vse WooCommerce produkte za dropdown if (class_exists('WooCommerce')) { $all_products = wc_get_products(array('limit' => -1, 'status' => 'publish')); } else { $all_products = array(); } ?>


:

1): ?>

' . esc_html($total_probability) . ''); ?>

0 && $total_probability < 1): ?>

' . esc_html($total_probability) . ''); ?>

get_name()) : esc_html($prod['product_id']); ?>

""" "./admin/js/admin.js" : """ jQuery(document).ready(function($) { 'use strict'; // Only run on our plugin's admin pages if (!$('.wheel-admin-page').length) { return; } // Tab navigation $('.nav-tab').on('click', function(e) { e.preventDefault(); $('.nav-tab').removeClass('nav-tab-active'); $(this).addClass('nav-tab-active'); $('.wheel-tab-content').hide(); $('#' + $(this).data('target')).show(); }); // --- Modal Logic --- var modal = $('#edit-prize-modal'); var span = modal.find('.close'); // Close modal span.on('click', function() { modal.hide(); }); $(window).on('click', function(event) { if ($(event.target).is(modal)) { modal.hide(); } }); // --- Add New Prize --- $('.add-new-prize').on('click', function(e) { e.preventDefault(); // Clear form for new prize $('#edit-prize-form')[0].reset(); $('#edit-prize-id').val(''); // Clear prize ID for new prize // Get wheel_id from the modal's data attribute var wheelId = $('#edit-prize-modal').data('wheel-id'); $('#edit-wheel-id').val(wheelId); console.log('Opening modal for new prize with wheel_id:', wheelId); modal.show(); }); // --- Edit Prize --- $('.edit-prize').on('click', function(e) { e.preventDefault(); var prizeId = $(this).data('id'); // AJAX call to get prize details $.post(ajaxurl, { action: 'wheel_get_prize_details', _ajax_nonce: wheel_admin_nonce._ajax_nonce, prize_id: prizeId }, function(response) { if (response.success) { var prize = response.data; // Populate form $('#edit-prize-id').val(prize.id); $('#edit-wheel-id').val(prize.wheel_id); // wheel_id je že v formi $('#edit-prize-name').val(prize.name); $('#edit-prize-description').val(prize.description); $('#edit-prize-probability').val(prize.probability); $('#edit-prize-is-active').prop('checked', parseInt(prize.is_active, 10)); $('#edit-prize-redemption-code').val(prize.redemption_code); $('#edit-prize-is-discount').prop('checked', parseInt(prize.is_discount, 10)); $('#edit-prize-discount-value').val(prize.discount_value); $('#edit-prize-email-subject').val(prize.email_subject); $('#edit-prize-email-template').val(prize.email_template); $('#edit-prize-is-try-again').prop('checked', parseInt(prize.is_try_again, 10)); modal.show(); } else { alert(response.data.message || 'An error occurred.'); } }).fail(function() { alert('An error occurred while loading prize details.'); }); }); // --- Save Prize (via AJAX) --- $('#edit-prize-form').on('submit', function(e) { e.preventDefault(); // Pripravimo podatke iz forme const formData = { action: 'wheel_save_prize', _ajax_nonce: wheel_admin_nonce._ajax_nonce, wheel_id: $('#edit-wheel-id').val(), // **KLJUČNO: pošljemo wheel_id** prize_id: $('#edit-prize-id').val(), name: $('#edit-prize-name').val(), description: $('#edit-prize-description').val(), probability: $('#edit-prize-probability').val(), is_active: $('#edit-prize-is-active').is(':checked') ? 1 : 0, redemption_code: $('#edit-prize-redemption-code').val(), is_discount: $('#edit-prize-is-discount').is(':checked') ? 1 : 0, discount_value: $('#edit-prize-discount-value').val(), email_subject: $('#edit-prize-email-subject').val(), email_template: $('#edit-prize-email-template').val(), is_try_again: $('#edit-prize-is-try-again').is(':checked') ? 1 : 0, }; // Debug: preveri wheel_id console.log('Wheel ID from form:', formData.wheel_id); console.log('Form data:', formData); // Validate form fields if (!formData.name) { alert('Prize name is required.'); return; } if (isNaN(parseFloat(formData.probability)) || formData.probability < 0 || formData.probability > 1) { alert('Probability must be a number between 0 and 1.'); return; } if (!formData.wheel_id || formData.wheel_id == 0) { alert('Wheel ID is missing. Please refresh the page and try again.'); return; } // AJAX call to save the prize $.post(ajaxurl, formData, function(response) { console.log('Server response:', response); if (response.success) { alert(response.data.message); modal.hide(); location.reload(); // Refresh page to see changes } else { alert(response.data.message || 'An error occurred while saving.'); } }).fail(function(xhr, status, error) { console.error('AJAX error:', {xhr: xhr, status: status, error: error}); alert('A critical error occurred. Please try again.'); }); }); // --- Delete Prize --- $('.delete-prize').on('click', function(e) { e.preventDefault(); if (confirm('Are you sure you want to delete this prize?')) { var prizeId = $(this).data('id'); $.post(ajaxurl, { action: 'wheel_delete_prize', _ajax_nonce: wheel_admin_nonce._ajax_nonce, prize_id: prizeId }, function(response) { if (response.success) { alert(response.data.message); location.reload(); } else { alert(response.data.message || 'An error occurred.'); } }); } }); // --- SETTINGS PAGE - TEST EMAIL --- if ($('#test-email-button').length) { $('#test-email-button').on('click', function(e) { e.preventDefault(); const button = $(this); const resultDiv = $('#test-email-result'); const recipientEmail = $('#test-email-recipient').val(); const spinner = button.siblings('.spinner'); if (!recipientEmail) { alert(wheel_admin_i18n.enter_recipient_email || 'Please enter a recipient email address.'); return; } button.prop('disabled', true); spinner.addClass('is-active'); resultDiv.slideUp().removeClass('notice notice-success notice-error is-dismissible').empty(); // Collect all settings from the form to test unsaved values const data = { action: 'wheel_test_email', _ajax_nonce: wheel_admin_nonce._ajax_nonce, recipient_email: recipientEmail, smtp_enabled: $('#wheel_smtp_enabled').is(':checked') ? '1' : '0', smtp_host: $('#wheel_smtp_host').val(), smtp_port: $('#wheel_smtp_port').val(), smtp_encryption: $('#wheel_smtp_encryption').val(), smtp_username: $('#wheel_smtp_username').val(), smtp_password: $('#wheel_smtp_password').val(), from_name: $('#wheel_email_from_name').val(), from_email: $('#wheel_email_from_email').val(), }; $.ajax({ url: ajaxurl, method: 'POST', data: data, success: function(response) { if (response.success) { resultDiv.addClass('notice notice-success is-dismissible').html('

' + response.data.message + '

').slideDown(); } else { const errorMessage = response.data.message || (wheel_admin_i18n.error_sending_email || 'An unknown error occurred.'); resultDiv.addClass('notice notice-error is-dismissible').html('

' + errorMessage + '

').slideDown(); } }, error: function(xhr) { let errorMessage = wheel_admin_i18n.error_sending_email || 'A communication error occurred with the server.'; if(xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) { errorMessage = xhr.responseJSON.data.message; } resultDiv.addClass('notice notice-error is-dismissible').html('

' + errorMessage + '

').slideDown(); }, complete: function() { button.prop('disabled', false); spinner.removeClass('is-active'); } }); }); } // Dodajanje/brisanje produkta za spine $('#add-product-button').on('click', function() { const button = $(this); const productId = $('#new-product-id').val(); const spins = $('#new-product-spins').val(); const wheelId = button.data('wheel-id'); if (!productId || !spins) { alert('Prosim vnesite ID produkta in število spinov.'); return; } $.ajax({ url: ajaxurl, method: 'POST', data: { action: 'ajax_update_wheel_product_spins', _ajax_nonce: wheel_admin_nonce._ajax_nonce, wheel_id: wheelId, product_id: productId, spins: spins }, success: function(response) { if(response.success) { location.reload(); } else { alert('Napaka: ' + response.data.message); } }, error: function() { alert('Prišlo je do kritične napake.'); } }); }); $('.delete-product-button').on('click', function() { if(!confirm('Ste prepričani?')) return; const button = $(this); const productId = button.data('product-id'); const wheelId = button.data('wheel-id'); $.ajax({ url: ajaxurl, method: 'POST', data: { action: 'ajax_delete_wheel_product', _ajax_nonce: wheel_admin_nonce._ajax_nonce, wheel_id: wheelId, product_id: productId }, success: function(response) { if(response.success) { location.reload(); } else { alert('Napaka: ' + response.data.message); } }, error: function() { alert('Prišlo je do kritične napake.'); } }); }); });""" "./admin/js/wheel.js" : """ /** * Wheel of Fortune - JavaScript for wheel functionality * Robust, smooth animation using requestAnimationFrame and easing functions. */ jQuery(document).ready(function($) { // Check if wheelSettings are available if (typeof wheelSettings === 'undefined') { console.error('Wheel of Fortune: Settings (wheelSettings) are not defined. Please check the plugin setup.'); return; } // Elements const wheel = $('.wheel'); const button = $('.wheel-button'); const resultDiv = $('.wheel-result'); const spinsCounter = $('.wheel-spins-counter span'); // Animation state let isSpinning = false; let animationFrameId = null; let accumulatedRotation = 0; // Total rotation is maintained between spins // Animation settings const spinDuration = 8000; // 8 seconds for a more dramatic effect const spinRotations = 5; // Number of full rotations before stopping /** * Easing function (cubic-bezier out) for natural deceleration * @param {number} t - Current time (0 to 1) * @returns {number} - Animation progress (0 to 1) */ function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); } /** * Main animation loop * @param {number} startTime - Animation start time * @param {number} startRotation - Starting rotation angle * @param {number} rotationAmount - Total rotation to perform for this spin */ function animateWheel(startTime, startRotation, rotationAmount) { const currentTime = Date.now(); const elapsedTime = currentTime - startTime; if (elapsedTime >= spinDuration) { wheel.css('transform', `rotate(${startRotation + rotationAmount}deg)`); accumulatedRotation = startRotation + rotationAmount; // Update the final position finishSpin(); return; } // Calculate progress within the animation duration const timeProgress = elapsedTime / spinDuration; // Apply the easing function to determine the rotation progress const rotationProgress = easeOutCubic(timeProgress); // Calculate the current rotation const newRotation = startRotation + (rotationAmount * rotationProgress); wheel.css('transform', `rotate(${newRotation}deg)`); // Continue to the next frame animationFrameId = requestAnimationFrame(() => animateWheel(startTime, startRotation, rotationAmount)); } /** * Function to execute after the spin animation is complete */ function finishSpin() { // Display the result with a slight delay for better effect setTimeout(() => { const prize = window.spinResult.prize; resultDiv.html(`

Congratulations!

You've won: ${prize.name}

`); resultDiv.addClass('win').fadeIn(300); isSpinning = false; // Re-enable the button if there are remaining spins if (window.spinResult.remaining_spins > 0) { button.prop('disabled', false); } else { button.prop('disabled', true); } }, 500); // 500ms pause before showing the result } // Spin button click handler button.on('click', function() { if (isSpinning) return; isSpinning = true; button.prop('disabled', true); resultDiv.hide().removeClass('win error'); $.ajax({ url: wheelSettings.ajaxUrl, method: 'POST', data: { action: 'wheel_spin', nonce: wheelSettings.nonce }, beforeSend: function(xhr) { xhr.setRequestHeader('X-WP-Nonce', wheelSettings.nonce); }, success: function(response) { if (response.success) { window.spinResult = response; // The target angle (0-360) for the wheel to stop at, sent by the server. const targetAngle = response.degree; // Add several full rotations for a nice visual effect. const fullSpins = spinRotations * 360; // Get the current angle of the wheel, normalized to 0-360 degrees. const currentAngle = accumulatedRotation % 360; // Calculate the rotation needed to get from the current angle to the target angle. // We must always rotate forward (clockwise, positive rotation). let rotationToTarget = targetAngle - currentAngle; if (rotationToTarget < 0) { rotationToTarget += 360; } // The total amount of rotation for this spin. const rotationAmount = fullSpins + rotationToTarget; const startRotation = accumulatedRotation; // Start the animation. The function will rotate the wheel by 'rotationAmount' degrees. animateWheel(Date.now(), startRotation, rotationAmount); // Update the remaining spins counter on the screen. spinsCounter.text(response.remaining_spins); } else { // Display error from server resultDiv.html(`

${response.data.message || 'An error occurred.'}

`); resultDiv.addClass('error').fadeIn(300); isSpinning = false; button.prop('disabled', false); } }, error: function(xhr) { const errorMsg = xhr.responseJSON ? xhr.responseJSON.message : 'A communication error occurred.'; resultDiv.html(`

${errorMsg}

`); resultDiv.addClass('error').fadeIn(300); isSpinning = false; button.prop('disabled', false); } }); }); // Initialization if (wheelSettings.remainingSpins <= 0) { button.prop('disabled', true); } });""" "./admin/partials/prize-form-fields.php" : """

""" "./admin/settings-page.php" : """ $product_id) { if (!empty($product_id) && isset($product_spins[$key])) { $spin_products[$product_id] = intval($product_spins[$key]); } } } update_option('wheel_spin_products', $spin_products); // Dnevni spin - shrani izbrano kolo $daily_spin_wheel_id = isset($_POST['wof_daily_spin_wheel_id']) ? intval($_POST['wof_daily_spin_wheel_id']) : ''; update_option('wof_daily_spin_wheel_id', $daily_spin_wheel_id); // Prikaži sporočilo o uspehu add_settings_error('wheel_settings', 'settings_updated', __('Nastavitve so bile uspešno shranjene.', 'wheel-of-fortune'), 'updated'); } // Ročno dodajanje spinov uporabniku se zdaj izvaja preko AJAX // Pridobi trenutne nastavitve $cooldown_minutes = get_option('wheel_cooldown_minutes', 0); $max_spins = get_option('wheel_max_spins', 0); $send_emails = get_option('wheel_send_emails', false); $spin_products = get_option('wheel_spin_products', array()); // Pridobi vsa kolesa za dropdown $wheels_table = $wpdb->prefix . 'wof_wheels'; $wheels = $wpdb->get_results("SELECT * FROM $wheels_table ORDER BY id ASC", ARRAY_A); $daily_spin_wheel_id = get_option('wof_daily_spin_wheel_id', ''); // Prikaži sporočila o napakah/uspehu settings_errors('wheel_settings'); ?>

""" "./admin/stats-page.php" : """ prefix . 'users'; $spins_table = $wpdb->prefix . 'wheel_spins'; $log_table = $wpdb->prefix . 'wheel_log'; $prizes_table = $wpdb->prefix . 'wheel_prizes'; $wheels_table = $wpdb->prefix . 'wof_wheels'; // Pridobi vsa kolesa $wheels = $wpdb->get_results("SELECT * FROM $wheels_table ORDER BY id ASC", ARRAY_A); // Izberi kolo (privzeto prvo) $selected_wheel_id = isset($_GET['wheel_id']) ? intval($_GET['wheel_id']) : 1; $selected_wheel = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wheels_table WHERE id = %d", $selected_wheel_id), ARRAY_A); if (!$selected_wheel) { $selected_wheel_id = 1; $selected_wheel = $wpdb->get_row("SELECT * FROM $wheels_table WHERE id = 1", ARRAY_A); } // Iskanje uporabnikov $search = isset($_GET['s']) ? sanitize_text_field($_GET['s']) : ''; $search_condition = ''; if (!empty($search)) { $search_condition = $wpdb->prepare( "AND (u.user_login LIKE %s OR u.user_email LIKE %s OR u.display_name LIKE %s)", "%{$search}%", "%{$search}%", "%{$search}%" ); } // Pridobi uporabnike z spin-i za izbrano kolo $users_with_spins = $wpdb->get_results( $wpdb->prepare( "SELECT u.ID, u.user_email, u.display_name, COALESCE(s.spins_available, 0) as spins_available, COUNT(l.id) as total_spins, MAX(l.spin_date) as last_spin_date FROM {$users_table} u LEFT JOIN {$spins_table} s ON u.ID = s.user_id AND s.wheel_id = %d LEFT JOIN {$log_table} l ON u.ID = l.user_id AND l.wheel_id = %d WHERE 1=1 {$search_condition} GROUP BY u.ID HAVING total_spins > 0 OR spins_available > 0 ORDER BY total_spins DESC", $selected_wheel_id, $selected_wheel_id ), ARRAY_A ); // Označi nagrado kot unovčeno if (isset($_POST['mark_redeemed']) && isset($_POST['prize_id'])) { check_admin_referer('mark_prize_redeemed_nonce', 'mark_prize_redeemed_nonce'); $prize_log_id = intval($_POST['prize_id']); $wpdb->update( $log_table, array('redeemed' => 1), array('id' => $prize_log_id) ); echo '

' . __('Nagrada je bila označena kot unovčena.', 'wheel-of-fortune') . '

'; } // Ponastavi spine na 0 za vse uporabnike na izbranem kolesu if (isset($_POST['reset_all_spins_wheel']) && isset($_POST['wheel_id'])) { check_admin_referer('reset_spins_wheel_nonce', 'reset_spins_wheel_nonce'); $wheel_id = intval($_POST['wheel_id']); // Preveri, če kolo obstaja $wheel_exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM $wheels_table WHERE id = %d", $wheel_id)); if ($wheel_exists) { // Ponastavi spine na 0 za vse uporabnike na tem kolesu $result = $wpdb->update( $spins_table, array('spins_available' => 0), array('wheel_id' => $wheel_id), array('%d'), array('%d') ); if ($result !== false) { echo '

' . sprintf(__('Vsi spini za kolo ID %d so bili uspešno ponastavljeni na 0.', 'wheel-of-fortune'), $wheel_id) . '

'; } else { echo '

' . __('Prišlo je do napake pri ponastavitvi spinov.', 'wheel-of-fortune') . '

'; } } else { echo '

' . __('Izbrano kolo ne obstaja.', 'wheel-of-fortune') . '

'; } } // Izberi uporabnika za podrobnosti $selected_user_id = isset($_GET['user_id']) ? intval($_GET['user_id']) : 0; // Pridobi podrobnosti o nagradah uporabnika za izbrano kolo, če je izbran $user_prizes = array(); if ($selected_user_id > 0) { $user_prizes = $wpdb->get_results( $wpdb->prepare( "SELECT l.id, p.name as prize_name, p.description as prize_description, l.spin_date, l.redeemed FROM {$log_table} l JOIN {$prizes_table} p ON l.prize_id = p.id WHERE l.user_id = %d AND l.wheel_id = %d ORDER BY l.spin_date DESC", $selected_user_id, $selected_wheel_id ), ARRAY_A ); } ?>

:


>
0) : ?>

display_name)); ?>

user_email); ?>
get_var($wpdb->prepare( "SELECT COUNT(*) FROM {$log_table} WHERE user_id = %d AND wheel_id = %d", $selected_user_id, $selected_wheel_id )); echo esc_html($total_spins); ?>
get_var($wpdb->prepare( "SELECT spins_available FROM {$spins_table} WHERE user_id = %d AND wheel_id = %d", $selected_user_id, $selected_wheel_id )); echo esc_html($spins ?: 0); ?>

' . esc_html__('Unovčeno', 'wheel-of-fortune') : ' ' . esc_html__('Neunovčeno', 'wheel-of-fortune'); ?>

""" "./admin/users-page.php" : """ prefix . 'wof_wheels'; $wheels = $wpdb->get_results("SELECT * FROM $wheels_table ORDER BY id ASC", ARRAY_A); // Izberi kolo (privzeto prvo) $selected_wheel_id = isset($_GET['wheel_id']) ? intval($_GET['wheel_id']) : 1; $selected_wheel = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wheels_table WHERE id = %d", $selected_wheel_id), ARRAY_A); if (!$selected_wheel) { $selected_wheel_id = 1; $selected_wheel = $wpdb->get_row("SELECT * FROM $wheels_table WHERE id = 1", ARRAY_A); } // Obdelaj akcije if (isset($_POST['add_spins_submit'])) { check_admin_referer('wheel_add_spins_nonce', 'wheel_add_spins_nonce'); $user_id = intval($_POST['user_id']); $spins = intval($_POST['spins']); $wheel_id = intval($_POST['wheel_id']); if ($user_id > 0 && $spins > 0 && $wheel_id > 0) { $spins_table = $wpdb->prefix . 'wheel_spins'; // Preveri, če uporabnik že ima zapis za to kolo $existing = $wpdb->get_var($wpdb->prepare( "SELECT id FROM $spins_table WHERE user_id = %d AND wheel_id = %d", $user_id, $wheel_id )); if ($existing) { $wpdb->query($wpdb->prepare( "UPDATE $spins_table SET spins_available = spins_available + %d WHERE user_id = %d AND wheel_id = %d", $spins, $user_id, $wheel_id )); } else { $wpdb->insert($spins_table, [ 'user_id' => $user_id, 'wheel_id' => $wheel_id, 'spins_available' => $spins, 'last_spin_date' => null ], ['%d', '%d', '%d', '%s']); } echo '

' . sprintf(__('Uporabniku ID %d je bilo uspešno dodanih %d spinov za kolo %s.', 'wheel-of-fortune'), $user_id, $spins, $selected_wheel['name']) . '

'; } } if (isset($_POST['reset_spins_submit'])) { check_admin_referer('wheel_reset_spins_nonce', 'wheel_reset_spins_nonce'); $user_id = intval($_POST['user_id']); $wheel_id = intval($_POST['wheel_id']); if ($user_id > 0 && $wheel_id > 0) { $spins_table = $wpdb->prefix . 'wheel_spins'; $wpdb->update( $spins_table, array('spins_available' => 0), array('user_id' => $user_id, 'wheel_id' => $wheel_id) ); echo '

' . sprintf(__('Spini uporabnika ID %d za kolo %s so bili uspešno ponastavljeni na 0.', 'wheel-of-fortune'), $user_id, $selected_wheel['name']) . '

'; } } // Pridobi podatke o uporabnikih $users_table = $wpdb->prefix . 'users'; $spins_table = $wpdb->prefix . 'wheel_spins'; $log_table = $wpdb->prefix . 'wheel_log'; $prizes_table = $wpdb->prefix . 'wheel_prizes'; // Iskanje uporabnikov $search = isset($_GET['s']) ? sanitize_text_field($_GET['s']) : ''; $search_condition = ''; if (!empty($search)) { $search_condition = $wpdb->prepare( "AND (u.user_login LIKE %s OR u.user_email LIKE %s OR u.display_name LIKE %s)", "%{$search}%", "%{$search}%", "%{$search}%" ); } // Pridobi uporabnike z spin-i za izbrano kolo $users = $wpdb->get_results( $wpdb->prepare( "SELECT u.ID, u.user_login, u.user_email, u.display_name, COALESCE(s.spins_available, 0) as spins_available, COUNT(l.id) as total_spins FROM {$users_table} u LEFT JOIN {$spins_table} s ON u.ID = s.user_id AND s.wheel_id = %d LEFT JOIN {$log_table} l ON u.ID = l.user_id AND l.wheel_id = %d WHERE 1=1 {$search_condition} GROUP BY u.ID HAVING total_spins > 0 OR spins_available > 0 ORDER BY total_spins DESC", $selected_wheel_id, $selected_wheel_id ), ARRAY_A ); // Izbrani uporabnik za urejanje $selected_user_id = isset($_GET['edit_user']) ? intval($_GET['edit_user']) : 0; $selected_user = null; $user_prizes = array(); if ($selected_user_id > 0) { $selected_user = get_userdata($selected_user_id); if ($selected_user) { // Pridobi spine za izbrano kolo $spins = $wpdb->get_var($wpdb->prepare( "SELECT spins_available FROM $spins_table WHERE user_id = %d AND wheel_id = %d", $selected_user_id, $selected_wheel_id )); $spins = $spins ?: 0; // Pridobi nagrade uporabnika za izbrano kolo $user_prizes = $wpdb->get_results( $wpdb->prepare( "SELECT l.id, p.name as prize_name, p.description as prize_description, l.spin_date, l.redeemed FROM {$log_table} l JOIN {$prizes_table} p ON l.prize_id = p.id WHERE l.user_id = %d AND l.wheel_id = %d ORDER BY l.spin_date DESC", $selected_user_id, $selected_wheel_id ), ARRAY_A ); } } ?>


>

display_name)); ?>

user_email); ?>
get_var($wpdb->prepare( "SELECT COUNT(*) FROM {$log_table} WHERE user_id = %d AND wheel_id = %d", $selected_user_id, $selected_wheel_id )); echo esc_html($total_spins); ?>

' . esc_html__('Unovčeno', 'wheel-of-fortune') : ' ' . esc_html__('Neunovčeno', 'wheel-of-fortune'); ?>
0) : ?>

""" "./admin/wheels-page.php" : """ prefix . 'wof_wheels'; // Handle adding a new wheel if (isset($_POST['add_wheel_submit']) && check_admin_referer('wof_add_wheel_nonce')) { $wheel_name = sanitize_text_field($_POST['wheel_name']); $wheel_slug = sanitize_title($_POST['wheel_name']); // Generate slug from name if (!empty($wheel_name)) { $wpdb->insert( $wheels_table, ['name' => $wheel_name, 'slug' => $wheel_slug, 'created_at' => current_time('mysql')], ['%s', '%s', '%s'] ); echo '

' . __('New wheel created successfully!', 'wheel-of-fortune') . '

'; } else { echo '

' . __('Please provide a name for the wheel.', 'wheel-of-fortune') . '

'; } } // Handle deleting a wheel if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['wheel_id']) && check_admin_referer('wof_delete_wheel_' . $_GET['wheel_id'])) { $wheel_id_to_delete = intval($_GET['wheel_id']); if ($wheel_id_to_delete !== 1) { // Prevent deleting the default wheel // TODO: Consider what to do with prizes associated with this wheel. For now, we'll just delete the wheel. $wpdb->delete($wheels_table, ['id' => $wheel_id_to_delete], ['%d']); echo '

' . __('Wheel deleted successfully.', 'wheel-of-fortune') . '

'; } else { echo '

' . __('The default wheel cannot be deleted.', 'wheel-of-fortune') . '

'; } } $wheels = $wpdb->get_results("SELECT * FROM $wheels_table ORDER BY id ASC", ARRAY_A); ?>

[wheel_of_fortune id=""] |
""" "./assets/css/admin.css" : """ /** * Kolo Sreče - CSS za administratorski vmesnik */ /* Splošni stil administratorske strani */ .wheel-admin-page { max-width: 1200px; margin: 20px 0; } .wheel-admin-page h1 { margin-bottom: 20px; } .wheel-admin-page .nav-tab-wrapper { margin-bottom: 20px; } /* Kartice */ .wheel-card { background: #fff; border: 1px solid #ccd0d4; box-shadow: 0 1px 1px rgba(0,0,0,.04); margin-bottom: 20px; padding: 20px; } .wheel-card h2 { margin-top: 0; padding-bottom: 12px; border-bottom: 1px solid #eee; } /* Tabela z nagradami */ .wheel-prizes-table { width: 100%; border-collapse: collapse; margin-top: 20px; } .wheel-prizes-table th, .wheel-prizes-table td { padding: 12px; text-align: left; border-bottom: 1px solid #eee; } .wheel-prizes-table th { background-color: #f9f9f9; } .wheel-prizes-table tr:hover { background-color: #f9f9f9; } /* Barve nagrad */ .wheel-color-preview { display: inline-block; width: 20px; height: 20px; border-radius: 50%; margin-right: 10px; vertical-align: middle; } .wheel-color-blue { background-color: #5DADE2; } .wheel-color-red { background-color: #E74C3C; } .wheel-color-gray { background-color: #D5D8DC; } /* Obrazci */ .wheel-form-table { width: 100%; margin-top: 20px; } .wheel-form-table th { width: 200px; padding: 15px 10px 15px 0; text-align: left; vertical-align: top; } .wheel-form-table td { padding: 15px 10px; vertical-align: top; } .wheel-form-table input[type="text"], .wheel-form-table input[type="number"], .wheel-form-table select, .wheel-form-table textarea { width: 100%; max-width: 400px; } .wheel-form-table .description { color: #666; font-style: italic; margin-top: 5px; display: block; } /* Gumbi */ .wheel-button-primary { background: #0073aa; border-color: #0073aa; color: #fff; padding: 5px 15px; border-radius: 3px; cursor: pointer; } .wheel-button-secondary { background: #f7f7f7; border-color: #ccc; color: #555; padding: 5px 15px; border-radius: 3px; cursor: pointer; } .wheel-button-delete { background: #d63638; border-color: #d63638; color: #fff; padding: 5px 15px; border-radius: 3px; cursor: pointer; } /* Statistika */ .wheel-stats-container { display: flex; flex-wrap: wrap; margin: 0 -10px; } .wheel-stats-box { background: #fff; border: 1px solid #ccd0d4; box-shadow: 0 1px 1px rgba(0,0,0,.04); padding: 20px; margin: 10px; flex: 1 0 calc(33.333% - 20px); min-width: 250px; text-align: center; } .wheel-stats-box h3 { margin-top: 0; color: #23282d; } .wheel-stats-number { font-size: 32px; font-weight: bold; color: #0073aa; margin: 10px 0; } /* Predogled kolesa */ .wheel-preview-container { max-width: 300px; margin: 20px auto; } /* Odzivnost */ @media screen and (max-width: 782px) { .wheel-form-table th { width: 100%; display: block; padding-bottom: 0; } .wheel-form-table td { width: 100%; display: block; } .wheel-stats-box { flex: 1 0 calc(50% - 20px); } } @media screen and (max-width: 600px) { .wheel-stats-box { flex: 1 0 calc(100% - 20px); } } """ "./assets/css/wheel.css" : """ /** * Wheel of Fortune - CSS for wheel styling * Modern game-show aesthetic with 3D effects and lighting */ /* Main container */ .wheel-container { position: relative; width: 100%; max-width: 600px; margin: 0 auto; background: none !important; border: none !important; box-shadow: none !important; border-radius: 0 !important; padding: 0 !important; } /* Dark theme */ .wheel-container.theme-dark { background: none !important; border: none !important; box-shadow: none !important; border-radius: 0 !important; padding: 0 !important; color: inherit; } /* Wheel sizes */ .wheel-container.size-small { max-width: 300px; transform: scale(0.9); } .wheel-container.size-medium { max-width: 650px; } .wheel-container.size-large { max-width: 800px; } /* Wheel wrapper with lighting effects */ .wheel-wrapper { position: relative; margin: 0 auto; width: 100%; max-width: 700px; filter: drop-shadow(0 0 15px rgba(0, 200, 255, 0.3)); padding-top: 80px; transition: transform 0.05s ease-out; } /* Outer ring lighting effect - POENOSTAVLJENO */ .wheel-wrapper::before { content: ''; position: absolute; top: 0; left: -5%; width: 110%; height: 110%; border-radius: 50%; /* Manj kompleksen gradient */ background: radial-gradient( circle at center, rgba(0, 150, 200, 0.1) 0%, rgba(0, 0, 0, 0) 70% ); z-index: -1; pointer-events: none; } /* Wheel */ .wheel { width: 100%; height: 100%; transform-origin: center; transition: none; /* POMEMBNO: ne uporabljamo CSS tranzicije, saj jo upravlja JS */ display: block; will-change: transform; /* Enostavnejši shadow, ki ga podpirajo vse naprave */ filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.25)); border: 4px solid #222; box-shadow: 0 0 10px 2px rgba(0, 223, 233, 0.5), 0 2px 8px rgba(0,0,0,0.3); border-radius: 50%; background: #111; } /* Wheel frame - BREZ SVG FILTRA */ .wheel-frame { fill: #0a4d6e; stroke: #0a7bb5; stroke-width: 8; } /* LED lighting on wheel frame - BREZ SVG FILTRA */ .wheel-light-track { fill: none; stroke: #00c8ff; stroke-width: 6; stroke-dasharray: 3, 8; animation: lightTrackAnimation 5s linear infinite; } @keyframes lightTrackAnimation { 0% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: 100; } } /* Fast light track animation for spinning */ @keyframes fastLightTrackAnimation { 0% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: 200; } } /* Wheel segment - BREZ SVG FILTRA */ .wheel-segment { stroke: #fff; stroke-width: 1px; transition: all 0.2s; } /* Simple solid fills for segment color classes */ .wheel-segment-red { fill: #ff3131; } .wheel-segment-green { fill: #7ed957; } .wheel-segment-purple { fill: #8c52ff; } .wheel-segment-orange { fill: #ff914d; } .wheel-segment-blue { fill: #1da3e7; } /* Divider between segments */ .wheel-divider { stroke: rgba(255, 255, 255, 0.5); stroke-width: 2px; } /* Pegs on the wheel */ .wheel-peg { fill: #e0e0e0; stroke: #666; stroke-width: 1px; /* Enostavnejši shadow namesto glow filtra */ filter: drop-shadow(0 1px 2px rgba(0,0,0,0.5)); } @keyframes pegPulse { from { fill: #e0e0e0; filter: url(#pegGlow); } to { fill: #ffffff; filter: url(#pegGlow) drop-shadow(0 0 3px rgba(255, 255, 255, 0.8)); } } /* Faster peg pulse for spinning */ @keyframes fastPegPulse { 0% { fill: #e0e0e0; filter: url(#pegGlow); } 50% { fill: #ffffff; filter: url(#pegGlow) drop-shadow(0 0 5px rgba(255, 255, 255, 0.9)); } 100% { fill: #e0e0e0; filter: url(#pegGlow); } } /* Text on wheel - BREZ SVG FILTRA */ .wheel-text { font-family: 'Arial Rounded MT Bold', 'Arial', sans-serif; font-size: 24px; font-weight: 900; fill: #ffffff; stroke: #000000; stroke-width: 0.5px; paint-order: stroke fill; letter-spacing: 1px; pointer-events: none; text-anchor: middle; /* Enostavnejša senca, ki deluje povsod */ text-shadow: 1px 1px 3px rgba(0,0,0,0.7); text-overflow: ellipsis; max-width: 70px; } /* Center hub - BREZ SVG FILTRA */ .wheel-hub-outer { fill: #0a4d6e; stroke: #0a7bb5; stroke-width: 4; } .wheel-hub-inner { fill: #999; } .wheel-hub-button { fill: #330066; } /* Hub text - BREZ SVG FILTRA */ .wheel-hub-text { font-family: 'Arial Rounded MT Bold', 'Arial', sans-serif; font-size: 20px; font-weight: bold; fill: #ff00aa; text-anchor: middle; /* Enostavnejša senca namesto glow filtra */ text-shadow: 0 0 10px #ff00aa; } /* Pointer - BREZ SVG FILTRA */ .wheel-pointer { position: absolute; top: 60px; left: 50%; transform: translateX(-50%); width: 60px; height: 70px; z-index: 10; pointer-events: none; filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.4)); transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); margin-bottom: 20px; } /* Pointer active state - BREZ SVG FILTRA */ .wheel-pointer.active { filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.8)); animation: pointerPulse 1s infinite alternate; } /* Pointer bounce effect when hitting a peg */ .wheel-pointer.bounce { animation: pointerBounce 0.3s ease-in-out; } @keyframes pointerPulse { from { opacity: 0.8; } to { opacity: 1; } } @keyframes pointerBounce { 0% { transform: translateX(-50%) rotate(0deg); } 25% { transform: translateX(-50%) rotate(-5deg); } 75% { transform: translateX(-50%) rotate(5deg); } 100% { transform: translateX(-50%) rotate(0deg); } } .wheel-pointer svg { width: 100%; height: 100%; } /* Pointer parts */ .pointer-mount { fill: #c0c0c0; stroke: #a0a0a0; stroke-width: 1; } .pointer-arm { fill: #a0a0a0; stroke: #808080; stroke-width: 1; } .pointer-tip { fill: #ff0066; stroke: #cc0055; stroke-width: 1; } /* Wheel button with improved styling */ .wheel-button { display: flex; align-items: center; justify-content: center; margin: 20px auto 0 auto; padding: 0 40px !important; background: linear-gradient(135deg, #ff00c4, #00dfe9); color: #fff; border: none; border-radius: 24px; font-size: 28px; font-weight: bold; cursor: pointer; box-shadow: 0 3px 10px #00dfe9; transition: all 0.3s ease; position: relative; overflow: hidden; height: 56px; min-width: 140px; letter-spacing: 2px; line-height: 1 !important; text-shadow: 0 2px 8px #000, 0 0 10px #0cf101; -webkit-appearance: none; appearance: none; } /* Button hover effect */ .wheel-button:hover { transform: translateY(-2px) scale(1.04); box-shadow: 0 7px 30px #ff00c4, 0 0 30px #00dfe9; background: linear-gradient(135deg, #00dfe9, #ff00c4); } /* Button active effect */ .wheel-button:active { transform: translateY(1px) scale(0.98); box-shadow: 0 3px 10px #00dfe9; } /* Button disabled state */ .wheel-button:disabled { background: linear-gradient(135deg, #aa6699, #774466); cursor: not-allowed; transform: none; box-shadow: none; opacity: 0.7; } /* Button ripple effect */ .wheel-button::after { content: ''; position: absolute; top: 50%; left: 50%; width: 5px; height: 5px; background: rgba(255, 255, 255, 0.5); opacity: 0; border-radius: 100%; transform: scale(1, 1) translate(-50%, -50%); transform-origin: 50% 50%; } .wheel-button:focus:not(:active)::after { animation: ripple 1s ease-out; } @keyframes ripple { 0% { transform: scale(0, 0); opacity: 0.5; } 20% { transform: scale(25, 25); opacity: 0.3; } 100% { opacity: 0; transform: scale(40, 40); } } /* Spins counter */ .wheel-spins-counter { text-align: center; margin: 18px 0 0 0; font-size: 22px; font-weight: bold; color: #fff; text-shadow: 0 2px 8px #000, 0 0 10px #0cf101, 0 0 20px #00dfe9; letter-spacing: 1px; background: rgba(0,0,0,0.35); border-radius: 10px; padding: 8px 0; } /* Result display */ .wheel-result { display: none; text-align: center; margin: 20px 0; padding: 15px; background: rgba(255, 255, 255, 0.1); border-radius: 10px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); color: white; animation: resultGlow 2s infinite alternate; } @keyframes resultGlow { from { box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); } to { box-shadow: 0 5px 25px rgba(255, 0, 170, 0.5); } } .wheel-result.win { background: rgba(255, 0, 170, 0.2); } .wheel-result h3 { font-size: 24px; margin: 0 0 10px 0; color: #ffffff; text-shadow: 0 0 10px rgba(255, 0, 170, 0.8); letter-spacing: 1px; } .wheel-result p { margin: 5px 0; } .wheel-result strong { color: #ffcc00; font-size: 1.2em; text-shadow: 0 0 5px rgba(255, 204, 0, 0.8); } /* Login required message */ .wheel-login-required, .wheel-no-spins { text-align: center; margin: 20px 0; padding: 20px; background: rgba(255, 255, 255, 0.1); border-radius: 10px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); color: white; } .wheel-login-required a { color: #00c8ff; text-decoration: none; font-weight: bold; transition: all 0.3s ease; text-shadow: 0 0 5px rgba(0, 200, 255, 0.5); } .wheel-login-required a:hover { color: #33d6ff; text-shadow: 0 0 10px rgba(0, 200, 255, 0.8); } /* Error message */ .wheel-error { text-align: center; margin: 20px 0; padding: 15px; background: rgba(255, 0, 0, 0.2); border-radius: 10px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); color: white; font-weight: bold; } /* Posodobljene medijske poizvedbe za manjše zaslone */ @media (max-width: 768px) { .wheel-text { font-size: 10px; max-width: 60px; } .wheel-container.size-large { max-width: 500px; transform: scale(0.95); } .wheel-hub-text { font-size: 18px; } .wheel-pointer { width: 50px; height: 70px; } .wheel-wrapper { padding-top: 35px; } } @media (max-width: 576px) { .wheel-container { padding: 20px 0; transform: scale(0.9); } .wheel-text { font-size: 8px; max-width: 50px; } .wheel-container.size-medium, .wheel-container.size-small { max-width: 320px; transform: scale(0.85); } .wheel-hub-text { font-size: 16px; } .wheel-button { padding: 8px 25px; font-size: 16px; margin: 15px auto; } .wheel-pointer { width: 40px; height: 60px; } .wheel-wrapper { padding-top: 30px; } } @media (max-width: 480px) { .wheel-container { transform: scale(0.8); padding: 15px 0; } .wheel-text { font-size: 7px; max-width: 40px; } .wheel-container.size-medium, .wheel-container.size-small, .wheel-container.size-large { max-width: 280px; transform: scale(0.75); } .wheel-hub-text { font-size: 14px; } .wheel-button { padding: 7px 20px; font-size: 14px; margin: 10px auto; } .wheel-pointer { width: 35px; height: 50px; } .wheel-wrapper { padding-top: 25px; } .wheel-result h3 { font-size: 18px; } .wheel-result p { font-size: 14px; } } /* Dodana nova medijska poizvedba za zelo majhne zaslone */ @media (max-width: 360px) { .wheel-container { transform: scale(0.7); padding: 10px 0; } .wheel-container.size-medium, .wheel-container.size-small, .wheel-container.size-large { max-width: 240px; transform: scale(0.65); } .wheel-text { font-size: 6px; max-width: 30px; } .wheel-hub-text { font-size: 12px; } .wheel-pointer { width: 30px; height: 45px; } .wheel-wrapper { padding-top: 20px; } } /* Dodana možnost za ročno prilagajanje velikosti */ .wheel-container.scale-90 { transform: scale(0.9); } .wheel-container.scale-80 { transform: scale(0.8); } .wheel-container.scale-70 { transform: scale(0.7); } .wheel-container.scale-60 { transform: scale(0.6); } .wheel-container.scale-50 { transform: scale(0.5); } """ "./assets/js/wheel.js" : """ /** * Wheel of Fortune - JavaScript for wheel functionality * Robust, smooth animation using requestAnimationFrame and easing functions. */ jQuery(document).ready(function($) { // Preveri, ali so podatki na voljo if (typeof wof_data === 'undefined') { console.error('WOF Data is missing.'); return; } // Elementi var wheelContainer = $('.wheel-container'); var wheelElement = wheelContainer.find('.wheel'); var spinButton = wheelContainer.find('.wheel-button'); var resultDiv = wheelContainer.find('.wheel-result'); var spinsCounter = wheelContainer.find('.wheel-spins-counter span'); // Stanje var isSpinning = false; var accumulatedRotation = 0; var wheelSpins = wof_data.spins_left; var l10n = wof_data.l10n; // Nastavitve animacije var spinDuration = 8000; var baseRotations = 5; // Funkcija za animacijo function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); } function animateWheel(startTime, startRotation, totalRotation) { var currentTime = Date.now(); var elapsedTime = currentTime - startTime; if (elapsedTime >= spinDuration) { wheelElement.css('transform', 'rotate(' + (startRotation + totalRotation) + 'deg)'); accumulatedRotation = (startRotation + totalRotation); finishSpin(); return; } var timeProgress = elapsedTime / spinDuration; var rotationProgress = easeOutCubic(timeProgress); var currentRotation = startRotation + (totalRotation * rotationProgress); wheelElement.css('transform', 'rotate(' + currentRotation + 'deg)'); requestAnimationFrame(function() { animateWheel(startTime, startRotation, totalRotation); }); } // Obdelava po koncu vrtenja function finishSpin() { var prize = window.wof_spin_result.prize; showPrizePopup(prize); isSpinning = false; if (wheelSpins > 0) { spinButton.prop('disabled', false).text(l10n.spin_button); } else { spinButton.prop('disabled', true).text(l10n.no_spins_left); } } // Prikaz nagrade function showPrizePopup(prize) { var isTryAgain = window.wof_spin_result.is_try_again === 1 || prize.is_try_again === 1; var html = ''; if (isTryAgain) { html = '

' + prize.name + '

'; } else { html = '

' + l10n.congratulations + '

' + l10n.you_won + ' ' + prize.name + '

'; } if (window.wof_spin_result.discount_code) { html += '

' + l10n.discount_code_sent + '

'; } resultDiv.html(html).addClass('win').fadeIn(300); } // Posodobitev števca spinov function updateSpinsCounter() { if (spinsCounter.length) { spinsCounter.text(wheelSpins); } } // Klik na gumb za vrtenje spinButton.on('click', function() { if (isSpinning || wheelSpins <= 0) return; isSpinning = true; spinButton.prop('disabled', true).text(l10n.spinning); resultDiv.hide().removeClass('win error'); $.ajax({ url: wof_data.rest_url, method: 'POST', beforeSend: function(xhr) { xhr.setRequestHeader('X-WP-Nonce', wof_data.nonce); }, data: { wheel_id: wof_data.wheel_id, }, success: function(response) { if (response.success) { window.wof_spin_result = response.data; wheelSpins = response.data.remaining_spins; updateSpinsCounter(); var targetAngle = response.data.degree; var fullSpinsRotation = baseRotations * 360; var currentAngle = accumulatedRotation % 360; var rotationToTarget = targetAngle - currentAngle; if (rotationToTarget < 0) { rotationToTarget += 360; } var totalRotationAmount = fullSpinsRotation + rotationToTarget; animateWheel(Date.now(), accumulatedRotation, totalRotationAmount); } else { handleError(response.data.message || l10n.error); } }, error: function(xhr) { var errorMsg = xhr.responseJSON ? (xhr.responseJSON.message || l10n.error) : l10n.error; handleError(errorMsg); } }); }); function handleError(message) { resultDiv.html('

' + message + '

').addClass('error').fadeIn(300); isSpinning = false; if(wheelSpins > 0){ spinButton.prop('disabled', false).text(l10n.spin_button); } } // Inicializacija function init() { updateSpinsCounter(); if (wheelSpins <= 0) { spinButton.prop('disabled', true).text(l10n.no_spins_left); } } init(); });""" "./languages/wheel-of-fortune.pot" : """ # Copyright (C) 2023 Vaše ime # This file is distributed under the GPL v2 or later. msgid "" msgstr "" "Project-Id-Version: Kolo Sreče 1.0.0\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wheel-of-fortune\n" "POT-Creation-Date: 2023-07-01T12:00:00+00:00\n" "PO-Revision-Date: 2023-07-01T12:00:00+00:00\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: WP-CLI 2.7.1\n" "X-Domain: wheel-of-fortune\n" #. Plugin Name of the plugin msgid "Kolo Sreče" msgstr "" #. Plugin URI of the plugin msgid "https://example.com/kolo-srece" msgstr "" #. Description of the plugin msgid "WordPress vtičnik za interaktivno kolo sreče z nagradami" msgstr "" #. Author of the plugin msgid "Vaše ime" msgstr "" #. Author URI of the plugin msgid "https://example.com" msgstr "" #: wheel-of-fortune.php:199 msgid "Nimate več razpoložljivih vrtljajev" msgstr "" #: wheel-of-fortune.php:214 msgid "Počakajte še %d minut pred naslednjim vrtenjem" msgstr "" #: wheel-of-fortune.php:304 msgid "Čestitamo! Zadeli ste nagrado na kolesu sreče - %s" msgstr "" #: wheel-of-fortune.php:307 msgid "" "Pozdravljeni %s,\n" "\n" "Čestitamo! Pravkar ste zadeli nagrado na našem kolesu sreče:\n" "\n" "%s - %s\n" "\n" "Za unovčenje nagrade se obrnite na našo podporo ali sledite navodilom na naši spletni strani.\n" "\n" "Lep pozdrav,\n" "Ekipa %s" msgstr "" #: wheel-of-fortune.php:372 msgid "Za sodelovanje v nagradni igri se morate prijaviti." msgstr "" #: wheel-of-fortune.php:383 msgid "Trenutno nimate razpoložljivih vrtljajev. Kupite izdelke za pridobitev novih vrtljajev." msgstr "" #: templates/wheel.php:37 msgid "Preostali vrtljaji:" msgstr "" #: templates/wheel.php:41 msgid "Zavrti kolo" msgstr "" #: admin/settings-page.php:37 msgid "Nastavitve so bile uspešno shranjene." msgstr "" #: admin/settings-page.php:53 msgid "Kolo Sreče - Nastavitve" msgstr "" #: admin/settings-page.php:56 msgid "Splošne nastavitve" msgstr "" #: admin/settings-page.php:57 msgid "Izdelki" msgstr "" #: admin/settings-page.php:58 msgid "Predogled" msgstr "" #: admin/settings-page.php:71 msgid "Časovna omejitev (minute)" msgstr "" #: admin/settings-page.php:75 msgid "Število minut, ki mora preteči med dvema vrtljajema. Vrednost 0 pomeni brez omejitve." msgstr "" #: admin/settings-page.php:80 msgid "Maksimalno število spinov" msgstr "" #: admin/settings-page.php:84 msgid "Maksimalno število spinov, ki jih lahko ima uporabnik. Vrednost 0 pomeni brez omejitve." msgstr "" #: admin/settings-page.php:89 msgid "Email obvestila" msgstr "" #: admin/settings-page.php:93 msgid "Pošlji email obvestilo ob zadetku nagrade" msgstr "" #: admin/settings-page.php:103 msgid "Upravljanje s spini" msgstr "" #: admin/settings-page.php:108 msgid "Dodeli spine uporabnikom" msgstr "" #: admin/settings-page.php:111 msgid "Uporabite spodnji obrazec za dodelitev spinov izbranim uporabnikom." msgstr "" #: admin/settings-page.php:114 msgid "Išči uporabnike..." msgstr "" #: admin/settings-page.php:116 msgid "Izberi uporabnika" msgstr "" #: admin/settings-page.php:118 msgid "Število spinov" msgstr "" #: admin/settings-page.php:119 msgid "Dodaj spine" msgstr "" #: admin/settings-page.php:125 msgid "Ponastavi vse spine" msgstr "" #: admin/settings-page.php:128 msgid "Ponastavi vse spine" msgstr "" #: admin/settings-page.php:129 msgid "Ta akcija bo ponastavila število spinov za vse uporabnike na 0. Tega ni mogoče razveljaviti!" msgstr "" #: admin/settings-page.php:139 msgid "Izdelki, ki podeljujejo spine" msgstr "" #: admin/settings-page.php:141 msgid "Nastavite izdelke, ki bodo ob nakupu podelili določeno število spinov." msgstr "" #: admin/settings-page.php:146 msgid "ID izdelka" msgstr "" #: admin/settings-page.php:147 msgid "Ime izdelka" msgstr "" #: admin/settings-page.php:148 msgid "Število spinov" msgstr "" #: admin/settings-page.php:149 msgid "Akcije" msgstr "" #: admin/settings-page.php:155 msgid "Ni nastavljenih izdelkov." msgstr "" #: admin/settings-page.php:169 msgid "Odstrani" msgstr "" #: admin/settings-page.php:178 msgid "ID izdelka" msgstr "" #: admin/settings-page.php:182 msgid "Število spinov" msgstr "" #: admin/settings-page.php:185 msgid "Dodaj izdelek" msgstr "" #: admin/settings-page.php:197 msgid "Predogled kolesa" msgstr "" #: admin/settings-page.php:209 msgid "To je predogled kolesa, ki ga bodo videli uporabniki. Za spreminjanje nagrad pojdite na stran \"Nagrade\"." msgstr "" #: admin/settings-page.php:215 msgid "Shrani nastavitve" msgstr "" #: admin/settings-page.php:233 msgid "Izdelek ni najden" msgstr "" #: admin/settings-page.php:236 msgid "Napaka pri iskanju izdelka" msgstr "" #: admin/settings-page.php:249 msgid "Vnesite veljaven ID izdelka in število spinov" msgstr "" #: admin/settings-page.php:256 msgid "Odstrani" msgstr "" #: admin/settings-page.php:276 msgid "Ni nastavljenih izdelkov." msgstr "" #: admin/settings-page.php:283 msgid "Ali ste prepričani, da želite ponastaviti vse spine za vse uporabnike? Tega ni mogoče razveljaviti!" msgstr "" #: admin/settings-page.php:292 msgid "Vsi spini so bili uspešno ponastavljeni." msgstr "" #: admin/settings-page.php:294 msgid "Napaka pri ponastavljanju spinov:" msgstr "" #: admin/settings-page.php:297 msgid "Prišlo je do napake pri komunikaciji s strežnikom." msgstr "" #: admin/settings-page.php:320 msgid "Izberi uporabnika" msgstr "" #: admin/settings-page.php:337 msgid "Izberite uporabnika in vnesite število spinov" msgstr "" #: admin/settings-page.php:352 msgid "Spini so bili uspešno dodani." msgstr "" #: admin/settings-page.php:354 msgid "Napaka pri dodajanju spinov:" msgstr "" #: admin/settings-page.php:357 msgid "Prišlo je do napake pri komunikaciji s strežnikom." msgstr "" #: admin/prizes-page.php:36 msgid "Ime nagrade je obvezno." msgstr "" #: admin/prizes-page.php:38 msgid "Verjetnost mora biti med 0 in 1." msgstr "" #: admin/prizes-page.php:54 msgid "Nagrada je bila uspešno dodana." msgstr "" #: admin/prizes-page.php:80 msgid "Ime nagrade je obvezno." msgstr "" #: admin/prizes-page.php:82 msgid "Verjetnost mora biti med 0 in 1." msgstr "" #: admin/prizes-page.php:98 msgid "Nagrada je bila uspešno posodobljena." msgstr "" #: admin/prizes-page.php:117 msgid "Nagrada je bila uspešno izbrisana." msgstr "" #: admin/prizes-page.php:134 msgid "Kolo Sreče - Nagrade" msgstr "" #: admin/prizes-page.php:139 msgid "Seznam nagrad" msgstr "" #: admin/prizes-page.php:143 msgid "Opozorilo: Skupna verjetnost vseh nagrad presega 1 (100%). Priporočamo, da prilagodite verjetnosti tako, da bo skupna vsota enaka 1." msgstr "" #: admin/prizes-page.php:144 msgid "Trenutna skupna verjetnost:" msgstr "" #: admin/prizes-page.php:147 msgid "Opozorilo: Skupna verjetnost vseh nagrad je manjša od 1 (100%). Priporočamo, da prilagodite verjetnosti tako, da bo skupna vsota enaka 1." msgstr "" #: admin/prizes-page.php:148 msgid "Trenutna skupna verjetnost:" msgstr "" #: admin/prizes-page.php:156 msgid "ID" msgstr "" #: admin/prizes-page.php:157 msgid "Ime" msgstr "" #: admin/prizes-page.php:158 msgid "Opis" msgstr "" #: admin/prizes-page.php:159 msgid "Verjetnost" msgstr "" #: admin/prizes-page.php:160 msgid "Barva" msgstr "" #: admin/prizes-page.php:161 msgid "Status" msgstr "" #: admin/prizes-page.php:162 msgid "Akcije" msgstr "" #: admin/prizes-page.php:168 msgid "Ni nagrad. Dodajte prvo nagrado spodaj." msgstr "" #: admin/prizes-page.php:179 msgid "Aktivna" msgstr "" #: admin/prizes-page.php:179 msgid "Neaktivna" msgstr "" #: admin/prizes-page.php:182 msgid "Uredi" msgstr "" #: admin/prizes-page.php:183 msgid "Izbriši" msgstr "" #: admin/prizes-page.php:194 msgid "Dodaj novo nagrado" msgstr "" #: admin/prizes-page.php:203 msgid "Ime nagrade" msgstr "" #: admin/prizes-page.php:210 msgid "Opis" msgstr "" #: admin/prizes-page.php:217 msgid "Verjetnost" msgstr "" #: admin/prizes-page.php:221 msgid "Vnesite vrednost med 0 in 1 (npr. 0.1 za 10% verjetnost)." msgstr "" #: admin/prizes-page.php:226 msgid "Barva" msgstr "" #: admin/prizes-page.php:230 msgid "Modra" msgstr "" #: admin/prizes-page.php:231 msgid "Rdeča" msgstr "" #: admin/prizes-page.php:232 msgid "Siva" msgstr "" #: admin/prizes-page.php:239 msgid "Nagrada je aktivna" msgstr "" #: admin/prizes-page.php:247 msgid "Dodaj nagrado" msgstr "" #: admin/prizes-page.php:258 msgid "Uredi nagrado" msgstr "" #: admin/prizes-page.php:267 msgid "Ime nagrade" msgstr "" #: admin/prizes-page.php:274 msgid "Opis" msgstr "" #: admin/prizes-page.php:281 msgid "Verjetnost" msgstr "" #: admin/prizes-page.php:285 msgid "Vnesite vrednost med 0 in 1 (npr. 0.1 za 10% verjetnost)." msgstr "" #: admin/prizes-page.php:290 msgid "Barva" msgstr "" #: admin/prizes-page.php:294 msgid "Modra" msgstr "" #: admin/prizes-page.php:295 msgid "Rdeča" msgstr "" #: admin/prizes-page.php:296 msgid "Siva" msgstr "" #: admin/prizes-page.php:303 msgid "Nagrada je aktivna" msgstr "" #: admin/prizes-page.php:311 msgid "Shrani spremembe" msgstr "" #: admin/prizes-page.php:335 msgid "Aktivna" msgstr "" #: admin/prizes-page.php:345 msgid "Ali ste prepričani, da želite izbrisati to nagrado?" msgstr "" #: admin/stats-page.php:134 msgid "Kolo Sreče - Statistika" msgstr "" #: admin/stats-page.php:138 msgid "Skupno število vrtljajev" msgstr "" #: admin/stats-page.php:143 msgid "Uporabniki z vrtljaji" msgstr "" #: admin/stats-page.php:148 msgid "Preostali vrtljaji" msgstr "" #: admin/stats-page.php:155 msgid "Nagrade po kategorijah" msgstr "" #: admin/stats-page.php:160 msgid "Nagrada" msgstr "" #: admin/stats-page.php:161 msgid "Barva" msgstr "" #: admin/stats-page.php:162 msgid "Število zadetkov" msgstr "" #: admin/stats-page.php:168 msgid "Ni podatkov." msgstr "" #: admin/stats-page.php:186 msgid "Vrtljaji po dnevih (zadnjih 30 dni)" msgstr "" #: admin/stats-page.php:189 msgid "Ni podatkov za prikaz." msgstr "" #: admin/stats-page.php:225 msgid "Uporabniki z največ vrtljaji" msgstr "" #: admin/stats-page.php:230 msgid "Uporabnik" msgstr "" #: admin/stats-page.php:231 msgid "Email" msgstr "" #: admin/stats-page.php:232 msgid "Število vrtljajev" msgstr "" #: admin/stats-page.php:238 msgid "Ni podatkov." msgstr "" #: admin/stats-page.php:255 msgid "Uporabniki z največ preostalimi vrtljaji" msgstr "" #: admin/stats-page.php:260 msgid "Uporabnik" msgstr "" #: admin/stats-page.php:261 msgid "Email" msgstr "" #: admin/stats-page.php:262 msgid "Preostali vrtljaji" msgstr "" #: admin/stats-page.php:268 msgid "Ni podatkov." msgstr "" #: admin/stats-page.php:285 msgid "Zadnjih 10 vrtljajev" msgstr "" #: admin/stats-page.php:290 msgid "ID" msgstr "" #: admin/stats-page.php:291 msgid "Uporabnik" msgstr "" #: admin/stats-page.php:292 msgid "Nagrada" msgstr "" #: admin/stats-page.php:293 msgid "Datum" msgstr "" #: admin/stats-page.php:294 msgid "Unovčeno" msgstr "" #: admin/stats-page.php:300 msgid "Ni podatkov." msgstr "" #: admin/stats-page.php:310 msgid "Da" msgstr "" #: admin/stats-page.php:312 msgid "Ne" msgstr "" #: admin/stats-page.php:325 msgid "Izvoz podatkov" msgstr "" #: admin/stats-page.php:327 msgid "Izvozite podatke o vrtljajih in nagradah za nadaljnjo analizo." msgstr "" #: admin/stats-page.php:336 msgid "Vsi vrtljaji" msgstr "" #: admin/stats-page.php:342 msgid "Nagrade in zadetki" msgstr "" #: admin/stats-page.php:348 msgid "Uporabniki in vrtljaji" msgstr "" #: admin/stats-page.php:353 msgid "Od datuma:" msgstr "" #: admin/stats-page.php:358 msgid "Do datuma:" msgstr "" #: admin/stats-page.php:363 msgid "Format:" msgstr "" #: admin/stats-page.php:372 msgid "Izvozi podatke" msgstr "" """ "./public/shortcode-template.php" : """
generate_wheel_svg($prizes); ?>
""" "./templates/emails/default-prize-email.html" : """ Congratulations on your win!
Congratulations, {user_name}!

You've won a prize!

You just won a fantastic prize on our Wheel of Fortune:

Prize: {prize_name}
Description: {prize_description}
Redemption code: {redemption_code}

To redeem your prize, follow the instructions on our website or contact our support team.

Thank you for participating!

This is an automated message. Please do not reply to this email.
© {date} {site_name}. All rights reserved.
""" "./templates/wheel.php" : """ prefix . 'wheel_spins'; $spins = $wpdb->get_var($wpdb->prepare("SELECT spins_available FROM $spins_table WHERE user_id = %d AND wheel_id = %d", $user_id, $wheel_id)); $spins = $spins === null ? 0 : (int) $spins; // Get prizes for the specific wheel $wheel = wheel_of_fortune(); $prizes = $wheel->get_wheel_prizes($wheel_id); // Set classes for size, theme and scale $container_classes = array( 'wheel-container', 'wheel-of-fortune-container', 'size-' . $atts['size'], 'theme-' . $atts['theme'] ); // Add scale class if provided if (!empty($atts['scale'])) { $container_classes[] = $atts['scale']; } // Generate unique ID for this wheel $wheel_id_html = 'wheel-of-fortune-' . uniqid(); ?>
generate_wheel_svg($prizes); } else { echo '
' . esc_html__('Error loading the wheel. No prizes available.', 'wheel-of-fortune') . '
'; } ?>
""" "./wheel-of-fortune.php" : """ prefix . 'wheel_spins'; wheel_of_fortune_debug_log("add_spins: Attempting to add $spins spins for user $user_id to wheel $wheel_id."); // Preveri, ali zapis že obstaja $existing_id = $wpdb->get_var($wpdb->prepare( "SELECT id FROM $table_spins WHERE user_id = %d AND wheel_id = %d", $user_id, $wheel_id )); if ($existing_id) { // Zapis obstaja, posodobi spine $result = $wpdb->query($wpdb->prepare( "UPDATE $table_spins SET spins_available = spins_available + %d WHERE id = %d", $spins, $existing_id )); wheel_of_fortune_debug_log("add_spins: Updated existing record. Result: $result"); } else { // Zapis ne obstaja, ustvari novega $result = $wpdb->insert( $table_spins, [ 'user_id' => $user_id, 'wheel_id' => $wheel_id, 'spins_available' => $spins, 'last_spin_date' => null ], ['%d', '%d', '%d', '%s'] ); wheel_of_fortune_debug_log("add_spins: Inserted new record. Result: $result"); } return $result !== false; } /** * Main plugin class */ class WheelOfFortune { private static $instance = null; public static function get_instance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } private function __construct() { // Register hooks add_action('init', array($this, 'init')); add_action('plugins_loaded', array($this, 'load_textdomain')); register_activation_hook(WHEEL_OF_FORTUNE_PLUGIN_FILE, array($this, 'activate')); register_deactivation_hook(WHEEL_OF_FORTUNE_PLUGIN_FILE, array($this, 'deactivate')); // --- CRON: filter in akcija vedno aktivna --- add_filter('cron_schedules', array($this, 'add_custom_cron_schedules')); add_action('wof_daily_spin_event', array($this, 'execute_daily_spin_cron')); // ------------------------------------------ add_action('admin_menu', array($this, 'admin_menu')); add_action('rest_api_init', array($this, 'register_rest_routes')); add_shortcode('wheel_of_fortune', array($this, 'shortcode')); if ($this->is_woocommerce_active()) { add_action('woocommerce_order_status_processing', array($this, 'assign_spins_on_purchase')); add_action('woocommerce_order_status_completed', array($this, 'assign_spins_on_purchase')); } add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_assets')); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); add_action('wp_ajax_wheel_get_prize_details', array($this, 'ajax_get_prize_details')); add_action('wp_ajax_wheel_save_prize', array($this, 'ajax_save_prize')); add_action('wp_ajax_wheel_delete_prize', array($this, 'ajax_delete_prize')); add_action('wp_ajax_wheel_search_users', array($this, 'ajax_search_users')); add_action('wp_ajax_wheel_add_spins', array($this, 'ajax_add_spins')); add_action('wp_ajax_wheel_reset_all_spins', array($this, 'ajax_reset_all_spins')); add_action('wp_ajax_wheel_get_product', array($this, 'ajax_get_product')); add_action('wp_ajax_wheel_test_email', array($this, 'ajax_test_email')); add_action('wp_ajax_wheel_get_prizes', array($this, 'ajax_get_prizes')); add_action('wp_ajax_ajax_update_wheel_product_spins', array($this, 'ajax_update_wheel_product_spins')); add_action('wp_ajax_wof_delete_wheel_product', array($this, 'ajax_delete_wheel_product')); add_action('wp_ajax_wheel_migrate_try_again', array($this, 'ajax_migrate_try_again')); add_action('wp_ajax_wheel_reset_spins_wheel', array($this, 'ajax_reset_spins_wheel')); // Vključi testno skripto za kupone if (is_admin()) { require_once WHEEL_OF_FORTUNE_PLUGIN_DIR . 'admin/coupon-test.php'; } } /** * Assigns spins to a user upon a completed WooCommerce purchase. * This method is hooked to 'woocommerce_order_status_processing' and 'woocommerce_order_status_completed'. * * @param int $order_id The ID of the WooCommerce order. */ public function assign_spins_on_purchase($order_id) { wheel_of_fortune_debug_log("=== SPIN ASSIGNMENT START ==="); wheel_of_fortune_debug_log("Hook triggered for order ID: $order_id"); if (!$order_id) { wheel_of_fortune_debug_log("No order ID provided. Exiting."); return; } $order = wc_get_order($order_id); if (!$order) { wheel_of_fortune_debug_log("Could not get order object for ID: $order_id. Exiting."); return; } $user_id = $order->get_user_id(); if (!$user_id) { wheel_of_fortune_debug_log("Order #$order_id has no associated user ID. Exiting."); return; } wheel_of_fortune_debug_log("Processing for User ID: $user_id"); global $wpdb; $wheel_products_table = $wpdb->prefix . 'wheel_of_fortune_products'; $total_spins_to_add_by_wheel = []; foreach ($order->get_items() as $item) { $product_id = $item->get_product_id(); $quantity = $item->get_quantity(); wheel_of_fortune_debug_log("Checking product ID: $product_id (Quantity: $quantity)"); $wheel_product = $wpdb->get_row($wpdb->prepare( "SELECT wheel_id, spins_per_purchase FROM $wheel_products_table WHERE product_id = %d", $product_id ), ARRAY_A); if ($wheel_product) { $wheel_id = (int)$wheel_product['wheel_id']; $spins_per_item = (int)$wheel_product['spins_per_purchase']; $spins_for_this_item = $spins_per_item * $quantity; wheel_of_fortune_debug_log("Product ID $product_id is a wheel product for Wheel ID: $wheel_id. Grants $spins_per_item spin(s) per purchase. Total for this item: $spins_for_this_item"); if (!isset($total_spins_to_add_by_wheel[$wheel_id])) { $total_spins_to_add_by_wheel[$wheel_id] = 0; } $total_spins_to_add_by_wheel[$wheel_id] += $spins_for_this_item; } else { wheel_of_fortune_debug_log("Product ID $product_id is not a wheel product."); } } if (!empty($total_spins_to_add_by_wheel)) { // Prepreci veckratno dodeljevanje spinov if (get_post_meta($order_id, '_wheel_spins_assigned', true)) { wheel_of_fortune_debug_log("Spins for order #$order_id have already been assigned. Exiting."); return; } wheel_of_fortune_debug_log("Total spins to add: " . print_r($total_spins_to_add_by_wheel, true)); $notes = []; foreach ($total_spins_to_add_by_wheel as $wheel_id => $spins) { if ($spins > 0) { wheel_of_fortune_debug_log("Adding $spins spin(s) to user $user_id for wheel $wheel_id."); if(wheel_of_fortune_add_spins($user_id, $spins, $wheel_id)){ $notes[] = sprintf(__('%d spin(s) for wheel #%d', 'wheel-of-fortune'), $spins, $wheel_id); } } } // Dodaj opombo k narocilu if(!empty($notes)){ $order->add_order_note(sprintf(__('User awarded Wheel of Fortune spins: %s.', 'wheel-of-fortune'), implode(', ', $notes))); update_post_meta($order_id, '_wheel_spins_assigned', true); } } else { wheel_of_fortune_debug_log("No wheel products found in order #$order_id."); } wheel_of_fortune_debug_log("=== SPIN ASSIGNMENT END ==="); } public function init() { // Initialization code can go here } public function activate() { $this->create_database_tables(); $this->run_migration(); $this->set_default_options(); // Dodaj cron opravilo za dnevne spine if (!wp_next_scheduled('wheel_daily_spins_cron')) { wp_schedule_event(time(), 'wheel_daily', 'wheel_daily_spins_cron'); } // Izvedi migracijo za is_try_again stolpec $this->migrate_add_is_try_again_column(); } public function deactivate() { wp_clear_scheduled_hook('wof_daily_spin_event'); error_log("Wheel of Fortune: Cron job 'wof_daily_spin_event' cleared."); flush_rewrite_rules(); } public function load_textdomain() { load_plugin_textdomain('wheel-of-fortune', false, dirname(plugin_basename(__FILE__)) . '/languages'); } private function create_database_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); // Ustvari tabelo za kolesa $wheels_table = $wpdb->prefix . 'wof_wheels'; $wheel_products_table = $wpdb->prefix . 'wheel_of_fortune_products'; // Debug informacije za tabelo error_log("=== TABLE DEBUG ==="); error_log("Wheels table: " . $wheels_table); error_log("Products table: " . $wheel_products_table); // Preveri, ali tabela obstaja $wheels_table_exists = $wpdb->get_var("SHOW TABLES LIKE '$wheels_table'") == $wheels_table; error_log("Wheels table exists: " . ($wheels_table_exists ? 'YES' : 'NO')); $products_table_exists = $wpdb->get_var("SHOW TABLES LIKE '$wheel_products_table'") == $wheel_products_table; error_log("Products table exists: " . ($products_table_exists ? 'YES' : 'NO')); if ($wheels_table_exists) { $wheels_table_structure = $wpdb->get_results("DESCRIBE $wheels_table"); error_log("Wheels table structure: " . print_r($wheels_table_structure, true)); } if ($products_table_exists) { $products_table_structure = $wpdb->get_results("DESCRIBE $wheel_products_table"); error_log("Products table structure: " . print_r($products_table_structure, true)); } $sql_wheels = "CREATE TABLE $wheels_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, name varchar(255) NOT NULL, slug varchar(100) NOT NULL, created_at datetime DEFAULT '0000-00-00 00:00:00' NOT NULL, PRIMARY KEY (id), UNIQUE KEY slug (slug) ) $charset_collate;"; $sql_products = "CREATE TABLE $wheel_products_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, wheel_id mediumint(9) NOT NULL, product_id bigint(20) NOT NULL, spins_per_purchase int(11) NOT NULL DEFAULT 1, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY wheel_product (wheel_id, product_id) ) $charset_collate;"; // --- 2. Posodobljena tabela za nagrade --- $table_prizes = $wpdb->prefix . 'wheel_prizes'; $sql_prizes = "CREATE TABLE $table_prizes ( id mediumint(9) NOT NULL AUTO_INCREMENT, wheel_id mediumint(9) NOT NULL DEFAULT 1, name varchar(255) NOT NULL, description text DEFAULT '', probability float NOT NULL, is_active tinyint(1) NOT NULL DEFAULT 1, image_url varchar(255) DEFAULT '', email_subject varchar(255) DEFAULT '', email_template text DEFAULT '', redemption_code varchar(100) DEFAULT '', is_discount tinyint(1) NOT NULL DEFAULT 0, discount_value float DEFAULT 0, is_try_again tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY wheel_id (wheel_id) ) $charset_collate;"; // --- 3. Posodobljena tabela za spine (NOVO - spini po kolesih) --- $table_spins = $wpdb->prefix . 'wheel_spins'; $sql_spins = "CREATE TABLE $table_spins ( id mediumint(9) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL, wheel_id mediumint(9) NOT NULL DEFAULT 1, spins_available int(11) NOT NULL DEFAULT 0, last_spin_date datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY user_wheel (user_id, wheel_id), KEY user_id (user_id), KEY wheel_id (wheel_id) ) $charset_collate;"; // --- 4. Posodobljena tabela za log (NOVO - log po kolesih) --- $table_log = $wpdb->prefix . 'wheel_log'; $sql_log = "CREATE TABLE $table_log ( id mediumint(9) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL, wheel_id mediumint(9) NOT NULL DEFAULT 1, prize_id mediumint(9) NOT NULL, spin_date datetime NOT NULL, redeemed tinyint(1) NOT NULL DEFAULT 0, redemption_code varchar(100) DEFAULT '', PRIMARY KEY (id), KEY user_id (user_id), KEY wheel_id (wheel_id), KEY prize_id (prize_id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql_wheels); // Dodajanje nove tabele dbDelta($sql_products); dbDelta($sql_prizes); dbDelta($sql_spins); dbDelta($sql_log); // Debug: preveri, ali so se tabele ustvarile $wheels_after = $wpdb->get_var("SHOW TABLES LIKE '$wheels_table'") == $wheels_table; $products_after = $wpdb->get_var("SHOW TABLES LIKE '$wheel_products_table'") == $wheel_products_table; error_log("After dbDelta - Wheels table exists: " . ($wheels_after ? 'YES' : 'NO')); error_log("After dbDelta - Products table exists: " . ($products_after ? 'YES' : 'NO')); if (!$wheels_after) { error_log("Wheel of Fortune: Tabela $wheels_table se ni ustvarila!"); } else { error_log("Wheel of Fortune: Tabela $wheels_table uspešno ustvarjena."); } if (!$products_after) { error_log("Wheel of Fortune: Tabela $wheel_products_table se ni ustvarila!"); } else { error_log("Wheel of Fortune: Tabela $wheel_products_table uspešno ustvarjena."); } // Migracija: dodaj stolpec is_try_again v obstoječe tabele $this->migrate_add_is_try_again_column(); } private function migrate_add_is_try_again_column() { global $wpdb; $table_prizes = $wpdb->prefix . 'wheel_prizes'; // Preveri, če stolpec is_try_again že obstaja $column_exists = $wpdb->get_results("SHOW COLUMNS FROM $table_prizes LIKE 'is_try_again'"); if (empty($column_exists)) { // Dodaj stolpec is_try_again $result = $wpdb->query("ALTER TABLE $table_prizes ADD COLUMN is_try_again tinyint(1) NOT NULL DEFAULT 0"); if ($result !== false) { error_log("Wheel of Fortune: Stolpec is_try_again uspešno dodan v tabelo $table_prizes"); } else { error_log("Wheel of Fortune: Napaka pri dodajanju stolpca is_try_again v tabelo $table_prizes"); } } else { error_log("Wheel of Fortune: Stolpec is_try_again že obstaja v tabeli $table_prizes"); } } private function run_migration() { global $wpdb; $wheels_table = $wpdb->prefix . 'wof_wheels'; $prizes_table = $wpdb->prefix . 'wheel_prizes'; $spins_table = $wpdb->prefix . 'wheel_spins'; $log_table = $wpdb->prefix . 'wheel_log'; // Debug informacije error_log("=== MIGRATION DEBUG ==="); error_log("Wheels table: " . $wheels_table); error_log("Prizes table: " . $prizes_table); error_log("Spins table: " . $spins_table); error_log("Log table: " . $log_table); // 1. Preveri, če "Default" kolo že obstaja $default_wheel_exists = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $wheels_table WHERE id = %d", 1)); error_log("Default wheel exists: " . $default_wheel_exists); // 2. Če ne obstaja, ga ustvari if (!$default_wheel_exists) { error_log("Creating default wheel..."); $result = $wpdb->insert( $wheels_table, [ 'id' => 1, 'name' => __('Default Wheel', 'wheel-of-fortune'), 'slug' => 'default', 'created_at' => current_time('mysql') ], ['%d', '%s', '%s', '%s'] ); error_log("Insert result: " . $result); error_log("Last SQL query: " . $wpdb->last_query); error_log("Last SQL error: " . $wpdb->last_error); } // 3. Posodobi obstoječe nagrade, da pripadajo privzetemu kolesu $prizes_updated = $wpdb->query("UPDATE $prizes_table SET wheel_id = 1 WHERE wheel_id = 0 OR wheel_id IS NULL"); error_log("Prizes updated: " . $prizes_updated); // 4. Migriraj obstoječe spine v novo strukturo $existing_spins = $wpdb->get_results("SELECT user_id, meta_value as spins FROM {$wpdb->usermeta} WHERE meta_key = '_wheel_spins' AND meta_value > 0"); error_log("Existing spins found: " . count($existing_spins)); foreach ($existing_spins as $spin_data) { // Preveri, če zapis že obstaja $existing_record = $wpdb->get_var($wpdb->prepare( "SELECT id FROM $spins_table WHERE user_id = %d AND wheel_id = 1", $spin_data->user_id )); if (!$existing_record) { $result = $wpdb->insert( $spins_table, [ 'user_id' => $spin_data->user_id, 'wheel_id' => 1, 'spins_available' => intval($spin_data->spins), 'last_spin_date' => current_time('mysql') ], ['%d', '%d', '%d', '%s'] ); error_log("Migrated spins for user {$spin_data->user_id}: " . $result); } } // 5. Migriraj obstoječi log (če obstaja) $log_updated = $wpdb->query("UPDATE $log_table SET wheel_id = 1 WHERE wheel_id = 0 OR wheel_id IS NULL"); error_log("Log updated: " . $log_updated); // 6. Dodaj privzete nagrade, če jih ni $existing_prizes = $wpdb->get_var("SELECT COUNT(*) FROM $prizes_table WHERE wheel_id = 1"); if ($existing_prizes == 0) { $this->add_default_prizes(); } } private function set_default_options() { $default_options = array( 'wheel_cooldown_minutes' => 0, 'wheel_max_spins' => 0, 'wheel_send_emails' => true, // E-pošta se vedno pošlje 'wheel_spin_products' => array(), 'wheel_smtp_enabled' => false, 'wheel_smtp_host' => '', 'wheel_smtp_port' => '587', 'wheel_smtp_username' => '', 'wheel_smtp_password' => '', 'wheel_smtp_encryption' => 'tls', 'wheel_email_from_name' => get_bloginfo('name'), 'wheel_email_from_email' => get_bloginfo('admin_email'), ); foreach ($default_options as $option => $value) { if (get_option($option) === false) { add_option($option, $value); } } $this->add_default_prizes(); } private function add_default_prizes() { global $wpdb; $table_name = $wpdb->prefix . 'wheel_prizes'; $count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name"); if ($count == 0) { // Default prizes with wheel_id $default_prizes = array( ['wheel_id' => 1, 'name' => '200k FTMO', 'description' => '200k FTMO voucher', 'probability' => 0.05, 'is_active' => 1], ['wheel_id' => 1, 'name' => '20% OFF', 'description' => '20% discount on your next purchase', 'probability' => 0.15, 'is_active' => 1], ['wheel_id' => 1, 'name' => '50k Funded7', 'description' => '50k Funded7 voucher', 'probability' => 0.10, 'is_active' => 1], ['wheel_id' => 1, 'name' => '15% OFF', 'description' => '15% discount on your next purchase', 'probability' => 0.20, 'is_active' => 1], ['wheel_id' => 1, 'name' => '100k TugeTrade', 'description' => '100k TugeTrade voucher', 'probability' => 0.10, 'is_active' => 1], ['wheel_id' => 1, 'name' => '5k Alpicap', 'description' => '5k Alpicap voucher', 'probability' => 0.15, 'is_active' => 1], ['wheel_id' => 1, 'name' => '30% OFF', 'description' => '30% discount on your next purchase', 'probability' => 0.10, 'is_active' => 1], ['wheel_id' => 1, 'name' => '200k FundedNext', 'description' => '200k FundedNext voucher', 'probability' => 0.05, 'is_active' => 1], ['wheel_id' => 1, 'name' => '75% OFF', 'description' => '75% discount on your next purchase', 'probability' => 0.05, 'is_active' => 1], ['wheel_id' => 1, 'name' => '200k FundingPips', 'description' => '200k FundingPips voucher', 'probability' => 0.05, 'is_active' => 1] ); foreach ($default_prizes as $prize) { $wpdb->insert($table_name, $prize); } } } public function admin_menu() { add_menu_page( __('Wheels of Fortune', 'wheel-of-fortune'), __('Wheels', 'wheel-of-fortune'), 'manage_options', 'wof-wheels', // slug array($this, 'wheels_page'), // callback 'dashicons-marker', 30 ); // Stran za urejanje posameznega kolesa (skrita iz glavnega menija, dostopna preko linkov) add_submenu_page( 'wof-wheels', // parent slug __('Edit Wheel', 'wheel-of-fortune'), __('Edit Wheel', 'wheel-of-fortune'), 'manage_options', 'wof-edit-wheel', array($this, 'edit_wheel_page') ); // Dodam podstran za nastavitve add_submenu_page( 'wof-wheels', __('Settings', 'wheel-of-fortune'), __('Settings', 'wheel-of-fortune'), 'manage_options', 'wof-settings', array($this, 'settings_page') ); // Statistika in uporabniki add_submenu_page('wof-wheels', __('Statistics', 'wheel-of-fortune'), __('Statistics', 'wheel-of-fortune'), 'manage_options', 'wof-stats', array($this, 'stats_page')); add_submenu_page('wof-wheels', __('Users & Spins', 'wheel-of-fortune'), __('Users & Spins', 'wheel-of-fortune'), 'manage_options', 'wof-users', array($this, 'users_page')); } // Dodam funkcijo za prikaz strani z nastavitvami public function settings_page() { require_once WHEEL_OF_FORTUNE_PLUGIN_DIR . 'admin/settings-page.php'; } public function wheels_page() { require_once WHEEL_OF_FORTUNE_PLUGIN_DIR . 'admin/wheels-page.php'; } public function edit_wheel_page() { require_once WHEEL_OF_FORTUNE_PLUGIN_DIR . 'admin/edit-wheel-page.php'; } public function prizes_page() { // Ta stran ni več v uporabi, saj so nagrade urejene pod vsakim kolesom // Pustimo jo zaenkrat prazno ali preusmerimo echo "

" . __("This page is deprecated. Please manage prizes under a specific wheel.", "wheel-of-fortune") . "

"; } public function stats_page() { include WHEEL_OF_FORTUNE_PLUGIN_DIR . 'admin/stats-page.php'; } public function users_page() { include WHEEL_OF_FORTUNE_PLUGIN_DIR . 'admin/users-page.php'; } public function register_rest_routes() { register_rest_route('wheel-of-fortune/v1', '/spin', array('methods' => 'POST', 'callback' => array($this, 'process_wheel_spin'), 'permission_callback' => 'is_user_logged_in')); register_rest_route('wheel-of-fortune/v1', '/test', array('methods' => 'GET', 'callback' => function() { return new WP_REST_Response(['success' => true, 'message' => 'REST API endpoint is working correctly.'], 200); }, 'permission_callback' => '__return_true')); } public function process_wheel_spin(WP_REST_Request $request) { $user_id = get_current_user_id(); if ($user_id === 0) { return new WP_Error('not_logged_in', __('You must be logged in to spin the wheel.', 'wheel-of-fortune'), ['status' => 401]); } $wheel_id = $request->get_param('wheel_id'); if (empty($wheel_id)) { return new WP_Error('no_wheel_id', __('Wheel ID is missing.', 'wheel-of-fortune'), ['status' => 400]); } global $wpdb; $spins_table = $wpdb->prefix . 'wheel_spins'; $user_spins = $wpdb->get_row($wpdb->prepare("SELECT spins_available FROM $spins_table WHERE user_id = %d AND wheel_id = %d", $user_id, $wheel_id)); $spins = $user_spins ? intval($user_spins->spins_available) : 0; if ($spins <= 0) { return new WP_Error('no_spins', __('You have no spins left.', 'wheel-of-fortune'), ['status' => 403]); } $prizes = $this->get_wheel_prizes($wheel_id); if (empty($prizes)) { return new WP_Error('no_prizes', __('No prizes available.', 'wheel-of-fortune'), ['status' => 500]); } // Uporabi delujočo logiko za izbiro nagrade $prize = $this->select_random_prize($prizes); $new_spins = $spins - 1; $wpdb->update($spins_table, ['spins_available' => $new_spins, 'last_spin_date' => current_time('mysql')], ['user_id' => $user_id, 'wheel_id' => $wheel_id]); $log_table = $wpdb->prefix . 'wheel_log'; $wpdb->insert($log_table, ['user_id' => $user_id, 'wheel_id' => $wheel_id, 'prize_id' => $prize['id'], 'spin_date' => current_time('mysql'), 'redeemed' => 0]); $log_id = $wpdb->insert_id; // Uporabi delujočo logiko za izračun kota $degree = $this->calculate_prize_degree($prize['id'], $prizes); $redemption_code = !empty($prize['redemption_code']) ? $prize['redemption_code'] : ''; $user = get_userdata($user_id); if ($prize['is_discount'] && $prize['discount_value'] > 0 && class_exists('WooCommerce')) { $coupon_code = 'WOF-' . uniqid() . '-' . $user->ID; $coupon_id = $this->create_coupon_with_best_method($coupon_code, $user, $prize['discount_value']); if ($coupon_id) { $redemption_code = $coupon_code; $wpdb->update($log_table, ['redemption_code' => $redemption_code], ['id' => $log_id]); $prize['redemption_code'] = $redemption_code; } } // Pošlji email samo, če ni "try again" nagrada if (get_option('wheel_send_emails', true) && (!isset($prize['is_try_again']) || $prize['is_try_again'] != 1)) { $this->send_prize_email($user_id, $prize); } $response_data = [ 'prize' => $prize, 'degree' => $degree, 'remaining_spins' => $new_spins, 'discount_code' => $redemption_code, 'is_try_again' => isset($prize['is_try_again']) ? (int)$prize['is_try_again'] : 0 ]; return new WP_REST_Response(['success' => true, 'data' => $response_data], 200); } public function get_wheel_prizes($wheel_id = 1) { global $wpdb; $table_name = $wpdb->prefix . 'wheel_prizes'; return $wpdb->get_results($wpdb->prepare("SELECT * FROM $table_name WHERE wheel_id = %d AND is_active = 1 ORDER BY id ASC", $wheel_id), ARRAY_A); } private function select_random_prize($prizes) { // Filter out prizes with 0 probability $winnable_prizes = array_filter($prizes, function($prize) { return isset($prize['probability']) && $prize['probability'] > 0; }); // If no winnable prizes, return null or handle as an error if (empty($winnable_prizes)) { return end($prizes); // Or null, depending on desired behavior for no-win scenario } // Recalculate total probability for winnable prizes $total_probability = array_sum(wp_list_pluck($winnable_prizes, 'probability')); // Normalize probabilities if they don't sum to 1 if ($total_probability > 0) { foreach ($winnable_prizes as &$prize) { $prize['probability'] = $prize['probability'] / $total_probability; } unset($prize); } $rand = mt_rand(1, 10000) / 10000; // Increased precision $cumulative = 0; foreach ($winnable_prizes as $prize) { $cumulative += $prize['probability']; if ($rand <= $cumulative) { return $prize; } } return end($winnable_prizes); // Fallback to the last winnable prize } private function calculate_prize_degree($prize_id, $prizes) { $prize_index = -1; foreach ($prizes as $index => $prize) { if ($prize['id'] == $prize_id) { $prize_index = $index; break; } } if ($prize_index === -1) return 0; $num_prizes = count($prizes); $cx = 350; $cy = 350; $outer_radius = 330; $inner_radius = 70; $angle = 360 / $num_prizes; $segment_size = $angle; $prize_angle = $prize_index * $segment_size + ($segment_size / 2); $target_stop_angle = 270 - $prize_angle; $variation_range = $segment_size * 0.20; $random_variation = mt_rand(-$variation_range, $variation_range); $final_angle = $target_stop_angle + $random_variation; $final_angle = fmod($final_angle, 360); return ($final_angle < 0) ? $final_angle + 360 : $final_angle; } public function shortcode($atts) { $atts = shortcode_atts(array( 'id' => '1', // Default to wheel ID 1 or slug 'default' ), $atts, 'wheel_of_fortune'); global $wpdb; $wheels_table = $wpdb->prefix . 'wof_wheels'; $wheel_id_or_slug = $atts['id']; if (is_numeric($wheel_id_or_slug)) { $wheel = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wheels_table WHERE id = %d", $wheel_id_or_slug), ARRAY_A); } else { $wheel = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wheels_table WHERE slug = %s", $wheel_id_or_slug), ARRAY_A); } if (!$wheel) { $wheel = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wheels_table WHERE id = 1"), ARRAY_A); if (!$wheel) { return ''; } } $wheel_id = $wheel['id']; if (!is_user_logged_in()) { return ''; } $prizes = $this->get_wheel_prizes($wheel_id); if (empty($prizes)) { return ''; } $this->enqueue_frontend_assets(); $user_id = get_current_user_id(); $spins_table = $wpdb->prefix . 'wheel_spins'; $spins_left = $wpdb->get_var($wpdb->prepare("SELECT spins_available FROM $spins_table WHERE user_id = %d AND wheel_id = %d", $user_id, $wheel_id)); $spins_left = ($spins_left === null) ? 0 : (int)$spins_left; // Definiramo polje za prevode, da bo dostopno v PHP in JS $l10n = [ 'spin_button' => __('SPIN', 'wheel-of-fortune'), 'spinning' => __('Spinning...', 'wheel-of-fortune'), 'no_spins_left' => __('No More Spins', 'wheel-of-fortune'), 'congratulations' => __('Congratulations!', 'wheel-of-fortune'), 'you_won' => __('You won:', 'wheel-of-fortune'), 'close' => __('Close', 'wheel-of-fortune'), 'error' => __('Error', 'wheel-of-fortune'), 'discount_code_sent' => __('Your discount code has been sent to your email.', 'wheel-of-fortune'), ]; wp_localize_script('wheel-of-fortune-js', 'wof_data', array( 'ajax_url' => admin_url('admin-ajax.php'), 'rest_url' => get_rest_url(null, 'wheel-of-fortune/v1/spin'), 'nonce' => wp_create_nonce('wp_rest'), 'wheel_id' => $wheel_id, 'spins_left' => $spins_left, 'prizes' => $prizes, 'l10n' => $l10n // Uporabimo definirano polje )); ob_start(); // Pass variables to the template include(WHEEL_OF_FORTUNE_PLUGIN_DIR . 'public/shortcode-template.php'); return ob_get_clean(); } public function enqueue_frontend_assets() { wp_enqueue_style('wheel-of-fortune-css', WHEEL_OF_FORTUNE_PLUGIN_URL . 'assets/css/wheel.css', [], WHEEL_OF_FORTUNE_VERSION); wp_enqueue_script('wheel-of-fortune-js', WHEEL_OF_FORTUNE_PLUGIN_URL . 'assets/js/wheel.js', ['jquery'], WHEEL_OF_FORTUNE_VERSION, true); } public function enqueue_admin_assets($hook) { // Preveri, ali je trenutna stran ena od naših admin strani if (strpos($hook, 'wof-') !== false || strpos($hook, 'wheel-') !== false) { wp_enqueue_style('wheel-of-fortune-admin-css', WHEEL_OF_FORTUNE_PLUGIN_URL . 'assets/css/admin.css', [], WHEEL_OF_FORTUNE_VERSION); wp_enqueue_script('wheel-of-fortune-admin-js', WHEEL_OF_FORTUNE_PLUGIN_URL . 'admin/js/admin.js', ['jquery'], WHEEL_OF_FORTUNE_VERSION, true); wp_localize_script('wheel-of-fortune-admin-js', 'wheel_admin_nonce', array('_ajax_nonce' => wp_create_nonce('wheel_admin_nonce'))); wp_localize_script('wheel-of-fortune-admin-js', 'wheel_admin_i18n', [ 'select_user' => __('Select a user', 'wheel-of-fortune'), 'select_user_and_spins' => __('Please select a user and enter the number of spins.', 'wheel-of-fortune'), 'confirm_reset_all_spins' => __('Are you sure you want to reset the spins for all users? This cannot be undone!', 'wheel-of-fortune'), 'enter_product_id_and_spins' => __('Please enter the product ID and number of spins.', 'wheel-of-fortune'), 'remove' => __('Remove', 'wheel-of-fortune'), 'no_products' => __('No products have been configured.', 'wheel-of-fortune'), 'product_not_found' => __('Product not found.', 'wheel-of-fortune'), 'error_finding_product' => __('Error finding product.', 'wheel-of-fortune'), 'active' => __('Active', 'wheel-of-fortune'), 'inactive' => __('Inactive', 'wheel-of-fortune'), 'confirm_delete_prize' => __('Are you sure you want to delete this prize?', 'wheel-of-fortune'), 'sending' => __('Sending...', 'wheel-of-fortune'), 'sending_test_email' => __('Sending test email...', 'wheel-of-fortune'), 'send_test_email' => __('Send Test Email', 'wheel-of-fortune'), 'error_sending_email' => __('Error sending email.', 'wheel-of-fortune'), 'enter_recipient_email' => __('Please enter a recipient email address.', 'wheel-of-fortune'), ]); } } private function is_woocommerce_active() { return in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins'))); } /** * Generates the SVG for the wheel with visible, curved, and auto-adjusting text. * This version uses for superior text rendering on segments. * * @param array $prizes The array of prizes. * @return string The generated SVG string. */ public function generate_wheel_svg($prizes) { $num_prizes = count($prizes); if ($num_prizes === 0) { return '
' . esc_html__('No prizes available.', 'wheel-of-fortune') . '
'; } // SVG dimensions and parameters $cx = 350; $cy = 350; $outer_radius = 330; $inner_radius = 70; $angle = 360 / $num_prizes; // Initialize SVG parts $defs = ''; $segments_paths = ''; $text_paths = ''; // Generate segments and text paths // Updated segment color palette $segment_colors = ['#ff3131', '#7ed957', '#8c52ff', '#ff914d', '#1da3e7']; for ($i = 0; $i < $num_prizes; $i++) { $prize = $prizes[$i]; $start_angle = $i * $angle; $end_angle = ($i + 1) * $angle; // --- 1. Draw the visible segment path --- $start_rad = deg2rad($start_angle); $end_rad = deg2rad($end_angle); $x1 = $cx + $outer_radius * cos($start_rad); $y1 = $cy + $outer_radius * sin($start_rad); $x2 = $cx + $outer_radius * cos($end_rad); $y2 = $cy + $outer_radius * sin($end_rad); $x3 = $cx + $inner_radius * cos($end_rad); $y3 = $cy + $inner_radius * sin($end_rad); $x4 = $cx + $inner_radius * cos($start_rad); $y4 = $cy + $inner_radius * sin($start_rad); $large_arc = ($end_angle - $start_angle > 180) ? 1 : 0; $path_data = "M{$x1},{$y1} A{$outer_radius},{$outer_radius} 0 {$large_arc},1 {$x2},{$y2} L{$x3},{$y3} A{$inner_radius},{$inner_radius} 0 {$large_arc},0 {$x4},{$y4} Z"; // Barva segmenta - ciklično, a če je nagrad 10, zadnji segment ne sme biti enak prvemu $color_index = $i % count($segment_colors); if ($num_prizes === 10 && $i === $num_prizes - 1 && $segment_colors[$color_index] === $segment_colors[0]) { $color_index = ($color_index + 1) % count($segment_colors); } $segment_color = $segment_colors[$color_index]; $segments_paths .= ""; // --- 2. Draw dividers and pegs --- $divider_x1 = $cx + $inner_radius * cos($start_rad); $divider_y1 = $cy + $inner_radius * sin($start_rad); $divider_x2 = $cx + $outer_radius * cos($start_rad); $divider_y2 = $cy + $outer_radius * sin($start_rad); $segments_paths .= ""; $segments_paths .= ""; // --- 3. Draw text vertically (rotated to segment center) --- $text_angle = $start_angle + ($angle / 2); $text_rad = deg2rad($text_angle); $text_radius = ($inner_radius + $outer_radius) / 2; $text_x = $cx + $text_radius * cos($text_rad); $text_y = $cy + $text_radius * sin($text_rad); $rotate = $text_angle; // Da je vedno navzven (ne na glavo) if ($rotate > 90 && $rotate < 270) { $rotate += 180; } $text_paths .= "{$prize['name']}"; } $defs .= ''; // Assemble the final SVG $svg = ''; $svg .= $defs; // Add definitions first $svg .= ''; $svg .= ''; $svg .= $segments_paths; // Add all segments, dividers, and pegs $svg .= ''; $svg .= ''; $svg .= $text_paths; // Add all text paths on top of segments $svg .= ''; $svg .= "SPIN"; $svg .= ''; return $svg; } public function ajax_get_prize_details() { check_ajax_referer('wheel_admin_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'wheel-of-fortune')]); } $prize_id = isset($_POST['prize_id']) ? intval($_POST['prize_id']) : 0; if (!$prize_id) { wp_send_json_error(['message' => __('Prize ID was not provided.', 'wheel-of-fortune')]); } global $wpdb; $prize = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wheel_prizes WHERE id = %d", $prize_id), ARRAY_A); if (!$prize) { wp_send_json_error(['message' => __('Prize not found.', 'wheel-of-fortune')]); } wp_send_json_success($prize); } public function ajax_save_prize() { check_ajax_referer('wheel_admin_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'wheel-of-fortune')]); } // Dodamo wheel_id $wheel_id = isset($_POST['wheel_id']) ? intval($_POST['wheel_id']) : 0; $prize_id = isset($_POST['prize_id']) ? intval($_POST['prize_id']) : 0; $name = isset($_POST['name']) ? sanitize_text_field($_POST['name']) : ''; $description = isset($_POST['description']) ? sanitize_textarea_field($_POST['description']) : ''; $probability = isset($_POST['probability']) ? floatval($_POST['probability']) : 0; $is_active = isset($_POST['is_active']) ? intval($_POST['is_active']) : 0; $redemption_code = isset($_POST['redemption_code']) ? sanitize_text_field($_POST['redemption_code']) : ''; $is_discount = isset($_POST['is_discount']) ? intval($_POST['is_discount']) : 0; $email_subject = isset($_POST['email_subject']) ? sanitize_text_field($_POST['email_subject']) : ''; $email_template = isset($_POST['email_template']) ? wp_kses_post($_POST['email_template']) : ''; $discount_value = isset($_POST['discount_value']) ? floatval($_POST['discount_value']) : 0; $is_try_again = isset($_POST['is_try_again']) ? intval($_POST['is_try_again']) : 0; if (empty($name)) { wp_send_json_error(['message' => __('Prize name is required.', 'wheel-of-fortune')]); } if ($wheel_id === 0) { wp_send_json_error(['message' => __('Wheel ID is missing.', 'wheel-of-fortune')]); } if ($probability < 0 || $probability > 1) { wp_send_json_error(['message' => __('Probability must be between 0 and 1.', 'wheel-of-fortune')]); } global $wpdb; $table_name = $wpdb->prefix . 'wheel_prizes'; $data = [ 'wheel_id' => $wheel_id, 'name' => $name, 'description' => $description, 'probability' => $probability, 'is_active' => $is_active, 'redemption_code' => $redemption_code, 'is_discount' => $is_discount, 'email_subject' => $email_subject, 'email_template' => $email_template, 'discount_value' => $discount_value, 'is_try_again' => $is_try_again, ]; $format = ['%d', '%s', '%s', '%f', '%d', '%s', '%d', '%s', '%s', '%f', '%d']; if ($prize_id > 0) { $result = $wpdb->update($table_name, $data, ['id' => $prize_id], $format, ['%d']); } else { $result = $wpdb->insert($table_name, $data, $format); $prize_id = $wpdb->insert_id; } if ($result === false) { wp_send_json_error(['message' => $wpdb->last_error]); } else { wp_send_json_success(['message' => __('Prize saved successfully!', 'wheel-of-fortune'), 'prize_id' => $prize_id]); } } public function ajax_delete_prize() { check_ajax_referer('wheel_admin_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'wheel-of-fortune')]); } $prize_id = isset($_POST['prize_id']) ? intval($_POST['prize_id']) : 0; if (!$prize_id) { wp_send_json_error(['message' => __('Prize ID was not provided.', 'wheel-of-fortune')]); } global $wpdb; $table_name = $wpdb->prefix . 'wheel_prizes'; $result = $wpdb->delete($table_name, ['id' => $prize_id]); if ($result !== false) { wp_send_json_success(['message' => __('Prize deleted successfully.', 'wheel-of-fortune')]); } else { wp_send_json_error(['message' => __('Failed to delete the prize.', 'wheel-of-fortune')]); } } public function ajax_search_users() { check_ajax_referer('wheel_admin_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'wheel-of-fortune')]); } $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : ''; if (empty($search)) { wp_send_json_error(['message' => __('Search term is required.', 'wheel-of-fortune')]); } $users = get_users([ 'search' => '*' . $search . '*', 'search_columns' => ['user_login', 'user_email', 'display_name'], 'number' => 10 ]); $results = []; foreach ($users as $user) { $results[] = [ 'id' => $user->ID, 'name' => $user->display_name, 'email' => $user->user_email ]; } wp_send_json_success($results); } public function ajax_add_spins() { check_ajax_referer('wheel_admin_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'wheel-of-fortune')]); } $user_id = isset($_POST['user_id']) ? intval($_POST['user_id']) : 0; $spins = isset($_POST['spins']) ? intval($_POST['spins']) : 0; if (!$user_id || $spins <= 0) { wp_send_json_error(['message' => __('Invalid user ID or number of spins.', 'wheel-of-fortune')]); } if (wheel_of_fortune_add_spins($user_id, $spins)) { wp_send_json_success(['message' => sprintf(__('Successfully added %d spins to user ID %d.', 'wheel-of-fortune'), $spins, $user_id)]); } else { wp_send_json_error(['message' => __('Failed to add spins to user.', 'wheel-of-fortune')]); } } public function ajax_reset_all_spins() { check_ajax_referer('wheel_admin_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'wheel-of-fortune')]); } global $wpdb; $table_name = $wpdb->prefix . 'wheel_spins'; $result = $wpdb->update( $table_name, ['spins_available' => 0], [], ['%d'], [] ); if ($result !== false) { // Reset user meta fields too $users = get_users(['fields' => 'ID']); foreach ($users as $user_id) { update_user_meta($user_id, '_wheel_spins', 0); } wp_send_json_success(['message' => __('All spins have been reset to 0.', 'wheel-of-fortune')]); } else { wp_send_json_error(['message' => __('Failed to reset spins.', 'wheel-of-fortune')]); } } public function ajax_get_product() { check_ajax_referer('wheel_admin_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'wheel-of-fortune')]); } if (!$this->is_woocommerce_active()) { wp_send_json_error(['message' => __('WooCommerce is not active.', 'wheel-of-fortune')]); } $product_id = isset($_POST['product_id']) ? intval($_POST['product_id']) : 0; if (!$product_id) { wp_send_json_error(['message' => __('Product ID is required.', 'wheel-of-fortune')]); } $product = wc_get_product($product_id); if (!$product) { wp_send_json_error(['message' => __('Product not found.', 'wheel-of-fortune')]); } wp_send_json_success([ 'id' => $product->get_id(), 'name' => $product->get_name() ]); } public function ajax_test_email() { check_ajax_referer('wheel_admin_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'wheel-of-fortune')]); return; } $recipient_email = isset($_POST['recipient_email']) ? sanitize_email($_POST['recipient_email']) : ''; if (empty($recipient_email) || !is_email($recipient_email)) { wp_send_json_error(['message' => __('Please provide a valid recipient email address.', 'wheel-of-fortune')]); return; } // Get settings from the form, passed via AJAX $smtp_enabled = isset($_POST['smtp_enabled']) && $_POST['smtp_enabled'] === '1'; $smtp_host = isset($_POST['smtp_host']) ? sanitize_text_field($_POST['smtp_host']) : ''; $smtp_port = isset($_POST['smtp_port']) ? intval($_POST['smtp_port']) : 587; $smtp_encryption = isset($_POST['smtp_encryption']) ? sanitize_text_field($_POST['smtp_encryption']) : 'tls'; $smtp_username = isset($_POST['smtp_username']) ? sanitize_text_field($_POST['smtp_username']) : ''; $smtp_password = isset($_POST['smtp_password']) ? wp_unslash($_POST['smtp_password']) : ''; $from_name = isset($_POST['from_name']) ? sanitize_text_field($_POST['from_name']) : get_bloginfo('name'); $from_email = isset($_POST['from_email']) ? sanitize_email($_POST['from_email']) : get_bloginfo('admin_email'); $user = wp_get_current_user(); // Prepare email content $subject = __('Wheel of Fortune - Test Email', 'wheel-of-fortune'); $message_body = sprintf( __('Hello %1$s,

This is a test email from the Wheel of Fortune plugin.

If you received this message, your email settings seem to be working.

Current Settings Used for This Test:
  • Use SMTP: %2$s
  • Host: %3$s
  • Port: %4$s
  • Encryption: %5$s
  • Username: %6$s
  • Sender Name: %7$s
  • Sender Email: %8$s

Best regards,
The Wheel of Fortune Plugin', 'wheel-of-fortune'), $user->display_name, $smtp_enabled ? __('Yes', 'wheel-of-fortune') : __('No (using default wp_mail)', 'wheel-of-fortune'), esc_html($smtp_host), esc_html($smtp_port), esc_html(strtoupper($smtp_encryption)), esc_html($smtp_username), esc_html($from_name), esc_html($from_email) ); if (!$smtp_enabled) { // Use standard wp_mail if SMTP is disabled $headers = ['Content-Type: text/html; charset=UTF-8']; if (!empty($from_email) && !empty($from_name)) { $headers[] = 'From: ' . $from_name . ' <' . $from_email . '>'; } $result = wp_mail($recipient_email, $subject, $message_body, $headers); if ($result) { wp_send_json_success(['message' => sprintf(__('Test email successfully sent to %s using the standard WordPress mail function.', 'wheel-of-fortune'), esc_html($recipient_email))]); } else { global $phpmailer; $error_info = (isset($phpmailer) && is_object($phpmailer) && !empty($phpmailer->ErrorInfo)) ? $phpmailer->ErrorInfo : 'Please check your server\'s PHP mail logs.'; wp_send_json_error(['message' => sprintf(__('Failed to send email using wp_mail(). Error: %s', 'wheel-of-fortune'), esc_html($error_info))]); } return; } // --- Use a dedicated PHPMailer instance for SMTP test --- require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php'; require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php'; require_once ABSPATH . WPINC . '/PHPMailer/Exception.php'; $mail = new PHPMailer\PHPMailer\PHPMailer(true); try { // Server settings $mail->isSMTP(); $mail->Host = $smtp_host; $mail->SMTPAuth = !empty($smtp_username); $mail->Username = $smtp_username; $mail->Password = $smtp_password; if ($smtp_encryption === 'ssl') { $mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS; } elseif ($smtp_encryption === 'tls') { $mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS; } $mail->Port = $smtp_port; // Recipients $mail->setFrom($from_email, $from_name); $mail->addAddress($recipient_email); // Content $mail->isHTML(true); $mail->CharSet = 'UTF-8'; $mail->Subject = $subject; $mail->Body = $message_body; $mail->AltBody = wp_strip_all_tags($message_body); // Send the email $mail->send(); wp_send_json_success(['message' => sprintf(__('SMTP test email successfully sent to %s.', 'wheel-of-fortune'), esc_html($recipient_email))]); } catch (PHPMailer\PHPMailer\Exception $e) { // PHPMailer exception wp_send_json_error(['message' => sprintf(__('Failed to send email. Error: %s', 'wheel-of-fortune'), esc_html($mail->ErrorInfo))]); } catch (Exception $e) { // Other exceptions wp_send_json_error(['message' => sprintf(__('An unexpected error occurred: %s', 'wheel-of-fortune'), esc_html($e->getMessage()))]); } } public function ajax_get_prizes() { check_ajax_referer('wheel_public_nonce', 'nonce'); $wheel_id = isset($_POST['wheel_id']) ? intval($_POST['wheel_id']) : 1; $prizes = $this->get_wheel_prizes($wheel_id); wp_send_json_success($prizes); } /** * Ustvari kupon z uporabo najboljše metode, določene s testiranjem * * @param string $code Koda kupona * @param WP_User $user Uporabnik, za katerega se ustvari kupon * @param float $discount_value Vrednost popusta (v odstotkih) * @return int|bool ID kupona ali false v primeru napake */ private function create_coupon_with_best_method($code, $user, $discount_value) { // Pridobi najboljšo metodo iz nastavitev $best_method = get_option('wheel_best_coupon_method', 'standard_api'); wheel_of_fortune_debug_log("Creating coupon using method: {$best_method}"); switch ($best_method) { case 'programmatic': // Metoda 2: Programsko ustvarjanje kupona try { $coupon_data = array( 'post_title' => $code, 'post_content' => '', 'post_status' => 'publish', 'post_author' => $user->ID, 'post_type' => 'shop_coupon' ); $coupon_id = wp_insert_post($coupon_data); if (!is_wp_error($coupon_id)) { // Dodaj meta podatke za kupon update_post_meta($coupon_id, 'discount_type', 'percent'); update_post_meta($coupon_id, 'coupon_amount', $discount_value); update_post_meta($coupon_id, 'individual_use', 'yes'); update_post_meta($coupon_id, 'usage_limit', '1'); update_post_meta($coupon_id, 'usage_limit_per_user', '1'); update_post_meta($coupon_id, 'customer_email', array($user->user_email)); update_post_meta($coupon_id, 'description', __('Wheel of Fortune', 'wheel-of-fortune')); // Nastavi veljavnost 10 dni update_post_meta($coupon_id, 'date_expires', strtotime('+10 days')); return $coupon_id; } } catch (Exception $e) { wheel_of_fortune_debug_log("Error in programmatic method: " . $e->getMessage()); } break; case 'wc_coupon_extended': // Metoda 3: Uporaba WC_Coupon z dodatnimi preverjanji try { // Preveri, ali koda že obstaja $existing_id = wc_get_coupon_id_by_code($code); if ($existing_id) { wheel_of_fortune_debug_log("Coupon code already exists: {$code}"); return false; } // Ustvari nov kupon z dodatnimi preverjanji $coupon = new WC_Coupon(); $coupon->set_code($code); $coupon->set_description(__('Wheel of Fortune', 'wheel-of-fortune')); $coupon->set_discount_type('percent'); $coupon->set_amount($discount_value); $coupon->set_individual_use(true); $coupon->set_usage_limit(1); $coupon->set_usage_limit_per_user(1); $coupon->set_email_restrictions(array($user->user_email)); // Nastavi veljavnost 10 dni $coupon->set_date_expires(strtotime('+10 days')); $coupon_id = $coupon->save(); // Preveri, ali je kupon res ustvarjen if ($coupon_id) { $verification_id = wc_get_coupon_id_by_code($code); if ($verification_id && $verification_id == $coupon_id) { return $coupon_id; } } } catch (Exception $e) { wheel_of_fortune_debug_log("Error in wc_coupon_extended method: " . $e->getMessage()); } break; case 'direct_db': // Metoda 4: Neposredno vstavljanje v podatkovno bazo try { global $wpdb; // Ustvari nov post tipa shop_coupon $wpdb->insert( $wpdb->posts, array( 'post_title' => $code, 'post_name' => sanitize_title($code), 'post_content' => '', 'post_status' => 'publish', 'post_author' => $user->ID, 'post_type' => 'shop_coupon', 'post_date' => current_time('mysql'), 'post_date_gmt' => current_time('mysql', 1) ) ); $coupon_id = $wpdb->insert_id; if ($coupon_id) { // Dodaj meta podatke za kupon $meta_data = array( 'discount_type' => 'percent', 'coupon_amount' => $discount_value, 'individual_use' => 'yes', 'usage_limit' => '1', 'usage_limit_per_user' => '1', 'customer_email' => serialize(array($user->user_email)), 'description' => __('Wheel of Fortune', 'wheel-of-fortune'), // Nastavi veljavnost 10 dni 'date_expires' => strtotime('+10 days') ); foreach ($meta_data as $meta_key => $meta_value) { $wpdb->insert( $wpdb->postmeta, array( 'post_id' => $coupon_id, 'meta_key' => $meta_key, 'meta_value' => $meta_value ) ); } return $coupon_id; } } catch (Exception $e) { wheel_of_fortune_debug_log("Error in direct_db method: " . $e->getMessage()); } break; case 'standard_api': default: // Metoda 1: Standardna WooCommerce API metoda (privzeta) try { $coupon = new WC_Coupon(); $coupon->set_code($code); $coupon->set_description(__('Wheel of Fortune', 'wheel-of-fortune')); $coupon->set_discount_type('percent'); $coupon->set_amount($discount_value); $coupon->set_individual_use(true); $coupon->set_usage_limit(1); $coupon->set_usage_limit_per_user(1); $coupon->set_email_restrictions(array($user->user_email)); // Nastavi veljavnost 10 dni $coupon->set_date_expires(strtotime('+10 days')); $coupon_id = $coupon->save(); if ($coupon_id) { return $coupon_id; } } catch (Exception $e) { wheel_of_fortune_debug_log("Error in standard_api method: " . $e->getMessage()); } break; } // Če pridemo do sem, je prišlo do napake pri ustvarjanju kupona return false; } /** * Sends an email notification to the user upon winning a prize. * * @param int $user_id The ID of the user who won. * @param array $prize The prize details array. */ public function send_prize_email($user_id, $prize) { // Ne pošlji emaila, če je nagrada "try again" if (isset($prize['is_try_again']) && $prize['is_try_again'] == 1) { wheel_of_fortune_debug_log("send_prize_email: Email ni poslan za 'try again' nagrado: {$prize['name']}"); return; } $user = get_userdata($user_id); if (!$user) { wheel_of_fortune_debug_log("send_prize_email: Uporabnik z ID {$user_id} ni bil najden."); return; } // Določi vsebino emaila - uporabi specifično predlogo za nagrado, če obstaja, sicer splošno $subject = !empty($prize['email_subject']) ? $prize['email_subject'] : sprintf(__('Congratulations! You won a prize on the Wheel of Fortune - %s', 'wheel-of-fortune'), get_bloginfo('name')); $body = !empty($prize['email_template']) ? $prize['email_template'] : file_get_contents(WHEEL_OF_FORTUNE_PLUGIN_DIR . 'templates/emails/default-prize-email.html'); // Pripravi nadomestne oznake (placeholders) $replacements = [ '{user_name}' => $user->display_name, '{user_email}' => $user->user_email, '{prize_name}' => $prize['name'], '{prize_description}' => $prize['description'], '{redemption_code}' => !empty($prize['redemption_code']) ? $prize['redemption_code'] : __('N/A', 'wheel-of-fortune'), '{site_name}' => get_bloginfo('name'), '{site_url}' => home_url(), '{date}' => date_i18n(get_option('date_format')), '{time}' => date_i18n(get_option('time_format')), ]; // Zamenjaj oznake v vsebini in zadevi $final_subject = str_replace(array_keys($replacements), array_values($replacements), $subject); $final_body = str_replace(array_keys($replacements), array_values($replacements), $body); // Pridobi nastavitve pošiljatelja $from_name = get_option('wheel_email_from_name', get_bloginfo('name')); $from_email = get_option('wheel_email_from_email', get_bloginfo('admin_email')); $headers = [ 'Content-Type: text/html; charset=UTF-8', 'From: ' . $from_name . ' <' . $from_email . '>' ]; // Preveri, ali je omogočen SMTP if (get_option('wheel_smtp_enabled', false)) { // Pošlji preko SMTP z uporabo PHPMailer require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php'; require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php'; require_once ABSPATH . WPINC . '/PHPMailer/Exception.php'; $mail = new PHPMailer\PHPMailer\PHPMailer(true); try { $mail->isSMTP(); $mail->Host = get_option('wheel_smtp_host'); $mail->SMTPAuth = !empty(get_option('wheel_smtp_username')); $mail->Username = get_option('wheel_smtp_username'); $mail->Password = get_option('wheel_smtp_password'); $mail->Port = get_option('wheel_smtp_port', 587); $smtp_encryption = get_option('wheel_smtp_encryption', 'tls'); if ($smtp_encryption === 'ssl') { $mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS; } elseif ($smtp_encryption === 'tls') { $mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS; } $mail->setFrom($from_email, $from_name); $mail->addAddress($user->user_email, $user->display_name); $mail->isHTML(true); $mail->CharSet = 'UTF-8'; $mail->Subject = $final_subject; $mail->Body = $final_body; $mail->AltBody = wp_strip_all_tags($final_body); $mail->send(); wheel_of_fortune_debug_log("Email uspešno poslan preko SMTP na {$user->user_email}."); } catch (Exception $e) { wheel_of_fortune_debug_log("Napaka pri pošiljanju emaila preko SMTP: {$mail->ErrorInfo}"); } } else { // Pošlji preko standardne wp_mail() funkcije if (wp_mail($user->user_email, $final_subject, $final_body, $headers)) { wheel_of_fortune_debug_log("Email uspešno poslan preko wp_mail() na {$user->user_email}."); } else { wheel_of_fortune_debug_log("Napaka pri pošiljanju emaila preko wp_mail()."); } } } public function ajax_update_wheel_product_spins() { check_ajax_referer('wof_update_wheel_product_spins'); if (!current_user_can('manage_options')) { wp_send_json_error(__('Nimaš dovoljenja.', 'wheel-of-fortune')); } global $wpdb; $id = isset($_POST['id']) ? intval($_POST['id']) : 0; $spins = isset($_POST['spins']) ? intval($_POST['spins']) : 1; // Debug informacije error_log("=== AJAX UPDATE WHEEL PRODUCT SPINS DEBUG ==="); error_log("POST data: " . print_r($_POST, true)); error_log("ID: " . $id); error_log("Spins: " . $spins); if ($id > 0 && $spins > 0) { $table = $wpdb->prefix . 'wheel_of_fortune_products'; error_log("Table: " . $table); $result = $wpdb->update($table, ['spins_per_purchase' => $spins], ['id' => $id], ['%d'], ['%d']); error_log("Update result: " . $result); error_log("Last SQL query: " . $wpdb->last_query); error_log("Last SQL error: " . $wpdb->last_error); if ($result !== false) { wp_send_json_success(); } } wp_send_json_error(__('Napaka pri shranjevanju.', 'wheel-of-fortune')); } public function ajax_delete_wheel_product() { check_ajax_referer('wof_delete_wheel_product', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'wheel-of-fortune')]); } $id = isset($_POST['id']) ? intval($_POST['id']) : 0; if (!$id) { wp_send_json_error(['message' => __('Invalid ID.', 'wheel-of-fortune')]); } global $wpdb; $table_name = $wpdb->prefix . 'wheel_of_fortune_products'; $result = $wpdb->delete($table_name, ['id' => $id]); if ($result !== false) { wp_send_json_success(['message' => __('Product deleted successfully.', 'wheel-of-fortune')]); } else { wp_send_json_error(['message' => __('Failed to delete product.', 'wheel-of-fortune')]); } } public function ajax_migrate_try_again() { check_ajax_referer('wheel_admin_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'wheel-of-fortune')]); } $this->migrate_add_is_try_again_column(); wp_send_json_success(['message' => __('Migration completed successfully. The is_try_again column has been added to the database.', 'wheel-of-fortune')]); } public function ajax_reset_spins_wheel() { check_ajax_referer('wheel_admin_nonce', '_ajax_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'wheel-of-fortune')]); } $wheel_id = isset($_POST['wheel_id']) ? intval($_POST['wheel_id']) : 0; if (!$wheel_id) { wp_send_json_error(['message' => __('Wheel ID is required.', 'wheel-of-fortune')]); } global $wpdb; $wheels_table = $wpdb->prefix . 'wof_wheels'; $spins_table = $wpdb->prefix . 'wheel_spins'; // Preveri, če kolo obstaja $wheel_exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM $wheels_table WHERE id = %d", $wheel_id)); if (!$wheel_exists) { wp_send_json_error(['message' => __('Selected wheel does not exist.', 'wheel-of-fortune')]); } // Ponastavi spine na 0 za vse uporabnike na tem kolesu $result = $wpdb->update( $spins_table, array('spins_available' => 0), array('wheel_id' => $wheel_id), array('%d'), array('%d') ); if ($result !== false) { wp_send_json_success([ 'message' => sprintf(__('All spins for wheel ID %d have been successfully reset to 0.', 'wheel-of-fortune'), $wheel_id), 'affected_users' => $result ]); } else { wp_send_json_error(['message' => __('An error occurred while resetting spins.', 'wheel-of-fortune')]); } } // --- NOVE METODE ZA CRON --- public function add_custom_cron_schedules($schedules) { // Lahko pustiš custom interval, če ga uporabljaš še kje drugje, sicer ni potreben // $schedules['every_2_minutes'] = array( // 'interval' => 120, // 'display' => __('Every 2 minutes', 'wheel-of-fortune') // ); return $schedules; } public function execute_daily_spin_cron() { wheel_of_fortune_debug_log('CRON: "wof_daily_spin_event" triggered.'); $wheel_id = get_option('wof_daily_spin_wheel_id'); if (empty($wheel_id)) { wheel_of_fortune_debug_log('CRON: No wheel selected for daily spin. Aborting.'); return; } $users = get_users(array('fields' => 'ID')); wheel_of_fortune_debug_log('CRON: Found ' . count($users) . ' users to process for wheel ID ' . $wheel_id); foreach ($users as $user_id) { $last_given_meta_key = '_wof_daily_spin_last_given_' . $wheel_id; $last_given_timestamp = get_user_meta($user_id, $last_given_meta_key, true); // Sprememba: 24 ur (23*HOUR_IN_SECONDS) if (empty($last_given_timestamp) || (time() - $last_given_timestamp > 23 * HOUR_IN_SECONDS)) { $ok = wheel_of_fortune_add_spins($user_id, 1, $wheel_id); if ($ok) { update_user_meta($user_id, $last_given_meta_key, time()); wheel_of_fortune_debug_log("CRON: Assigned spin to user $user_id for wheel $wheel_id. Result: " . var_export($ok, true)); } else { wheel_of_fortune_debug_log("CRON: Failed to assign spin to user $user_id for wheel $wheel_id."); } } else { wheel_of_fortune_debug_log("CRON: User $user_id already received a spin recently. Skipping."); } } } // ---------------------------- } // Initialize plugin function wheel_of_fortune() { return WheelOfFortune::get_instance(); } wheel_of_fortune(); wheel_of_fortune();"""