<?php // Sicurezza first - blocca accesso diretto
if (!defined('ABSPATH')){exit;}
// Verifica che WooCommerce sia attivo
if (!in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
add_action('admin_notices', function(){echo '<div class="notice notice-error"><p><strong>WooCommerce Sale Percentage Pro</strong> richiede WooCommerce per funzionare!</p></div>';});return;
}
class WC_Sale_Percentage_Pro {
private $plugin_url;
private $plugin_path;
private $version;
public function __construct() {
$this->plugin_url = plugin_dir_url(//orma-store.com/wp-content/plugins/wooperc/assets/css/__FILE__);
$this->plugin_path = plugin_dir_path(__FILE__);
$this->version = '1.0.0';
// Hook principali
add_action('init', array($this, 'load_textdomain'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_assets'));
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'register_settings'));
add_action('wp_head', array($this, 'output_custom_styles'));
// Hook per sostituire "In offerta!"
add_filter('woocommerce_sale_flash', array($this, 'custom_sale_flash'), 10, 3);
} public function custom_sale_flash($html, $post, $product) {
// Gestione robusta dell'oggetto prodotto
if (!is_a($product, 'WC_Product')) {
$product = wc_get_product($post->ID);
}
// Controlli di sicurezza
if (!$product || !$product->is_on_sale()) {
return $html;
}
$settings = get_option('wcsp_settings', $this->get_default_settings());
$percentage = $this->calculate_discount_percentage($product);
if ($percentage <= 0) {
return $html;
}
// Formato del testo
$display_format = $settings['display_format'];
$badge_style = $settings['badge_style'];
$show_animation = $settings['show_animation'];
// Gestione prodotti variabili
if ($product->is_type('variable')) {
$display_text = str_replace('{percentage}', $percentage, $settings['variable_format']);
} else {
$display_text = str_replace('{percentage}', $percentage, $display_format);
}
// Classi CSS
$css_classes = array(
'onsale',
'wcsp-badge',
'wcsp-' . $badge_style
);
if ($show_animation === 'yes') {
$css_classes[] = 'wcsp-animated';
}
$html = '<span class="' . implode(' ', $css_classes) . '">' . esc_html($display_text) . '</span>';
return $html;
} private function calculate_discount_percentage($product) {
$percentage = 0;
if ($product->is_type('variable')) {
$percentages = array();
$variations = $product->get_available_variations();
foreach ($variations as $variation) {
$variation_obj = wc_get_product($variation['variation_id']);
if ($variation_obj && $variation_obj->is_on_sale()) {
$regular_price = (float) $variation_obj->get_regular_price();
$sale_price = (float) $variation_obj->get_sale_price();
if ($regular_price > 0 && $sale_price > 0 && $sale_price < $regular_price) {
$current_percentage = round((($regular_price - $sale_price) / $regular_price) * 100);
$percentages[] = $current_percentage;
}
}
}
if (!empty($percentages)) {
$percentage = max($percentages);
}
} else {
$regular_price = (float) $product->get_regular_price();
$sale_price = (float) $product->get_sale_price();
if ($regular_price > 0 && $sale_price > 0 && $sale_price < $regular_price) {
$percentage = round((($regular_price - $sale_price) / $regular_price) * 100);
}
}
return $percentage;
} public function load_textdomain() {
load_plugin_textdomain('wc-sale-percentage', false, dirname(plugin_basename(__FILE__)) . '/languages/');
} public function enqueue_frontend_assets() {
wp_enqueue_style('wcsp-frontend', $this->plugin_url . 'assets/css/frontend.css', array(), $this->version);
} public function add_admin_menu() {
add_options_page(
'Sale Percentage Pro',
'Sale Percentage',
'manage_options',
'wc-sale-percentage',
array($this, 'admin_page')
);
} public function register_settings() {
register_setting('wcsp_settings_group', 'wcsp_settings', array($this, 'sanitize_settings'));
add_settings_section('wcsp_main_section', 'Impostazioni Principali', null, 'wc-sale-percentage');
add_settings_field('display_format', 'Formato Prodotti Semplici', array($this, 'display_format_field'), 'wc-sale-percentage', 'wcsp_main_section');
add_settings_field('variable_format', 'Formato Prodotti Variabili', array($this, 'variable_format_field'), 'wc-sale-percentage', 'wcsp_main_section');
add_settings_field('badge_style', 'Stile Badge', array($this, 'badge_style_field'), 'wc-sale-percentage', 'wcsp_main_section');
add_settings_field('show_animation', 'Animazione', array($this, 'animation_field'), 'wc-sale-percentage', 'wcsp_main_section');
add_settings_field('custom_css', 'CSS Personalizzato', array($this, 'custom_css_field'), 'wc-sale-percentage', 'wcsp_main_section');
} public function display_format_field() {
$settings = get_option('wcsp_settings', $this->get_default_settings());
echo '<input type="text" name="wcsp_settings[display_format]" value="' . esc_attr($settings['display_format']) . '" class="regular-text" />';
echo '<p class="description">Usa {percentage} come placeholder. Es: "-{percentage}%" o "SCONTO {percentage}%"</p>';
}
public function variable_format_field() {
$settings = get_option('wcsp_settings', $this->get_default_settings());
echo '<input type="text" name="wcsp_settings[variable_format]" value="' . esc_attr($settings['variable_format']) . '" class="regular-text" />';
echo '<p class="description">Formato per prodotti variabili. Es: "Fino al {percentage}% di sconto!"</p>';
}
public function badge_style_field() {
$settings = get_option('wcsp_settings', $this->get_default_settings());
$styles = array(
'default' => 'Default',
'modern' => 'Moderno',
'minimal' => 'Minimale',
'vibrant' => 'Vibrante',
'gradient' => 'Gradiente'
);
echo '<select name="wcsp_settings[badge_style]">';
foreach ($styles as $key => $label) {
echo '<option value="' . esc_attr($key) . '"' . selected($settings['badge_style'], $key, false) . '>' . esc_html($label) . '</option>';
}
echo '</select>';
}
public function animation_field() {
$settings = get_option('wcsp_settings', $this->get_default_settings());
echo '<label><input type="checkbox" name="wcsp_settings[show_animation]" value="yes"' . checked($settings['show_animation'], 'yes', false) . ' /> Abilita animazione pulse</label>';
}
public function custom_css_field() {
$settings = get_option('wcsp_settings', $this->get_default_settings());
echo '<textarea name="wcsp_settings[custom_css]" rows="8" cols="50" class="large-text">' . esc_textarea($settings['custom_css']) . '</textarea>';
echo '<p class="description">CSS personalizzato per ulteriori modifiche al badge.</p>';
} public function sanitize_settings($input) {
$sanitized = array();
$sanitized['display_format'] = sanitize_text_field($input['display_format']);
$sanitized['variable_format'] = sanitize_text_field($input['variable_format']);
$sanitized['badge_style'] = sanitize_text_field($input['badge_style']);
$sanitized['show_animation'] = isset($input['show_animation']) ? 'yes' : 'no';
$sanitized['custom_css'] = sanitize_textarea_field($input['custom_css']);
return $sanitized;
} public function admin_page() {
?>
<div class="wrap">
<h1>🔥 Sale Percentage Pro</h1>
<p>Personalizza come vengono mostrate le percentuali di sconto sui tuoi prodotti!</p>
<form method="post" action="options.php">
<?php
settings_fields('wcsp_settings_group');
do_settings_sections('wc-sale-percentage');
submit_button('Salva Impostazioni', 'primary', 'submit', true, array('style' => 'background: #ff6b6b; border-color: #ff6b6b;'));
?>
</form>
<div class="wcsp-preview" style="margin-top: 30px; padding: 20px; border: 1px solid #ddd; background: #f9f9f9; border-radius: 8px;">
<h3>🎨 Anteprima Stili</h3>
<div style="display: flex; gap: 20px; flex-wrap: wrap; margin-top: 15px;">
<div style="text-align: center;">
<span class="onsale wcsp-badge wcsp-default" style="position: relative; display: inline-block;">-25%</span>
<p><strong>Default</strong></p>
</div>
<div style="text-align: center;">
<span class="onsale wcsp-badge wcsp-modern" style="position: relative; display: inline-block;">-25%</span>
<p><strong>Moderno</strong></p>
</div>
<div style="text-align: center;">
<span class="onsale wcsp-badge wcsp-minimal" style="position: relative; display: inline-block;">-25%</span>
<p><strong>Minimale</strong></p>
</div>
<div style="text-align: center;">
<span class="onsale wcsp-badge wcsp-vibrant" style="position: relative; display: inline-block;">-25%</span>
<p><strong>Vibrante</strong></p>
</div>
<div style="text-align: center;">
<span class="onsale wcsp-badge wcsp-gradient" style="position: relative; display: inline-block;">-25%</span>
<p><strong>Gradiente</strong></p>
</div>
</div>
</div>
</div>
<?php
} public function output_custom_styles() {
$settings = get_option('wcsp_settings', $this->get_default_settings());
if (!empty($settings['custom_css'])) {
echo '<style type="text/css">' . wp_strip_all_tags($settings['custom_css']) . '</style>';
}
} private function get_default_settings() {
return array(
'display_format' => '-{percentage}%',
'variable_format' => 'Fino al {percentage}%',
'badge_style' => 'default',
'show_animation' => 'yes',
'custom_css' => ''
);
}
}
// Inizializza il plugin
new WC_Sale_Percentage_Pro();
// Hook attivazione
register_activation_hook(__FILE__, function() {
add_option('wcsp_settings', array(
'display_format' => '-{percentage}%',
'variable_format' => 'Fino al {percentage}%',
'badge_style' => 'default',
'show_animation' => 'yes',
'custom_css' => ''
));
});
// Hook disattivazione
register_deactivation_hook(__FILE__, function() {
// Cleanup opzionale
});  .wcsp-badge {
display: inline-flex;
align-items: center;
justify-content: center;
text-align: center;
color: white;
border-radius: 50%;
position: absolute;
top: 10px;
left: 10px;
z-index: 999;
min-width: 48px;
min-height: 48px;
font-size: 16px;
font-weight: 700;
line-height: 1.2;
box-sizing: border-box;
transition: all 0.3s ease;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} .wcsp-badge.wcsp-default {
background: #ff6b6b;
border: 2px solid #ff5252;
box-shadow: 0 2px 8px rgba(255, 107, 107, 0.3);
} .wcsp-badge.wcsp-modern {
background: #2196F3;
border: none;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(33, 150, 243, 0.4);
font-weight: 600;
} .wcsp-badge.wcsp-minimal {
background: #333;
border: 1px solid #555;
border-radius: 4px;
font-weight: 400;
box-shadow: none;
} .wcsp-badge.wcsp-vibrant {
background: #FF9800;
border: 3px solid #FFB74D;
box-shadow: 0 0 20px rgba(255, 152, 0, 0.6);
animation: vibrant-glow 2s ease-in-out infinite alternate;
}
@keyframes vibrant-glow {
from { box-shadow: 0 0 20px rgba(255, 152, 0, 0.6); }
to { box-shadow: 0 0 30px rgba(255, 152, 0, 0.8); }
} .wcsp-badge.wcsp-gradient {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
position: relative;
overflow: hidden;
}
.wcsp-badge.wcsp-gradient::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
transition: left 0.5s;
}
.wcsp-badge.wcsp-gradient:hover::before {
left: 100%;
} .wcsp-badge.wcsp-animated {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
} .wcsp-badge:hover {
transform: scale(1.1);
} @media(max-width:768px){
.wcsp-badge {
min-width: 40px;
min-height: 40px;
font-size: 14px;
top: 8px;
left: 8px;
}
}
@media(max-width:480px){
.wcsp-badge {
min-width: 36px;
min-height: 36px;
font-size: 12px;
top: 6px;
left: 6px;
}
} .rtl .wcsp-badge {
left: auto;
right: 10px;
} @media (prefers-reduced-motion: reduce) {
.wcsp-badge.wcsp-animated, .wcsp-badge {
animation: none;
transition: none;
}
}  .wcsp-badge {
display: inline-flex;
align-items: center;
justify-content: center;
text-align: center;
color: white;
border-radius: 50px;
position: absolute;
top: 10px;
left: 10px;
z-index: 999;
width: 48px;
height: 48px;
min-width: 48px;
min-height: 48px;
font-size: 16px;
font-weight: 700;
line-height: 1.2;
padding: 8px;
box-sizing: border-box;
transition: all 0.3s ease;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} .wcsp-badge.wcsp-default {
background: #ff6b6b;
border: 2px solid #ff5252;
box-shadow: 0 2px 8px rgba(255, 107, 107, 0.3);
} .wcsp-badge.wcsp-modern {
background: #2196F3;
border: none;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(33, 150, 243, 0.4);
font-weight: 600;
} .wcsp-badge.wcsp-minimal {
background: #333;
border: 1px solid #555;
border-radius: 4px;
font-weight: 400;
box-shadow: none;
} .wcsp-badge.wcsp-vibrant {
background: #FF9800;
border: 3px solid #FFB74D;
box-shadow: 0 0 20px rgba(255, 152, 0, 0.6);
animation: vibrant-glow 2s ease-in-out infinite alternate;
}
@keyframes vibrant-glow {
from { box-shadow: 0 0 20px rgba(255, 152, 0, 0.6); }
to { box-shadow: 0 0 30px rgba(255, 152, 0, 0.8); }
} .wcsp-badge.wcsp-gradient {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
position: relative;
overflow: hidden;
}
.wcsp-badge.wcsp-gradient::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
transition: left 0.5s;
}
.wcsp-badge.wcsp-gradient:hover::before {
left: 100%;
} .wcsp-badge.wcsp-animated {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
} .wcsp-badge:hover {
transform: scale(1.1);
} @media(max-width:768px){
.wcsp-badge {
width: 40px;
height: 40px;
min-width: 40px;
min-height: 40px;
font-size: 14px;
top: 8px;
left: 8px;
padding: 6px;
}
}
@media(max-width:480px){
.wcsp-badge {
width: 36px;
height: 36px;
min-width: 36px;
min-height: 36px;
font-size: 12px;
top: 6px;
left: 6px;
padding: 4px;
}
} .rtl .wcsp-badge {
left: auto;
right: 10px;
} @media (prefers-reduced-motion: reduce) {
.wcsp-badge.wcsp-animated, .wcsp-badge {
animation: none;
transition: none;
}
}