array( 'label' => __('Politica de Privacidad', 'ibamu-plugin-domain'), ), 'cookies' => array( 'label' => __('Politica de Cookies', 'ibamu-plugin-domain'), ), 'cookie-table' => array( 'label' => __('Tabla de Cookies', 'ibamu-plugin-domain'), ), 'notice' => array( 'label' => __('Aviso Legal', 'ibamu-plugin-domain'), ), 'terms' => array( 'label' => __('Terminos y Condiciones', 'ibamu-plugin-domain'), ), 'purchase' => array( 'label' => __('Condiciones de Compra', 'ibamu-plugin-domain'), ), ); return apply_filters('lawwwing_shortcode_documents_map', $documents); } /** * Returns the endpoint template used to load legal documents JSON. * * The template must contain one `%s` placeholder for the Plugin ID. * * @return string */ function lawwwing_get_documents_endpoint_template() { $api_host = defined('LW_API_HOST') ? rtrim(LW_API_HOST, '/') : ''; return apply_filters( 'lawwwing_documents_endpoint_template', $api_host . '/api/documents/widget/%s/' ); } /** * Rewrite a document URL to use the configured rendering host. * * @param string $url * @return string|false */ function lawwwing_get_render_document_url($url) { $safe_url = esc_url_raw($url); if (empty($safe_url)) { return false; } if (!defined('LW_DOCUMENTS_BASE_HOST') || LW_DOCUMENTS_BASE_HOST === '') { return $safe_url; } $base_parts = wp_parse_url(LW_DOCUMENTS_BASE_HOST); $url_parts = wp_parse_url($safe_url); if (!is_array($base_parts) || !is_array($url_parts) || empty($base_parts['scheme']) || empty($base_parts['host'])) { return $safe_url; } $rewritten_url = $base_parts['scheme'] . '://'; if (!empty($base_parts['user'])) { $rewritten_url .= $base_parts['user']; if (!empty($base_parts['pass'])) { $rewritten_url .= ':' . $base_parts['pass']; } $rewritten_url .= '@'; } $rewritten_url .= $base_parts['host']; if (!empty($base_parts['port'])) { $rewritten_url .= ':' . $base_parts['port']; } $rewritten_url .= !empty($url_parts['path']) ? $url_parts['path'] : '/'; if (!empty($url_parts['query'])) { $rewritten_url .= '?' . $url_parts['query']; } if (!empty($url_parts['fragment'])) { $rewritten_url .= '#' . $url_parts['fragment']; } return $rewritten_url; } /** * Normalize category aliases returned by API to plugin document keys. * * Maps API category names to internal document type keys. * Returns both forward (api -> plugin) and reverse (plugin -> api) mappings. * * @return array Array with 'forward' and 'reverse' keys. */ function lawwwing_get_category_aliases() { $forward = apply_filters( 'lawwwing_documents_category_aliases_forward', array( // Aviso Legal. 'notice' => 'notice', 'legal_notice' => 'notice', // Politica de Privacidad (manual key can be priv or privacy). 'priv' => 'privacy', 'privacy' => 'privacy', 'privacy_policy' => 'privacy', // Politica de Cookies (manual key can be cookies or privacy). 'cookies' => 'cookies', 'cookie_policy' => 'cookies', 'privacy_and_cookies' => 'privacy', // Tabla de Cookies. 'cookie-table' => 'cookie-table', 'cookie_table' => 'cookie-table', 'cookies_table' => 'cookie-table', // Terminos y Condiciones / Condiciones de Compra (manual keys interchangeable). 'terms' => 'terms', 'terms_conditions' => 'terms', 'terms_and_conditions' => 'terms', 'purchase' => 'purchase', 'purchase_conditions' => 'purchase', ) ); $reverse = array_flip($forward); return array( 'forward' => $forward, 'reverse' => $reverse, ); } /** * Build endpoint URL using the configured plugin ID. * * @param string $plugin_id * @return string|false */ function lawwwing_get_documents_endpoint_url($plugin_id) { if (empty($plugin_id)) { return false; } $template = lawwwing_get_documents_endpoint_template(); if (strpos($template, '%s') === false) { return false; } return sprintf($template, rawurlencode($plugin_id)); } /** * Detect WordPress site language code. * * Returns the current site language or a fallback. * * @return string Language code (e.g., 'es', 'en', 'fr'). */ function lawwwing_get_site_language() { $lang = get_locale(); if (!empty($lang)) { $lang_parts = explode('_', $lang); if (!empty($lang_parts[0])) { return strtolower($lang_parts[0]); } } return 'en'; } /** * Get URL from a language variant, with fallback. * * @param array $language_variants Array of language keys to objects with 'url'. * @param string $preferred_lang Preferred language code. * @return string|false */ function lawwwing_extract_url_from_language_variants($language_variants, $preferred_lang) { if (!is_array($language_variants) || empty($language_variants)) { return false; } if (isset($language_variants[$preferred_lang]['url'])) { $url = $language_variants[$preferred_lang]['url']; $safe_url = esc_url_raw($url); if (!empty($safe_url)) { return $safe_url; } } foreach ($language_variants as $lang_code => $lang_data) { if (is_array($lang_data) && !empty($lang_data['url'])) { $url = $lang_data['url']; $safe_url = esc_url_raw($url); if (!empty($safe_url)) { return $safe_url; } } } return false; } /** * Parse API payload into a normalized map: {type => url}. * * Handles the nested language structure: * { * "notice": { * "es": {"url": "...", "title": "...", ...}, * "en": {"url": "...", "title": "...", ...} * }, * "privacy": { ... } * } * * @param array $payload * @return array */ function lawwwing_parse_documents_payload($payload) { $aliases = lawwwing_get_category_aliases(); $forward_aliases = $aliases['forward']; $resolved = array(); if (!is_array($payload)) { return $resolved; } $preferred_lang = lawwwing_get_site_language(); foreach ($payload as $category_key => $category_data) { if (!is_array($category_data)) { continue; } $normalized_category = sanitize_key($category_key); if (!isset($forward_aliases[$normalized_category])) { continue; } $document_type = $forward_aliases[$normalized_category]; $url = lawwwing_extract_url_from_language_variants($category_data, $preferred_lang); if ($url) { $resolved[$document_type] = $url; } } // Manual configuration compatibility: // - Politica de cookies can be keyed as `cookies` or `privacy`. // - Terms and purchase can be keyed as `terms` or `purchase`. if (empty($resolved['cookies']) && !empty($resolved['privacy'])) { $resolved['cookies'] = $resolved['privacy']; } if (empty($resolved['privacy']) && !empty($resolved['cookies'])) { $resolved['privacy'] = $resolved['cookies']; } if (empty($resolved['terms']) && !empty($resolved['purchase'])) { $resolved['terms'] = $resolved['purchase']; } if (empty($resolved['purchase']) && !empty($resolved['terms'])) { $resolved['purchase'] = $resolved['terms']; } return $resolved; } /** * Fetch and cache legal documents map from API. * * @return array */ function lawwwing_get_remote_documents_map($force_refresh = false) { $plugin_id = lawwwing_get_plugin_id(); if (empty($plugin_id)) { return array(); } $cache_key = 'lawwwing_docs_' . md5($plugin_id); $cached = get_transient($cache_key); if (!$force_refresh && is_array($cached)) { return $cached; } $endpoint = lawwwing_get_documents_endpoint_url($plugin_id); if (!$endpoint) { return array(); } $response = wp_remote_get( $endpoint, array( 'timeout' => 8, 'headers' => array( 'Accept' => 'application/json', ), ) ); if (is_wp_error($response)) { return array(); } $status = wp_remote_retrieve_response_code($response); if ((int) $status !== 200) { return array(); } $body = wp_remote_retrieve_body($response); if (empty($body)) { return array(); } $payload = json_decode($body, true); if (!is_array($payload)) { return array(); } $documents = lawwwing_parse_documents_payload($payload); set_transient($cache_key, $documents, 30 * MINUTE_IN_SECONDS); return $documents; } /** * Delete cached documents map by plugin ID. * * @param string $plugin_id * @return void */ function lawwwing_delete_documents_cache($plugin_id) { if (empty($plugin_id)) { return; } delete_transient('lawwwing_docs_' . md5($plugin_id)); } /** * Invalidate documents cache when plugin options are updated. * * @param mixed $old_value * @param mixed $new_value * @return void */ function lawwwing_invalidate_documents_cache($old_value, $new_value) { $old_id = ''; $new_id = ''; if (is_array($old_value) && !empty($old_value['ibamu_widget_uuid'])) { $old_id = $old_value['ibamu_widget_uuid']; } if (is_array($new_value) && !empty($new_value['ibamu_widget_uuid'])) { $new_id = $new_value['ibamu_widget_uuid']; } lawwwing_delete_documents_cache($old_id); lawwwing_delete_documents_cache($new_id); } add_action('update_option_ibamu_options', 'lawwwing_invalidate_documents_cache', 20, 2); /** * Resolve a document type to the URL obtained from the API. * * @param string $type Document type key. * @return string|false */ function lawwwing_get_document_url_by_type($type) { $documents = lawwwing_get_remote_documents_map(); $url = isset($documents[$type]) ? $documents[$type] : false; // Retry once bypassing cache in case API content changed after transient creation. if (empty($url)) { $documents = lawwwing_get_remote_documents_map(true); $url = isset($documents[$type]) ? $documents[$type] : false; } return !empty($url) ? lawwwing_get_render_document_url($url) : false; } /** * Get debug information about shortcode resolution. * * @param string $type Document type. * @return array */ function lawwwing_get_shortcode_debug_info($type) { $plugin_id = lawwwing_get_plugin_id(); $endpoint = lawwwing_get_documents_endpoint_url($plugin_id); $documents = lawwwing_get_remote_documents_map(); $url = lawwwing_get_document_url_by_type($type); $site_lang = lawwwing_get_site_language(); return array( 'type' => $type, 'plugin_id' => $plugin_id, 'plugin_id_set' => !empty($plugin_id), 'endpoint' => $endpoint, 'site_language' => $site_lang, 'documents_available' => array_keys($documents), 'requested_url' => $url, 'found' => !empty($url), 'raw_documents_map' => $documents, ); } /** * Extract the body contents from an HTML document. * * @param string $html * @return string */ function lawwwing_extract_html_body($html) { if (!is_string($html) || $html === '') { return ''; } if (preg_match('/
]*>(.*)<\/body>/is', $html, $matches)) { return $matches[1]; } return $html; } /** * Convert a raw text response into basic HTML paragraphs. * * @param string $text * @return string */ function lawwwing_format_inline_text_content($text) { if (!is_string($text)) { return ''; } $text = trim($text); if ($text === '') { return ''; } return wpautop(esc_html($text)); } /** * Build the raw URL used for inline document rendering. * * @param string $url * @return string|false */ function lawwwing_get_inline_raw_url($url) { $url_parts = wp_parse_url($url); if (!is_array($url_parts) || empty($url_parts['scheme']) || empty($url_parts['host'])) { return false; } $raw_url = $url_parts['scheme'] . '://'; if (!empty($url_parts['user'])) { $raw_url .= $url_parts['user']; if (!empty($url_parts['pass'])) { $raw_url .= ':' . $url_parts['pass']; } $raw_url .= '@'; } $raw_url .= $url_parts['host']; if (!empty($url_parts['port'])) { $raw_url .= ':' . $url_parts['port']; } $raw_url .= !empty($url_parts['path']) ? $url_parts['path'] : '/'; $raw_url .= '?raw=True'; if (!empty($url_parts['fragment'])) { $raw_url .= '#' . $url_parts['fragment']; } return $raw_url; } /** * Fetch a document HTML and return sanitized inline markup. * * @param string $url * @param bool $force_refresh * @return string|false */ function lawwwing_get_inline_document_content($url, $force_refresh = false) { if (empty($url)) { return false; } $raw_url = lawwwing_get_inline_raw_url($url); if (!$raw_url) { return false; } $cache_key = 'lawwwing_doc_html_' . md5($raw_url); $cached = get_transient($cache_key); if (!$force_refresh && is_string($cached) && $cached !== '') { return $cached; } $response = wp_remote_get( $raw_url, array( 'timeout' => 12, 'headers' => array( 'Accept' => 'text/html,application/xhtml+xml', ), ) ); if (is_wp_error($response)) { return false; } if ((int) wp_remote_retrieve_response_code($response) !== 200) { return false; } $body = wp_remote_retrieve_body($response); if (!is_string($body) || $body === '') { return false; } $inline_html = trim(lawwwing_extract_html_body($body)); $allowed_tags = wp_kses_allowed_html('post'); $allowed_tags['style'] = array(); $allowed_tags['link'] = array( 'rel' => true, 'href' => true, 'type' => true, 'media' => true, ); $allowed_tags['meta'] = array( 'charset' => true, 'name' => true, 'content' => true, 'http-equiv' => true, ); $inline_html = wp_kses($inline_html, $allowed_tags); if ($inline_html === '') { $inline_html = lawwwing_format_inline_text_content(wp_strip_all_tags($body)); } if ($inline_html === '') { return false; } set_transient($cache_key, $inline_html, 30 * MINUTE_IN_SECONDS); return $inline_html; } /** * Generic shortcode handler. * * Usage: * [lawwwing_document type="privacy" mode="embed"] * [lawwwing_document type="cookies" mode="link" label="Ver politica"] * * @param array $atts * @return string */ function lawwwing_document_shortcode($atts) { $atts = shortcode_atts( array( 'type' => 'privacy', 'mode' => 'embed', // embed | link | inline 'label' => '', 'class' => '', 'target' => '_blank', 'height' => '1200', 'title' => '', 'debug' => '0', ), $atts, 'lawwwing_document' ); $type = sanitize_key($atts['type']); $mode = sanitize_key($atts['mode']); $debug_enabled = ($atts['debug'] === '1' || $atts['debug'] === 'true'); $documents = lawwwing_get_documents_map(); if (!isset($documents[$type])) { if (WP_DEBUG) { return ''; } return ''; } $url = lawwwing_get_document_url_by_type($type); if (!$url) { if ($debug_enabled && current_user_can('manage_options')) { $debug = lawwwing_get_shortcode_debug_info($type); $message = 'Lawwwing shortcode debug - type: ' . esc_html($type); $message .= ' | plugin_id: ' . esc_html($debug['plugin_id'] ? $debug['plugin_id'] : 'NOT SET'); $message .= ' | endpoint: ' . esc_html($debug['endpoint'] ? $debug['endpoint'] : 'INVALID'); $message .= ' | site_language: ' . esc_html($debug['site_language']); $message .= ' | available: ' . esc_html(empty($debug['documents_available']) ? 'NONE' : implode(', ', $debug['documents_available'])); return '