<?php
if (!defined('_PS_VERSION_')) {
exit;
}

class OffreSpecialeBadge extends Module
{
const BADGE_TYPES = array('BF10', 'BF20', 'BF30');

public function __construct()
{
$this->name = 'offrespecialebadge';
$this->tab = 'front_office_features';
$this->version = '1.0.1';
$this->author = 'Greg';
$this->need_instance = 0;

parent::__construct();

$this->displayName = $this->l('Offre Spéciale Badge');
$this->description = $this->l('Ajoute des badges d\'offre spéciale (BF10, BF20, BF30) sur des produits spécifiques');
}

public function install()
{
// Créer la table
$sql = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'offre_speciale_products` (
`id_product` int(11) NOT NULL,
`badge_type` varchar(10) NOT NULL,
`date_add` datetime NOT NULL,
PRIMARY KEY (`id_product`),
KEY `badge_type` (`badge_type`)
) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;';

return parent::install()
&& Db::getInstance()->execute($sql)
&& $this->registerHook('displayProductListReviews')
&& $this->registerHook('displayProductAdditionalInfo')
&& $this->registerHook('header')
&& $this->registerHook('displayAfterBodyOpeningTag')
&& $this->registerHook('actionOutputHTMLBefore');
}

public function uninstall()
{
// Supprimer la table
$sql = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'offre_speciale_products`';

return parent::uninstall()
&& Db::getInstance()->execute($sql);
}

public function hookHeader()
{
// Ajouter le CSS
$this->context->controller->addCSS($this->_path . 'views/css/offrespeciale.css');

// Ajouter le JavaScript pour gérer les shortcodes côté client
$this->context->controller->addJS($this->_path . 'views/js/offrespeciale.js');

// Récupérer TOUS les badges pour injection en JavaScript
$allBadges = $this->getAllBadgesForJS();

// Injecter les données des badges en JavaScript
$idProduct = 0;
if (isset($this->context->controller->php_self) && $this->context->controller->php_self == 'product') {
$idProduct = (int)Tools::getValue('id_product');
}

$badgeData = $this->getBadgeForProduct($idProduct);

Media::addJsDef(array(
'offreSpecialeBadgeData' => array(
'currentProductId' => $idProduct,
'currentBadge' => $badgeData,
'allBadges' => $allBadges,
'ajaxUrl' => $this->context->link->getModuleLink('offrespecialebadge', 'ajax', array(), true)
)
));
}

/**
* Récupère tous les badges pour injection JavaScript
*/
private function getAllBadgesForJS()
{
$badges = Db::getInstance()->executeS('
SELECT id_product, badge_type
FROM `' . _DB_PREFIX_ . 'offre_speciale_products`
');

$badgesArray = array();
if ($badges) {
foreach ($badges as $badge) {
$badgesArray[$badge['id_product']] = $badge['badge_type'];
}
}

return $badgesArray;
}

/**
* Hook pour remplacer les shortcodes dans le HTML avant affichage
*/
public function hookActionOutputHTMLBefore($params)
{
if (isset($params['html'])) {
$params['html'] = $this->processShortcodes($params['html']);
}
}

/**
* Traite les shortcodes dans le contenu HTML
*/
private function processShortcodes($html)
{
// Pattern pour {offre_badge} ou {offre_badge id=123}
$pattern = '/\{offre_badge(?:\s+id=(\d+))?\}/';

$html = preg_replace_callback($pattern, function($matches) {
$idProduct = isset($matches[1]) ? (int)$matches[1] : 0;

// Si pas d'ID fourni, essayer de récupérer le produit actuel
if (!$idProduct && isset($this->context->controller->php_self) && $this->context->controller->php_self == 'product') {
$idProduct = (int)Tools::getValue('id_product');
}

if ($idProduct) {
return $this->displayBadge($idProduct);
}

return '<span class="offre-badge-placeholder" data-product-id="current"></span>';
}, $html);

return $html;
}

private function getBadgeForProduct($productId)
{
if (!$productId) {
return null;
}

$badge = Db::getInstance()->getRow('
SELECT badge_type
FROM `' . _DB_PREFIX_ . 'offre_speciale_products`
WHERE id_product = ' . (int)$productId
);

return $badge ? $badge['badge_type'] : null;
}

public function getContent()
{
$output = '';

// Traitement de l'ajout de produits
if (Tools::isSubmit('submitOffreSpecialeProducts')) {
$badgeType = Tools::getValue('BADGE_TYPE');
$products = Tools::getValue('OFFRE_PRODUCTS');

if (!in_array($badgeType, self::BADGE_TYPES)) {
$output .= $this->displayError($this->l('Type de badge invalide'));
} else {
$result = $this->addProducts($products, $badgeType);

if ($result['success']) {
$output .= $this->displayConfirmation(
sprintf($this->l('%d produit(s) ajouté(s) avec le badge %s'), $result['added'], $badgeType)
);
}
if ($result['errors'] > 0) {
$output .= $this->displayWarning(
sprintf($this->l('%d produit(s) déjà présent(s) ou invalide(s)'), $result['errors'])
);
}
}
}

// Traitement de la suppression d'un badge spécifique
if (Tools::isSubmit('submitClearBadge')) {
$badgeType = Tools::getValue('clear_badge_type');
if ($this->clearBadgeProducts($badgeType)) {
$output .= $this->displayConfirmation(
sprintf($this->l('Tous les produits du badge %s ont été supprimés'), $badgeType)
);
}
}

// Traitement de la suppression totale
if (Tools::isSubmit('submitClearAll')) {
if ($this->clearAllProducts()) {
$output .= $this->displayConfirmation($this->l('Tous les produits ont été supprimés'));
}
}

// Traitement de la suppression d'un produit spécifique
if (Tools::isSubmit('deleteProduct')) {
$idProduct = (int)Tools::getValue('id_product');
if ($this->removeProduct($idProduct)) {
$output .= $this->displayConfirmation($this->l('Produit supprimé'));
}
}

return $output . $this->displayShortcodeInfo() . $this->displayForm() . $this->displayCurrentProducts();
}

public function displayShortcodeInfo()
{
$html = '<div class="alert alert-info">
<h4><i class="icon-code"></i> ' . $this->l('Utilisation des shortcodes dans Creative Elements') . '</h4>
<p><strong>' . $this->l('Pour afficher le badge d\'un produit spécifique :') . '</strong></p>
<code>{offre_badge id=5139}</code>
<p style="margin-top: 10px;"><strong>' . $this->l('Pour afficher le badge du produit actuel (page produit) :') . '</strong></p>
<code>{offre_badge}</code>
<p style="margin-top: 10px;"><strong>' . $this->l('Alternative avec balise HTML (fonctionne partout) :') . '</strong></p>
<code>&lt;span class="offre-badge-shortcode" data-product-id="5139"&gt;&lt;/span&gt;</code>
<p style="margin-top: 5px;"><em>' . $this->l('Pour le produit actuel :') . '</em></p>
<code>&lt;span class="offre-badge-shortcode" data-product-id="current"&gt;&lt;/span&gt;</code>
</div>';

return $html;
}

public function displayForm()
{
$badgeOptions = array();
foreach (self::BADGE_TYPES as $badge) {
$badgeOptions[] = array(
'id' => $badge,
'name' => $badge
);
}

$fields_form = array(
'form' => array(
'legend' => array(
'title' => $this->l('Ajouter des produits avec badge'),
'icon' => 'icon-tag'
),
'input' => array(
array(
'type' => 'select',
'label' => $this->l('Type de badge'),
'name' => 'BADGE_TYPE',
'required' => true,
'options' => array(
'query' => $badgeOptions,
'id' => 'id',
'name' => 'name'
)
),
array(
'type' => 'textarea',
'label' => $this->l('IDs des produits'),
'desc' => $this->l('Entrez les IDs séparés par des virgules, espaces ou retours à la ligne (ex: 1, 5, 12, 45)'),
'name' => 'OFFRE_PRODUCTS',
'rows' => 5,
'cols' => 60,
'required' => true,
),
),
'submit' => array(
'title' => $this->l('Ajouter les produits'),
'class' => 'btn btn-default pull-right',
'icon' => 'process-icon-plus'
),
'buttons' => array(
array(
'href' => AdminController::$currentIndex . '&configure=' . $this->name . '&submitClearAll=1&token=' . Tools::getAdminTokenLite('AdminModules'),
'title' => $this->l('Tout supprimer'),
'icon' => 'process-icon-eraser',
'class' => 'btn btn-danger',
'js' => 'return confirm(\'' . $this->l('Êtes-vous sûr de vouloir supprimer tous les produits ?') . '\');'
)
)
),
);

$helper = new HelperForm();
$helper->module = $this;
$helper->name_controller = $this->name;
$helper->token = Tools::getAdminTokenLite('AdminModules');
$helper->currentIndex = AdminController::$currentIndex . '&configure=' . $this->name;
$helper->submit_action = 'submitOffreSpecialeProducts';

return $helper->generateForm(array($fields_form));
}

public function displayCurrentProducts()
{
$html = '';

foreach (self::BADGE_TYPES as $badgeType) {
$products = $this->getProductsByBadge($badgeType);

$html .= '<div class="panel">
<div class="panel-heading">
<i class="icon-tag"></i> ' . sprintf($this->l('Produits avec badge %s'), $badgeType) . ' (' . count($products) . ')
<span class="panel-heading-action">
<a href="' . AdminController::$currentIndex . '&configure=' . $this->name
. '&submitClearBadge=1&clear_badge_type=' . $badgeType
. '&token=' . Tools::getAdminTokenLite('AdminModules') . '"
class="btn btn-default btn-sm"
onclick="return confirm(\'' . sprintf($this->l('Supprimer tous les produits du badge %s ?'), $badgeType) . '\');">
<i class="icon-eraser"></i> ' . $this->l('Vider ce badge') . '
</a>
</span>
</div>';

if (empty($products)) {
$html .= '<div class="alert alert-info" style="margin: 10px;">'
. sprintf($this->l('Aucun produit avec le badge %s'), $badgeType)
. '</div>';
} else {
$html .= '<table class="table"><thead><tr>
<th>ID</th>
<th>' . $this->l('Nom du produit') . '</th>
<th>' . $this->l('Référence') . '</th>
<th>' . $this->l('Date d\'ajout') . '</th>
<th>' . $this->l('Actions') . '</th>
</tr></thead><tbody>';

foreach ($products as $product) {
$productObj = new Product($product['id_product'], false, $this->context->language->id);
$html .= '<tr>
<td>' . (int)$product['id_product'] . '</td>
<td>' . ($productObj->name ?: $this->l('Produit introuvable')) . '</td>
<td>' . $productObj->reference . '</td>
<td>' . $product['date_add'] . '</td>
<td>
<a href="' . AdminController::$currentIndex . '&configure=' . $this->name
. '&deleteProduct=1&id_product=' . (int)$product['id_product']
. '&token=' . Tools::getAdminTokenLite('AdminModules') . '"
class="btn btn-default btn-sm" onclick="return confirm(\'' . $this->l('Supprimer ce produit ?') . '\');">
<i class="icon-trash"></i>
</a>
</td>
</tr>';
}

$html .= '</tbody></table>';
}

$html .= '</div>';
}

return $html;
}

private function addProducts($productsString, $badgeType)
{
// Nettoyer et extraire les IDs
$productsString = preg_replace('/[^0-9,\s\n\r]/', '', $productsString);
$productIds = preg_split('/[\s,\n\r]+/', $productsString, -1, PREG_SPLIT_NO_EMPTY);
$productIds = array_unique(array_map('intval', $productIds));

$added = 0;
$errors = 0;

foreach ($productIds as $idProduct) {
if ($idProduct <= 0) {
$errors++;
continue;
}

// Vérifier si le produit existe
if (!Product::existsInDatabase($idProduct, 'product')) {
$errors++;
continue;
}

// Supprimer l'ancien badge si existant
Db::getInstance()->delete('offre_speciale_products', 'id_product = ' . (int)$idProduct);

// Insérer le nouveau
$result = Db::getInstance()->insert('offre_speciale_products', array(
'id_product' => (int)$idProduct,
'badge_type' => pSQL($badgeType),
'date_add' => date('Y-m-d H:i:s')
));

if ($result) {
$added++;
} else {
$errors++;
}
}

return array('success' => $added > 0, 'added' => $added, 'errors' => $errors);
}

private function clearAllProducts()
{
return Db::getInstance()->execute('TRUNCATE TABLE `' . _DB_PREFIX_ . 'offre_speciale_products`');
}

private function clearBadgeProducts($badgeType)
{
return Db::getInstance()->delete('offre_speciale_products', 'badge_type = "' . pSQL($badgeType) . '"');
}

private function removeProduct($idProduct)
{
return Db::getInstance()->delete('offre_speciale_products', 'id_product = ' . (int)$idProduct);
}

private function getProductsByBadge($badgeType)
{
return Db::getInstance()->executeS('
SELECT id_product, badge_type, date_add
FROM `' . _DB_PREFIX_ . 'offre_speciale_products`
WHERE badge_type = "' . pSQL($badgeType) . '"
ORDER BY date_add DESC
');
}

public function hookDisplayProductListReviews($params)
{
if (!isset($params['product']['id_product'])) {
return;
}

return $this->displayBadge($params['product']['id_product']);
}

public function hookDisplayProductAdditionalInfo($params)
{
if (!isset($params['product']['id_product'])) {
return;
}

return $this->displayBadge($params['product']['id_product']);
}

public function displayBadge($productId)
{
$badge = Db::getInstance()->getRow('
SELECT badge_type
FROM `' . _DB_PREFIX_ . 'offre_speciale_products`
WHERE id_product = ' . (int)$productId
);

if ($badge && isset($badge['badge_type'])) {
$badgeType = $badge['badge_type'];
$badgeClass = 'badge-offre-' . strtolower($badgeType);

return '<span class="badge-offre-speciale ' . $badgeClass . '">' . $badgeType . '</span>';
}

return '';
}
}

Wingfoil – Ailes, Planches & Packs Complets | GlissEvolution

Wing Foil

Il y a 10 produits.

Les News du Blog Glissevolution

Wingfoil – Équipements & Packs Complets chez GlissEvolution

Wingfoil : La Nouvelle Sensation des Sports de Glisse

Le wingfoil (ou wingsurf) combine les atouts du windsurf, du kitesurf et du foil. Avec une aile gonflable et une planche équipée d’un foil, il permet de voler au-dessus de l’eau et d’éprouver une sensation de liberté unique.
Chez GlissEvolution, nous proposons une gamme complète d’équipements de wingfoil pour débutants comme pour experts : ailes, planches, foils, packs complets et accessoires.

À Qui S’Adresse le Wingfoil ?

Le wingfoil est un sport accessible à tous :

  • Débutants : planches stables et volumineuses pour un apprentissage facile.

  • Intermédiaires : optimisez vos transitions et explorez de nouveaux spots.

  • Experts : figures aériennes, carving et navigation engagée pour repousser vos limites.

Bien Choisir Son Aile de Wingfoil

L’aile est le moteur de votre équipement. Son choix dépend du vent et de votre pratique :

  • Taille : grandes ailes (jusqu’à 9m²) pour vent léger, petites ailes pour vent fort.

  • Rigidité et poids : une aile rigide offre précision et contrôle, une aile légère favorise maniabilité et confort.

  • Matériaux : tissus légers et durables pour performance et longévité.

Bien Choisir Sa Planche de Wingfoil

La planche influence la stabilité et la maniabilité :

  • Volume : jusqu’à 160L pour plus de flottabilité et de stabilité (idéal débutants).

  • Dimensions : planches larges et longues pour stabilité, modèles compacts pour agilité.

  • Matériaux : carbone, époxy ou fibre de verre pour un rapport solidité/poids optimal.

Les Foils de Wingfoil

Le foil permet à la planche de décoller et de glisser au-dessus de l’eau :

  • Mât : court pour débuter, long pour plus de portance et de contrôle.

  • Aile avant : grande surface pour stabilité, plus petite pour vitesse et maniabilité.

  • Matériaux : carbone (rigidité et légèreté) ou aluminium (robustesse et accessibilité).

Packs Complets Wingfoil

Simplifiez vos débuts avec nos packs complets wingfoil incluant aile, planche et foil. Ces ensembles équilibrés et assortis permettent de se lancer en toute sécurité avec un matériel adapté et un excellent rapport qualité-prix.

Accessoires de Sécurité & Confort

Pour profiter du wingfoil en toute confiance, équipez-vous avec :

  • Casques et gilets d’impact pour la sécurité.

  • Harnais pour soulager la traction de l’aile.

  • Leashs pour sécuriser planche et aile.

Nos Marques Partenaires

Nous travaillons avec les meilleures marques de wingfoil :
Duotone, F-ONE, Takuma, reconnues pour leur expertise, leur innovation et la qualité de leurs produits.

Pourquoi Choisir GlissEvolution ?

  • Une sélection experte des meilleures marques.

  • Des packs complets adaptés à chaque niveau.

  • Des conseils personnalisés par des passionnés.

  • Un blog de guides et tutos pour progresser pas à pas.

Découvrez dès maintenant notre sélection de matériel de wingfoil et vivez des sessions uniques, que vous soyez débutant ou confirmé.

Guides et tutos Wingfoil