diff --git a/.gitea/workflows/deploy-to-production.yml b/.gitea/workflows/deploy-to-production.yml deleted file mode 100644 index 0dd3a5d..0000000 --- a/.gitea/workflows/deploy-to-production.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Deploy to Production Server - -on: - push: - branches: - - master - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set SSH key permissions - shell: bash - run: | - # Izpis trenutne delovne mape in seznama datotek - pwd - ls -la - - # Uporabimo SSH ključ iz skrivnosti - mkdir -p ~/.ssh - echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa_gitea - chmod 600 ~/.ssh/id_rsa_gitea - echo "SSH ključ ustvarjen iz skrivnosti" - - - name: Install rsync - shell: bash - continue-on-error: true - run: | - # Najprej preverimo, ali je rsync že nameščen - if command -v rsync &> /dev/null; then - echo "rsync je že nameščen, verzija:" - rsync --version | head -n 1 - else - # Če rsync ni nameščen, ga poskušamo namestiti - echo "rsync ni nameščen, poskušam namestiti..." - if command -v apt-get &> /dev/null; then - echo "Poskušam namestiti rsync z apt-get..." - sudo apt-get update && sudo apt-get install -y rsync || echo "Ni mogoče namestiti rsync z apt-get" - elif command -v yum &> /dev/null; then - echo "Poskušam namestiti rsync z yum..." - sudo yum install -y rsync || echo "Ni mogoče namestiti rsync z yum" - else - echo "Ni mogoče namestiti rsync - manjka paketni upravljalnik" - fi - - # Ponovno preverimo, ali je namestitev uspela - if command -v rsync &> /dev/null; then - echo "rsync je uspešno nameščen, verzija:" - rsync --version | head -n 1 - else - echo "rsync ni uspešno nameščen, uporabili bomo tar" - fi - fi - - # Vedno vrnemo uspešno kodo za izhod, da korak ne bo označen kot neuspešen - exit 0 - - - name: Deploy to production server - shell: bash - run: | - SSH_KEY="~/.ssh/id_rsa_gitea" - - echo "Uporabljam SSH ključ: $SSH_KEY" - - # Testiranje SSH povezave - ssh -p 5050 -i "$SSH_KEY" -o StrictHostKeyChecking=no forexana@152.89.234.215 "echo 'SSH povezava uspešna'" - - # Izbrišemo obstoječo vsebino v mapi vtičnika v produkcijskem okolju - ssh -p 5050 -i "$SSH_KEY" -o StrictHostKeyChecking=no forexana@152.89.234.215 "rm -rf public_html/wp-content/plugins/custom_wheel\ 2/*" - - # Poskusimo najprej z rsync, če je nameščen - if command -v rsync &> /dev/null; then - echo "Prenašanje datotek z rsync..." - rsync -avz -e "ssh -p 5050 -i $SSH_KEY -o StrictHostKeyChecking=no" --exclude=".git" --exclude=".gitea" --exclude="id_rsa_gitea*" ./ forexana@152.89.234.215:public_html/wp-content/plugins/custom_wheel\ 2/ - else - # Alternativna metoda prenosa z uporabo tar preko SSH - echo "Prenašanje datotek s tar preko SSH..." - tar czf - --exclude=".git" --exclude=".gitea" --exclude="id_rsa_gitea*" ./ | ssh -p 5050 -i "$SSH_KEY" -o StrictHostKeyChecking=no forexana@152.89.234.215 "tar xzf - -C public_html/wp-content/plugins/custom_wheel\ 2/" - fi - - # Nastavimo pravilna dovoljenja - ssh -p 5050 -i "$SSH_KEY" -o StrictHostKeyChecking=no forexana@152.89.234.215 "chmod -R 755 public_html/wp-content/plugins/custom_wheel\ 2/" - - echo "Production deployment completed successfully!" \ No newline at end of file diff --git a/.gitea/workflows/deploy-to-test.yml b/.gitea/workflows/deploy-to-test.yml deleted file mode 100644 index 4aaba12..0000000 --- a/.gitea/workflows/deploy-to-test.yml +++ /dev/null @@ -1,103 +0,0 @@ -name: Deploy to Test Server - -on: - push: - branches: - - develop - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set SSH key permissions - shell: bash - run: | - # Izpis trenutne delovne mape in seznama datotek - pwd - ls -la - ls -la .gitea/runner/ || echo "Mapa runner ne obstaja" - - # Poskusimo najti SSH ključ - find . -name "id_rsa_gitea*" -type f - - # Uporabimo SSH ključ v bazi repozitorija - if [ -f ".gitea/runner/id_rsa_gitea" ]; then - chmod 600 .gitea/runner/id_rsa_gitea - echo "SSH ključ najden in nastavljen" - else - echo "SSH ključ ni bil najden na pričakovani lokaciji!" - # Ustvarimo SSH ključ iz kodiranega besedila - mkdir -p ~/.ssh - echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa_gitea - chmod 600 ~/.ssh/id_rsa_gitea - echo "SSH ključ ustvarjen iz skrivnosti" - fi - - - name: Install rsync - shell: bash - continue-on-error: true - run: | - # Najprej preverimo, ali je rsync že nameščen - if command -v rsync &> /dev/null; then - echo "rsync je že nameščen, verzija:" - rsync --version | head -n 1 - else - # Če rsync ni nameščen, ga poskušamo namestiti - echo "rsync ni nameščen, poskušam namestiti..." - if command -v apt-get &> /dev/null; then - echo "Poskušam namestiti rsync z apt-get..." - sudo apt-get update && sudo apt-get install -y rsync || echo "Ni mogoče namestiti rsync z apt-get" - elif command -v yum &> /dev/null; then - echo "Poskušam namestiti rsync z yum..." - sudo yum install -y rsync || echo "Ni mogoče namestiti rsync z yum" - else - echo "Ni mogoče namestiti rsync - manjka paketni upravljalnik" - fi - - # Ponovno preverimo, ali je namestitev uspela - if command -v rsync &> /dev/null; then - echo "rsync je uspešno nameščen, verzija:" - rsync --version | head -n 1 - else - echo "rsync ni uspešno nameščen, uporabili bomo tar" - fi - fi - - # Vedno vrnemo uspešno kodo za izhod, da korak ne bo označen kot neuspešen - exit 0 - - - name: Deploy to test server - shell: bash - run: | - # Najprej preverimo SSH povezavo - if [ -f ".gitea/runner/id_rsa_gitea" ]; then - SSH_KEY=".gitea/runner/id_rsa_gitea" - else - SSH_KEY="~/.ssh/id_rsa_gitea" - fi - - echo "Uporabljam SSH ključ: $SSH_KEY" - - # Testiranje SSH povezave - ssh -p 5050 -i "$SSH_KEY" -o StrictHostKeyChecking=no forexana@152.89.234.215 "echo 'SSH povezava uspešna'" - - # Izbrišemo obstoječo vsebino v mapi vtičnika - ssh -p 5050 -i "$SSH_KEY" -o StrictHostKeyChecking=no forexana@152.89.234.215 "rm -rf test.forexanalysis.com/wp-content/plugins/custom_wheel\ 2/*" - - # Poskusimo najprej z rsync, če je nameščen - if command -v rsync &> /dev/null; then - echo "Prenašanje datotek z rsync..." - rsync -avz -e "ssh -p 5050 -i $SSH_KEY -o StrictHostKeyChecking=no" --exclude=".git" --exclude=".gitea" --exclude="id_rsa_gitea*" ./ forexana@152.89.234.215:test.forexanalysis.com/wp-content/plugins/custom_wheel\ 2/ - else - # Alternativna metoda prenosa z uporabo tar preko SSH - echo "Prenašanje datotek s tar preko SSH..." - tar czf - --exclude=".git" --exclude=".gitea" --exclude="id_rsa_gitea*" ./ | ssh -p 5050 -i "$SSH_KEY" -o StrictHostKeyChecking=no forexana@152.89.234.215 "tar xzf - -C test.forexanalysis.com/wp-content/plugins/custom_wheel\ 2/" - fi - - # Nastavimo pravilna dovoljenja - ssh -p 5050 -i "$SSH_KEY" -o StrictHostKeyChecking=no forexana@152.89.234.215 "chmod -R 755 test.forexanalysis.com/wp-content/plugins/custom_wheel\ 2/" - - echo "Deployment completed successfully!" \ No newline at end of file diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..4dd9399 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,59 @@ +name: Deploy to Development and Production + +# 1. EN trigger, ki se sproži ob potisku na OBE veji +on: + push: + branches: + - develop + - master + +# 2. EN jobs blok +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + # Ta korak se vedno izvede + - name: Checkout repository + uses: actions/checkout@v4 + + # Preveri, ali je rsync nameščen, sicer ga namesti + - name: Ensure rsync is installed + run: | + if ! command -v rsync >/dev/null; then + sudo apt-get update + sudo apt-get install -y rsync + fi + + # Ta korak se vedno izvede - priprava SSH ključa + - name: Setup SSH + run: | + mkdir -p ~/.ssh + echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + + # 3. KORAK ZA DEVELOP: Izvede se samo, če je veja 'develop' + - name: Deploy to Development Server + if: gitea.ref_name == 'develop' + run: | + echo "🚀 Deploying to Development..." + rsync -avz --delete \ + --exclude=".git/" \ + --exclude=".gitea/" \ + --exclude=".gitignore" \ + -e "ssh -p ${{ secrets.SSH_PORT }} -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no" \ + ./ \ + ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.TARGET_DIR }} + + # 4. KORAK ZA MASTER: Izvede se samo, če je veja 'master' + - name: Deploy to Production Server + if: gitea.ref_name == 'master' + run: | + echo "🚀 Deploying to Production..." + rsync -avz --delete \ + --exclude=".git/" \ + --exclude=".gitea/" \ + --exclude=".gitignore" \ + -e "ssh -p ${{ secrets.SSH_PORT }} -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no" \ + ./ \ + ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.TARGET_MASTER_DIR }} diff --git a/.gitignore b/.gitignore index f017775..fe3637f 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,5 @@ vendor/ *.tar *.bak *.swp -*.tmp \ No newline at end of file +*.tmp +code_export.txt diff --git a/README.md b/README.md index 4693d88..663b40e 100644 --- a/README.md +++ b/README.md @@ -1,748 +1,154 @@ -# Wheel of Fortune Plugin +# Wheel of Fortune za WooCommerce (Kolo Sreče) -Test posodobitve za preverjanje Gitea workflow. +**Wheel of Fortune** je napreden WordPress/WooCommerce vtičnik, ki omogoča gamifikacijo spletne trgovine. Kupcem omogoča, da z nakupom določenih izdelkov pridobijo "vrtljaje" (spins) in zavrtijo kolo sreče za priboritev nagrad (popusti, kuponi, fizična darila ali sporočila). -# Wheel of Fortune - WordPress Plugin +Vtičnik podpira več različnih koles, napredno upravljanje verjetnosti, samodejno ustvarjanje WooCommerce kuponov, pošiljanje e-poštnih obvestil (SMTP) in dnevne nagrade (Cron job). -Vtičnik "Kolo Sreče" (Wheel of Fortune) omogoča uporabnikom vrtenje kolesa za nagrade. Povezan je z WooCommerce za dodeljevanje spinov ob nakupu izdelkov. +## 🚀 Glavne funkcionalnosti -## Funkcionalnosti +* **Več koles:** Ustvarite lahko neomejeno število različnih koles za različne priložnosti. +* **Povezava z WooCommerce:** Določite, kateri izdelki prinašajo vrtljaje in koliko. +* **Napredne nagrade:** + * Avtomatsko ustvarjanje kuponov za popuste (odstotni popust). + * Fizične nagrade. + * "Poskusi znova" (Try Again) polja. + * Nastavljanje verjetnosti dobitka (uteženo). +* **Uporabniški račun:** Beleženje zgodovine vrtljajev in preostalih vrtljajev za vsakega uporabnika. +* **Dnevni vrtljaji:** Možnost nastavitve, da vsak uporabnik prejme 1 brezplačen vrtljaj vsakih 24 ur. +* **E-poštna obvestila:** Integriran SMTP klient za zanesljivo pošiljanje obvestil o nagradah. +* **Diagnostika:** Orodje za testiranje generiranja kuponov in pošiljanja e-pošte. -- Prikaz interaktivnega kolesa sreče preko kratke kode [wheel_of_fortune] -- Povezava z WooCommerce za dodeljevanje spinov ob nakupu izdelkov -- Upravljanje z nagradami, verjetnostmi in barvami -- Statistika vrtljajev in zadetkov +--- -## Namestitev +## 📂 Struktura map in datotek -1. Prenesite datoteke vtičnika v mapo `/wp-content/plugins/wheel-of-fortune/` -2. Aktivirajte vtičnik preko menija 'Vtičniki' v WordPress skrbniški plošči -3. Nastavite vtičnik preko menija 'Kolo sreče' v skrbniški plošči +Spodaj je podroben opis vseh datotek v projektu in njihov namen. -## Uporaba +### root (koren mape) +* **`wheel-of-fortune.php`**: + * Glavna datoteka vtičnika. + * Inicializira vtičnik, registrira 'hooks' (akcije in filtre). + * Ustvari in posodablja podatkovne baze (`dbDelta`). + * Registrira REST API endpoint (`/wp-json/wheel-of-fortune/v1/spin`) za varno izvedbo vrtljaja. + * Obravnava logiko dodeljevanja spinov ob zaključku WooCommerce naročila (`woocommerce_order_status_completed`). + * Vsebuje logiko za pošiljanje e-pošte in generiranje SVG kolesa. -### Osnovna uporaba +### `/admin` (Administracija) +Vsebuje logiko za backend vmesnik v WP Adminu. -Vstavite kratko kodo `[wheel_of_fortune]` na katerokoli stran ali prispevek, kjer želite prikazati kolo sreče. +* **`coupon-test.php`**: + * Diagnostično orodje. Preizkusi 4 različne metode ustvarjanja WooCommerce kuponov (API, programsko, DB insert), da najde tisto, ki deluje na strežniku, in jo shrani kot privzeto. +* **`wheels-page.php`**: + * Prikazuje seznam vseh ustvarjenih koles. + * Omogoča dodajanje novih koles in brisanje obstoječih. +* **`edit-wheel-page.php`**: + * Glavni urejevalnik za posamezno kolo. + * Upravljanje nagrad (CRUD): dodajanje, urejanje, verjetnosti, barve, template e-pošte. + * Povezovanje WooCommerce izdelkov s tem kolesom (koliko spinov prinese izdelek). +* **`settings-page.php`**: + * Globalne nastavitve: SMTP podatki, Cron nastavitve za dnevne spine, "cooldown" čas. + * Vsebuje orodja za ročno dodajanje spinov in testiranje e-pošte. +* **`stats-page.php`**: + * Analitika: Kdo je vrtel, kaj je zadel, kdaj. + * Filtriranje uporabnikov glede na preostale spine. +* **`users-page.php`**: + * Pregled uporabnikov in njihovih spinov. + * Možnost ročnega resetiranja ali dodajanja spinov specifičnemu uporabniku. +* **`partials/prize-form-fields.php`**: + * HTML delček (partial) za obrazec nagrade, ki se uporablja pri dodajanju in urejanju, da se koda ne podvaja. +* **`js/admin.js`**: + * jQuery skripte za admin vmesnik (odpiranje modalov, AJAX klici za shranjevanje nagrad, testiranje e-pošte). +* **`js/wheel.js`** (Admin preview): + * Skripta za animacijo kolesa v predogledu v administraciji. -### Parametri kratke kode +### `/assets` (Sredstva) +Datoteke, ki se nalagajo na frontend-u (in delno v adminu za stilizacijo). -Kratka koda sprejme naslednje parametre: +* **`css/admin.css`**: + * Stili za WP Admin (kartice, tabele, gumbi, postavitev urejevalnika). +* **`css/wheel.css`**: + * CSS za izgled kolesa na spletni strani. + * Vsebuje animacije (utripanje lučk), senčenje (SVG filtri) in odzivnost (responsive design) za mobilne naprave. +* **`js/wheel.js`**: + * **Glavna frontend logika.** + * Komunicira z REST API-jem za pridobitev rezultata vrtljaja (rezultat se določi na strežniku, ne v brskalniku zaradi varnosti). + * Izvaja animacijo vrtenja (easing functions) in prikaže "popup" z rezultatom. -- `size`: Velikost kolesa (možnosti: `small`, `medium`, `large`, privzeto: `medium`) -- `theme`: Tema kolesa (možnosti: `light`, `dark`, privzeto: `light`) -- `scale`: Dodatno prilagajanje velikosti (možnosti: `scale-90`, `scale-80`, `scale-70`, `scale-60`, `scale-50`, privzeto: prazen) +### `/languages` +* **`wheel-of-fortune.pot`**: + * Predloga za prevajanje vtičnika v druge jezike (npr. v slovenščino). -#### Primeri +### `/public` +* **`shortcode-template.php`**: + * HTML predloga, ki se izpiše, ko na stran vstavite `[wheel_of_fortune]`. Vsebuje kontejner za SVG kolo in gumb za vrtenje. +### `/templates` +* **`wheel.php`**: + * Alternativna ali starejša PHP predloga za generiranje SVG strukture (gradienti, filtri). +* **`emails/default-prize-email.html`**: + * HTML predloga za e-poštno sporočilo, ki ga prejme uporabnik ob zadetku. Vsebuje "placeholders" kot so `{user_name}`, `{prize_name}`, itd. + +--- + +## 🗄️ Podatkovna baza + +Vtičnik ustvari naslednje tabele v bazi: +1. `wp_wof_wheels`: Seznam koles. +2. `wp_wheel_prizes`: Nagrade povezane s kolesi (verjetnost, tip nagrade). +3. `wp_wheel_spins`: Stanje spinov za vsakega uporabnika (ločeno po kolesih). +4. `wp_wheel_log`: Zgodovina vseh vrtljajev (log). +5. `wp_wheel_of_fortune_products`: Povezovalna tabela med WC izdelki in kolesi. + +--- + +## 🛠️ Navodila za uporabo + +### 1. Namestitev in Aktivacija +1. Naložite mapo `wheel-of-fortune` v mapo `wp-content/plugins/` ali namestite preko ZIP datoteke v WP Adminu. +2. Aktivirajte vtičnik. Ob aktivaciji se bodo ustvarile potrebne tabele. + +### 2. Ustvarjanje Kolesa +1. Pojdite na **Wheels of Fortune > Wheels**. +2. Vnesite ime kolesa in kliknite "Add New Wheel". +3. Kliknite na ime kolesa ali "Edit" za urejanje. + +### 3. Dodajanje Nagrad (Prizes) +V urejevalniku kolesa: +1. Kliknite "Add New Prize". +2. Izpolnite podatke: + * **Probability:** Verjetnost med 0 in 1 (npr. 0.1 je 10%). *Pomembno: Vsota vseh verjetnosti naj bo 1.* + * **Is Discount?:** Če označite, vpišite odstotek. Sistem bo avtomatsko generiral unikatno kodo kupona. + * **Is Try Again?:** Če uporabnik ne zadene ničesar, a lahko vrti ponovno. +3. Shranite nagrado. + +### 4. Povezava z Izdelki (Kako do spinov?) +Na isti strani urejanja kolesa (spodaj): +1. V sekciji "Produkti & spini" izberite WooCommerce izdelek. +2. Določite "Število spinov na nakup" (npr. 1). +3. Kliknite "Dodaj". Ko kupec kupi ta izdelek (status naročila: Completed/Processing), bo prejel spine. + +### 5. Prikaz na spletni strani +Na katerokoli stran ali prispevek dodajte shortcode: ``` -[wheel_of_fortune] -[wheel_of_fortune size="large" theme="dark"] -[wheel_of_fortune size="medium" theme="light" scale="scale-80"] +[wheel_of_fortune id="1"] ``` +*Zamenjajte `1` z ID-jem vašega kolesa.* -## Vsebina -1. [Namen vtičnika](#namen-vtičnika) -2. [Ključne funkcionalnosti](#ključne-funkcionalnosti) -3. [Tehnični opis](#tehnični-opis) -4. [Logika delovanja](#logika-delovanja) -5. [Administratorske nastavitve](#administratorske-nastavitve) -6. [Kratka koda](#kratka-koda) -7. [Varnost in integracije](#varnost-in-integracije) -8. [Možne razširitve](#možne-razširitve) -9. [Implementacija kolesa](#implementacija-kolesa) -10. [Namestitev in uporaba](#namestitev-in-uporaba) +### 6. Nastavitve SMTP in Dnevni Spin +Pojdite na **Wheels of Fortune > Settings**: +* **SMTP:** Vnesite podatke vašega mail strežnika za zanesljivo dostavo nagrad. +* **Daily Spin:** Izberite kolo, ki se uporablja za dnevne nagrade. Uporabniki bodo avtomatsko prejeli 1 spin vsakih 24h za to kolo. +* **Coupon Test:** Če se kuponi ne generirajo, zaženite "Testiraj ustvarjanje kuponov" in shranite delujočo metodo. -## Namen vtičnika +--- -Vtičnik »Kolo Sreče« omogoča obiskovalcem spletne strani, da zavrtijo interaktivno kolo in prejmejo naključno nagrado. Vrtljaji so vezani na nakupe določenih izdelkov in uporabniške račune, kar omogoča gamificirano izkušnjo zvestobe in nagrajevanja. +## ⚠️ Pomembna opozorila -## Ključne funkcionalnosti +* **DEBUG mode:** V datoteki `wheel-of-fortune.php` je konstanta `WHEEL_OF_FORTUNE_DEBUG` nastavljena na `true`. Za produkcijo jo nastavite na `false`, da ne polnite `debug.log` datoteke. +* **Varnost:** Vtičnik uporablja "Nonce" preverjanje za vse AJAX in REST klice, zato je varen pred CSRF napadi. +* **Login:** Kolo se prikaže samo prijavljenim uporabnikom. Neprijavljeni vidijo obvestilo za prijavo. -- 🎯 Naključni izbor nagrade ob vrtenju kolesa -- 🔐 Vezava spinov (vrtljajev) na uporabniški račun -- 🛒 Samodejna dodelitev spinov ob nakupu določenih izdelkov -- 🧠 Nastavljive nagrade in verjetnosti zadetkov -- 📊 Spremljanje števila preostalih spinov -- 🎛️ Kratka koda za vključitev kolesa kjerkoli v WordPress vsebino -- 📬 Email obvestila ob zadetkih (opcijsko) -- 📈 Administratorska statistika: število zadetkov, porabljenih spinov ipd. +--- -## Tehnični opis - -### Arhitektura -- WordPress Plugin: zgrajen po WordPress standardih (object-oriented, namespacing, varnostni hooki) -- Frontend: JavaScript za animacijo kolesa, AJAX za komunikacijo z backendom -- Backend: PHP (WP REST API), povezano z WP uporabniki in WooCommerce naročili -- Baza podatkov: uporaba wp_usermeta, lastne tabele wp_wheel_spins in wp_wheel_prizes - -### Tabele podatkovne baze - -#### wp_wheel_spins - -| Polje | Tip | Opis | -|-------|-----|------| -| id | INT | Primarni ključ | -| user_id | INT | ID uporabnika (WordPress) | -| spins_available | INT | Število razpoložljivih spinov | -| last_spin_date | DATETIME | Datum zadnjega vrtenja | - -#### wp_wheel_prizes - -| Polje | Tip | Opis | -|-------|-----|------| -| id | INT | Primarni ključ | -| name | VARCHAR(255) | Ime nagrade | -| description | TEXT | Opis nagrade | -| probability | FLOAT | Verjetnost zadetka (0-1) | -| color | VARCHAR(20) | Barva segmenta (HEX ali RGB) | -| is_active | TINYINT | Ali je nagrada aktivna | -| image_url | VARCHAR(255) | URL do slike nagrade (opcijsko) | - -#### wp_wheel_log - -| Polje | Tip | Opis | -|-------|-----|------| -| id | INT | Primarni ključ | -| user_id | INT | ID uporabnika | -| prize_id | INT | ID zadete nagrade | -| spin_date | DATETIME | Datum in čas vrtenja | -| redeemed | TINYINT | Ali je nagrada unovčena | - -## Logika delovanja - -### Podeljevanje spinov - -Ob zaključenem nakupu določenega izdelka (npr. ID = 123), WooCommerce hook (woocommerce_order_status_completed) sproži funkcijo za dodelitev spinov. - -```php -function assign_spins_on_purchase($order_id) { - $order = wc_get_order($order_id); - foreach ($order->get_items() as $item) { - $product_id = $item->get_product_id(); - $spin_products = get_option('wheel_spin_products', array()); - - if (array_key_exists($product_id, $spin_products)) { - $user_id = $order->get_user_id(); - $current_spins = get_user_meta($user_id, '_wheel_spins', true) ?: 0; - $new_spins = $current_spins + $spin_products[$product_id]; - update_user_meta($user_id, '_wheel_spins', $new_spins); - - // Posodobi tudi v tabeli wp_wheel_spins - global $wpdb; - $table_name = $wpdb->prefix . 'wheel_spins'; - - $existing = $wpdb->get_var($wpdb->prepare( - "SELECT id FROM $table_name WHERE user_id = %d", - $user_id - )); - - if ($existing) { - $wpdb->update( - $table_name, - array('spins_available' => $new_spins), - array('user_id' => $user_id) - ); - } else { - $wpdb->insert( - $table_name, - array( - 'user_id' => $user_id, - 'spins_available' => $new_spins, - 'last_spin_date' => current_time('mysql') - ) - ); - } - } - } -} -add_action('woocommerce_order_status_completed', 'assign_spins_on_purchase'); -``` - -### Vrtenje kolesa - -1. Uporabnik klikne gumb "Zavrti" -2. JS animira kolo (CSS transformacija) -3. Medtem AJAX zahteva iz strežnika rezultat -4. PHP backend izbere nagrado glede na verjetnosti -5. Backend zabeleži zadetek, zmanjša število spinov - -```php -// REST API endpoint za vrtenje kolesa -function spin_wheel_endpoint() { - register_rest_route('wheel-of-fortune/v1', '/spin', array( - 'methods' => 'POST', - 'callback' => 'process_wheel_spin', - 'permission_callback' => function() { - return is_user_logged_in(); - } - )); -} -add_action('rest_api_init', 'spin_wheel_endpoint'); - -function process_wheel_spin($request) { - $user_id = get_current_user_id(); - $spins = get_user_meta($user_id, '_wheel_spins', true) ?: 0; - - if ($spins <= 0) { - return new WP_Error('no_spins', 'Nimate več razpoložljivih vrtljajev', array('status' => 403)); - } - - // Preveri časovni interval med vrtljaji - global $wpdb; - $table_name = $wpdb->prefix . 'wheel_spins'; - $last_spin = $wpdb->get_var($wpdb->prepare( - "SELECT last_spin_date FROM $table_name WHERE user_id = %d", - $user_id - )); - - $cooldown_minutes = get_option('wheel_cooldown_minutes', 0); - if ($cooldown_minutes > 0 && $last_spin) { - $time_diff = (strtotime(current_time('mysql')) - strtotime($last_spin)) / 60; - if ($time_diff < $cooldown_minutes) { - return new WP_Error( - 'cooldown', - sprintf('Počakajte še %d minut pred naslednjim vrtenjem', ceil($cooldown_minutes - $time_diff)), - array('status' => 403) - ); - } - } - - // Izberi nagrado glede na verjetnosti - $prize = select_random_prize($prizes); - - // Posodobi število spinov - update_user_meta($user_id, '_wheel_spins', $spins - 1); - - // Posodobi tabelo wp_wheel_spins - $wpdb->update( - $table_name, - array( - 'spins_available' => $spins - 1, - 'last_spin_date' => current_time('mysql') - ), - array('user_id' => $user_id) - ); - - // Zabeleži zadetek - $log_table = $wpdb->prefix . 'wheel_log'; - $wpdb->insert( - $log_table, - array( - 'user_id' => $user_id, - 'prize_id' => $prize['id'], - 'spin_date' => current_time('mysql'), - 'redeemed' => 0 - ) - ); - - // Pošlji email obvestilo (če je vključeno) - if (get_option('wheel_send_emails', false)) { - send_prize_email($user_id, $prize); - } - - return array( - 'success' => true, - 'prize' => $prize, - 'remaining_spins' => $spins - 1, - 'degree' => calculate_prize_degree($prize['id'], $prizes) - ); -} - -function calculate_prize_degree($prize_id, $prizes) { - // Izračunaj stopinje za animacijo kolesa - $prize_index = 0; - foreach ($prizes as $index => $prize) { - if ($prize['id'] == $prize_id) { - $prize_index = $index; - break; - } - } - - $segment_size = 360 / count($prizes); - $degree = 360 - ($prize_index * $segment_size + $segment_size / 2); - // Dodaj naključno odstopanje znotraj segmenta - $degree += mt_rand(-($segment_size / 4), $segment_size / 4); - // Dodaj še nekaj obratov - $degree += 360 * 5; - - return $degree; -} -``` - -## Administratorske nastavitve - -- 📦 Seznam izdelkov, ki podeljujejo spin (ID + št. spinov) -- 🎁 Nastavitev nagrad (ime, opis, slika, verjetnost, barva) -- 🗳️ Max. spinov na uporabnika -- 🕒 Časovna omejitev (1 spin na X minut) -- 🔧 Reset spinov / bonus kampanje -- 📧 Nastavitve email obvestil - -```php -function wheel_admin_menu() { - add_menu_page( - 'Kolo Sreče', - 'Kolo Sreče', - 'manage_options', - 'wheel-of-fortune', - 'wheel_settings_page', - 'dashicons-marker', - 30 - ); - - add_submenu_page( - 'wheel-of-fortune', - 'Nastavitve', - 'Nastavitve', - 'manage_options', - 'wheel-of-fortune', - 'wheel_settings_page' - ); - - add_submenu_page( - 'wheel-of-fortune', - 'Nagrade', - 'Nagrade', - 'manage_options', - 'wheel-prizes', - 'wheel_prizes_page' - ); - - add_submenu_page( - 'wheel-of-fortune', - 'Statistika', - 'Statistika', - 'manage_options', - 'wheel-stats', - 'wheel_stats_page' - ); -} -add_action('admin_menu', 'wheel_admin_menu'); -``` - -## Kratka koda - -Kolo lahko prikažemo kjerkoli s pomočjo: - -``` -[wheel_of_fortune] -``` - -Možne opcije: - -``` -[wheel_of_fortune size="large" theme="dark"] -``` - -Implementacija kratke kode: - -```php -function wheel_shortcode($atts) { - $atts = shortcode_atts(array( - 'size' => 'medium', // small, medium, large - 'theme' => 'light', // light, dark - ), $atts); - - // Preveri, če je uporabnik prijavljen - if (!is_user_logged_in()) { - return '
Za sodelovanje v nagradni igri se morate prijaviti.
'; - } - - // Preveri, če ima uporabnik na voljo spine - $user_id = get_current_user_id(); - $spins = get_user_meta($user_id, '_wheel_spins', true) ?: 0; - - if ($spins <= 0) { - return '
Trenutno nimate razpoložljivih vrtljajev. Kupite izdelke za pridobitev novih vrtljajev.
'; - } - - // Naloži potrebne skripte in stile - wp_enqueue_style('wheel-of-fortune-css', plugin_dir_url(__FILE__) . 'assets/css/wheel.css'); - wp_enqueue_script('wheel-of-fortune-js', plugin_dir_url(__FILE__) . 'assets/js/wheel.js', array('jquery'), '1.0', true); - - wp_localize_script('wheel-of-fortune-js', 'wheelSettings', array( - 'ajaxUrl' => rest_url('wheel-of-fortune/v1/spin'), - 'nonce' => wp_create_nonce('wp_rest'), - 'prizes' => get_wheel_prizes(), - 'remainingSpins' => $spins - )); - - // Vrni HTML za kolo - ob_start(); - include plugin_dir_path(__FILE__) . 'templates/wheel.php'; - return ob_get_clean(); -} -add_shortcode('wheel_of_fortune', 'wheel_shortcode'); -``` - -## Varnost in integracije - -- CSRF zaščita za AJAX klice (WordPress nonce) -- Preverjanje uporabniške prijave -- Integracija z WooCommerce -- Možna integracija z emailing sistemom (npr. MailPoet, Sendinblue) -- Preverjanje vlog in pravic uporabnikov -- Sanitizacija vnosov in validacija podatkov - -## Možne razširitve - -- 🎟️ Shranjevanje zgodovine zadetkov (WP tabelo wp_wheel_log) -- 🏆 Leaderboard: največ zadetkov / spinov -- 🔗 Integracija z zvestobnim sistemom (CLIP) -- 📱 Mobilna verzija (PWA) -- 🔔 Push obvestila za bonus spin -- 📊 Napredna analitika in poročila -- 🌐 Večjezična podpora - -## Implementacija kolesa - -Glede na priloženo sliko, bo kolo implementirano kot interaktivna SVG ali Canvas komponenta z naslednjimi lastnostmi: - -### Struktura kolesa -- Kolo je razdeljeno na več segmentov (8-12) -- Vsak segment ima svojo barvo (modra, rdeča, siva) -- Segmenti vsebujejo besedilo z nagrado -- Kazalec na vrhu označuje zmagovalni segment - -### CSS in JS implementacija - -```css -.wheel-container { - position: relative; - width: 100%; - max-width: 500px; - margin: 0 auto; -} - -.wheel { - width: 100%; - height: auto; - transform-origin: center; - transition: transform 5s cubic-bezier(0.17, 0.67, 0.83, 0.67); -} - -.wheel-pointer { - position: absolute; - top: 0; - left: 50%; - transform: translateX(-50%); - width: 40px; - height: 40px; - z-index: 10; -} - -.wheel-segment { - transform-origin: center; -} - -.wheel-segment-blue { - fill: #5DADE2; -} - -.wheel-segment-red { - fill: #E74C3C; -} - -.wheel-segment-gray { - fill: #D5D8DC; -} - -.wheel-text { - font-family: Arial, sans-serif; - font-size: 14px; - font-weight: bold; - fill: white; - text-anchor: middle; -} - -.wheel-button { - display: block; - margin: 20px auto; - padding: 10px 20px; - background-color: #E74C3C; - color: white; - border: none; - border-radius: 5px; - font-size: 16px; - cursor: pointer; -} - -.wheel-button:disabled { - background-color: #D5D8DC; - cursor: not-allowed; -} - -.wheel-spins-counter { - text-align: center; - margin-top: 10px; - font-size: 16px; -} - -.wheel-result { - text-align: center; - margin-top: 20px; - padding: 15px; - border-radius: 5px; - display: none; -} - -.wheel-result.win { - background-color: #D5F5E3; - border: 1px solid #2ECC71; -} -``` - -```javascript -jQuery(document).ready(function($) { - var wheel = $('.wheel'); - var button = $('.wheel-button'); - var result = $('.wheel-result'); - var spinning = false; - - button.on('click', function() { - if (spinning) return; - - spinning = true; - button.prop('disabled', true); - result.hide(); - - $.ajax({ - url: wheelSettings.ajaxUrl, - method: 'POST', - beforeSend: function(xhr) { - xhr.setRequestHeader('X-WP-Nonce', wheelSettings.nonce); - }, - success: function(response) { - if (response.success) { - // Animiraj kolo - wheel.css({ - 'transform': 'rotate(' + response.degree + 'deg)' - }); - - // Posodobi števec spinov - $('.wheel-spins-counter span').text(response.remaining_spins); - - // Prikaži rezultat po končani animaciji - setTimeout(function() { - result.html('

Čestitamo!

Zadeli ste: ' + response.prize.name + '

'); - result.addClass('win').show(); - spinning = false; - - if (response.remaining_spins > 0) { - button.prop('disabled', false); - } - }, 5500); - } - }, - error: function(xhr) { - var errorMsg = 'Prišlo je do napake.'; - if (xhr.responseJSON && xhr.responseJSON.message) { - errorMsg = xhr.responseJSON.message; - } - - result.html('

' + errorMsg + '

'); - result.removeClass('win').show(); - spinning = false; - button.prop('disabled', false); - } - }); - }); -}); -``` - -### SVG implementacija kolesa - -```php -function generate_wheel_svg($prizes) { - $num_prizes = count($prizes); - $angle = 360 / $num_prizes; - $svg = ''; - - // Center point - $cx = 250; - $cy = 250; - $radius = 240; - - // Generate segments - for ($i = 0; $i < $num_prizes; $i++) { - $prize = $prizes[$i]; - $start_angle = $i * $angle; - $end_angle = ($i + 1) * $angle; - - $start_rad = deg2rad($start_angle); - $end_rad = deg2rad($end_angle); - - $x1 = $cx + $radius * cos($start_rad); - $y1 = $cy + $radius * sin($start_rad); - $x2 = $cx + $radius * cos($end_rad); - $y2 = $cy + $radius * sin($end_rad); - - // Determine if the arc should be drawn as a large arc - $large_arc = $end_angle - $start_angle > 180 ? 1 : 0; - - // Create the path for the segment - $path = "M{$cx},{$cy} L{$x1},{$y1} A{$radius},{$radius} 0 {$large_arc},1 {$x2},{$y2} Z"; - - // Determine the color class - $color_class = ''; - switch ($prize['color']) { - case 'blue': - $color_class = 'wheel-segment-blue'; - break; - case 'red': - $color_class = 'wheel-segment-red'; - break; - default: - $color_class = 'wheel-segment-gray'; - } - - $svg .= ""; - - // Add text to the segment - $text_angle = $start_angle + ($angle / 2); - $text_rad = deg2rad($text_angle); - $text_x = $cx + ($radius * 0.75) * cos($text_rad); - $text_y = $cy + ($radius * 0.75) * sin($text_rad); - - // Rotate text to be readable - $text_rotate = $text_angle; - if ($text_angle > 90 && $text_angle < 270) { - $text_rotate += 180; - } - - $svg .= "{$prize['name']}"; - } - - // Add center circle - $svg .= ""; - - $svg .= ''; - return $svg; -} -``` - -## Namestitev in uporaba - -### Namestitev vtičnika - -1. Prenesite ZIP datoteko vtičnika -2. V WordPress administratorski plošči pojdite na "Vtičniki" > "Dodaj nov" -3. Kliknite "Naloži vtičnik" in izberite preneseno ZIP datoteko -4. Aktivirajte vtičnik - -### Konfiguracija - -1. V administratorski plošči pojdite na "Kolo Sreče" > "Nastavitve" -2. Konfigurirajte izdelke, ki podeljujejo spine -3. Nastavite časovne omejitve in druge možnosti - -### Dodajanje nagrad - -1. Pojdite na "Kolo Sreče" > "Nagrade" -2. Dodajte nove nagrade z imenom, opisom, verjetnostjo in barvo -3. Zagotovite, da skupna vsota verjetnosti ne presega 1.0 - -### Prikaz kolesa na strani - -Dodajte kratko kodo `[wheel_of_fortune]` na želeno stran ali prispevek. - -Za prilagoditev videza uporabite dodatne parametre: -``` -[wheel_of_fortune size="large" theme="dark"] -``` - -### Prilagajanje velikosti kolesa - -Če želite dodatno prilagoditi velikost kolesa, da bo vse prišlo na zaslon, lahko uporabite parameter `scale`: - -- `scale-90`: Zmanjša kolo na 90% privzete velikosti -- `scale-80`: Zmanjša kolo na 80% privzete velikosti -- `scale-70`: Zmanjša kolo na 70% privzete velikosti -- `scale-60`: Zmanjša kolo na 60% privzete velikosti -- `scale-50`: Zmanjša kolo na 50% privzete velikosti - -Primer: `[wheel_of_fortune scale="scale-70"]` - -### Dodeljevanje spinov uporabnikom - -Spine lahko uporabnikom dodelite na naslednje načine: - -1. **Avtomatsko ob nakupu**: Nastavite število spinov za posamezne izdelke v nastavitvah vtičnika -2. **Ročno**: Uporabite stran "Uporabniki" v meniju "Kolo sreče" za ročno dodeljevanje spinov - -## Nastavitve - -### Splošne nastavitve - -- **Zahtevaj prijavo**: Če je omogočeno, morajo biti uporabniki prijavljeni za vrtenje kolesa -- **Dovoli samo en spin na dan**: Če je omogočeno, lahko uporabniki zavrtijo kolo samo enkrat na dan -- **Pošlji e-pošto ob zmagi**: Če je omogočeno, se uporabniku pošlje e-pošta ob zmagi - -### Nagrade - -Upravljajte z nagradami, ki jih lahko uporabniki zadanejo: - -- **Ime**: Ime nagrade -- **Opis**: Opis nagrade -- **Verjetnost**: Verjetnost, da uporabnik zadane to nagrado (v odstotkih) -- **Barva**: Barva segmenta na kolesu -- **Aktivna**: Ali je nagrada aktivna - -### Izdelki - -Nastavite število spinov, ki jih uporabnik prejme ob nakupu določenih izdelkov: - -1. Vnesite ID izdelka -2. Nastavite število spinov -3. Kliknite "Dodaj izdelek" - -## Statistika - -Spremljajte statistiko vrtljajev in zadetkov: - -- **Skupno število vrtljajev**: Skupno število vrtljajev vseh uporabnikov -- **Zadetki po nagradah**: Število zadetkov za vsako nagrado -- **Uporabniki z največ vrtljaji**: Seznam uporabnikov z največ vrtljaji - -## Razvijalcem - -### Filtri - -Vtičnik ponuja naslednje filtre za prilagajanje: - -- `wheel_of_fortune_prizes`: Prilagodite seznam nagrad -- `wheel_of_fortune_spin_result`: Prilagodite rezultat vrtljaja -- `wheel_of_fortune_email_content`: Prilagodite vsebino e-pošte ob zmagi - -### Akcije - -Vtičnik ponuja naslednje akcije: - -- `wheel_of_fortune_before_spin`: Izvede se pred vrtljajem -- `wheel_of_fortune_after_spin`: Izvede se po vrtljaju -- `wheel_of_fortune_prize_won`: Izvede se, ko uporabnik zadane nagrado - -## Pogosta vprašanja - -### Kolo se ne prikaže na strani - -Preverite naslednje: - -1. Ali je kratka koda pravilno vstavljena? -2. Ali so nagrade nastavljene in aktivne? -3. Ali je uporabnik prijavljen (če je to zahtevano)? -4. Ali ima uporabnik na voljo spine? -5. Poskusite zmanjšati velikost kolesa z uporabo parametra `scale`: `[wheel_of_fortune scale="scale-80"]` - -### Uporabnik ne prejme spinov ob nakupu - -Preverite naslednje: - -1. Ali je WooCommerce aktiviran? -2. Ali je izdelek pravilno nastavljen v nastavitvah vtičnika? -3. Ali je naročilo označeno kot "Zaključeno"? - -## Podpora - -Za podporo nas kontaktirajte na support@example.com \ No newline at end of file +**Avtor:** Mark Poljansek +**Verzija:** 1.0.1 \ No newline at end of file diff --git a/admin/edit-wheel-page.php b/admin/edit-wheel-page.php index 32235b9..8df0bac 100644 --- a/admin/edit-wheel-page.php +++ b/admin/edit-wheel-page.php @@ -20,6 +20,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { $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( @@ -35,8 +36,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { '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', '%s', '%s', '%f', '%d', '%s', '%d', '%f', '%s', '%s', '%d'] ); if ($result !== false) { @@ -62,6 +64,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { $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( @@ -77,9 +80,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { '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', '%s', '%s', '%f', '%d', '%s', '%d', '%f', '%s', '%s', '%d'], ['%d'] ); @@ -231,6 +235,17 @@ if (class_exists('WooCommerce')) {
+ +
+

: + + + +

+
+

@@ -359,6 +374,7 @@ if (class_exists('WooCommerce')) {

+

{user_name}, {prize_name}, etc.

@@ -417,4 +433,41 @@ jQuery(document).ready(function($) { }); }); }); + + + \ No newline at end of file diff --git a/admin/js/admin.js b/admin/js/admin.js index ba20346..8bf5494 100644 --- a/admin/js/admin.js +++ b/admin/js/admin.js @@ -71,6 +71,7 @@ jQuery(document).ready(function($) { $('#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 { @@ -100,6 +101,7 @@ jQuery(document).ready(function($) { 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 diff --git a/admin/partials/prize-form-fields.php b/admin/partials/prize-form-fields.php index fab7749..dbf8335 100644 --- a/admin/partials/prize-form-fields.php +++ b/admin/partials/prize-form-fields.php @@ -66,4 +66,11 @@ if (!defined('ABSPATH')) exit;

+ + + + + +

+ \ No newline at end of file diff --git a/admin/settings-page.php b/admin/settings-page.php index 00ba747..31b5c8c 100644 --- a/admin/settings-page.php +++ b/admin/settings-page.php @@ -8,6 +8,8 @@ if (!defined('ABSPATH')) { exit; } +global $wpdb; + // Preveri, če ima uporabnik pravice za dostop if (!current_user_can('manage_options')) { return; @@ -67,6 +69,10 @@ if (isset($_POST['wheel_save_settings']) && check_admin_referer('wheel_settings_ } 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'); } @@ -79,6 +85,11 @@ $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'); ?> @@ -118,6 +129,20 @@ settings_errors('wheel_settings');

+ + + + + + +

+ + diff --git a/admin/stats-page.php b/admin/stats-page.php index 62b9d30..6f47051 100644 --- a/admin/stats-page.php +++ b/admin/stats-page.php @@ -81,6 +81,41 @@ if (isset($_POST['mark_redeemed']) && isset($_POST['prize_id'])) { '

'; } +// 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; @@ -106,6 +141,22 @@ if ($selected_user_id > 0) {

+ +
+

+ : + +

+ + + +
+

+
+
@@ -132,9 +183,45 @@ if ($selected_user_id > 0) {
+

- + +
+

+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
@@ -254,4 +341,200 @@ if ($selected_user_id > 0) {

- \ No newline at end of file + + + \ No newline at end of file diff --git a/assets/css/wheel.css b/assets/css/wheel.css index 82a4e59..b021691 100644 --- a/assets/css/wheel.css +++ b/assets/css/wheel.css @@ -128,12 +128,12 @@ transition: all 0.2s; } -/* Simple gradients for cross-browser compatibility */ -.wheel-segment-yellow { fill: #ffdd00; } -.wheel-segment-green { fill: #88ff00; } -.wheel-segment-red { fill: #ff5500; } -.wheel-segment-pink { fill: #ff44aa; } -.wheel-segment-blue { fill: #00ccff; } +/* 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 { @@ -301,7 +301,7 @@ align-items: center; justify-content: center; margin: 20px auto 0 auto; - padding: 0 40px; + padding: 0 40px !important; background: linear-gradient(135deg, #ff00c4, #00dfe9); color: #fff; border: none; @@ -316,7 +316,15 @@ height: 56px; min-width: 140px; letter-spacing: 2px; + line-height: normal !important; text-shadow: 0 2px 8px #000, 0 0 10px #0cf101; + -webkit-appearance: none; + appearance: none; +} + +/* Prevent accidental
tags inside the button from breaking centering */ +.wheel-button br { + display: none; } /* Button hover effect */ @@ -631,4 +639,4 @@ .wheel-container.scale-50 { transform: scale(0.5); -} \ No newline at end of file +} diff --git a/assets/js/wheel.js b/assets/js/wheel.js index ec60fb5..2d13c31 100644 --- a/assets/js/wheel.js +++ b/assets/js/wheel.js @@ -67,7 +67,13 @@ jQuery(document).ready(function($) { // Prikaz nagrade function showPrizePopup(prize) { - var html = '

' + l10n.congratulations + '

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

'; + 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 + '

'; } diff --git a/code_export.txt b/code_export.txt deleted file mode 100644 index 63fada5..0000000 --- a/code_export.txt +++ /dev/null @@ -1,6876 +0,0 @@ -"./README.md" : -""" -# Wheel of Fortune - WordPress Plugin - -Vtičnik "Kolo Sreče" (Wheel of Fortune) omogoča uporabnikom vrtenje kolesa za nagrade. Povezan je z WooCommerce za dodeljevanje spinov ob nakupu izdelkov. - -## Funkcionalnosti - -- Prikaz interaktivnega kolesa sreče preko kratke kode [wheel_of_fortune] -- Povezava z WooCommerce za dodeljevanje spinov ob nakupu izdelkov -- Upravljanje z nagradami, verjetnostmi in barvami -- Statistika vrtljajev in zadetkov - -## Namestitev - -1. Prenesite datoteke vtičnika v mapo `/wp-content/plugins/wheel-of-fortune/` -2. Aktivirajte vtičnik preko menija 'Vtičniki' v WordPress skrbniški plošči -3. Nastavite vtičnik preko menija 'Kolo sreče' v skrbniški plošči - -## Uporaba - -### Osnovna uporaba - -Vstavite kratko kodo `[wheel_of_fortune]` na katerokoli stran ali prispevek, kjer želite prikazati kolo sreče. - -### Parametri kratke kode - -Kratka koda sprejme naslednje parametre: - -- `size`: Velikost kolesa (možnosti: `small`, `medium`, `large`, privzeto: `medium`) -- `theme`: Tema kolesa (možnosti: `light`, `dark`, privzeto: `light`) -- `scale`: Dodatno prilagajanje velikosti (možnosti: `scale-90`, `scale-80`, `scale-70`, `scale-60`, `scale-50`, privzeto: prazen) - -#### Primeri - -``` -[wheel_of_fortune] -[wheel_of_fortune size="large" theme="dark"] -[wheel_of_fortune size="medium" theme="light" scale="scale-80"] -``` - -## Vsebina -1. [Namen vtičnika](#namen-vtičnika) -2. [Ključne funkcionalnosti](#ključne-funkcionalnosti) -3. [Tehnični opis](#tehnični-opis) -4. [Logika delovanja](#logika-delovanja) -5. [Administratorske nastavitve](#administratorske-nastavitve) -6. [Kratka koda](#kratka-koda) -7. [Varnost in integracije](#varnost-in-integracije) -8. [Možne razširitve](#možne-razširitve) -9. [Implementacija kolesa](#implementacija-kolesa) -10. [Namestitev in uporaba](#namestitev-in-uporaba) - -## Namen vtičnika - -Vtičnik »Kolo Sreče« omogoča obiskovalcem spletne strani, da zavrtijo interaktivno kolo in prejmejo naključno nagrado. Vrtljaji so vezani na nakupe določenih izdelkov in uporabniške račune, kar omogoča gamificirano izkušnjo zvestobe in nagrajevanja. - -## Ključne funkcionalnosti - -- 🎯 Naključni izbor nagrade ob vrtenju kolesa -- 🔐 Vezava spinov (vrtljajev) na uporabniški račun -- 🛒 Samodejna dodelitev spinov ob nakupu določenih izdelkov -- 🧠 Nastavljive nagrade in verjetnosti zadetkov -- 📊 Spremljanje števila preostalih spinov -- 🎛️ Kratka koda za vključitev kolesa kjerkoli v WordPress vsebino -- 📬 Email obvestila ob zadetkih (opcijsko) -- 📈 Administratorska statistika: število zadetkov, porabljenih spinov ipd. - -## Tehnični opis - -### Arhitektura -- WordPress Plugin: zgrajen po WordPress standardih (object-oriented, namespacing, varnostni hooki) -- Frontend: JavaScript za animacijo kolesa, AJAX za komunikacijo z backendom -- Backend: PHP (WP REST API), povezano z WP uporabniki in WooCommerce naročili -- Baza podatkov: uporaba wp_usermeta, lastne tabele wp_wheel_spins in wp_wheel_prizes - -### Tabele podatkovne baze - -#### wp_wheel_spins - -| Polje | Tip | Opis | -|-------|-----|------| -| id | INT | Primarni ključ | -| user_id | INT | ID uporabnika (WordPress) | -| spins_available | INT | Število razpoložljivih spinov | -| last_spin_date | DATETIME | Datum zadnjega vrtenja | - -#### wp_wheel_prizes - -| Polje | Tip | Opis | -|-------|-----|------| -| id | INT | Primarni ključ | -| name | VARCHAR(255) | Ime nagrade | -| description | TEXT | Opis nagrade | -| probability | FLOAT | Verjetnost zadetka (0-1) | -| color | VARCHAR(20) | Barva segmenta (HEX ali RGB) | -| is_active | TINYINT | Ali je nagrada aktivna | -| image_url | VARCHAR(255) | URL do slike nagrade (opcijsko) | - -#### wp_wheel_log - -| Polje | Tip | Opis | -|-------|-----|------| -| id | INT | Primarni ključ | -| user_id | INT | ID uporabnika | -| prize_id | INT | ID zadete nagrade | -| spin_date | DATETIME | Datum in čas vrtenja | -| redeemed | TINYINT | Ali je nagrada unovčena | - -## Logika delovanja - -### Podeljevanje spinov - -Ob zaključenem nakupu določenega izdelka (npr. ID = 123), WooCommerce hook (woocommerce_order_status_completed) sproži funkcijo za dodelitev spinov. - -```php -function assign_spins_on_purchase($order_id) { - $order = wc_get_order($order_id); - foreach ($order->get_items() as $item) { - $product_id = $item->get_product_id(); - $spin_products = get_option('wheel_spin_products', array()); - - if (array_key_exists($product_id, $spin_products)) { - $user_id = $order->get_user_id(); - $current_spins = get_user_meta($user_id, '_wheel_spins', true) ?: 0; - $new_spins = $current_spins + $spin_products[$product_id]; - update_user_meta($user_id, '_wheel_spins', $new_spins); - - // Posodobi tudi v tabeli wp_wheel_spins - global $wpdb; - $table_name = $wpdb->prefix . 'wheel_spins'; - - $existing = $wpdb->get_var($wpdb->prepare( - "SELECT id FROM $table_name WHERE user_id = %d", - $user_id - )); - - if ($existing) { - $wpdb->update( - $table_name, - array('spins_available' => $new_spins), - array('user_id' => $user_id) - ); - } else { - $wpdb->insert( - $table_name, - array( - 'user_id' => $user_id, - 'spins_available' => $new_spins, - 'last_spin_date' => current_time('mysql') - ) - ); - } - } - } -} -add_action('woocommerce_order_status_completed', 'assign_spins_on_purchase'); -``` - -### Vrtenje kolesa - -1. Uporabnik klikne gumb "Zavrti" -2. JS animira kolo (CSS transformacija) -3. Medtem AJAX zahteva iz strežnika rezultat -4. PHP backend izbere nagrado glede na verjetnosti -5. Backend zabeleži zadetek, zmanjša število spinov - -```php -// REST API endpoint za vrtenje kolesa -function spin_wheel_endpoint() { - register_rest_route('wheel-of-fortune/v1', '/spin', array( - 'methods' => 'POST', - 'callback' => 'process_wheel_spin', - 'permission_callback' => function() { - return is_user_logged_in(); - } - )); -} -add_action('rest_api_init', 'spin_wheel_endpoint'); - -function process_wheel_spin($request) { - $user_id = get_current_user_id(); - $spins = get_user_meta($user_id, '_wheel_spins', true) ?: 0; - - if ($spins <= 0) { - return new WP_Error('no_spins', 'Nimate več razpoložljivih vrtljajev', array('status' => 403)); - } - - // Preveri časovni interval med vrtljaji - global $wpdb; - $table_name = $wpdb->prefix . 'wheel_spins'; - $last_spin = $wpdb->get_var($wpdb->prepare( - "SELECT last_spin_date FROM $table_name WHERE user_id = %d", - $user_id - )); - - $cooldown_minutes = get_option('wheel_cooldown_minutes', 0); - if ($cooldown_minutes > 0 && $last_spin) { - $time_diff = (strtotime(current_time('mysql')) - strtotime($last_spin)) / 60; - if ($time_diff < $cooldown_minutes) { - return new WP_Error( - 'cooldown', - sprintf('Počakajte še %d minut pred naslednjim vrtenjem', ceil($cooldown_minutes - $time_diff)), - array('status' => 403) - ); - } - } - - // Izberi nagrado glede na verjetnosti - $prize = select_random_prize($prizes); - - // Posodobi število spinov - update_user_meta($user_id, '_wheel_spins', $spins - 1); - - // Posodobi tabelo wp_wheel_spins - $wpdb->update( - $table_name, - array( - 'spins_available' => $spins - 1, - 'last_spin_date' => current_time('mysql') - ), - array('user_id' => $user_id) - ); - - // Zabeleži zadetek - $log_table = $wpdb->prefix . 'wheel_log'; - $wpdb->insert( - $log_table, - array( - 'user_id' => $user_id, - 'prize_id' => $prize['id'], - 'spin_date' => current_time('mysql'), - 'redeemed' => 0 - ) - ); - - // Pošlji email obvestilo (če je vključeno) - if (get_option('wheel_send_emails', false)) { - send_prize_email($user_id, $prize); - } - - return array( - 'success' => true, - 'prize' => $prize, - 'remaining_spins' => $spins - 1, - 'degree' => calculate_prize_degree($prize['id'], $prizes) - ); -} - -function calculate_prize_degree($prize_id, $prizes) { - // Izračunaj stopinje za animacijo kolesa - $prize_index = 0; - foreach ($prizes as $index => $prize) { - if ($prize['id'] == $prize_id) { - $prize_index = $index; - break; - } - } - - $segment_size = 360 / count($prizes); - $degree = 360 - ($prize_index * $segment_size + $segment_size / 2); - // Dodaj naključno odstopanje znotraj segmenta - $degree += mt_rand(-($segment_size / 4), $segment_size / 4); - // Dodaj še nekaj obratov - $degree += 360 * 5; - - return $degree; -} -``` - -## Administratorske nastavitve - -- 📦 Seznam izdelkov, ki podeljujejo spin (ID + št. spinov) -- 🎁 Nastavitev nagrad (ime, opis, slika, verjetnost, barva) -- 🗳️ Max. spinov na uporabnika -- 🕒 Časovna omejitev (1 spin na X minut) -- 🔧 Reset spinov / bonus kampanje -- 📧 Nastavitve email obvestil - -```php -function wheel_admin_menu() { - add_menu_page( - 'Kolo Sreče', - 'Kolo Sreče', - 'manage_options', - 'wheel-of-fortune', - 'wheel_settings_page', - 'dashicons-marker', - 30 - ); - - add_submenu_page( - 'wheel-of-fortune', - 'Nastavitve', - 'Nastavitve', - 'manage_options', - 'wheel-of-fortune', - 'wheel_settings_page' - ); - - add_submenu_page( - 'wheel-of-fortune', - 'Nagrade', - 'Nagrade', - 'manage_options', - 'wheel-prizes', - 'wheel_prizes_page' - ); - - add_submenu_page( - 'wheel-of-fortune', - 'Statistika', - 'Statistika', - 'manage_options', - 'wheel-stats', - 'wheel_stats_page' - ); -} -add_action('admin_menu', 'wheel_admin_menu'); -``` - -## Kratka koda - -Kolo lahko prikažemo kjerkoli s pomočjo: - -``` -[wheel_of_fortune] -``` - -Možne opcije: - -``` -[wheel_of_fortune size="large" theme="dark"] -``` - -Implementacija kratke kode: - -```php -function wheel_shortcode($atts) { - $atts = shortcode_atts(array( - 'size' => 'medium', // small, medium, large - 'theme' => 'light', // light, dark - ), $atts); - - // Preveri, če je uporabnik prijavljen - if (!is_user_logged_in()) { - return ''; - } - - // Preveri, če ima uporabnik na voljo spine - $user_id = get_current_user_id(); - $spins = get_user_meta($user_id, '_wheel_spins', true) ?: 0; - - if ($spins <= 0) { - return '
Trenutno nimate razpoložljivih vrtljajev. Kupite izdelke za pridobitev novih vrtljajev.
'; - } - - // Naloži potrebne skripte in stile - wp_enqueue_style('wheel-of-fortune-css', plugin_dir_url(__FILE__) . 'assets/css/wheel.css'); - wp_enqueue_script('wheel-of-fortune-js', plugin_dir_url(__FILE__) . 'assets/js/wheel.js', array('jquery'), '1.0', true); - - wp_localize_script('wheel-of-fortune-js', 'wheelSettings', array( - 'ajaxUrl' => rest_url('wheel-of-fortune/v1/spin'), - 'nonce' => wp_create_nonce('wp_rest'), - 'prizes' => get_wheel_prizes(), - 'remainingSpins' => $spins - )); - - // Vrni HTML za kolo - ob_start(); - include plugin_dir_path(__FILE__) . 'templates/wheel.php'; - return ob_get_clean(); -} -add_shortcode('wheel_of_fortune', 'wheel_shortcode'); -``` - -## Varnost in integracije - -- CSRF zaščita za AJAX klice (WordPress nonce) -- Preverjanje uporabniške prijave -- Integracija z WooCommerce -- Možna integracija z emailing sistemom (npr. MailPoet, Sendinblue) -- Preverjanje vlog in pravic uporabnikov -- Sanitizacija vnosov in validacija podatkov - -## Možne razširitve - -- 🎟️ Shranjevanje zgodovine zadetkov (WP tabelo wp_wheel_log) -- 🏆 Leaderboard: največ zadetkov / spinov -- 🔗 Integracija z zvestobnim sistemom (CLIP) -- 📱 Mobilna verzija (PWA) -- 🔔 Push obvestila za bonus spin -- 📊 Napredna analitika in poročila -- 🌐 Večjezična podpora - -## Implementacija kolesa - -Glede na priloženo sliko, bo kolo implementirano kot interaktivna SVG ali Canvas komponenta z naslednjimi lastnostmi: - -### Struktura kolesa -- Kolo je razdeljeno na več segmentov (8-12) -- Vsak segment ima svojo barvo (modra, rdeča, siva) -- Segmenti vsebujejo besedilo z nagrado -- Kazalec na vrhu označuje zmagovalni segment - -### CSS in JS implementacija - -```css -.wheel-container { - position: relative; - width: 100%; - max-width: 500px; - margin: 0 auto; -} - -.wheel { - width: 100%; - height: auto; - transform-origin: center; - transition: transform 5s cubic-bezier(0.17, 0.67, 0.83, 0.67); -} - -.wheel-pointer { - position: absolute; - top: 0; - left: 50%; - transform: translateX(-50%); - width: 40px; - height: 40px; - z-index: 10; -} - -.wheel-segment { - transform-origin: center; -} - -.wheel-segment-blue { - fill: #5DADE2; -} - -.wheel-segment-red { - fill: #E74C3C; -} - -.wheel-segment-gray { - fill: #D5D8DC; -} - -.wheel-text { - font-family: Arial, sans-serif; - font-size: 14px; - font-weight: bold; - fill: white; - text-anchor: middle; -} - -.wheel-button { - display: block; - margin: 20px auto; - padding: 10px 20px; - background-color: #E74C3C; - color: white; - border: none; - border-radius: 5px; - font-size: 16px; - cursor: pointer; -} - -.wheel-button:disabled { - background-color: #D5D8DC; - cursor: not-allowed; -} - -.wheel-spins-counter { - text-align: center; - margin-top: 10px; - font-size: 16px; -} - -.wheel-result { - text-align: center; - margin-top: 20px; - padding: 15px; - border-radius: 5px; - display: none; -} - -.wheel-result.win { - background-color: #D5F5E3; - border: 1px solid #2ECC71; -} -``` - -```javascript -jQuery(document).ready(function($) { - var wheel = $('.wheel'); - var button = $('.wheel-button'); - var result = $('.wheel-result'); - var spinning = false; - - button.on('click', function() { - if (spinning) return; - - spinning = true; - button.prop('disabled', true); - result.hide(); - - $.ajax({ - url: wheelSettings.ajaxUrl, - method: 'POST', - beforeSend: function(xhr) { - xhr.setRequestHeader('X-WP-Nonce', wheelSettings.nonce); - }, - success: function(response) { - if (response.success) { - // Animiraj kolo - wheel.css({ - 'transform': 'rotate(' + response.degree + 'deg)' - }); - - // Posodobi števec spinov - $('.wheel-spins-counter span').text(response.remaining_spins); - - // Prikaži rezultat po končani animaciji - setTimeout(function() { - result.html('

Čestitamo!

Zadeli ste: ' + response.prize.name + '

'); - result.addClass('win').show(); - spinning = false; - - if (response.remaining_spins > 0) { - button.prop('disabled', false); - } - }, 5500); - } - }, - error: function(xhr) { - var errorMsg = 'Prišlo je do napake.'; - if (xhr.responseJSON && xhr.responseJSON.message) { - errorMsg = xhr.responseJSON.message; - } - - result.html('

' + errorMsg + '

'); - result.removeClass('win').show(); - spinning = false; - button.prop('disabled', false); - } - }); - }); -}); -``` - -### SVG implementacija kolesa - -```php -function generate_wheel_svg($prizes) { - $num_prizes = count($prizes); - $angle = 360 / $num_prizes; - $svg = ''; - - // Center point - $cx = 250; - $cy = 250; - $radius = 240; - - // Generate segments - for ($i = 0; $i < $num_prizes; $i++) { - $prize = $prizes[$i]; - $start_angle = $i * $angle; - $end_angle = ($i + 1) * $angle; - - $start_rad = deg2rad($start_angle); - $end_rad = deg2rad($end_angle); - - $x1 = $cx + $radius * cos($start_rad); - $y1 = $cy + $radius * sin($start_rad); - $x2 = $cx + $radius * cos($end_rad); - $y2 = $cy + $radius * sin($end_rad); - - // Determine if the arc should be drawn as a large arc - $large_arc = $end_angle - $start_angle > 180 ? 1 : 0; - - // Create the path for the segment - $path = "M{$cx},{$cy} L{$x1},{$y1} A{$radius},{$radius} 0 {$large_arc},1 {$x2},{$y2} Z"; - - // Determine the color class - $color_class = ''; - switch ($prize['color']) { - case 'blue': - $color_class = 'wheel-segment-blue'; - break; - case 'red': - $color_class = 'wheel-segment-red'; - break; - default: - $color_class = 'wheel-segment-gray'; - } - - $svg .= ""; - - // Add text to the segment - $text_angle = $start_angle + ($angle / 2); - $text_rad = deg2rad($text_angle); - $text_x = $cx + ($radius * 0.75) * cos($text_rad); - $text_y = $cy + ($radius * 0.75) * sin($text_rad); - - // Rotate text to be readable - $text_rotate = $text_angle; - if ($text_angle > 90 && $text_angle < 270) { - $text_rotate += 180; - } - - $svg .= "{$prize['name']}"; - } - - // Add center circle - $svg .= ""; - - $svg .= ''; - return $svg; -} -``` - -## Namestitev in uporaba - -### Namestitev vtičnika - -1. Prenesite ZIP datoteko vtičnika -2. V WordPress administratorski plošči pojdite na "Vtičniki" > "Dodaj nov" -3. Kliknite "Naloži vtičnik" in izberite preneseno ZIP datoteko -4. Aktivirajte vtičnik - -### Konfiguracija - -1. V administratorski plošči pojdite na "Kolo Sreče" > "Nastavitve" -2. Konfigurirajte izdelke, ki podeljujejo spine -3. Nastavite časovne omejitve in druge možnosti - -### Dodajanje nagrad - -1. Pojdite na "Kolo Sreče" > "Nagrade" -2. Dodajte nove nagrade z imenom, opisom, verjetnostjo in barvo -3. Zagotovite, da skupna vsota verjetnosti ne presega 1.0 - -### Prikaz kolesa na strani - -Dodajte kratko kodo `[wheel_of_fortune]` na želeno stran ali prispevek. - -Za prilagoditev videza uporabite dodatne parametre: -``` -[wheel_of_fortune size="large" theme="dark"] -``` - -### Prilagajanje velikosti kolesa - -Če želite dodatno prilagoditi velikost kolesa, da bo vse prišlo na zaslon, lahko uporabite parameter `scale`: - -- `scale-90`: Zmanjša kolo na 90% privzete velikosti -- `scale-80`: Zmanjša kolo na 80% privzete velikosti -- `scale-70`: Zmanjša kolo na 70% privzete velikosti -- `scale-60`: Zmanjša kolo na 60% privzete velikosti -- `scale-50`: Zmanjša kolo na 50% privzete velikosti - -Primer: `[wheel_of_fortune scale="scale-70"]` - -### Dodeljevanje spinov uporabnikom - -Spine lahko uporabnikom dodelite na naslednje načine: - -1. **Avtomatsko ob nakupu**: Nastavite število spinov za posamezne izdelke v nastavitvah vtičnika -2. **Ročno**: Uporabite stran "Uporabniki" v meniju "Kolo sreče" za ročno dodeljevanje spinov - -## Nastavitve - -### Splošne nastavitve - -- **Zahtevaj prijavo**: Če je omogočeno, morajo biti uporabniki prijavljeni za vrtenje kolesa -- **Dovoli samo en spin na dan**: Če je omogočeno, lahko uporabniki zavrtijo kolo samo enkrat na dan -- **Pošlji e-pošto ob zmagi**: Če je omogočeno, se uporabniku pošlje e-pošta ob zmagi - -### Nagrade - -Upravljajte z nagradami, ki jih lahko uporabniki zadanejo: - -- **Ime**: Ime nagrade -- **Opis**: Opis nagrade -- **Verjetnost**: Verjetnost, da uporabnik zadane to nagrado (v odstotkih) -- **Barva**: Barva segmenta na kolesu -- **Aktivna**: Ali je nagrada aktivna - -### Izdelki - -Nastavite število spinov, ki jih uporabnik prejme ob nakupu določenih izdelkov: - -1. Vnesite ID izdelka -2. Nastavite število spinov -3. Kliknite "Dodaj izdelek" - -## Statistika - -Spremljajte statistiko vrtljajev in zadetkov: - -- **Skupno število vrtljajev**: Skupno število vrtljajev vseh uporabnikov -- **Zadetki po nagradah**: Število zadetkov za vsako nagrado -- **Uporabniki z največ vrtljaji**: Seznam uporabnikov z največ vrtljaji - -## Razvijalcem - -### Filtri - -Vtičnik ponuja naslednje filtre za prilagajanje: - -- `wheel_of_fortune_prizes`: Prilagodite seznam nagrad -- `wheel_of_fortune_spin_result`: Prilagodite rezultat vrtljaja -- `wheel_of_fortune_email_content`: Prilagodite vsebino e-pošte ob zmagi - -### Akcije - -Vtičnik ponuja naslednje akcije: - -- `wheel_of_fortune_before_spin`: Izvede se pred vrtljajem -- `wheel_of_fortune_after_spin`: Izvede se po vrtljaju -- `wheel_of_fortune_prize_won`: Izvede se, ko uporabnik zadane nagrado - -## Pogosta vprašanja - -### Kolo se ne prikaže na strani - -Preverite naslednje: - -1. Ali je kratka koda pravilno vstavljena? -2. Ali so nagrade nastavljene in aktivne? -3. Ali je uporabnik prijavljen (če je to zahtevano)? -4. Ali ima uporabnik na voljo spine? -5. Poskusite zmanjšati velikost kolesa z uporabo parametra `scale`: `[wheel_of_fortune scale="scale-80"]` - -### Uporabnik ne prejme spinov ob nakupu - -Preverite naslednje: - -1. Ali je WooCommerce aktiviran? -2. Ali je izdelek pravilno nastavljen v nastavitvah vtičnika? -3. Ali je naročilo označeno kot "Zaključeno"? - -## Podpora - -Za podporo nas kontaktirajte na support@example.com """ - - -"./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']) : ''; - - 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, - ], - ['%d', '%s', '%s', '%f', '%d', '%s', '%d', '%f', '%s', '%s'] - ); - - 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']) : ''; - - 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, - ], - ['id' => $prize_id], - ['%d', '%s', '%s', '%f', '%d', '%s', '%d', '%f', '%s', '%s'], - ['%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(); - } - }); - - // --- 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', - nonce: wheel_admin_nonce.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); - - modal.show(); - } else { - alert(response.data.message || 'An error occurred.'); - } - }); - }); - - // --- Save Prize (via AJAX) --- - $('#edit-prize-form').on('submit', function(e) { - e.preventDefault(); - - // Pripravimo podatke iz forme - const formData = { - action: 'wheel_save_prize', - nonce: wheel_admin_nonce.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(), - }; - - // 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; - } - - // AJAX call to save the prize - $.post(ajaxurl, formData, function(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() { - 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', - nonce: wheel_admin_nonce.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', - nonce: wheel_admin_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'); - } - }); - }); - } -});""" - - -"./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); - - // 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()); - -// 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') . - '

'; -} - -// 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 25px rgba(0, 200, 255, 0.5)); - padding-top: 80px; - transition: transform 0.05s ease-out; -} - -/* Outer ring lighting effect */ -.wheel-wrapper::before { - content: ''; - position: absolute; - top: 0; - left: -5%; - width: 110%; - height: 110%; - border-radius: 50%; - background: radial-gradient( - circle at center, - rgba(0, 200, 255, 0.2) 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; - filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.25)); - border: 4px solid #222; - box-shadow: 0 0 10px 2px #00dfe9, 0 2px 8px rgba(0,0,0,0.3); - border-radius: 50%; - background: #111; -} - -/* Wheel frame */ -.wheel-frame { - fill: #0a4d6e; - stroke: #0a7bb5; - stroke-width: 8; - filter: url(#bevel); -} - -/* LED lighting on wheel frame */ -.wheel-light-track { - fill: none; - stroke: #00c8ff; - stroke-width: 6; - stroke-dasharray: 3, 8; - filter: url(#glow); - 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 segments */ -.wheel-segment { - transform-origin: center; - transition: all 0.3s ease; - filter: url(#innerShadow); -} - -/* Segment colors */ -.wheel-segment-yellow { - /* fill: url(#gradientYellow); */ -} - -.wheel-segment-green { - /* fill: url(#gradientGreen); */ -} - -.wheel-segment-red { - /* fill: url(#gradientRed); */ -} - -.wheel-segment-pink { - /* fill: url(#gradientPink); */ -} - -.wheel-segment-blue { - /* fill: url(#gradientBlue); */ -} - -/* Segment dividers */ -.wheel-divider { - fill: #303030; - stroke: #505050; - stroke-width: 1; - filter: url(#bevel); -} - -/* Pegs at the end of dividers */ -.wheel-peg { - fill: #e0e0e0; - stroke: #c0c0c0; - stroke-width: 1; - filter: url(#pegGlow); -} - -@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 */ -.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; - filter: url(#textShadow); - text-overflow: ellipsis; - max-width: 70px; -} - -/* Center hub */ -.wheel-hub-outer { - fill: #0a4d6e; - stroke: #0a7bb5; - stroke-width: 4; - filter: url(#bevel); -} - -.wheel-hub-inner { - fill: url(#hubGradient); - filter: url(#innerGlow); -} - -.wheel-hub-button { - fill: url(#buttonGradient); - filter: url(#buttonGlow); -} - -.wheel-hub-text { - font-family: 'Arial Rounded MT Bold', 'Arial', sans-serif; - font-size: 20px; - font-weight: bold; - fill: #ff00aa; - text-anchor: middle; - filter: url(#neonGlow); - animation: neonPulse 2s infinite alternate; -} - -@keyframes neonPulse { - from { - filter: url(#neonGlow); - text-shadow: 0 0 10px #ff00aa, 0 0 20px #ff00aa; - } - to { - filter: url(#neonGlowBright); - text-shadow: 0 0 15px #ff00aa, 0 0 25px #ff00aa, 0 0 30px #ff00aa; - } -} - -/* Fast neon pulse for spinning */ -@keyframes fastNeonPulse { - 0% { - filter: url(#neonGlow); - text-shadow: 0 0 10px #ff00aa, 0 0 20px #ff00aa; - } - 50% { - filter: url(#neonGlowBright); - text-shadow: 0 0 20px #ff00aa, 0 0 30px #ff00aa, 0 0 40px #ff00aa; - } - 100% { - filter: url(#neonGlow); - text-shadow: 0 0 10px #ff00aa, 0 0 20px #ff00aa; - } -} - -/* Pointer */ -.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 */ -.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 { - filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.8)); - } - to { - filter: drop-shadow(0 0 15px #ff00aa) drop-shadow(0 0 25px rgba(255, 255, 255, 0.9)); - } -} - -@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; - 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; - text-shadow: 0 2px 8px #000, 0 0 10px #0cf101; -} - -/* 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 - const wheelContainer = $('.wheel-container'); - const wheelElement = wheelContainer.find('.wheel'); - const spinButton = wheelContainer.find('.wheel-button'); - const resultDiv = wheelContainer.find('.wheel-result'); - const spinsCounter = wheelContainer.find('.wheel-spins-counter span'); - - // Stanje - let isSpinning = false; - let accumulatedRotation = 0; - let wheelSpins = wof_data.spins_left; - const l10n = wof_data.l10n; - - // Nastavitve animacije - const spinDuration = 8000; - const baseRotations = 5; - - // Funkcija za animacijo - function easeOutCubic(t) { - return 1 - Math.pow(1 - t, 3); - } - - function animateWheel(startTime, startRotation, totalRotation) { - const currentTime = Date.now(); - const elapsedTime = currentTime - startTime; - - if (elapsedTime >= spinDuration) { - wheelElement.css('transform', `rotate(${startRotation + totalRotation}deg)`); - accumulatedRotation = (startRotation + totalRotation); - finishSpin(); - return; - } - - const timeProgress = elapsedTime / spinDuration; - const rotationProgress = easeOutCubic(timeProgress); - const currentRotation = startRotation + (totalRotation * rotationProgress); - wheelElement.css('transform', `rotate(${currentRotation}deg)`); - - requestAnimationFrame(() => animateWheel(startTime, startRotation, totalRotation)); - } - - // Obdelava po koncu vrtenja - function finishSpin() { - const 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) { - let 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(); - - const finalAngle = response.data.degree; - const totalRotation = (360 * baseRotations) + finalAngle; - - animateWheel(Date.now(), accumulatedRotation, totalRotation); - - } else { - handleError(response.data.message || l10n.error); - } - }, - error: function(xhr) { - const 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')); - 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_wof_delete_wheel_product', array($this, 'ajax_delete_wheel_product')); - add_action('wp_ajax_wof_update_wheel_product_spins', array($this, 'ajax_update_wheel_product_spins')); - - // 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() { - error_log("Wheel of Fortune: Aktivacija plugina..."); - $this->create_database_tables(); - $this->run_migration(); - $this->set_default_options(); - $this->add_default_prizes(); - - // Debug: Preveri, ali se tabela za kolesa ustvari - global $wpdb; - $wheels_table = $wpdb->prefix . 'wof_wheels'; - $wheel_products_table = $wpdb->prefix . 'wheel_of_fortune_products'; - - $wheels_exists = $wpdb->get_var("SHOW TABLES LIKE '$wheels_table'") == $wheels_table; - $products_exists = $wpdb->get_var("SHOW TABLES LIKE '$wheel_products_table'") == $wheel_products_table; - - error_log("=== ACTIVATION DEBUG ==="); - error_log("Wheels table exists: " . ($wheels_exists ? 'YES' : 'NO')); - error_log("Products table exists: " . ($products_exists ? 'YES' : 'NO')); - - if (!$wheels_exists) { - error_log('Wheel of Fortune: Wheels table was not created properly during activation.'); - } else { - error_log('Wheel of Fortune: Wheels table created successfully.'); - } - - flush_rewrite_rules(); - error_log("Wheel of Fortune: Aktivacija končana."); - } - - public function deactivate() { - 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, - 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."); - } - } - - 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') - ); - - // 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')); - } - - 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; - } - } - - if (get_option('wheel_send_emails', true)) { - $this->send_prize_email($user_id, $prize); - } - - $response_data = [ 'prize' => $prize, 'degree' => $degree, 'remaining_spins' => $new_spins, 'discount_code' => $redemption_code ]; - - 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('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 - $segment_colors = ['#00dfe9', '#ff00c4', '#0cf101']; - 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', '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', '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; - - 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, - ]; - - $format = ['%d', '%s', '%s', '%f', '%d', '%s', '%d', '%s', '%s', '%f']; - - 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', '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')); - - 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)); - - // Dodatna nastavitev - $coupon->set_date_expires(strtotime('+30 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') - ); - - 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)); - $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) { - $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'); - 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; - - // Debug informacije - error_log("=== AJAX DELETE WHEEL PRODUCT DEBUG ==="); - error_log("POST data: " . print_r($_POST, true)); - error_log("ID to delete: " . $id); - - if ($id > 0) { - $table = $wpdb->prefix . 'wheel_of_fortune_products'; - error_log("Table: " . $table); - - $result = $wpdb->delete($table, ['id' => $id], ['%d']); - error_log("Delete 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 brisanju.', 'wheel-of-fortune')); - } -} - -// Initialize plugin -function wheel_of_fortune() { - return WheelOfFortune::get_instance(); -} -wheel_of_fortune(); -wheel_of_fortune();""" - - diff --git a/export_code.sh b/export_code.sh old mode 100644 new mode 100755 index a0428ad..1a37df6 --- a/export_code.sh +++ b/export_code.sh @@ -1,26 +1,58 @@ #!/bin/bash -# Set the output file +# Nastavitev imena izhodne datoteke OUTPUT_FILE="code_export.txt" -# Remove the output file if it exists +# Odstrani izhodno datoteko, če že obstaja, da začnemo s čisto datoteko if [ -f "$OUTPUT_FILE" ]; then rm "$OUTPUT_FILE" fi -# Find all files except hidden files and directories -find . -type f -not -path "*/\.*" -not -path "*node_modules*" -not -path "*vendor*" | sort | while read -r file; do - # Skip the output file itself and this script +# Poišči vse datoteke, razen skritih datotek in določenih map (npr. node_modules). +# Dodane so izjeme za slike, videoposnetke, PDF-je, arhive in druge binarne datoteke +# neposredno v ukaz 'find' za boljšo zmogljivost. +find . -type f \ + -not -path "*/\.*" \ + -not -path "*node_modules*" \ + -not -path "*vendor*" \ + -not -path "*dist*" \ + -not -path "*build*" \ + -not -path "*/images/*" \ + -not -iname "*.jpg" -not -iname "*.jpeg" \ + -not -iname "*.png" -not -iname "*.gif" \ + -not -iname "*.bmp" -not -iname "*.tiff" \ + -not -iname "*.svg" -not -iname "*.ico" \ + -not -iname "*.webp" \ + -not -iname "*.mp4" -not -iname "*.mov" \ + -not -iname "*.avi" -not -iname "*.mkv" \ + -not -iname "*.webm" \ + -not -iname "*.mp3" -not -iname "*.wav" \ + -not -iname "*.ogg" -not -iname "*.flac" \ + -not -iname "*.pdf" \ + -not -iname "*.zip" \ + -not -iname "*.tar" \ + -not -iname "*.gz" \ + -not -iname "*.bz2" \ + -not -iname "*.rar" \ + -not -iname "*.7z" \ + -not -iname "*.doc" -not -iname "*.docx" \ + -not -iname "*.xls" -not -iname "*.xlsx" \ + -not -iname "*.ppt" -not -iname "*.pptx" \ + -not -iname "*.eot" -not -iname "*.ttf" \ + -not -iname "*.woff" -not -iname "*.woff2" \ + | sort | while read -r file; do + + # Preskoči samo izhodno datoteko in to skripto if [[ "$file" == "./$OUTPUT_FILE" || "$file" == "./export_code.sh" ]]; then continue fi - # Skip binary files and the output file itself - if file "$file" | grep -q "binary"; then + # Dodatna varnostna preverba: preskoči slikovne datoteke po MIME tipu + if file --mime-type -b "$file" | grep -qiE '^(image)/'; then continue fi - # Add the filename and content to the output file + # Dodaj ime datoteke in njeno vsebino v izhodno datoteko echo "\"$file\" : " >> "$OUTPUT_FILE" echo "\"\"\"" >> "$OUTPUT_FILE" cat "$file" >> "$OUTPUT_FILE" @@ -29,4 +61,4 @@ find . -type f -not -path "*/\.*" -not -path "*node_modules*" -not -path "*vendo echo "" >> "$OUTPUT_FILE" done -echo "Code export completed. Output saved to $OUTPUT_FILE" \ No newline at end of file +echo "Izvoz kode končan. Vsebina je shranjena v datoteko $OUTPUT_FILE" diff --git a/wheel-of-fortune.php b/wheel-of-fortune.php index 5036c92..1d6657b 100644 --- a/wheel-of-fortune.php +++ b/wheel-of-fortune.php @@ -105,6 +105,12 @@ class WheelOfFortune { 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')); @@ -125,8 +131,10 @@ class WheelOfFortune { 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_wof_update_wheel_product_spins', array($this, 'ajax_update_wheel_product_spins')); + 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()) { @@ -226,35 +234,22 @@ class WheelOfFortune { } public function activate() { - error_log("Wheel of Fortune: Aktivacija plugina..."); $this->create_database_tables(); $this->run_migration(); $this->set_default_options(); - $this->add_default_prizes(); - // Debug: Preveri, ali se tabela za kolesa ustvari - global $wpdb; - $wheels_table = $wpdb->prefix . 'wof_wheels'; - $wheel_products_table = $wpdb->prefix . 'wheel_of_fortune_products'; - - $wheels_exists = $wpdb->get_var("SHOW TABLES LIKE '$wheels_table'") == $wheels_table; - $products_exists = $wpdb->get_var("SHOW TABLES LIKE '$wheel_products_table'") == $wheel_products_table; - - error_log("=== ACTIVATION DEBUG ==="); - error_log("Wheels table exists: " . ($wheels_exists ? 'YES' : 'NO')); - error_log("Products table exists: " . ($products_exists ? 'YES' : 'NO')); - - if (!$wheels_exists) { - error_log('Wheel of Fortune: Wheels table was not created properly during activation.'); - } else { - error_log('Wheel of Fortune: Wheels table created successfully.'); + // Dodaj cron opravilo za dnevne spine + if (!wp_next_scheduled('wheel_daily_spins_cron')) { + wp_schedule_event(time(), 'wheel_daily', 'wheel_daily_spins_cron'); } - flush_rewrite_rules(); - error_log("Wheel of Fortune: Aktivacija končana."); + // 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(); } @@ -326,6 +321,7 @@ class WheelOfFortune { 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;"; @@ -385,6 +381,29 @@ class WheelOfFortune { } 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() { @@ -537,11 +556,26 @@ class WheelOfFortune { 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'; } @@ -621,11 +655,18 @@ class WheelOfFortune { } } - if (get_option('wheel_send_emails', true)) { + // 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 ]; + $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); } @@ -830,7 +871,8 @@ class WheelOfFortune { $text_paths = ''; // Generate segments and text paths - $segment_colors = ['#00dfe9', '#ff00c4', '#0cf101']; + // 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; @@ -936,6 +978,7 @@ class WheelOfFortune { $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')]); @@ -963,9 +1006,10 @@ class WheelOfFortune { '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']; + $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']); @@ -1270,7 +1314,8 @@ class WheelOfFortune { 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) { @@ -1298,12 +1343,9 @@ class WheelOfFortune { $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('+30 days')); - + // 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); @@ -1320,7 +1362,6 @@ class WheelOfFortune { // Metoda 4: Neposredno vstavljanje v podatkovno bazo try { global $wpdb; - // Ustvari nov post tipa shop_coupon $wpdb->insert( $wpdb->posts, @@ -1335,9 +1376,7 @@ class WheelOfFortune { 'post_date_gmt' => current_time('mysql', 1) ) ); - $coupon_id = $wpdb->insert_id; - if ($coupon_id) { // Dodaj meta podatke za kupon $meta_data = array( @@ -1347,9 +1386,10 @@ class WheelOfFortune { 'usage_limit' => '1', 'usage_limit_per_user' => '1', 'customer_email' => serialize(array($user->user_email)), - 'description' => __('Wheel of Fortune', 'wheel-of-fortune') + '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, @@ -1360,7 +1400,6 @@ class WheelOfFortune { ) ); } - return $coupon_id; } } catch (Exception $e) { @@ -1381,8 +1420,9 @@ class WheelOfFortune { $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; } @@ -1403,6 +1443,12 @@ class WheelOfFortune { * @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."); @@ -1524,33 +1570,123 @@ class WheelOfFortune { } public function ajax_delete_wheel_product() { - check_ajax_referer('wof_delete_wheel_product'); + check_ajax_referer('wof_delete_wheel_product', '_ajax_nonce'); + if (!current_user_can('manage_options')) { - wp_send_json_error(__('Nimaš dovoljenja.', 'wheel-of-fortune')); + wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'wheel-of-fortune')]); } - global $wpdb; + $id = isset($_POST['id']) ? intval($_POST['id']) : 0; - // Debug informacije - error_log("=== AJAX DELETE WHEEL PRODUCT DEBUG ==="); - error_log("POST data: " . print_r($_POST, true)); - error_log("ID to delete: " . $id); + if (!$id) { + wp_send_json_error(['message' => __('Invalid ID.', 'wheel-of-fortune')]); + } - if ($id > 0) { - $table = $wpdb->prefix . 'wheel_of_fortune_products'; - error_log("Table: " . $table); - - $result = $wpdb->delete($table, ['id' => $id], ['%d']); - error_log("Delete 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(); + 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."); } } - wp_send_json_error(__('Napaka pri brisanju.', 'wheel-of-fortune')); } + + // ---------------------------- } // Initialize plugin