dbManager = new AFES_DbManager(); add_action('admin_menu', array($this, 'add_menus_page_affiliation_table')); add_shortcode(AFES_Constants::TABLE_TAG, array($this, 'affieasy_table_content_callback')); add_shortcode(AFES_Constants::LINK_TAG, array($this, 'affieasy_link_content_callback')); add_action( 'template_redirect', array( __CLASS__, 'link_redirect' ) ); add_action('add_meta_boxes', array($this, 'register_quick_link_metabox')); add_action('admin_enqueue_scripts', array($this, 'enqueue_quick_link_script')); add_action('wp_ajax_afes_detect_webshop', array($this, 'ajax_detect_webshop')); add_action('wp_ajax_afes_quick_create_link', array($this, 'ajax_quick_create_link')); add_action('wp_enqueue_scripts', function () { wp_enqueue_style('dashicons'); wp_enqueue_style( 'rendering-style', plugins_url('/' . AFES_Utils::get_plugin_name() . '/css/rendering.css'), array(), AFES_Constants::PLUGIN_VERSION); }); } public static function initialize_affieasy_plugin() { $staticDbManager = AFES_DbManager::get_instance(); if (!$staticDbManager->table_exists(TABLE_WEBSHOP)) { $staticDbManager->create_table_webshop(); } if (!$staticDbManager->table_exists(TABLE_TABLE)) { $staticDbManager->create_table_table(); } if (!$staticDbManager->table_exists(TABLE_LINK)) { $staticDbManager->create_table_link(); } } public static function update_affieasy_plugin() { $staticDbManager = AFES_DbManager::get_instance(); $staticDbManager->update_table_webshop_encodeUrl(); // Update 1.2.0: creation champ productDomains $staticDbManager->update_table_webshop_productDomains(); } public function add_menus_page_affiliation_table() { add_menu_page( 'AffiEasy', 'AffiEasy', 'manage_options', 'affieasy-table', array($this, 'display_table_views'), 'dashicons-editor-table', 20 ); add_submenu_page( 'affieasy-table', esc_html__('Tables', 'affieasy'), esc_html__('Tables', 'affieasy'), 'manage_options', 'affieasy-table', array($this, 'display_table_views') ); add_submenu_page( 'affieasy-table', esc_html__('Affiliate links', 'affieasy'), esc_html__('Affiliate links', 'affieasy'), 'manage_options', 'affieasy-link', array($this, 'display_links_view') ); add_submenu_page( 'affieasy-table', esc_html__('Webshops', 'affieasy'), esc_html__('Webshops', 'affieasy'), 'manage_options', 'affieasy-webshop', array($this, 'display_webshop_views') ); } public function display_table_views() { if (is_admin() && current_user_can('manage_options')) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin routing for the plugin page view. $action = isset($_GET['action']) ? sanitize_key($_GET['action']) : null; switch ($action) { case 'edit-table': include(dirname(__DIR__) . '/views/admin/edit-table.php'); break; default: include(dirname(__DIR__) . '/views/admin/list-table.php'); break; } } } public function display_links_view() { if (is_admin() && current_user_can('manage_options')) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin page render with no state change. include(dirname(__DIR__) . '/views/admin/edit-links.php'); } } public function display_webshop_views() { if (is_admin() && current_user_can('manage_options')) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin routing for the plugin page view. $action = isset($_GET['action']) ? sanitize_key($_GET['action']) : null; switch ($action) { case 'edit-webshop': include(dirname(__DIR__) . '/views/admin/edit-webshop.php'); break; default: include(dirname(__DIR__) . '/views/admin/list-webshop.php'); break; } } } public function affieasy_table_content_callback($atts) { ob_start(); $table = $this->dbManager->get_table_by_id(intval($atts['id'])); if ($table->getId() == null) { ?>
dbManager->get_link_by_id(intval($atts['id'])); if (isset($link)) { AFES_GenerationUtils::generate_link($link); } return ob_get_clean(); } public static function link_redirect() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Front-end short-link redirect is intentionally public and read-only. $linkId = isset($_GET['affieasy-link']) ? absint(wp_unslash($_GET['affieasy-link'])) : 0; if ($linkId > 0) { $link = AFES_DbManager::get_instance()->get_link_by_id($linkId); if ($link->getId() === null) { global $wp_query; $wp_query->set_404(); status_header( 404 ); get_template_part( 404 ); } else { wp_redirect( $link->getUrl(), intval(301)); } exit(); } } public function register_quick_link_metabox() { $postTypes = array('post', 'page'); foreach ($postTypes as $postType) { add_meta_box( 'afes-quick-link', esc_html__('Créer un lien affilié', 'affieasy'), array($this, 'render_quick_link_metabox'), $postType, 'side', 'default' ); } } public function render_quick_link_metabox($post) { include(dirname(__DIR__) . '/views/admin/metabox-quick-link.php'); } public function enqueue_quick_link_script($hook) { if (!in_array($hook, array('post.php', 'post-new.php'))) { return; } $pluginName = AFES_Utils::get_plugin_name(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only current post context used to prefill the metabox. $postId = isset($_GET['post']) ? absint(wp_unslash($_GET['post'])) : 0; wp_enqueue_script( 'afes-quick-link-script', plugins_url('/' . $pluginName . '/js/quick-link.js'), array('jquery'), AFES_Constants::PLUGIN_VERSION, true ); wp_localize_script('afes-quick-link-script', 'afesQuickLink', array( 'ajaxUrl' => admin_url('admin-ajax.php'), 'detectWebshopNonce' => wp_create_nonce('afes_detect_webshop_nonce'), 'quickCreateNonce' => wp_create_nonce('afes_quick_create_link_nonce'), 'postId' => $postId, 'i18n' => array( 'shopDetected' => esc_html__('Boutique détectée', 'affieasy'), 'shopNotDetected' => esc_html__('Boutique non détectée', 'affieasy'), 'creating' => esc_html__('Création en cours…', 'affieasy'), 'createError' => esc_html__('Erreur lors de la création du lien', 'affieasy'), 'copied' => esc_html__('Copié !', 'affieasy'), 'copy' => esc_html__('Copier', 'affieasy'), 'copyFallback' => esc_html__('Appuyez sur Ctrl+C ou Cmd+C', 'affieasy'), ), )); } /** * AJAX: detect which webshop matches the hostname of a given product URL. * Returns {webshopId, webshopName} on match, or null data on no match. */ public function ajax_detect_webshop() { check_ajax_referer('afes_detect_webshop_nonce', 'nonce'); if (!current_user_can('edit_posts')) { $this->send_forbidden_error(); return; } $productUrl = isset($_POST['product_url']) ? esc_url_raw(wp_unslash($_POST['product_url'])) : ''; if (empty($productUrl)) { wp_send_json_success(null); return; } $webshop = $this->find_webshop_by_product_url($productUrl); if ($webshop !== null) { wp_send_json_success(array( 'webshopId' => $webshop->getId(), 'webshopName' => $webshop->getName(), )); return; } wp_send_json_success(null); } /** * AJAX: create an affiliate link from a product URL. * Auto-detects the webshop from the URL domain, optionally uses the post slug * (from post_id) to pre-fill category and click_ref. * Returns {id, shortUrl, shortcode}. */ public function ajax_quick_create_link() { check_ajax_referer('afes_quick_create_link_nonce', 'nonce'); if (!current_user_can('edit_posts')) { $this->send_forbidden_error(); return; } $productUrl = isset($_POST['product_url']) ? esc_url_raw(wp_unslash($_POST['product_url'])) : ''; if (empty($productUrl)) { wp_send_json_error(esc_html__("L'URL du produit est obligatoire.", 'affieasy')); return; } $postId = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0; if ($postId > 0 && !current_user_can('edit_post', $postId)) { $this->send_forbidden_error(); return; } // Detect webshop by domain. $parsed = wp_parse_url($productUrl); if (!is_array($parsed)) { wp_send_json_error(esc_html__('Invalid product URL.', 'affieasy')); return; } $webshop = $this->find_webshop_by_product_url($productUrl); if ($webshop === null) { wp_send_json_error(esc_html__('No webshop found for this URL', 'affieasy')); return; } // Resolve post slug for category / click_ref $postSlug = ''; if ($postId > 0) { $slug = get_post_field('post_name', $postId); if (!is_wp_error($slug) && !empty($slug)) { $postSlug = $slug; } } // Label: last path segment of product URL $path = isset($parsed['path']) ? $parsed['path'] : ''; $segments = array_filter(explode('/', $path)); $label = !empty($segments) ? urldecode(end($segments)) : basename($productUrl); // Build parameters array from webshop template placeholders $parameters = array(); foreach ($webshop->getParameters() as $param) { if ($param === 'product_url') { $parameters[$param] = $productUrl; } elseif ($param === 'click_ref') { $parameters[$param] = $postSlug; } else { $parameters[$param] = ''; } } // Build final URL by replacing [[param]] placeholders $url = $webshop->getUrl(); foreach ($parameters as $key => $value) { $url = str_replace("[[{$key}]]", $value, $url); } // Remove any remaining unreplaced placeholders $url = preg_replace('/\[\[[\s\S]+?]]/', '', $url); $url = esc_url_raw($url); $link = new AFES_Link( null, $webshop->getId(), sanitize_text_field($label), sanitize_text_field($postSlug), $parameters, $url, true, true ); $newId = $this->dbManager->edit_link($link); wp_send_json_success(array( 'id' => $newId, 'shortUrl' => AFES_Utils::get_base_url() . '?' . AFES_Constants::SHORT_LINK_SLUG . '=' . $newId, 'shortcode' => '[' . AFES_Constants::LINK_TAG . ' id=' . $newId . ']', )); } private function send_forbidden_error() { wp_send_json_error( esc_html__('You are not allowed to perform this action.', 'affieasy'), 403 ); } private function find_webshop_by_product_url($productUrl) { $parsed = wp_parse_url($productUrl); if (!is_array($parsed) || empty($parsed['host'])) { return null; } $host = strtolower(preg_replace('/^www\./i', '', $parsed['host'])); foreach ($this->dbManager->get_webshop_list() as $webshop) { $domains = $webshop->getProductDomains(); if (empty($domains)) { continue; } foreach (array_map('trim', explode(',', $domains)) as $domain) { $domain = strtolower(preg_replace('/^www\./i', '', $domain)); if (!empty($domain) && $domain === $host) { return $webshop; } } } return null; } }