HEX
Server: Apache
System: Linux webd001.cluster127.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
User: mciraet (192513)
PHP: 8.2.31
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
File: /home/mciraet/www/wp-content/plugins/categories-images/categories-images.php
<?php
/**
 * @package CategoriesImages
 */

/**
 * Plugin Name: Categories Images
 * Plugin URI: https://zahlan.net/blog/categories-images/
 * Description: Categories Images Plugin allow you to add an image to category or any custom term.
 * Author: Muhammad El Zahlan
 * Version: 3.3.3
 * Author URI: https://zahlan.net/
 * Domain Path: /languages
 * Text Domain: categories-images
 * License: GPLv2 or later
 */

if (!defined('ABSPATH'))
    die;

if (!defined('Z_PLUGIN_URL'))
    define('Z_PLUGIN_URL', untrailingslashit(plugins_url('', __FILE__)));

if (!defined('ZCI_VERSION'))
    define('ZCI_VERSION', '3.3.3');

class ZCategoriesImages
{
    public $plugin_name;
    private $zci_placeholder;
    private static $instance = null;

    /**
     * Singleton instance
     */
    public static function get_instance() {
        if ( null == self::$instance ) {
            self::$instance = new self;
        }
        return self::$instance;
    }

    private function __construct() {
        $this->plugin_name = plugin_basename(__FILE__);

        // The placeholder image url
        $this->zci_placeholder = plugins_url('/assets/images/placeholder.png', __FILE__);
        add_action('init', [$this, 'zInit']);
        add_action('admin_init', [$this, 'zAdminInit']);

        // save our taxonomy image while edit or create term
        add_action('edit_term', [$this, 'zSaveTaxonomyImage']);
        add_action('create_term', [$this, 'zSaveTaxonomyImage']);

        // Plugin menu in admin panel
        add_action('admin_menu', [$this, 'zSettingsMenu']);

        // Settings page link in plugins list
        add_filter("plugin_action_links_{$this->plugin_name}", [$this, 'zSettingsLink']);

        // Elementor Dynamic Tag Registration
        add_action('elementor/dynamic_tags/register', [$this, 'zRegisterElementorTags']);

        // Register REST API Field
        add_action('rest_api_init', [$this, 'zInitRestApi']);

        // Enqueue frontend styles
        add_action('wp_enqueue_scripts', [$this, 'zPublicEnqueue']);
    }

    /**
     * Abstraction for retrieving term metadata
     */
    public function zci_get_term_meta( $term_id, $key ) {
        if ( function_exists( 'get_term_meta' ) ) {
            return get_term_meta( $term_id, $key, true );
        } else {
            return get_option( $key . $term_id );
        }
    }

    /**
     * Abstraction for updating term metadata
     */
    public function zci_update_term_meta( $term_id, $key, $value ) {
        if ( function_exists( 'update_term_meta' ) ) {
            return update_term_meta( $term_id, $key, $value );
        } else {
            return update_option( $key . $term_id, $value );
        }
    }

    /**
     * Abstraction for deleting term metadata
     */
    public function zci_delete_term_meta( $term_id, $key ) {
        if ( function_exists( 'delete_term_meta' ) ) {
            return delete_term_meta( $term_id, $key );
        } else {
            return delete_option( $key . $term_id );
        }
    }

    function zAdminInit() {
        // Migration Routine: Move data from options to term_meta if supported and not yet done
        if ( function_exists( 'get_term_meta' ) && ! get_option( 'zci_migrated_to_termmeta' ) ) {
            $taxonomies = get_taxonomies();
            foreach ( $taxonomies as $taxonomy ) {
                $terms = get_terms( $taxonomy, ['hide_empty' => false] );
                if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
                    foreach ( $terms as $term ) {
                        // Check for old option data
                        $app_option_url = get_option( 'z_taxonomy_image' . $term->term_id );
                        $app_option_id  = get_option( 'z_taxonomy_image_id' . $term->term_id );

                        if ( $app_option_url !== false ) {
                            update_term_meta( $term->term_id, 'z_taxonomy_image', $app_option_url );
                            delete_option( 'z_taxonomy_image' . $term->term_id );
                        }
                        if ( $app_option_id !== false ) {
                            update_term_meta( $term->term_id, 'z_taxonomy_image_id', $app_option_id );
                            delete_option( 'z_taxonomy_image_id' . $term->term_id );
                        }
                    }
                }
            }
            update_option( 'zci_migrated_to_termmeta', 1 );
        }

        $z_taxonomies = get_taxonomies();
        if (is_array($z_taxonomies)) {
            $zci_options = (array) get_option('zci_options', []);
            
            if (empty($zci_options['excluded_taxonomies']) || !is_array($zci_options['excluded_taxonomies']))
                $zci_options['excluded_taxonomies'] = [];
            
            foreach ($z_taxonomies as $z_taxonomy) {
                if (in_array($z_taxonomy, $zci_options['excluded_taxonomies']))
                    continue;
                add_action($z_taxonomy.'_add_form_fields', [$this, 'zAddTexonomyField']);
                add_action($z_taxonomy.'_edit_form_fields', [$this, 'zEditTexonomyField']);
                add_filter('manage_edit-'.$z_taxonomy.'_columns', [$this, 'zTaxonomyColumns']);
                add_filter('manage_'.$z_taxonomy.'_custom_column', [$this, 'zTaxonomyColumn'], 10, 3 );

                // If tax is deleted
                // Note: term_meta deletes automatically when term is deleted in WP 4.4+, but we need to handle fallback.
                add_action("delete_{$z_taxonomy}", [$this, 'zDeleteTaxonomyData']);
            }
        }

        // Register styles and scripts
        if ( strpos( $_SERVER['SCRIPT_NAME'], 'edit-tags.php' ) > 0 || strpos( $_SERVER['SCRIPT_NAME'], 'term.php' ) > 0 ) {
            add_action('admin_enqueue_scripts', [$this, 'zAdminEnqueue']);
            add_action('quick_edit_custom_box', [$this, 'zQuickEditCustomBox'], 10, 3);
        }

        // Register settings
        register_setting('zci_options', 'zci_options');
        add_settings_section('zci_settings', __('Categories Images settings', 'categories-images'), [$this, 'zSectionText'], 'zci-options');
        add_settings_field('z_excluded_taxonomies', __('Excluded Taxonomies', 'categories-images'), [$this, 'zExcludedTaxonomies'], 'zci-options', 'zci_settings');
    }

    function zInit() {
        // Register Shortcodes
        add_shortcode('z_taxonomy_image', [$this, 'z_taxonomy_image_shortcode']);
        add_shortcode('z_taxonomy_list', [$this, 'z_taxonomy_list_shortcode']);
    }

    function zDeleteTaxonomyData($tt_id) {
        // delete_term_meta handles itself, but for backward compatibility with options:
        if ( ! function_exists( 'delete_term_meta' ) ) {
            delete_option('z_taxonomy_image'.(int)$tt_id);
            delete_option('z_taxonomy_image_id'.(int)$tt_id);
        }
    }

    function zAdminEnqueue() {
        wp_enqueue_style('categories-images-admin-styles', plugins_url('/assets/css/zci-admin.css', __FILE__), [], ZCI_VERSION);
        wp_enqueue_script('categories-images-scripts', plugins_url('/assets/js/zci-scripts.js', __FILE__), [], ZCI_VERSION, true);

        $zci_js_config = [
            'wordpress_ver' => get_bloginfo("version"),
            'placeholder' => $this->zci_placeholder
        ];
        wp_localize_script('categories-images-scripts', 'zci_config', $zci_js_config);
    }
    
    function zPublicEnqueue() {
        wp_enqueue_style('categories-images-styles', plugins_url('/assets/css/zci-styles.css', __FILE__), [], ZCI_VERSION);
    }

    // add image field in add form
    function zAddTexonomyField() {
        wp_enqueue_media();
        
        $nonce = wp_create_nonce( 'zci_save_image' );
        
        echo '<div class="form-field">
            <input type="hidden" name="zci_nonce" value="' . esc_attr($nonce) . '" />
            <input type="hidden" name="zci_taxonomy_image_id" id="zci_taxonomy_image_id" value="" />
            <label for="zci_taxonomy_image">' . esc_html__('Image', 'categories-images') . '</label>
            <input type="text" name="zci_taxonomy_image" id="zci_taxonomy_image" value="" />
            <br/>
            <button class="z_upload_image_button button">' . esc_html__('Upload/Add image', 'categories-images') . '</button>
        </div>';
    }

    // add image field in edit form
    function zEditTexonomyField($taxonomy) {
        wp_enqueue_media();
        
        if ($this->zTaxonomyImageUrl( $taxonomy->term_id, NULL, TRUE ) == $this->zci_placeholder) {
            $image_url = "";
            $image_id  = "";
        } else {
            $image_url = $this->zTaxonomyImageUrl( $taxonomy->term_id, NULL, TRUE );
            $image_id  = $this->zTaxonomyImageID( $taxonomy->term_id );
        }
        
        $nonce = wp_create_nonce( 'zci_save_image' );

        echo '<tr class="form-field">
            <th scope="row" valign="top"><label for="zci_taxonomy_image">' . esc_html__('Image', 'categories-images') . '</label></th>
            <td>
            <input type="hidden" name="zci_nonce" value="' . esc_attr($nonce) . '" />
            <input type="hidden" name="zci_taxonomy_image_id" id="zci_taxonomy_image_id" value="'.esc_attr($image_id).'" /><img class="zci-taxonomy-image" src="' . esc_url( $this->zTaxonomyImageUrl( $taxonomy->term_id, 'medium', TRUE ) ) . '"/><br/><input type="text" name="zci_taxonomy_image" id="zci_taxonomy_image" value="'.esc_url($image_url).'" /><br />
            <button class="z_upload_image_button button">' . esc_html__('Upload/Add image', 'categories-images') . '</button>
            <button class="z_remove_image_button button">' . esc_html__('Remove image', 'categories-images') . '</button>
            </td>
        </tr>';
    }

    function zTaxonomyColumns( $columns ) {
        $new_columns = [];

        // Keep checkbox column if it exists
        if ( isset( $columns['cb'] ) ) {
            $new_columns['cb'] = $columns['cb'];
            unset( $columns['cb'] );
        }

        // Add image column
        $new_columns['thumb'] = esc_html__( 'Image', 'categories-images' );

        // Merge back the rest of the columns
        return array_merge( $new_columns, $columns );
    }

    function zTaxonomyColumn( $columns, $column, $id ) {
        if ( $column == 'thumb' ) {
            // Get full URL and ID for Quick Edit
            $full_url = $this->zTaxonomyImageUrl($id, 'full', FALSE);
            $image_id = $this->zTaxonomyImageID($id);
            
            $columns = '<span><img src="' . esc_url($this->zTaxonomyImageUrl($id, 'thumbnail', TRUE)) . '" alt="' . esc_attr(__('Thumbnail', 'categories-images')) . '" class="wp-post-image" />';
            $columns .= '<span class="zci-data" style="display:none;" data-url="' . esc_url($full_url) . '" data-id="' . esc_attr($image_id) . '"></span>';
            $columns .= '</span>';
        }
        
        return $columns;
    }

    function zQuickEditCustomBox($column_name, $screen, $name) {
        if ($column_name == 'thumb') {
            $nonce = wp_create_nonce( 'zci_save_image' );
            echo '<fieldset>
            <div class="thumb inline-edit-col">
                <label>
                    <span class="title"><img src="" alt="Thumbnail"/></span>
                    <span class="input-text-wrap"><input type="text" name="zci_taxonomy_image" value="" class="tax_list" /></span>
                    <span class="input-text-wrap">
                        <input type="hidden" name="zci_nonce" value="' . esc_attr($nonce) . '" />
                        <button class="z_upload_image_button button">' . esc_html__('Upload/Add image', 'categories-images') . '</button>
                        <button class="z_remove_image_button button">' . esc_html__('Remove image', 'categories-images') . '</button>
                    </span>
                    <span class="input-text-wrap">
                        <input type="hidden" name="zci_taxonomy_image_id" value="" />
                    </span>
                </label>
            </div>
        </fieldset>';
        }
    }

    function zSaveTaxonomyImage($term_id) {
        // Security check
        if ( ! isset( $_POST['zci_nonce'] ) || ! wp_verify_nonce( $_POST['zci_nonce'], 'zci_save_image' ) ) {
            return;
        }

        if(isset($_POST['zci_taxonomy_image'])) {
            $this->zci_update_term_meta($term_id, 'z_taxonomy_image', esc_url_raw($_POST['zci_taxonomy_image']));
        }
        if(isset($_POST['zci_taxonomy_image_id'])) {
            $this->zci_update_term_meta($term_id, 'z_taxonomy_image_id', absint($_POST['zci_taxonomy_image_id']));
        }
    }

    // get attachment ID by image url
    function zGetAttachmentIdByUrl($image_src) {
        $id = attachment_url_to_postid($image_src);
        return (!empty($id)) ? $id : NULL;
    }

    // get attachment ID by term id
    function zTaxonomyImageID($term_id = NULL) {
        if (!$term_id) {
            if (is_category())
                $term_id = get_query_var('cat');
            elseif (is_tag())
                $term_id = get_query_var('tag_id');
            elseif (is_tax()) {
                $current_term = get_term_by('slug', get_query_var('term'), get_query_var('taxonomy'));
                $term_id = $current_term->term_id;
            }
        }
        
        return $this->zci_get_term_meta($term_id, 'z_taxonomy_image_id');
    }

    // get taxonomy image url for the given term_id (Place holder image by default)
    function zTaxonomyImageUrl($term_id = NULL, $size = 'full', $return_placeholder = FALSE) {
        if (!$term_id) {
            if (is_category())
                $term_id = get_query_var('cat');
            elseif (is_tag())
                $term_id = get_query_var('tag_id');
            elseif (is_tax()) {
                $current_term = get_term_by('slug', get_query_var('term'), get_query_var('taxonomy'));
                $term_id = $current_term->term_id;
            }
        }
        
        $attachment_id = $this->zTaxonomyImageID($term_id);
        $taxonomy_image_url = '';

        if (!empty($attachment_id)) {
            $taxonomy_image_src = wp_get_attachment_image_src($attachment_id, $size);
            if ($taxonomy_image_src) {
                $taxonomy_image_url = $taxonomy_image_src[0];
            }
        }

        if (empty($taxonomy_image_url)) {
            $taxonomy_image_url = $this->zci_get_term_meta($term_id, 'z_taxonomy_image');
            if (!empty($taxonomy_image_url) && empty($attachment_id)) {
                $attachment_id = $this->zGetAttachmentIdByUrl($taxonomy_image_url);
                if (!empty($attachment_id)) {
                    $taxonomy_image_src = wp_get_attachment_image_src($attachment_id, $size);
                    if ($taxonomy_image_src) {
                        $taxonomy_image_url = $taxonomy_image_src[0];
                    }
                }
            }
        }

        if ($return_placeholder)
            return ($taxonomy_image_url != '') ? $taxonomy_image_url : $this->zci_placeholder;
        else
            return $taxonomy_image_url;
    }

    // display taxonomy image for the given term_id
    function zTaxonomyImage($term_id = NULL, $size = 'full', $attr = NULL, $echo = TRUE) {
        if (!$term_id) {
            if (is_category())
                $term_id = get_query_var('cat');
            elseif (is_tag())
                $term_id = get_query_var('tag_id');
            elseif (is_tax()) {
                $current_term = get_term_by('slug', get_query_var('term'), get_query_var('taxonomy'));
                $term_id = $current_term->term_id;
            }
        }
        
        $attachment_id = $this->zTaxonomyImageID($term_id);
        $taxonomy_image_url = '';
        $taxonomy_image = '';

        if (empty($attachment_id)) {
            $taxonomy_image_url = $this->zci_get_term_meta($term_id, 'z_taxonomy_image');
            if (!empty($taxonomy_image_url)) {
                $attachment_id = $this->zGetAttachmentIdByUrl($taxonomy_image_url);
            }
        } else {
            $taxonomy_image_url = $this->zci_get_term_meta($term_id, 'z_taxonomy_image');
        }

        if (!empty($attachment_id)) {
            $taxonomy_image = wp_get_attachment_image($attachment_id, $size, FALSE, $attr);
        }

        // Fallback to raw URL if wp_get_attachment_image failed or no ID found
        if (empty($taxonomy_image) && !empty($taxonomy_image_url)) {
            $image_attr = '';
            if(is_array($attr)) {
                if(!empty($attr['class']))
                    $image_attr .= ' class="'.esc_attr($attr['class']).'" ';
                if(!empty($attr['alt']))
                    $image_attr .= ' alt="'.esc_attr($attr['alt']).'" ';
                if(!empty($attr['width']))
                    $image_attr .= ' width="'.esc_attr($attr['width']).'" ';
                if(!empty($attr['height']))
                    $image_attr .= ' height="'.esc_attr($attr['height']).'" ';
                if(!empty($attr['title']))
                    $image_attr .= ' title="'.esc_attr($attr['title']).'" ';
            }
            $taxonomy_image = '<img src="'.esc_url($taxonomy_image_url).'" '.$image_attr.'/>';
        }

        if ($echo)
            echo wp_kses_post($taxonomy_image);
        else
            return $taxonomy_image;
    }

    function zSettingsMenu() {
        add_options_page(__('Categories Images settings', 'categories-images'), __('Categories Images', 'categories-images'), 'manage_options', 'zci_settings', [$this, 'zSettingsPage']);
    }

    // Plugin option page
    function zSettingsPage() {
        if (!current_user_can('manage_options'))
            wp_die(esc_html__( 'You do not have sufficient permissions to access this page.', 'categories-images'));
        
        // Enqueue admin styles for settings page if not already there
        wp_enqueue_style('categories-images-admin-styles', plugins_url('/assets/css/zci-admin.css', __FILE__), [], ZCI_VERSION);
        
        require_once plugin_dir_path(__FILE__).'templates/admin.php';
    }

    function zSettingsLink($links) {
        $settings_link = '<a href="options-general.php?page=zci_settings">Settings</a>';
        array_push($links, $settings_link);
        return $links;
    }

    // Settings section description
    function zSectionText() {
        echo '<p>'.esc_html__('Please select the taxonomies you want to exclude it from Categories Images plugin', 'categories-images').'</p>';
    }

    // Excluded taxonomies checkboxs
    function zExcludedTaxonomies() {
        $options = (array) get_option('zci_options', []);
        $disabled_taxonomies = ['nav_menu', 'link_category', 'post_format'];
        foreach (get_taxonomies() as $tax) : if (in_array($tax, $disabled_taxonomies)) continue; ?>
            <input type="checkbox" name="zci_options[excluded_taxonomies][<?php echo esc_attr($tax) ?>]" value="<?php echo esc_attr($tax) ?>" <?php checked(isset($options['excluded_taxonomies'][$tax]) && is_array($options['excluded_taxonomies']) && $options['excluded_taxonomies'][$tax] == $tax); ?> /> <?php echo esc_html($tax) ;?><br />
        <?php endforeach;
    }
    
    function activate() {
        // Things will happen if the plugin activated.
        flush_rewrite_rules();
    }

    function deactivate() {
        // Things will happen if the plugin deactivated.
        flush_rewrite_rules();
    }

    function zRegisterElementorTags( $dynamic_tags ) {
        // Include the tag class file
        if ( ! class_exists( 'ZCI_Elementor_Taxonomy_Image_Tag' ) ) {
            require_once plugin_dir_path( __FILE__ ) . 'includes/elementor-integration.php';
        }

        // Register the tag
        $dynamic_tags->register( new ZCI_Elementor_Taxonomy_Image_Tag() );
    }

    // Register field 'z_taxonomy_image_url' to the WP REST API
    function zInitRestApi() {
        $taxonomies = get_taxonomies();
        $zci_options = (array) get_option('zci_options', []);
        if (!isset($zci_options['excluded_taxonomies']) || !is_array($zci_options['excluded_taxonomies']))
            $zci_options['excluded_taxonomies'] = [];

        foreach ($taxonomies as $taxonomy) {
            if (in_array($taxonomy, $zci_options['excluded_taxonomies']))
                continue;
            
            register_rest_field($taxonomy, 'z_taxonomy_image_url', [
                'get_callback' => [$this, 'zGetTermImage'],
                'update_callback' => null,
                'schema' => null,
            ]);
        }
    }

    function zGetTermImage($object, $field_name, $request) {
        $term_id = $object['id'];
        $image_url = $this->zTaxonomyImageUrl($term_id, 'full', true);
        
        return $image_url;
    }

    /**
     * Shortcode [z_taxonomy_image]
     */
    function z_taxonomy_image_shortcode($atts) {
        $raw_atts = $atts;
        $atts = shortcode_atts([
            'term_id'  => '',
            'taxonomy' => 'category',
            'size'     => 'full',
            'class'    => '',
            'default'  => null,
            'link'     => 'no', // yes, no, custom_url
            'format'   => 'img' // img, url
        ], $atts);

        // Handle positional 'default' attribute
        if (is_array($raw_atts) && in_array('default', $raw_atts, true)) {
            $atts['default'] = "";
        }

        $term_id = $atts['term_id'];

        // If no term_id, try to detect
        if (empty($term_id)) {
            $term_id = get_queried_object_id();
        }

        // Output logic
        if ($atts['format'] === 'url') {
            $url = $this->zTaxonomyImageUrl($term_id, $atts['size'], true);
            if ($url == $this->zci_placeholder) {
                if ($atts['default'] === "") {
                    return $this->zci_placeholder;
                } elseif (!empty($atts['default'])) {
                    return $atts['default'];
                } elseif (is_null($atts['default'])) {
                    return ''; // No placeholder by default
                }
            }
            return $url;
        }

        $image = $this->zTaxonomyImage($term_id, $atts['size'], ['class' => $atts['class']], false);
        
        // Handle empty/placeholder
        if (empty($image) || strpos($image, 'placeholder.png') !== false) {
             if ($atts['default'] === "") {
                 $image = '<img src="' . esc_url($this->zci_placeholder) . '" class="' . esc_attr($atts['class']) . '" />';
             } elseif (!empty($atts['default'])) {
                 $image = '<img src="' . esc_url($atts['default']) . '" class="' . esc_attr($atts['class']) . '" />';
             } else {
                 $image = '';
             }
        }

        // Link wrapping
        if ($atts['link'] === 'yes') {
             $term_link = get_term_link((int)$term_id, $atts['taxonomy']);
             if (!is_wp_error($term_link)) {
                 $image = '<a href="' . esc_url($term_link) . '">' . $image . '</a>';
             }
        } elseif ($atts['link'] !== 'no') {
             // custom URL
             $image = '<a href="' . esc_url($atts['link']) . '">' . $image . '</a>';
        }

        return $image;
    }

    /**
     * Shortcode [z_taxonomy_list]
     */
    function z_taxonomy_list_shortcode($atts) {
        $atts = shortcode_atts([
            'taxonomy'   => 'category',
            'post_id'    => '',
            'include'    => '',
            'exclude'    => '',
            'parent'     => '',
            'orderby'    => 'name',
            'order'      => 'ASC',
            'hide_empty' => 'yes',
            'size'       => 'full',
            'style'      => 'list', // list, grid, inline
            'columns'    => '3',
            'show_name'  => 'no',
            'show_count' => 'no',
            'format'     => 'img'
        ], $atts);

        // Get Terms
        $args = [
            'taxonomy' => $atts['taxonomy'],
            'orderby' => $atts['orderby'],
            'order' => $atts['order'],
            'hide_empty' => ($atts['hide_empty'] === 'yes'),
        ];

        if (!empty($atts['include'])) {
            $args['include'] = explode(',', $atts['include']);
        }
        if (!empty($atts['exclude'])) {
            $args['exclude'] = explode(',', $atts['exclude']);
        }
        if ($atts['parent'] !== '') {
            $args['parent'] = (int)$atts['parent'];
        }

        $terms = [];
        if (!empty($atts['post_id'])) {
            $pid = ($atts['post_id'] === 'current') ? get_the_ID() : (int)$atts['post_id'];
            $terms = get_the_terms($pid, $atts['taxonomy']);
        } else {
            $terms = get_terms($args);
        }

        if (empty($terms) || is_wp_error($terms)) {
            return '';
        }

        if ($atts['format'] === 'array') {
            return '<pre>' . print_r($terms, true) . '</pre>';
        }

        // CSS Classes
        $classes = 'zci-taxonomy-list zci-' . $atts['style'];

        // Grid Styles
        $style_attr = '';
        if ($atts['style'] === 'grid') {
             $style_attr = 'style="--zci-columns: ' . intval($atts['columns']) . ';"';
        }

        $output = '<ul class="' . esc_attr($classes) . '" ' . $style_attr . '>';
        
        foreach ($terms as $term) {
            $image = $this->zTaxonomyImage($term->term_id, $atts['size'], ['class' => 'zci-img'], false);

            // Skip if no image and no name to show
            if (empty($image) && $atts['show_name'] !== 'yes') {
                continue;
            }

            $link = get_term_link($term);
            
            $output .= '<li class="zci-item">';
            $output .= '<a href="' . esc_url($link) . '" class="zci-link">';
            $output .= '<span class="zci-image">' . $image . '</span>';
            
            if ($atts['show_name'] === 'yes') {
                 $output .= '<span class="zci-term-name">' . esc_html($term->name);
                 if ($atts['show_count'] === 'yes') {
                     $output .= ' <span class="zci-term-count">(' . $term->count . ')</span>';
                 }
                 $output .= '</span>';
            }
            $output .= '</a>';
            $output .= '</li>';
        }

        $output .= '</ul>';

        return $output;
    }
}

if (class_exists('ZCategoriesImages')) {
    // Instantiate the class via Singleton
    $z_categories_images = ZCategoriesImages::get_instance();

    // After activating the plugin
    register_activation_hook(__FILE__, [$z_categories_images, 'activate']);

    // After deactivating the plugin
    register_deactivation_hook(__FILE__, [$z_categories_images, 'deactivate']);
    
    function z_taxonomy_image_url($term_id = NULL, $size = 'full', $return_placeholder = FALSE) {
        $zci = ZCategoriesImages::get_instance();
        return $zci->zTaxonomyImageUrl($term_id, $size, $return_placeholder);
    }

    function z_taxonomy_image_id($term_id = NULL) {
        $zci = ZCategoriesImages::get_instance();
        return $zci->zTaxonomyImageID($term_id);
    }

    function z_taxonomy_image($term_id = NULL, $size = 'full', $attr = NULL, $echo = TRUE) {
        $zci = ZCategoriesImages::get_instance();
        return $zci->zTaxonomyImage($term_id, $size, $attr, $echo);
    }
}