array('label' => __('Hero', 'ai-eshop-optimizer'), 'icon' => 'image'), 'seo' => array('label' => __('SEO', 'ai-eshop-optimizer'), 'icon' => 'search'), 'layout' => array('label' => __('Layout', 'ai-eshop-optimizer'), 'icon' => 'layout-template'), 'catalog' => array('label' => __('Catalog', 'ai-eshop-optimizer'), 'icon' => 'package'), 'related' => array('label' => __('Related', 'ai-eshop-optimizer'), 'icon' => 'link'), ); if (in_array('product_cat', $this->managed_taxonomies(), true)) { $tabs['marketplace'] = array('label' => __('Marketplace', 'ai-eshop-optimizer'), 'icon' => 'send'); } return $tabs; } public function get_tab_base_url() { $tax = $this->managed_taxonomies(); $tax = !empty($tax) ? (string) reset($tax) : 'product_cat'; return admin_url('edit-tags.php?taxonomy=' . rawurlencode($tax)); } const LEGACY_PREFIX = 'woocommerce_advanced_categories_'; public static function templates() { return (array) apply_filters('aieo_dmm_category_extras_templates', array( 'default' => __('Default (theme archive)', 'ai-eshop-optimizer'), 'hero' => __('Hero (image + headline + description)', 'ai-eshop-optimizer'), 'hero_brands' => __('Hero + featured brands grid', 'ai-eshop-optimizer'), 'landing' => __('Landing page (no products grid)', 'ai-eshop-optimizer'), 'hidden' => __('Hidden (404 the archive)', 'ai-eshop-optimizer'), )); } public function register_hooks() { if (is_admin()) { add_action('admin_enqueue_scripts', function () { if ((isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '') === 'ai-eshop-dmm' && (isset($_GET['dmm_tab']) ? sanitize_text_field(wp_unslash($_GET['dmm_tab'])) : '') === 'category_extras') { $css = 'assets/css/aieo-dmm-category-extras-admin.css'; wp_enqueue_style('aieo-dmm-category-extras-admin', AIEO_URI . $css, array(), file_exists(AIEO_DIR . $css) ? (string) filemtime(AIEO_DIR . $css) : '1'); } }); } add_action('wp_ajax_aieo_dmm_category_extras_migrate', array($this, 'ajax_migrate')); add_action('wp_ajax_aieo_dmm_category_extras_h3_migrate', array($this, 'ajax_migrate_h3_headlines')); add_action('wp_ajax_aieo_dmm_category_extras_save', array($this, 'ajax_save')); add_action('wp_ajax_aieo_dmm_category_extras_search', array($this, 'ajax_search')); add_action('wp_ajax_aieo_dmm_marketplace_tax_browse', array($this, 'ajax_marketplace_tax_browse')); add_action('wp_ajax_aieo_dmm_marketplace_tax_search', array($this, 'ajax_marketplace_tax_search')); add_action('wp_ajax_aieo_dmm_category_extras_save_settings', array($this, 'ajax_save_settings')); add_shortcode('aieo_category_hero', array($this, 'shortcode_category_hero')); add_shortcode('aieo_category_chips', array($this, 'shortcode_category_chips')); add_action('init', array($this, 'register_block')); add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_assets')); add_action('pre_get_posts', array($this, 'exclude_blog_hero_post')); if (!is_admin()) { return; } foreach ($this->managed_taxonomies() as $tax) { add_action($tax . '_edit_form_fields', array($this, 'render_term_extras'), 1, 2); add_action('edited_' . $tax, array($this, 'save_term_extras'), 10, 2); add_action('created_' . $tax, array($this, 'save_term_extras'), 10, 2); } add_action('admin_enqueue_scripts', array($this, 'enqueue_term_admin_assets')); } private function managed_taxonomies() { return (array) apply_filters( 'aieo_dmm_category_extras_managed_taxonomies', array('product_cat') ); } private function description_editor_taxonomies() { $list = array_merge( $this->managed_taxonomies(), array( 'product_brand', 'product_tag', 'supplier', 'pa_need', 'pa-need', 'category', 'post_tag', ) ); $list = (array) apply_filters('aieo_dmm_description_editor_taxonomies', $list); return array_values(array_unique(array_filter(array_map('sanitize_key', $list)))); } public function is_enabled() { if (method_exists('AIEO_DMM_Settings', 'is_module_enabled')) { return (bool) AIEO_DMM_Settings::is_module_enabled('category_extras'); } return true; } public static function table() { global $wpdb; return $wpdb->prefix . 'aieo_dmm_category_extras'; } public static function get($taxonomy, $term_id) { global $wpdb; $taxonomy = sanitize_key($taxonomy); $term_id = (int) $term_id; if (!$taxonomy || !$term_id) return null; $tbl = self::table(); $row = $wpdb->get_row( $wpdb->prepare("SELECT * FROM {$tbl} WHERE taxonomy = %s AND term_id = %d LIMIT 1", $taxonomy, $term_id), ARRAY_A ); if (!$row) return null; $row['landing_pages'] = self::decode_landing_pages($row['landing_pages']); foreach (array('related_brand_ids','related_post_ids','related_campaign_ids') as $k) { $row[$k] = isset($row[$k]) ? self::decode_id_list($row[$k]) : array(); } return $row; } public static function save($taxonomy, $term_id, array $data) { global $wpdb; $taxonomy = sanitize_key($taxonomy); $term_id = (int) $term_id; if (!$taxonomy || !$term_id) return false; $tbl = self::table(); $patch = self::sanitize_patch($data); $patch['taxonomy'] = $taxonomy; $patch['term_id'] = $term_id; $existing = $wpdb->get_var($wpdb->prepare( "SELECT id FROM {$tbl} WHERE taxonomy = %s AND term_id = %d LIMIT 1", $taxonomy, $term_id )); if ($existing) { return $wpdb->update($tbl, $patch, array('id' => (int) $existing)); } return $wpdb->insert($tbl, $patch); } private static function sanitize_patch(array $data) { $out = array(); if (array_key_exists('headline', $data)) { $out['headline'] = wp_kses_post((string) $data['headline']); } if (array_key_exists('description', $data)) { $out['description'] = wp_kses_post((string) $data['description']); } if (array_key_exists('image_mobile_id', $data)) { $out['image_mobile_id'] = $data['image_mobile_id'] ? (int) $data['image_mobile_id'] : null; } if (array_key_exists('image_desktop_id', $data)) { $out['image_desktop_id'] = $data['image_desktop_id'] ? (int) $data['image_desktop_id'] : null; } if (array_key_exists('seo_title', $data)) { $out['seo_title'] = sanitize_text_field((string) $data['seo_title']); } if (array_key_exists('sidebar_text', $data)) { $out['sidebar_text'] = wp_kses_post((string) $data['sidebar_text']); } if (array_key_exists('footer_text', $data)) { $out['footer_text'] = wp_kses_post((string) $data['footer_text']); } if (array_key_exists('columns', $data)) { $c = (int) $data['columns']; $out['columns'] = ($c >= 1 && $c <= 6) ? $c : null; } if (array_key_exists('disable_nested', $data)) { $out['disable_nested'] = !empty($data['disable_nested']) ? 1 : 0; } if (array_key_exists('template', $data)) { $tpl = sanitize_key((string) $data['template']); $allowed = array_keys(self::templates()); $out['template'] = in_array($tpl, $allowed, true) ? $tpl : 'default'; } if (array_key_exists('landing_pages', $data)) { $out['landing_pages'] = self::encode_landing_pages($data['landing_pages']); } if (array_key_exists('pro_only', $data)) { $out['pro_only'] = !empty($data['pro_only']) ? 1 : 0; } if (array_key_exists('reference_quantity', $data)) { $rq = $data['reference_quantity']; $out['reference_quantity'] = ($rq === null || $rq === '') ? null : max(0, (int) $rq); } if (array_key_exists('synonyms', $data)) { $out['synonyms'] = (string) $data['synonyms'] !== '' ? (string) $data['synonyms'] : null; } foreach (array('season_start', 'season_end', 'season_midpoint') as $key) { if (!array_key_exists($key, $data)) continue; $val = (string) $data[$key]; $out[$key] = (preg_match('/^\d{4}-\d{2}-\d{2}$/', $val) && $val !== '0000-00-00') ? $val : null; } foreach (array('google_category_id', 'facebook_category_id') as $idkey) { if (array_key_exists($idkey, $data)) { $v = $data[$idkey]; $out[$idkey] = ($v === null || $v === '') ? null : max(0, (int) $v); if ($out[$idkey] === 0) { $out[$idkey] = null; } } } foreach (array('google_category_path', 'facebook_category_path') as $pathkey) { if (array_key_exists($pathkey, $data)) { $v = sanitize_text_field((string) $data[$pathkey]); $out[$pathkey] = $v !== '' ? mb_substr($v, 0, 500) : null; } } foreach (array('related_brand_ids', 'related_post_ids', 'related_campaign_ids') as $key) { if (array_key_exists($key, $data)) { $out[$key] = self::encode_id_list($data[$key]); } } return $out; } private static function encode_id_list($val) { if (!is_array($val)) return null; $clean = array_values(array_unique(array_map('intval', $val))); $clean = array_values(array_filter($clean, static function ($v) { return $v > 0; })); return empty($clean) ? null : wp_json_encode($clean); } private static function decode_id_list($json) { if (!$json) return array(); $arr = json_decode((string) $json, true); return is_array($arr) ? array_values(array_map('intval', $arr)) : array(); } private static function encode_landing_pages($val) { if (is_array($val)) { $clean = array(); foreach ($val as $lp) { if (!is_array($lp)) continue; $clean[] = array( 'title' => isset($lp['title']) ? sanitize_text_field((string) $lp['title']) : '', 'link' => isset($lp['link']) ? esc_url_raw((string) $lp['link']) : '', 'image_id' => isset($lp['image_id']) ? (int) $lp['image_id'] : 0, ); } return wp_json_encode(array_values($clean)); } return null; } private static function decode_landing_pages($json) { if (!$json) return array(); $arr = json_decode((string) $json, true); return is_array($arr) ? $arr : array(); } public function run_migration() { $taxonomies = (array) apply_filters( 'aieo_dmm_category_extras_migration_taxonomies', array('product_cat', 'product_brand') ); $stats = array( 'processed' => 0, 'migrated' => 0, 'skipped' => 0, 'taxonomies' => array(), 'pods_loaded' => array(), ); foreach ($taxonomies as $taxonomy) { if (!taxonomy_exists($taxonomy)) continue; $stats['taxonomies'][$taxonomy] = 0; $pods_cache = $this->load_pods_caches($taxonomy); $stats['pods_loaded'][$taxonomy] = !empty($pods_cache['pod_id']); $term_ids = get_terms(array( 'taxonomy' => $taxonomy, 'hide_empty' => false, 'fields' => 'ids', )); if (is_wp_error($term_ids) || empty($term_ids)) continue; foreach ($term_ids as $term_id) { $stats['processed']++; $patch = $this->extract_legacy_for_term((int) $term_id); if (!empty($pods_cache['pod_id'])) { $patch = array_merge($patch, $this->extract_pods_for_term((int) $term_id, $pods_cache)); } if (empty($patch)) { $stats['skipped']++; continue; } self::save($taxonomy, (int) $term_id, $patch); $stats['migrated']++; $stats['taxonomies'][$taxonomy]++; } } update_option('aieo_dmm_category_extras_migrated_at', time(), false); return $stats; } private function load_pods_caches($pod_name) { global $wpdb; $cache = array( 'pod_id' => 0, 'pods_table' => array(), 'rel_fields' => array(), 'rel_data' => array(), ); $pod_id = (int) $wpdb->get_var($wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = '_pods_pod' AND post_name = %s LIMIT 1", $pod_name )); if (!$pod_id) return $cache; $cache['pod_id'] = $pod_id; $relevant = array('related_brand', 'related_posts', 'related_campaigns'); $field_rows = $wpdb->get_results($wpdb->prepare( "SELECT ID, post_name FROM {$wpdb->posts} WHERE post_type = '_pods_field' AND post_parent = %d AND post_name IN ('" . implode("','", $relevant) . "')", $pod_id )); $field_id_to_name = array(); foreach ((array) $field_rows as $row) { $cache['rel_fields'][$row->post_name] = (int) $row->ID; $field_id_to_name[(int) $row->ID] = $row->post_name; } $tbl = $wpdb->prefix . 'pods_' . $pod_name; $exists = ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $tbl)) === $tbl); if ($exists) { $rows = $wpdb->get_results("SELECT * FROM {$tbl}", ARRAY_A); foreach ((array) $rows as $row) { if (!isset($row['id'])) continue; $cache['pods_table'][(int) $row['id']] = $row; } } if (!empty($field_id_to_name)) { $field_ids = array_keys($field_id_to_name); $place = implode(',', array_fill(0, count($field_ids), '%d')); $params = array_merge(array($pod_id), $field_ids); $rows = $wpdb->get_results($wpdb->prepare( "SELECT item_id, field_id, related_item_id FROM {$wpdb->prefix}podsrel WHERE pod_id = %d AND field_id IN ({$place}) ORDER BY item_id, field_id, weight ASC", $params )); foreach ((array) $rows as $row) { $name = $field_id_to_name[(int) $row->field_id] ?? null; if (!$name) continue; $cache['rel_data'][(int) $row->item_id][$name][] = (int) $row->related_item_id; } } return $cache; } private function extract_pods_for_term($term_id, array $cache) { $patch = array(); if (isset($cache['pods_table'][$term_id])) { $row = $cache['pods_table'][$term_id]; if (!empty($row['pro_only'])) { $patch['pro_only'] = 1; } if (isset($row['reference_quantity']) && $row['reference_quantity'] !== null && $row['reference_quantity'] !== '') { $rq = (int) $row['reference_quantity']; if ($rq > 0) $patch['reference_quantity'] = $rq; } if (!empty($row['synonyms'])) { $patch['synonyms'] = (string) $row['synonyms']; } foreach (array('season_start', 'season_end', 'season_midpoint') as $col) { if (!empty($row[$col]) && $row[$col] !== '0000-00-00') { $patch[$col] = (string) $row[$col]; } } } if (isset($cache['rel_data'][$term_id])) { $rels = $cache['rel_data'][$term_id]; if (!empty($rels['related_brand'])) { $patch['related_brand_ids'] = $rels['related_brand']; } if (!empty($rels['related_posts'])) { $patch['related_post_ids'] = $rels['related_posts']; } if (!empty($rels['related_campaigns'])) { $patch['related_campaign_ids'] = $rels['related_campaigns']; } } return $patch; } private function extract_legacy_for_term($term_id) { $patch = array(); $seo = get_term_meta($term_id, self::LEGACY_PREFIX . 'seo_title', true); if ($seo !== '') $patch['seo_title'] = $seo; $bg = get_term_meta($term_id, self::LEGACY_PREFIX . 'background', true); if (is_array($bg) && !empty($bg['id'])) { $patch['image_desktop_id'] = (int) $bg['id']; } elseif (is_numeric($bg) && (int) $bg > 0) { $patch['image_desktop_id'] = (int) $bg; } $short = get_term_meta($term_id, self::LEGACY_PREFIX . 'short_description', true); if ($short !== '') $patch['headline'] = $short; $sidebar = get_term_meta($term_id, self::LEGACY_PREFIX . 'sidebar_text', true); if ($sidebar !== '') $patch['sidebar_text'] = $sidebar; $footer = get_term_meta($term_id, self::LEGACY_PREFIX . 'footer_text', true); if ($footer !== '') $patch['footer_text'] = $footer; $cols = get_term_meta($term_id, self::LEGACY_PREFIX . 'product_category_columns', true); if ($cols !== '' && (int) $cols > 0) $patch['columns'] = (int) $cols; $nested = get_term_meta($term_id, self::LEGACY_PREFIX . 'disable_nested_categories', true); if ($nested === '1' || $nested === 1) $patch['disable_nested'] = 1; $landing = array(); for ($i = 1; $i <= 8; $i++) { $title = get_term_meta($term_id, self::LEGACY_PREFIX . 'landingpage_' . $i . '_title', true); $link = get_term_meta($term_id, self::LEGACY_PREFIX . 'landingpage_' . $i . '_link', true); $img = get_term_meta($term_id, self::LEGACY_PREFIX . 'landingpage_' . $i . '_image', true); $img_id = 0; if (is_array($img) && !empty($img['id'])) { $img_id = (int) $img['id']; } elseif (is_numeric($img) && (int) $img > 0) { $img_id = (int) $img; } if ($title === '' && $link === '' && !$img_id) continue; $landing[] = array( 'title' => (string) $title, 'link' => (string) $link, 'image_id' => $img_id, ); } if (!empty($landing)) $patch['landing_pages'] = $landing; return $patch; } public function ajax_migrate() { AIEO_DMM::verify_ajax_request(); if (function_exists('ob_get_level') && ob_get_level()) { @ob_end_clean(); } $stats = $this->run_migration(); wp_send_json_success(array( 'stats' => $stats, 'migrated_at' => time(), )); } public function run_h3_headline_migration() { $stats = array( 'processed' => 0, 'headlines' => 0, 'style_stripped' => 0, 'skipped' => 0, 'errors' => 0, 'taxonomies' => array(), ); $nav_module = null; if (class_exists('AIEO_DMM') && method_exists('AIEO_DMM', 'instance')) { $dmm = AIEO_DMM::instance(); if ($dmm && method_exists($dmm, 'get_module')) { $nav_module = $dmm->get_module('navigator'); } } if ($nav_module) { remove_action('created_term', array($nav_module, 'on_term_change'), 99); remove_action('edited_term', array($nav_module, 'on_term_change'), 99); } foreach ($this->managed_taxonomies() as $taxonomy) { if (!taxonomy_exists($taxonomy)) continue; $stats['taxonomies'][$taxonomy] = 0; $terms = get_terms(array( 'taxonomy' => $taxonomy, 'hide_empty' => false, )); if (is_wp_error($terms) || empty($terms)) continue; foreach ($terms as $term) { $stats['processed']++; $desc = (string) $term->description; if ($desc === '') { $stats['skipped']++; continue; } $headline_inner = ''; $headline_offset = 0; $headline_length = 0; $stripped = ''; if (preg_match('#^\s*]*>(.*?)\s*#is', $desc, $m, PREG_OFFSET_CAPTURE)) { $headline_inner = trim((string) $m[1][0]); $headline_offset = (int) $m[0][1]; $headline_length = strlen((string) $m[0][0]); } elseif (preg_match('#^\s*]*>(.*?)\s*(?:|\r\n|\n|$)#is', $desc, $m, PREG_OFFSET_CAPTURE)) { $headline_inner = trim((string) $m[1][0]); $headline_offset = (int) $m[0][1]; $headline_length = strlen((string) $m[0][0]); } elseif (preg_match('#^\s*]*>(.*?)\s*#is', $desc, $m, PREG_OFFSET_CAPTURE)) { $headline_inner = trim((string) $m[1][0]); $headline_offset = (int) $m[0][1]; $headline_length = strlen((string) $m[0][0]); } $new_desc = $desc; if ($headline_length > 0) { $stripped = trim(html_entity_decode(wp_strip_all_tags($headline_inner), ENT_QUOTES | ENT_HTML5, 'UTF-8')); if ($stripped === '') { $headline_inner = ''; $headline_length = 0; } else { $new_desc = (string) substr_replace($desc, '', $headline_offset, $headline_length); } } $cleaned = $new_desc; $cleaned = (string) preg_replace('#\s*text-align\s*:\s*[^;"\']+;?\s*#i', '', $cleaned); $cleaned = (string) preg_replace('#\s*style\s*=\s*"\s*"#i', '', $cleaned); $cleaned = (string) preg_replace("#\\s*style\\s*=\\s*'\\s*'#i", '', $cleaned); $cleaned = trim($cleaned); if ($cleaned !== $new_desc) { $stats['style_stripped']++; $new_desc = $cleaned; } $headline_changed = ($headline_length > 0 && $headline_inner !== ''); $description_changed = ($new_desc !== $desc); if (!$headline_changed && !$description_changed) { $stats['skipped']++; continue; } if ($headline_changed) { $ok = self::save($taxonomy, (int) $term->term_id, array('headline' => $stripped)); if ($ok === false) { $stats['errors']++; continue; } $stats['headlines']++; } if ($description_changed) { $upd = wp_update_term((int) $term->term_id, $taxonomy, array('description' => $new_desc)); if (is_wp_error($upd)) { $stats['errors']++; continue; } } $stats['taxonomies'][$taxonomy]++; } } if ($nav_module) { add_action('created_term', array($nav_module, 'on_term_change'), 99, 3); add_action('edited_term', array($nav_module, 'on_term_change'), 99, 3); if (method_exists($nav_module, 'generate_json')) { $nav_module->generate_json(); } } update_option('aieo_dmm_category_extras_h3_migrated_at', time(), false); return $stats; } public function ajax_migrate_h3_headlines() { AIEO_DMM::verify_ajax_request(); if (function_exists('ob_get_level') && ob_get_level()) { @ob_end_clean(); } $migrated_at = (int) get_option('aieo_dmm_category_extras_h3_migrated_at', 0); $force = !empty($_POST['force']); if ($migrated_at && !$force) { wp_send_json_error(array( 'message' => __('H3 → headline migration has already run on this site. Re-running would touch term descriptions again. Pass `force=1` to override.', 'ai-eshop-optimizer'), 'migrated_at' => $migrated_at, )); } $stats = $this->run_h3_headline_migration(); wp_send_json_success(array( 'stats' => $stats, 'migrated_at' => time(), )); } public function ajax_save_settings() { AIEO_DMM::verify_ajax_request(); $patch = array(); if (isset($_POST['chips_enabled'])) { $patch['category_extras_chips_enabled'] = !empty($_POST['chips_enabled']); } if (isset($_POST['chips_mobile_slider'])) { $patch['category_extras_chips_mobile_slider'] = !empty($_POST['chips_mobile_slider']); } $int_fields = array( 'chips_border_width' => 'category_extras_chips_border_width', 'chips_border_radius' => 'category_extras_chips_border_radius', 'chips_padding_y' => 'category_extras_chips_padding_y', 'chips_padding_x' => 'category_extras_chips_padding_x', 'chips_gap_y' => 'category_extras_chips_gap_y', 'chips_gap_x' => 'category_extras_chips_gap_x', 'nav_border_width' => 'navigator_border_width', 'nav_border_radius' => 'navigator_border_radius', ); foreach ($int_fields as $post_key => $setting_key) { if (isset($_POST[$post_key]) && $_POST[$post_key] !== '') { $patch[$setting_key] = (int) $_POST[$post_key]; } } $color_fields = array( 'chips_border_color' => 'category_extras_chips_border_color', 'chips_text_color' => 'category_extras_chips_text_color', 'nav_border_color' => 'navigator_border_color', ); foreach ($color_fields as $post_key => $setting_key) { if (isset($_POST[$post_key])) { $patch[$setting_key] = sanitize_text_field( wp_unslash( (string) $_POST[$post_key] ) ); } } if (!empty($patch)) { AIEO_DMM_Settings::update($patch); } wp_send_json_success(array( 'chips_enabled' => self::chips_enabled() ? 1 : 0, )); } public function ajax_save() { AIEO_DMM::verify_ajax_request(); $taxonomy = isset($_POST['taxonomy']) ? sanitize_key( wp_unslash( (string) $_POST['taxonomy'] ) ) : ''; $term_id = isset($_POST['term_id']) ? (int) $_POST['term_id'] : 0; if (!$taxonomy || !$term_id) { wp_send_json_error(array('message' => __('Missing taxonomy/term_id.', 'ai-eshop-optimizer'))); } $payload = array(); foreach (array('headline','description','seo_title','sidebar_text','footer_text','columns','disable_nested','image_mobile_id','image_desktop_id') as $k) { if (isset($_POST[$k])) { $payload[$k] = is_array($_POST[$k]) ? map_deep(wp_unslash($_POST[$k]), 'sanitize_text_field') : sanitize_textarea_field(wp_unslash($_POST[$k])); } } if (isset($_POST['landing_pages']) && is_array($_POST['landing_pages'])) { $payload['landing_pages'] = map_deep(wp_unslash($_POST['landing_pages']), 'sanitize_text_field'); } $ok = self::save($taxonomy, $term_id, $payload); if ($ok === false) { wp_send_json_error(array('message' => __('Save failed.', 'ai-eshop-optimizer'))); } wp_send_json_success(array('row' => self::get($taxonomy, $term_id))); } public function render_term_extras($term, $taxonomy) { if (!is_object($term) || !isset($term->term_id)) return; $row = self::get($taxonomy, (int) $term->term_id); if (!$row) { $row = array( 'headline' => '', 'image_mobile_id' => 0, 'image_desktop_id' => 0, 'seo_title' => '', 'sidebar_text' => '', 'footer_text' => '', 'columns' => null, 'disable_nested' => 0, 'template' => 'default', 'pro_only' => 0, 'reference_quantity' => '', 'synonyms' => '', 'season_start' => '', 'season_end' => '', 'season_midpoint' => '', 'related_brand_ids' => array(), 'related_post_ids' => array(), 'related_campaign_ids' => array(), 'google_category_id' => null, 'google_category_path' => '', 'facebook_category_id' => null, 'facebook_category_path' => '', ); } $mobile_url = $row['image_mobile_id'] ? wp_get_attachment_image_url((int) $row['image_mobile_id'], 'medium') : ''; $desktop_url = $row['image_desktop_id'] ? wp_get_attachment_image_url((int) $row['image_desktop_id'], 'medium') : ''; $brand_chips = $this->resolve_term_chips((array) $row['related_brand_ids'], 'product_brand'); $post_chips = $this->resolve_post_chips((array) $row['related_post_ids']); $campaign_chips = $this->resolve_post_chips((array) $row['related_campaign_ids']); $allow_brand_picker = ($taxonomy !== 'product_brand') && taxonomy_exists('product_brand'); ?>
term_id, 'aieo_ce_nonce'); ?>

>

>

'aieo_ce[headline]', 'textarea_rows' => 5, 'media_buttons' => true, 'teeny' => false, 'quicktags' => true, ) ); ?>

'aieo_ce[sidebar_text]', 'textarea_rows' => 8, 'media_buttons' => true, 'teeny' => false, 'quicktags' => true, ) ); ?>

'aieo_ce[footer_text]', 'textarea_rows' => 12, 'media_buttons' => true, 'teeny' => false, 'quicktags' => true, ) ); ?>

term_id); } else { echo '

' . esc_html__('Related-tab component unavailable.', 'ai-eshop-optimizer') . '

'; } ?>

$pid, 'label' => $title); } return $out; } private static function mt_current_notice($path, $set, $about = '') { $html = '' . esc_html__('Current:', 'ai-eshop-optimizer') . ' ' . '' . esc_html('' !== $path ? $path : __('(not set)', 'ai-eshop-optimizer')) . '' . ''; return AIEO_DMM_UI::notice($html, 'info'); } private static function describe_editor($editor_id, $help_id) { $needle = 'id="' . esc_attr($editor_id) . '">'; $filter = static function ($html) use ($needle, $help_id, &$filter) { if (!is_string($html) || false === strpos($html, $needle)) { return $html; } remove_filter('the_editor', $filter); return str_replace($needle, substr($needle, 0, -1) . ' aria-describedby="' . esc_attr($help_id) . '">', $html); }; add_filter('the_editor', $filter); } private function resolve_term_chips(array $ids, $taxonomy) { $ids = array_filter(array_map('intval', $ids)); if (empty($ids)) return array(); $out = array(); foreach ($ids as $tid) { $term = get_term($tid, $taxonomy); $label = ($term && !is_wp_error($term)) ? $term->name : ('#' . $tid); $out[] = array('id' => $tid, 'label' => $label); } return $out; } public function save_term_extras($term_id, $tt_id = 0) { $term_id = (int) $term_id; if (!$term_id) return; if (empty($_POST['aieo_ce_nonce']) || !wp_verify_nonce( sanitize_key(wp_unslash($_POST['aieo_ce_nonce'])), 'aieo_ce_term_edit_' . $term_id )) { return; } if (empty($_POST['aieo_ce']) || !is_array($_POST['aieo_ce'])) return; $term = get_term($term_id); if (!$term || is_wp_error($term)) return; if (!in_array($term->taxonomy, $this->managed_taxonomies(), true)) return; $raw = map_deep(wp_unslash($_POST['aieo_ce']), 'sanitize_textarea_field'); $patch = array(); if (isset($raw['headline'])) $patch['headline'] = (string) $raw['headline']; if (isset($raw['image_mobile_id'])) $patch['image_mobile_id'] = (int) $raw['image_mobile_id']; if (isset($raw['image_desktop_id'])) $patch['image_desktop_id'] = (int) $raw['image_desktop_id']; if (isset($raw['seo_title'])) $patch['seo_title'] = (string) $raw['seo_title']; if (isset($raw['sidebar_text'])) $patch['sidebar_text'] = (string) $raw['sidebar_text']; if (isset($raw['footer_text'])) $patch['footer_text'] = (string) $raw['footer_text']; if (isset($raw['template'])) $patch['template'] = (string) $raw['template']; $patch['columns'] = (isset($raw['columns']) && $raw['columns'] !== '') ? (int) $raw['columns'] : null; $patch['disable_nested'] = !empty($raw['disable_nested']) ? 1 : 0; $patch['pro_only'] = !empty($raw['pro_only']) ? 1 : 0; $patch['reference_quantity'] = (isset($raw['reference_quantity']) && $raw['reference_quantity'] !== '') ? (int) $raw['reference_quantity'] : null; if (isset($raw['synonyms'])) $patch['synonyms'] = (string) $raw['synonyms']; foreach (array('season_start', 'season_end', 'season_midpoint') as $k) { if (array_key_exists($k, $raw)) $patch[$k] = (string) $raw[$k]; } if ($term->taxonomy === 'product_cat') { foreach (array('google', 'facebook') as $source) { $id_key = $source . '_category_id'; $path_key = $source . '_category_path'; if (array_key_exists($id_key, $raw) || array_key_exists($path_key, $raw)) { $id = isset($raw[$id_key]) ? (int) $raw[$id_key] : 0; $path = isset($raw[$path_key]) ? (string) $raw[$path_key] : ''; $patch[$id_key] = $id > 0 ? $id : null; $patch[$path_key] = $path !== '' ? $path : null; } } } self::save($term->taxonomy, $term_id, $patch); } public function enqueue_term_admin_assets($hook) { if ($hook !== 'term.php') return; $tax = isset($_GET['taxonomy']) ? sanitize_key(wp_unslash($_GET['taxonomy'])) : ''; if (!$tax || !in_array($tax, $this->description_editor_taxonomies(), true)) return; wp_enqueue_media(); wp_enqueue_editor(); wp_enqueue_style( 'aieo-dmm-category-extras-admin', AIEO_URI . 'assets/css/aieo-dmm-category-extras-admin.css', array(), AIEO_VERSION ); wp_enqueue_script( 'aieo-dmm-category-extras-admin', AIEO_URI . 'assets/js/aieo-dmm-category-extras-admin.js', array('jquery'), AIEO_VERSION, true ); wp_localize_script('aieo-dmm-category-extras-admin', 'aieoCategoryExtras', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce(AIEO_DMM::NONCE_ACTION), 'i18n' => array( 'noResults' => __('No matches.', 'ai-eshop-optimizer'), 'searching' => __('Searching…', 'ai-eshop-optimizer'), 'pickImage' => __('Select image', 'ai-eshop-optimizer'), 'useImage' => __('Use this image', 'ai-eshop-optimizer'), 'notSet' => __('(not set)', 'ai-eshop-optimizer'), 'rootLabel' => __('Top level', 'ai-eshop-optimizer'), 'pickPrompt' => __('— pick —', 'ai-eshop-optimizer'), 'picked' => __('Picked', 'ai-eshop-optimizer'), 'browseAria' => __('Browse taxonomy level', 'ai-eshop-optimizer'), ), 'mt' => array( 'actionBrowse' => 'aieo_dmm_marketplace_tax_browse', 'actionSearch' => 'aieo_dmm_marketplace_tax_search', ), )); } public function ajax_search() { AIEO_DMM::verify_ajax_request(); $source = isset($_POST['source']) ? sanitize_text_field(wp_unslash($_POST['source'])) : ''; $q = isset($_POST['q']) ? sanitize_text_field(wp_unslash($_POST['q'])) : ''; $allowed_post_types = (array) apply_filters( 'aieo_dmm_category_extras_search_post_types', array('post', 'campaign', 'page') ); $allowed_taxonomies = (array) apply_filters( 'aieo_dmm_category_extras_search_taxonomies', array('product_brand', 'product_cat') ); $items = array(); if (strpos($source, 'post:') === 0) { $type = sanitize_key(substr($source, 5)); if (in_array($type, $allowed_post_types, true)) { $posts = get_posts(array( 'post_type' => $type, 'post_status' => 'publish', 's' => $q, 'posts_per_page' => 20, 'orderby' => 'date', 'order' => 'DESC', 'no_found_rows' => true, )); foreach ($posts as $p) { $items[] = array('id' => (int) $p->ID, 'label' => get_the_title($p) ?: '#' . $p->ID); } } } elseif (strpos($source, 'taxonomy:') === 0) { $tax = sanitize_key(substr($source, 9)); if (in_array($tax, $allowed_taxonomies, true) && taxonomy_exists($tax)) { $terms = get_terms(array( 'taxonomy' => $tax, 'hide_empty' => false, 'name__like' => $q, 'number' => 20, 'orderby' => 'name', 'order' => 'ASC', )); if (!is_wp_error($terms)) { foreach ($terms as $t) { $items[] = array('id' => (int) $t->term_id, 'label' => $t->name); } } } } wp_send_json_success(array('items' => $items)); } public function ajax_marketplace_tax_browse() { AIEO_DMM::verify_ajax_request(); $source = isset($_POST['source']) ? sanitize_key(wp_unslash($_POST['source'])) : ''; $parent_id = isset($_POST['parent_id']) ? (int) $_POST['parent_id'] : 0; if (!in_array($source, array('google', 'facebook'), true)) { wp_send_json_error(array('message' => __('Bad source.', 'ai-eshop-optimizer'))); } if (!class_exists('AIEO_DMM_Marketplace_Taxonomy')) { wp_send_json_error(array('message' => __('Marketplace taxonomy helper unavailable.', 'ai-eshop-optimizer'))); } $children = AIEO_DMM_Marketplace_Taxonomy::get_children($source, $parent_id); $out = array(); foreach ($children as $n) { $has_kids = !empty(AIEO_DMM_Marketplace_Taxonomy::get_children($source, (int) $n['i'])); $out[] = array( 'id' => (int) $n['i'], 'name' => (string) $n['n'], 'path' => (string) $n['h'], 'has_children' => $has_kids, 'depth' => (int) $n['d'], ); } wp_send_json_success(array('nodes' => $out)); } public function ajax_marketplace_tax_search() { AIEO_DMM::verify_ajax_request(); $source = isset($_POST['source']) ? sanitize_key(wp_unslash($_POST['source'])) : ''; $q = isset($_POST['q']) ? sanitize_text_field(wp_unslash($_POST['q'])) : ''; if (!in_array($source, array('google', 'facebook'), true)) { wp_send_json_error(array('message' => __('Bad source.', 'ai-eshop-optimizer'))); } if (!class_exists('AIEO_DMM_Marketplace_Taxonomy')) { wp_send_json_error(array('message' => __('Marketplace taxonomy helper unavailable.', 'ai-eshop-optimizer'))); } $hits = AIEO_DMM_Marketplace_Taxonomy::search($source, $q, 25); $out = array(); foreach ($hits as $n) { $out[] = array( 'id' => (int) $n['i'], 'name' => (string) $n['n'], 'path' => (string) $n['h'], 'site_path' => (string) ($n['site_path'] ?? $n['h']), 'matched_locale' => (string) ($n['matched_locale'] ?? ''), 'depth' => (int) $n['d'], ); } wp_send_json_success(array('hits' => $out)); } public static function get_template($taxonomy, $term_id) { $row = self::get($taxonomy, $term_id); if (!$row || empty($row['template'])) return 'default'; $allowed = array_keys(self::templates()); return in_array($row['template'], $allowed, true) ? $row['template'] : 'default'; } public static function should_hide_products($taxonomy, $term_id) { $tpl = self::get_template($taxonomy, $term_id); return in_array($tpl, array('landing', 'hidden'), true); } private function current_term() { if (!function_exists('is_tax') && !function_exists('is_category')) return null; $obj = get_queried_object(); if (!$obj || empty($obj->term_id) || empty($obj->taxonomy)) return null; if (!in_array($obj->taxonomy, $this->managed_taxonomies(), true)) return null; return $obj; } public function shortcode_category_hero($atts = array()) { $atts = shortcode_atts(array( 'taxonomy' => '', 'term_id' => 0, 'template' => '', ), (array) $atts, 'aieo_category_hero'); if (empty($atts['taxonomy']) || empty($atts['term_id'])) { $term = $this->current_term(); if (!$term) return ''; $atts['taxonomy'] = $term->taxonomy; $atts['term_id'] = (int) $term->term_id; } return $this->render_hero($atts['taxonomy'], (int) $atts['term_id'], (string) $atts['template']); } public function register_block() { if (!function_exists('register_block_type')) return; if (class_exists('AIEO_Engine_Blocks', false) && AIEO_Engine_Blocks::none_rendered()) { return; } register_block_type(__DIR__ . '/category-blocks/category-hero', array( 'title' => __('Rooster One Category Hero', 'ai-eshop-optimizer'), 'category' => 'widgets', 'icon' => 'cover-image', 'attributes' => array( 'taxonomy' => array('type' => 'string', 'default' => ''), 'termId' => array('type' => 'number', 'default' => 0), 'template' => array('type' => 'string', 'default' => ''), 'align' => array('type' => 'string'), ), 'supports' => array( 'align' => array('wide', 'full'), ), 'render_callback' => array($this, 'render_block_callback'), )); register_block_type(__DIR__ . '/category-blocks/category-chips', array( 'title' => __('Rooster One Category Chips', 'ai-eshop-optimizer'), 'category' => 'widgets', 'icon' => 'tag', 'attributes' => array( 'taxonomy' => array('type' => 'string', 'default' => ''), 'termId' => array('type' => 'number', 'default' => 0), 'align' => array('type' => 'string'), ), 'supports' => array( 'align' => array('wide', 'full'), ), 'render_callback' => array($this, 'render_chips_block_callback'), )); if (!wp_style_is('aieo-dmm-category-extras', 'registered')) { $css_rel = 'assets/css/aieo-dmm-category-extras.css'; $css_path = defined('AIEO_DIR') ? AIEO_DIR . $css_rel : ''; $css_ver = ($css_path && file_exists($css_path)) ? (string) filemtime($css_path) : (defined('AIEO_VERSION') ? AIEO_VERSION : '1.0'); wp_register_style('aieo-dmm-category-extras', AIEO_URI . $css_rel, array(), $css_ver); } register_block_type(__DIR__ . '/category-blocks/page-hero', array( 'title' => __('Rooster One Page Hero', 'ai-eshop-optimizer'), 'category' => 'theme', 'icon' => 'cover-image', 'description' => __('Manual hero — top title, left headline, right description and desktop/mobile images. Identical look to the category hero, usable on any page.', 'ai-eshop-optimizer'), 'keywords' => array('hero', 'banner', 'title', 'headline', 'image'), 'attributes' => array( 'topTitle' => array('type' => 'string', 'default' => ''), 'headline' => array('type' => 'string', 'default' => ''), 'description' => array('type' => 'string', 'default' => ''), 'desktopId' => array('type' => 'number', 'default' => 0), 'desktopUrl' => array('type' => 'string', 'default' => ''), 'mobileId' => array('type' => 'number', 'default' => 0), 'mobileUrl' => array('type' => 'string', 'default' => ''), 'align' => array('type' => 'string', 'default' => ''), ), 'supports' => array('align' => array('wide', 'full')), 'style' => 'aieo-dmm-category-extras', 'editor_style' => 'aieo-dmm-category-extras', 'render_callback' => array($this, 'render_page_hero_block'), )); register_block_type(__DIR__ . '/category-blocks/blog-hero', array( 'title' => __('Rooster One Blog Hero (Latest Post)', 'ai-eshop-optimizer'), 'category' => 'theme', 'icon' => 'admin-post', 'description' => __('Featured latest-post hero with the category-hero look: a top title (default “Blog”), the newest post’s title as the headline, its excerpt as the description and its featured image — all clickable, no underline. On a blog category/tag archive it features that term’s latest post.', 'ai-eshop-optimizer'), 'keywords' => array('blog', 'hero', 'header', 'latest', 'post', 'archive', 'category'), 'attributes' => array( 'postId' => array('type' => 'number', 'default' => 0), 'topTitle' => array('type' => 'string', 'default' => ''), 'showExcerpt' => array('type' => 'boolean', 'default' => true), 'align' => array('type' => 'string', 'default' => ''), ), 'supports' => array('align' => array('wide', 'full')), 'style' => 'aieo-dmm-category-extras', 'editor_style' => 'aieo-dmm-category-extras', 'render_callback' => array($this, 'render_blog_hero_block'), )); register_block_type(__DIR__ . '/category-blocks/blog-subcategories', array( 'title' => __('Rooster One Blog Subcategories (Dropdown)', 'ai-eshop-optimizer'), 'category' => 'theme', 'icon' => 'list-view', 'description' => __('A dropdown of the current blog category’s subcategories, styled like the blog-post categories dropdown. Renders nothing when the category has none.', 'ai-eshop-optimizer'), 'keywords' => array('blog', 'category', 'subcategories', 'dropdown', 'filter'), 'attributes' => array( 'categoryId' => array('type' => 'number', 'default' => 0), 'label' => array('type' => 'string', 'default' => ''), ), 'supports' => array('spacing' => array('margin' => true, 'padding' => true), 'typography' => array('fontSize' => true)), 'style' => 'aieo-dmm-category-extras', 'editor_style' => 'aieo-dmm-category-extras', 'render_callback' => array($this, 'render_blog_subcategories_block'), )); register_block_type(__DIR__ . '/category-blocks/category-lead', array( 'title' => __('Rooster One Category Lead', 'ai-eshop-optimizer'), 'category' => 'theme', 'icon' => 'text', 'description' => __('The current category’s description, falling back to its name when it has none. For blog category archive headers.', 'ai-eshop-optimizer'), 'keywords' => array('category', 'blog', 'description', 'title', 'archive'), 'attributes' => array('categoryId' => array('type' => 'number', 'default' => 0)), 'render_callback' => array($this, 'render_category_lead_block'), )); register_block_type(__DIR__ . '/category-blocks/category-footer-hero', array( 'title' => __('Rooster One Category Footer Hero', 'ai-eshop-optimizer'), 'category' => 'widgets', 'icon' => 'align-pull-left', 'description' => __('The category’s SEO footer text beside the category image (image left, rich text right) with the hero divider line. Auto-fills from the queried product category.', 'ai-eshop-optimizer'), 'keywords' => array('category', 'footer', 'seo', 'hero', 'archive', 'description'), 'attributes' => array( 'taxonomy' => array('type' => 'string', 'default' => ''), 'termId' => array('type' => 'number', 'default' => 0), 'align' => array('type' => 'string'), ), 'supports' => array('align' => array('wide', 'full')), 'style' => 'aieo-dmm-category-extras', 'editor_style' => 'aieo-dmm-category-extras', 'render_callback' => array($this, 'render_category_footer_hero_block'), )); } public function render_block_callback($attributes) { $tax = isset($attributes['taxonomy']) ? sanitize_key((string) $attributes['taxonomy']) : ''; $tid = isset($attributes['termId']) ? (int) $attributes['termId'] : 0; $tpl = isset($attributes['template']) ? sanitize_key((string) $attributes['template']) : ''; $align = isset($attributes['align']) ? sanitize_key((string) $attributes['align']) : ''; if (!$tax || !$tid) { $term = $this->current_term(); if (!$term) return ''; $tax = $term->taxonomy; $tid = (int) $term->term_id; } $html = $this->render_hero($tax, $tid, $tpl); if ($html && in_array($align, array('wide', 'full'), true)) { $html = preg_replace( '/^
render_responsive_image($mobile_id, $desktop_id, $title, true); if ($title === '' && $headline === '' && $desc === '' && $img_html === '') { return ''; } $extra = in_array($align, array('wide', 'full'), true) ? array('align' . $align) : array(); return $this->build_hero_html($title, $headline, $desc, $img_html, 'default', $extra); } public function render_blog_hero_block($attributes) { $attributes = is_array($attributes) ? $attributes : array(); $post_id = isset($attributes['postId']) ? (int) $attributes['postId'] : 0; if ($post_id <= 0) { $qo = get_queried_object(); if ($qo instanceof WP_Term && in_array($qo->taxonomy, array('category', 'post_tag'), true)) { $post_id = $this->latest_post_in_term($qo->taxonomy, (int) $qo->term_id); } else { $ids = get_posts(array('post_type' => 'post', 'posts_per_page' => 1, 'post_status' => 'publish', 'ignore_sticky_posts' => true, 'no_found_rows' => true, 'fields' => 'ids')); $post_id = !empty($ids) ? (int) $ids[0] : 0; } } if ($post_id <= 0 || get_post_type($post_id) !== 'post' || get_post_status($post_id) !== 'publish') { return ''; } $top = isset($attributes['topTitle']) ? trim((string) $attributes['topTitle']) : ''; if ($top === '') { $qo = get_queried_object(); $top = ($qo instanceof WP_Term) ? (string) $qo->name : __('Blog', 'ai-eshop-optimizer'); } $headline = get_the_title($post_id); $show_excerpt = !isset($attributes['showExcerpt']) || !empty($attributes['showExcerpt']); $desc = ''; if ($show_excerpt) { $desc = has_excerpt($post_id) ? get_the_excerpt($post_id) : wp_trim_words(wp_strip_all_tags(strip_shortcodes((string) get_post_field('post_content', $post_id))), 30); } $link = (string) get_permalink($post_id); $thumb_id = (int) get_post_thumbnail_id($post_id); $img_html = $thumb_id ? $this->render_responsive_image($thumb_id, $thumb_id, $headline, true) : ''; $align = isset($attributes['align']) ? sanitize_key((string) $attributes['align']) : ''; $extra = in_array($align, array('wide', 'full'), true) ? array('align' . $align) : array(); return $this->build_hero_html($top, $headline, $desc, $img_html, 'blog', $extra, $link); } public function render_blog_subcategories_block($attributes) { $attributes = is_array($attributes) ? $attributes : array(); $term_id = isset($attributes['categoryId']) ? (int) $attributes['categoryId'] : 0; if ($term_id <= 0) { $qo = get_queried_object(); if ($qo instanceof WP_Term && $qo->taxonomy === 'category') { $term_id = (int) $qo->term_id; } } if ($term_id <= 0) { return ''; } $children = get_terms(array( 'taxonomy' => 'category', 'parent' => $term_id, 'hide_empty' => false, 'orderby' => 'name', 'order' => 'ASC', )); if (empty($children) || is_wp_error($children)) { $term = get_term($term_id, 'category'); $parent_id = ($term && !is_wp_error($term)) ? (int) $term->parent : 0; if ($parent_id > 0) { $children = get_terms(array( 'taxonomy' => 'category', 'parent' => $parent_id, 'hide_empty' => false, 'orderby' => 'name', 'order' => 'ASC', )); } } if (empty($children) || is_wp_error($children)) { return ''; } $label = (isset($attributes['label']) && trim((string) $attributes['label']) !== '') ? trim((string) $attributes['label']) : __('Categories', 'ai-eshop-optimizer'); $opts = array(); foreach ($children as $child) { $link = get_term_link($child); if (!$link || is_wp_error($link)) { continue; } $opts[] = array('name' => $child->name, 'url' => (string) $link); } if (empty($opts)) { return ''; } $wrapper = get_block_wrapper_attributes(array('class' => 'aieo-blog-subcats')); $arrow = 'data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2012%2012%22%20fill=%22%23222222%22%3E%3Cpath%20d=%22M6%208.5%201.5%204h9z%22/%3E%3C/svg%3E'; $sel_style = 'appearance:none;-webkit-appearance:none;-moz-appearance:none;width:auto;max-width:100%;' . 'padding:7px 28px 7px 12px;border:1px solid var(--wp--preset--color--contrast,#1a1a1a);border-radius:999px;' . 'background-color:transparent;color:inherit;font-family:inherit;font-size:0.8rem;line-height:1.2;cursor:pointer;' . 'background-image:url(' . $arrow . ');background-repeat:no-repeat;background-position:right 10px center;background-size:9px 9px;'; ob_start(); ?>
>
taxonomy === 'category') { $term_id = (int) $qo->term_id; } } if ($term_id <= 0) { return ''; } $term = get_term($term_id, 'category'); if (!$term || is_wp_error($term)) { return ''; } $desc = trim((string) $term->description); $is_desc = ($desc !== ''); $text = $is_desc ? $desc : (string) $term->name; if (trim($text) === '') { return ''; } $wrapper = get_block_wrapper_attributes(array('class' => 'rooster-blog-archive__term-description roosterx-blog-archive__term-description aieo-category-lead' . ($is_desc ? '' : ' aieo-category-lead--name'))); return '
' . ($is_desc ? wp_kses_post(wpautop($desc)) : esc_html($text)) . '
'; } public function render_category_footer_hero_block($attributes) { $attributes = is_array($attributes) ? $attributes : array(); $tax = isset($attributes['taxonomy']) ? sanitize_key((string) $attributes['taxonomy']) : ''; $tid = isset($attributes['termId']) ? (int) $attributes['termId'] : 0; if (!$tax || $tid <= 0) { $qo = get_queried_object(); if ($qo instanceof WP_Term) { $tax = (string) $qo->taxonomy; $tid = (int) $qo->term_id; } } if (!$tax || $tid <= 0) { return ''; } $row = array(); $footer = ''; if ($tax === 'product_brand' && class_exists('AIEO_DMM_Brand_Extras_Data')) { $row = AIEO_DMM_Brand_Extras_Data::get($tax, $tid); $footer = (string) (is_array($row) ? ($row['footer_text'] ?? '') : ''); } elseif ($tax === 'pa_need' && class_exists('AIEO_DMM_Need_Extras_Data')) { $row = AIEO_DMM_Need_Extras_Data::get($tax, $tid); $footer = (string) (is_array($row) ? ($row['footer_text'] ?? '') : ''); } elseif ($tax === 'product_cat') { $row = self::get($tax, $tid); $footer = is_array($row) ? (string) ($row['footer_text'] ?? '') : ''; } $term = get_term($tid, $tax); $footer = (string) apply_filters('aieo_taxonomy_archive_footer_text', $footer, $tax, $tid, ($term && !is_wp_error($term)) ? $term : null); $footer = trim($footer); if ($footer === '') { return ''; } $img_html = ''; if ($tax === 'product_cat' && is_array($row)) { $mobile_id = !empty($row['image_mobile_id']) ? (int) $row['image_mobile_id'] : 0; $desktop_id = !empty($row['image_desktop_id']) ? (int) $row['image_desktop_id'] : 0; if ((!$mobile_id || !$desktop_id) && $term && !is_wp_error($term) && !empty($term->parent)) { $ancestor = $this->resolve_root_ancestor($tax, $tid); if ($ancestor) { $anc_row = self::get($tax, (int) $ancestor->term_id); if (is_array($anc_row)) { if (!$mobile_id && !empty($anc_row['image_mobile_id'])) $mobile_id = (int) $anc_row['image_mobile_id']; if (!$desktop_id && !empty($anc_row['image_desktop_id'])) $desktop_id = (int) $anc_row['image_desktop_id']; } } } $alt = ($term && !is_wp_error($term)) ? $term->name : ''; $img_html = $this->render_responsive_image($mobile_id, $desktop_id, $alt); } $align = isset($attributes['align']) ? sanitize_html_class((string) $attributes['align']) : ''; $align_cls = ('full' === $align) ? ' alignfull' : (('wide' === $align) ? ' alignwide' : ''); $no_img = $img_html ? '' : ' aieo-category-footer-hero--no-image'; $img_kses = array( 'picture' => array('class' => array()), 'source' => array('media' => array(), 'srcset' => array(), 'data-srcset' => array(), 'sizes' => array(), 'type' => array()), 'img' => array('src' => array(), 'srcset' => array(), 'sizes' => array(), 'alt' => array(), 'class' => array(), 'width' => array(), 'height' => array(), 'loading' => array(), 'decoding' => array()), 'figure' => array('class' => array()), 'span' => array('class' => array()), ); ob_start(); ?> 'post', 'posts_per_page' => 1, 'post_status' => 'publish', 'ignore_sticky_posts' => true, 'no_found_rows' => true, 'fields' => 'ids', 'tax_query' => array(array('taxonomy' => (string) $taxonomy, 'field' => 'term_id', 'terms' => $term_id)), )); return !empty($ids) ? (int) $ids[0] : 0; } public function exclude_blog_hero_post($query) { if (is_admin() || !($query instanceof WP_Query) || !$query->is_main_query() || !$query->is_category()) { return; } $term_id = (int) $query->get('cat'); if ($term_id <= 0) { $slug = (string) $query->get('category_name'); if ($slug !== '') { $slug = sanitize_title(basename(untrailingslashit($slug))); $t = get_term_by('slug', $slug, 'category'); if ($t && !is_wp_error($t)) { $term_id = (int) $t->term_id; } } } if ($term_id <= 0) { return; } $featured = $this->latest_post_in_term('category', $term_id); if ($featured <= 0) { return; } if (!apply_filters('aieo_dmm_exclude_blog_hero_post', true, $term_id, $query)) { return; } $exclude = array_map('intval', (array) $query->get('post__not_in')); if (!in_array($featured, $exclude, true)) { $exclude[] = $featured; $query->set('post__not_in', $exclude); } } public function render_hero($taxonomy, $term_id, $template_override = '') { $row = self::get($taxonomy, $term_id); $term = get_term($term_id, $taxonomy); if (!$term || is_wp_error($term)) return ''; $template = $template_override !== '' ? sanitize_key($template_override) : self::get_template($taxonomy, $term_id); if ($template === 'hidden') return ''; $row = is_array($row) ? $row : array(); $title = !empty($row['seo_title']) ? (string) $row['seo_title'] : $term->name; $headline = !empty($row['headline']) ? (string) $row['headline'] : ''; $desc = (string) $term->description; $mobile_id = !empty($row['image_mobile_id']) ? (int) $row['image_mobile_id'] : 0; $desktop_id = !empty($row['image_desktop_id']) ? (int) $row['image_desktop_id'] : 0; if ((!$mobile_id || !$desktop_id) && !empty($term->parent)) { $ancestor = $this->resolve_root_ancestor($taxonomy, (int) $term->term_id); if ($ancestor) { $anc_row = self::get($taxonomy, (int) $ancestor->term_id); if (is_array($anc_row)) { if (!$mobile_id && !empty($anc_row['image_mobile_id'])) $mobile_id = (int) $anc_row['image_mobile_id']; if (!$desktop_id && !empty($anc_row['image_desktop_id'])) $desktop_id = (int) $anc_row['image_desktop_id']; } } } $img_html = $this->render_responsive_image($mobile_id, $desktop_id, $title, true); return $this->build_hero_html($title, $headline, $desc, $img_html, $template); } private function build_hero_html($title, $headline, $desc, $img_html, $template = 'default', $extra_classes = array(), $link = '') { $link = (string) $link; $classes = array_merge(array( 'aieo-category-hero', 'aieo-category-hero--' . sanitize_html_class($template), ), (array) $extra_classes); if ($link !== '') { $classes[] = 'aieo-category-hero--linked'; } if (!$headline) $classes[] = 'aieo-category-hero--no-headline'; if (!$desc) $classes[] = 'aieo-category-hero--no-description'; ob_start(); ?>

' . wp_kses_post($headline) . ''; } else { echo wp_kses_post($headline); } ?>
' . wp_kses_post(wpautop($desc)) . ''; } else { echo wp_kses_post(wpautop($desc)); } ?>

parent > 0) { $current = get_term((int) $current->parent, $taxonomy); } $cache[$key] = ($current && !is_wp_error($current)) ? $current : null; return $cache[$key]; } private function render_responsive_image($mobile_id, $desktop_id, $alt_fallback, $is_lcp = false) { if (!$mobile_id && !$desktop_id) return ''; $alt = (string) $alt_fallback; $img_attr = array('alt' => $alt, 'class' => 'aieo-category-hero__img', 'loading' => $is_lcp ? 'eager' : 'lazy', 'decoding' => 'async'); if ($is_lcp) { $img_attr['fetchpriority'] = 'high'; } if ($mobile_id && $desktop_id) { $desktop_url = wp_get_attachment_image_url($desktop_id, 'full'); $desktop_srcset = wp_get_attachment_image_srcset($desktop_id, 'full'); $mobile_img = wp_get_attachment_image( $mobile_id, 'large', false, $img_attr ); if ($mobile_img && $desktop_url) { $sources = '' . $sources . $mobile_img . ''; } } $only = $desktop_id ?: $mobile_id; return wp_get_attachment_image( $only, 'large', false, $img_attr ); } private function resolve_chip_siblings($taxonomy, $term_id) { $out = array('parent' => null, 'items' => array()); $term = get_term((int) $term_id, $taxonomy); if (!$term || is_wp_error($term)) return $out; $kids = get_term_children($term->term_id, $taxonomy); $parent_id = (!empty($kids) && !is_wp_error($kids)) ? (int) $term->term_id : ((int) $term->parent ?: (int) $term->term_id); $args = (array) apply_filters('aieo_dmm_category_chips_query_args', array( 'taxonomy' => $taxonomy, 'parent' => $parent_id, 'hide_empty' => true, 'orderby' => 'menu_order', 'order' => 'ASC', ), $taxonomy, $term_id); $siblings = get_terms($args); if (is_wp_error($siblings)) $siblings = array(); $excluded = $this->excluded_term_keys_normalised(); if (!empty($excluded)) { $siblings = array_values(array_filter($siblings, static function ($t) use ($taxonomy, $excluded) { return !isset($excluded[$taxonomy . ':' . (int) $t->term_id]); })); } $out['items'] = $siblings; if ($parent_id !== (int) $term->term_id) { $parent = get_term($parent_id, $taxonomy); if ($parent && !is_wp_error($parent)) { $out['parent'] = $parent; } } return $out; } private function excluded_term_keys_normalised() { $raw = (string) AIEO_DMM_Settings::get('navigator_excluded_terms', ''); if ($raw === '') return array(); $out = array(); foreach (preg_split('/[,\s]+/', $raw) as $tok) { $tok = trim($tok); if ($tok === '') continue; if (strpos($tok, ':') === false) { $out[] = 'product_cat:' . (int) $tok; } else { list($tax, $id) = explode(':', $tok, 2); $out[] = sanitize_key($tax) . ':' . (int) $id; } } return array_flip(array_unique($out)); } public function shortcode_category_chips($atts = array()) { $atts = shortcode_atts(array( 'taxonomy' => '', 'term_id' => 0, ), (array) $atts, 'aieo_category_chips'); if (empty($atts['taxonomy']) || empty($atts['term_id'])) { $term = $this->current_term(); if (!$term) return ''; $atts['taxonomy'] = $term->taxonomy; $atts['term_id'] = (int) $term->term_id; } return $this->render_chip_strip($atts['taxonomy'], (int) $atts['term_id']); } public function render_chips_block_callback($attributes) { $tax = isset($attributes['taxonomy']) ? sanitize_key((string) $attributes['taxonomy']) : ''; $tid = isset($attributes['termId']) ? (int) $attributes['termId'] : 0; $align = isset($attributes['align']) ? sanitize_key((string) $attributes['align']) : ''; if (!$tax || !$tid) { $term = $this->current_term(); if (!$term) return ''; $tax = $term->taxonomy; $tid = (int) $term->term_id; } $html = $this->render_chip_strip($tax, $tid); if ($html && in_array($align, array('wide', 'full'), true)) { $html = preg_replace( '/^(\s*)