Created
October 13, 2025 11:56
-
-
Save JusTruetice/267e940d4347bea9029ab0a31e27c3fc to your computer and use it in GitHub Desktop.
VIRUS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Theme functions and definitions | |
| * | |
| * @package HelloElementor | |
| */ | |
| if ( ! defined( 'ABSPATH' ) ) { | |
| exit; // Exit if accessed directly. | |
| } | |
| define( 'HELLO_ELEMENTOR_VERSION', '3.4.4' ); | |
| define( 'EHP_THEME_SLUG', 'hello-elementor' ); | |
| define( 'HELLO_THEME_PATH', get_template_directory() ); | |
| define( 'HELLO_THEME_URL', get_template_directory_uri() ); | |
| define( 'HELLO_THEME_ASSETS_PATH', HELLO_THEME_PATH . '/assets/' ); | |
| define( 'HELLO_THEME_ASSETS_URL', HELLO_THEME_URL . '/assets/' ); | |
| define( 'HELLO_THEME_SCRIPTS_PATH', HELLO_THEME_ASSETS_PATH . 'js/' ); | |
| define( 'HELLO_THEME_SCRIPTS_URL', HELLO_THEME_ASSETS_URL . 'js/' ); | |
| define( 'HELLO_THEME_STYLE_PATH', HELLO_THEME_ASSETS_PATH . 'css/' ); | |
| define( 'HELLO_THEME_STYLE_URL', HELLO_THEME_ASSETS_URL . 'css/' ); | |
| define( 'HELLO_THEME_IMAGES_PATH', HELLO_THEME_ASSETS_PATH . 'images/' ); | |
| define( 'HELLO_THEME_IMAGES_URL', HELLO_THEME_ASSETS_URL . 'images/' ); | |
| if ( ! isset( $content_width ) ) { | |
| $content_width = 800; // Pixels. | |
| } | |
| if ( ! function_exists( 'hello_elementor_setup' ) ) { | |
| /** | |
| * Set up theme support. | |
| * | |
| * @return void | |
| */ | |
| function hello_elementor_setup() { | |
| if ( is_admin() ) { | |
| hello_maybe_update_theme_version_in_db(); | |
| } | |
| if ( apply_filters( 'hello_elementor_register_menus', true ) ) { | |
| register_nav_menus( [ 'menu-1' => esc_html__( 'Header', 'hello-elementor' ) ] ); | |
| register_nav_menus( [ 'menu-2' => esc_html__( 'Footer', 'hello-elementor' ) ] ); | |
| } | |
| if ( apply_filters( 'hello_elementor_post_type_support', true ) ) { | |
| add_post_type_support( 'page', 'excerpt' ); | |
| } | |
| if ( apply_filters( 'hello_elementor_add_theme_support', true ) ) { | |
| add_theme_support( 'post-thumbnails' ); | |
| add_theme_support( 'automatic-feed-links' ); | |
| add_theme_support( 'title-tag' ); | |
| add_theme_support( | |
| 'html5', | |
| [ | |
| 'search-form', | |
| 'comment-form', | |
| 'comment-list', | |
| 'gallery', | |
| 'caption', | |
| 'script', | |
| 'style', | |
| 'navigation-widgets', | |
| ] | |
| ); | |
| add_theme_support( | |
| 'custom-logo', | |
| [ | |
| 'height' => 100, | |
| 'width' => 350, | |
| 'flex-height' => true, | |
| 'flex-width' => true, | |
| ] | |
| ); | |
| add_theme_support( 'align-wide' ); | |
| add_theme_support( 'responsive-embeds' ); | |
| /* | |
| * Editor Styles | |
| */ | |
| add_theme_support( 'editor-styles' ); | |
| add_editor_style( 'editor-styles.css' ); | |
| /* | |
| * WooCommerce. | |
| */ | |
| if ( apply_filters( 'hello_elementor_add_woocommerce_support', true ) ) { | |
| // WooCommerce in general. | |
| add_theme_support( 'woocommerce' ); | |
| // Enabling WooCommerce product gallery features (are off by default since WC 3.0.0). | |
| // zoom. | |
| add_theme_support( 'wc-product-gallery-zoom' ); | |
| // lightbox. | |
| add_theme_support( 'wc-product-gallery-lightbox' ); | |
| // swipe. | |
| add_theme_support( 'wc-product-gallery-slider' ); | |
| } | |
| } | |
| } | |
| } | |
| add_action( 'after_setup_theme', 'hello_elementor_setup' ); | |
| function hello_maybe_update_theme_version_in_db() { | |
| $theme_version_option_name = 'hello_theme_version'; | |
| // The theme version saved in the database. | |
| $hello_theme_db_version = get_option( $theme_version_option_name ); | |
| // If the 'hello_theme_version' option does not exist in the DB, or the version needs to be updated, do the update. | |
| if ( ! $hello_theme_db_version || version_compare( $hello_theme_db_version, HELLO_ELEMENTOR_VERSION, '<' ) ) { | |
| update_option( $theme_version_option_name, HELLO_ELEMENTOR_VERSION ); | |
| } | |
| } | |
| if ( ! function_exists( 'hello_elementor_display_header_footer' ) ) { | |
| /** | |
| * Check whether to display header footer. | |
| * | |
| * @return bool | |
| */ | |
| function hello_elementor_display_header_footer() { | |
| $hello_elementor_header_footer = true; | |
| return apply_filters( 'hello_elementor_header_footer', $hello_elementor_header_footer ); | |
| } | |
| } | |
| if ( ! function_exists( 'hello_elementor_scripts_styles' ) ) { | |
| /** | |
| * Theme Scripts & Styles. | |
| * | |
| * @return void | |
| */ | |
| function hello_elementor_scripts_styles() { | |
| if ( apply_filters( 'hello_elementor_enqueue_style', true ) ) { | |
| wp_enqueue_style( | |
| 'hello-elementor', | |
| HELLO_THEME_STYLE_URL . 'reset.css', | |
| [], | |
| HELLO_ELEMENTOR_VERSION | |
| ); | |
| } | |
| if ( apply_filters( 'hello_elementor_enqueue_theme_style', true ) ) { | |
| wp_enqueue_style( | |
| 'hello-elementor-theme-style', | |
| HELLO_THEME_STYLE_URL . 'theme.css', | |
| [], | |
| HELLO_ELEMENTOR_VERSION | |
| ); | |
| } | |
| if ( hello_elementor_display_header_footer() ) { | |
| wp_enqueue_style( | |
| 'hello-elementor-header-footer', | |
| HELLO_THEME_STYLE_URL . 'header-footer.css', | |
| [], | |
| HELLO_ELEMENTOR_VERSION | |
| ); | |
| } | |
| } | |
| } | |
| add_action( 'wp_enqueue_scripts', 'hello_elementor_scripts_styles' ); | |
| if ( ! function_exists( 'hello_elementor_register_elementor_locations' ) ) { | |
| /** | |
| * Register Elementor Locations. | |
| * | |
| * @param ElementorPro\Modules\ThemeBuilder\Classes\Locations_Manager $elementor_theme_manager theme manager. | |
| * | |
| * @return void | |
| */ | |
| function hello_elementor_register_elementor_locations( $elementor_theme_manager ) { | |
| if ( apply_filters( 'hello_elementor_register_elementor_locations', true ) ) { | |
| $elementor_theme_manager->register_all_core_location(); | |
| } | |
| } | |
| } | |
| add_action( 'elementor/theme/register_locations', 'hello_elementor_register_elementor_locations' ); | |
| if ( ! function_exists( 'hello_elementor_content_width' ) ) { | |
| /** | |
| * Set default content width. | |
| * | |
| * @return void | |
| */ | |
| function hello_elementor_content_width() { | |
| $GLOBALS['content_width'] = apply_filters( 'hello_elementor_content_width', 800 ); | |
| } | |
| } | |
| add_action( 'after_setup_theme', 'hello_elementor_content_width', 0 ); | |
| if ( ! function_exists( 'hello_elementor_add_description_meta_tag' ) ) { | |
| /** | |
| * Add description meta tag with excerpt text. | |
| * | |
| * @return void | |
| */ | |
| function hello_elementor_add_description_meta_tag() { | |
| if ( ! apply_filters( 'hello_elementor_description_meta_tag', true ) ) { | |
| return; | |
| } | |
| if ( ! is_singular() ) { | |
| return; | |
| } | |
| $post = get_queried_object(); | |
| if ( empty( $post->post_excerpt ) ) { | |
| return; | |
| } | |
| echo '<meta name="description" content="' . esc_attr( wp_strip_all_tags( $post->post_excerpt ) ) . '">' . "\n"; | |
| } | |
| } | |
| add_action( 'wp_head', 'hello_elementor_add_description_meta_tag' ); | |
| // Settings page | |
| require get_template_directory() . '/includes/settings-functions.php'; | |
| // Header & footer styling option, inside Elementor | |
| require get_template_directory() . '/includes/elementor-functions.php'; | |
| if ( ! function_exists( 'hello_elementor_customizer' ) ) { | |
| // Customizer controls | |
| function hello_elementor_customizer() { | |
| if ( ! is_customize_preview() ) { | |
| return; | |
| } | |
| if ( ! hello_elementor_display_header_footer() ) { | |
| return; | |
| } | |
| require get_template_directory() . '/includes/customizer-functions.php'; | |
| } | |
| } | |
| add_action( 'init', 'hello_elementor_customizer' ); | |
| if ( ! function_exists( 'hello_elementor_check_hide_title' ) ) { | |
| /** | |
| * Check whether to display the page title. | |
| * | |
| * @param bool $val default value. | |
| * | |
| * @return bool | |
| */ | |
| function hello_elementor_check_hide_title( $val ) { | |
| if ( defined( 'ELEMENTOR_VERSION' ) ) { | |
| $current_doc = Elementor\Plugin::instance()->documents->get( get_the_ID() ); | |
| if ( $current_doc && 'yes' === $current_doc->get_settings( 'hide_title' ) ) { | |
| $val = false; | |
| } | |
| } | |
| return $val; | |
| } | |
| } | |
| add_filter( 'hello_elementor_page_title', 'hello_elementor_check_hide_title' ); | |
| /** | |
| * BC: | |
| * In v2.7.0 the theme removed the `hello_elementor_body_open()` from `header.php` replacing it with `wp_body_open()`. | |
| * The following code prevents fatal errors in child themes that still use this function. | |
| */ | |
| if ( ! function_exists( 'hello_elementor_body_open' ) ) { | |
| function hello_elementor_body_open() { | |
| wp_body_open(); | |
| } | |
| } | |
| require HELLO_THEME_PATH . '/theme.php'; | |
| HelloTheme\Theme::instance(); | |
| /* category item element */ | |
| function register_custom_category_widget($widgets_manager) { | |
| class Custom_Category_Element extends \Elementor\Widget_Base { | |
| public function get_name() { | |
| return 'new_category'; | |
| } | |
| public function get_title() { | |
| return esc_html__('New Category', 'textdomain'); | |
| } | |
| public function get_icon() { | |
| return 'eicon-folder'; | |
| } | |
| public function get_categories() { | |
| return ['basic']; | |
| } | |
| public function get_script_depends() { | |
| return ['swiper-js']; | |
| } | |
| public function get_style_depends() { | |
| return ['swiper-css']; | |
| } | |
| protected function register_controls() { | |
| $this->start_controls_section( | |
| 'content_section', | |
| [ | |
| 'label' => esc_html__('Content Settings', 'textdomain'), | |
| 'tab' => \Elementor\Controls_Manager::TAB_CONTENT, | |
| ] | |
| ); | |
| $product_categories = get_terms([ | |
| 'taxonomy' => 'product_cat', | |
| 'hide_empty' => false, | |
| ]); | |
| $categories_options = []; | |
| foreach ($product_categories as $category) { | |
| $categories_options[$category->term_id] = $category->name; | |
| } | |
| $this->add_control( | |
| 'selected_categories', | |
| [ | |
| 'label' => esc_html__('Select Categories', 'textdomain'), | |
| 'type' => \Elementor\Controls_Manager::SELECT2, | |
| 'multiple' => true, | |
| 'options' => $categories_options, | |
| 'default' => [], | |
| ] | |
| ); | |
| $this->add_control( | |
| 'columns_desktop', | |
| [ | |
| 'label' => esc_html__('Desktop Columns', 'textdomain'), | |
| 'type' => \Elementor\Controls_Manager::SELECT, | |
| 'default' => '4', | |
| 'options' => [ | |
| '2' => '2', | |
| '3' => '3', | |
| '4' => '4', | |
| '6' => '6' | |
| ], | |
| ] | |
| ); | |
| $this->add_control( | |
| 'carousel_settings', | |
| [ | |
| 'label' => esc_html__('Carousel Settings', 'textdomain'), | |
| 'type' => \Elementor\Controls_Manager::HEADING, | |
| 'separator' => 'before', | |
| ] | |
| ); | |
| $this->add_control( | |
| 'show_navigation', | |
| [ | |
| 'label' => esc_html__('Show Navigation', 'textdomain'), | |
| 'type' => \Elementor\Controls_Manager::SWITCHER, | |
| 'label_on' => esc_html__('Yes', 'textdomain'), | |
| 'label_off' => esc_html__('No', 'textdomain'), | |
| 'default' => 'yes', | |
| ] | |
| ); | |
| $this->add_control( | |
| 'show_pagination', | |
| [ | |
| 'label' => esc_html__('Show Pagination', 'textdomain'), | |
| 'type' => \Elementor\Controls_Manager::SWITCHER, | |
| 'label_on' => esc_html__('Yes', 'textdomain'), | |
| 'label_off' => esc_html__('No', 'textdomain'), | |
| 'default' => 'yes', | |
| ] | |
| ); | |
| $this->end_controls_section(); | |
| $this->start_controls_section( | |
| 'style_section', | |
| [ | |
| 'label' => esc_html__('Style Settings', 'textdomain'), | |
| 'tab' => \Elementor\Controls_Manager::TAB_STYLE, | |
| ] | |
| ); | |
| $this->add_control( | |
| 'background_color', | |
| [ | |
| 'label' => esc_html__('Background Color', 'textdomain'), | |
| 'type' => \Elementor\Controls_Manager::COLOR, | |
| 'default' => '#191919', | |
| 'selectors' => [ | |
| '{{WRAPPER}} .category-item' => 'background-color: {{VALUE}}' | |
| ], | |
| ] | |
| ); | |
| $this->add_control( | |
| 'text_color', | |
| [ | |
| 'label' => esc_html__('Text Color', 'textdomain'), | |
| 'type' => \Elementor\Controls_Manager::COLOR, | |
| 'default' => '#ffffff', | |
| 'selectors' => [ | |
| '{{WRAPPER}} .category-title' => 'color: {{VALUE}}' | |
| ], | |
| ] | |
| ); | |
| $this->end_controls_section(); | |
| } | |
| public function __construct($data = [], $args = null) { | |
| parent::__construct($data, $args); | |
| wp_register_script( | |
| 'swiper-js', | |
| 'https://unpkg.com/swiper/swiper-bundle.min.js', | |
| [], | |
| '8.4.5', | |
| true | |
| ); | |
| wp_register_style( | |
| 'swiper-css', | |
| 'https://unpkg.com/swiper/swiper-bundle.min.css', | |
| [], | |
| '8.4.5' | |
| ); | |
| } | |
| protected function render() { | |
| $settings = $this->get_settings_for_display(); | |
| $selected_categories = $settings['selected_categories']; | |
| if (empty($selected_categories)) { | |
| echo esc_html__('Please select categories to display', 'textdomain'); | |
| return; | |
| } | |
| $args = array( | |
| 'taxonomy' => 'product_cat', | |
| 'hide_empty' => false, | |
| 'include' => $selected_categories, | |
| 'orderby' => 'include' | |
| ); | |
| $categories = get_terms($args); | |
| if (!empty($categories)) { | |
| wp_enqueue_script('jquery'); | |
| $widget_id = $this->get_id(); | |
| ?> | |
| <style> | |
| .custom-category-grid { | |
| display: grid; | |
| grid-template-columns: repeat(<?php echo esc_attr($settings['columns_desktop']); ?>, 1fr); | |
| gap: 20px; | |
| padding: 20px; | |
| } | |
| .category-item { | |
| position: relative; | |
| overflow: hidden; | |
| transition: all 0.3s ease; | |
| padding: 20px; | |
| text-align: center; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| background-color: <?php echo esc_attr($settings['background_color']); ?>; | |
| } | |
| .category-title { | |
| font-size: 18px; | |
| font-weight: 600; | |
| margin: 0; | |
| padding: 10px 0; | |
| color: <?php echo esc_attr($settings['text_color']); ?>; | |
| } | |
| .category-image { | |
| margin-top: 15px; | |
| position: relative; | |
| padding-bottom: 56.25%; | |
| max-height: 200px; | |
| } | |
| .category-image img { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| object-fit: contain; | |
| } | |
| .view-more { | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| background-color: #F6AB2F; | |
| color: #fff; | |
| padding: 15px; | |
| opacity: 0; | |
| transform: translateY(100%); | |
| transition: all 0.3s ease; | |
| text-align: center; | |
| font-weight: 500; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| text-decoration: none; | |
| } | |
| .category-item:hover .view-more { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| .view-more svg { | |
| width: 16px; | |
| height: 16px; | |
| transform: rotate(45deg); | |
| } | |
| @media (min-width: 768px) { | |
| .mobile-carousel { | |
| display: none; | |
| } | |
| } | |
| @media (max-width: 767px) { | |
| .custom-category-grid { | |
| display: none; | |
| } | |
| .mobile-carousel { | |
| display: block; | |
| padding: 10px; | |
| } | |
| .mobile-grid { | |
| display: grid; | |
| grid-template-columns: repeat(2, 1fr); | |
| gap: 10px; | |
| } | |
| .category-item { | |
| height: auto; | |
| padding: 10px; | |
| margin: 0; | |
| } | |
| .category-title { | |
| font-size: 14px; | |
| padding: 5px 0; | |
| margin: 0; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| line-height: 1.2; | |
| } | |
| .category-image { | |
| margin-top: 8px; | |
| padding-bottom: 60%; | |
| } | |
| .view-more { | |
| opacity: 0; | |
| transform: translateY(100%); | |
| padding: 8px; | |
| font-size: 13px; | |
| } | |
| .category-item.active .view-more { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| .view-more svg { | |
| width: 14px; | |
| height: 14px; | |
| } | |
| } | |
| @media (max-width: 359px) { | |
| .category-title { | |
| font-size: 13px; | |
| } | |
| .view-more { | |
| font-size: 12px; | |
| padding: 6px; | |
| } | |
| } | |
| </style> | |
| <!-- Desktop Grid --> | |
| <div class="custom-category-grid"> | |
| <?php foreach ($categories as $category) : | |
| $thumbnail_id = get_term_meta($category->term_id, 'thumbnail_id', true); | |
| $image = wp_get_attachment_url($thumbnail_id); | |
| // Get current language | |
| $current_lang = function_exists('wpml_get_current_language') ? wpml_get_current_language() : ''; | |
| // Используем прямую ссылку на категорию WooCommerce | |
| $category_link = get_term_link($category->term_id, 'product_cat'); | |
| // Set text based on current language | |
| $view_more_text = $current_lang === 'en' ? 'MORE' : 'სრულად ნახვა'; | |
| ?> | |
| <div class="category-item"> | |
| <h3 class="category-title"><?php echo esc_html($category->name); ?></h3> | |
| <?php if ($image) : ?> | |
| <div class="category-image"> | |
| <img src="<?php echo esc_url($image); ?>" alt="<?php echo esc_attr($category->name); ?>" loading="lazy"> | |
| </div> | |
| <?php endif; ?> | |
| <a href="<?php echo esc_url($category_link); ?>" | |
| class="view-more" | |
| data-category-id="<?php echo esc_attr($category->term_id); ?>"> | |
| <?php echo esc_html($view_more_text); ?> | |
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M5 12h14M12 5l7 7-7 7"/> | |
| </svg> | |
| </a> | |
| </div> | |
| <?php endforeach; ?> | |
| </div> | |
| <!-- Mobile Grid --> | |
| <div class="mobile-carousel"> | |
| <div class="mobile-grid"> | |
| <?php foreach ($categories as $category) : | |
| $thumbnail_id = get_term_meta($category->term_id, 'thumbnail_id', true); | |
| $image = wp_get_attachment_url($thumbnail_id); | |
| // Get current language | |
| $current_lang = function_exists('wpml_get_current_language') ? wpml_get_current_language() : ''; | |
| // Используем прямую ссылку на категорию WooCommerce | |
| $category_link = get_term_link($category->term_id, 'product_cat'); | |
| // Set text based on current language | |
| $view_more_text = $current_lang === 'en' ? 'MORE' : 'სრულად ნახვა'; | |
| ?> | |
| <div class="category-item"> | |
| <h3 class="category-title"><?php echo esc_html($category->name); ?></h3> | |
| <?php if ($image) : ?> | |
| <div class="category-image"> | |
| <img src="<?php echo esc_url($image); ?>" alt="<?php echo esc_attr($category->name); ?>" loading="lazy"> | |
| </div> | |
| <?php endif; ?> | |
| <a href="<?php echo esc_url($category_link); ?>" | |
| class="view-more" | |
| data-category-id="<?php echo esc_attr($category->term_id); ?>"> | |
| <?php echo esc_html($view_more_text); ?> | |
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M5 12h14M12 5l7 7-7 7"/> | |
| </svg> | |
| </a> | |
| </div> | |
| <?php endforeach; ?> | |
| </div> | |
| </div> | |
| <script> | |
| jQuery(document).ready(function($) { | |
| // Mobile category item click handler | |
| $('.mobile-carousel .category-item').on('click', function(e) { | |
| if (!$(e.target).hasClass('view-more') && !$(e.target).closest('.view-more').length) { | |
| $(this).addClass('active'); | |
| // Remove active class from other items | |
| $('.mobile-carousel .category-item').not(this).removeClass('active'); | |
| } | |
| }); | |
| // Update links when language is switched | |
| $(document).on('wpml_language_switched', function(event, data) { | |
| var newLang = data.to; | |
| var viewMoreText = newLang === 'en' ? 'MORE' : 'სრულად ნახვა'; | |
| // Update text for view more buttons | |
| $('.view-more').each(function() { | |
| // Сохраняем только текст, без затрагивания дочерних элементов | |
| $(this).contents().filter(function() { | |
| return this.nodeType === 3; // Текстовые узлы | |
| }).first().replaceWith(viewMoreText); | |
| }); | |
| }); | |
| }); | |
| </script><?php | |
| } | |
| } | |
| } | |
| $widgets_manager->register(new Custom_Category_Element()); | |
| } | |
| add_action('elementor/widgets/register', 'register_custom_category_widget'); | |
| add_shortcode('product_stock_status', 'show_product_stock_status'); | |
| function show_product_stock_status() { | |
| global $product; | |
| if (!$product) return ''; | |
| $stock_status = $product->get_stock_status(); | |
| // Default Georgian text | |
| $in_stock_text = 'მარაგშია'; | |
| $out_of_stock_text = 'არ არის მარაგში'; | |
| // Check if WPML functions exist and get current language | |
| if (function_exists('icl_object_id')) { | |
| $current_language = apply_filters('wpml_current_language', NULL); | |
| if ($current_language == 'en') { | |
| $in_stock_text = 'In Stock'; | |
| $out_of_stock_text = 'Out of Stock'; | |
| } | |
| } | |
| if ($stock_status == 'instock') { | |
| return '<span style="color: green;">' . $in_stock_text . '</span>'; | |
| } else { | |
| return '<span style="color: red;">' . $out_of_stock_text . '</span>'; | |
| } | |
| } | |
| // Регистрация виджета Elementor для бейджа WooCommerce | |
| add_action('elementor/widgets/widgets_registered', function ($widgets_manager) { | |
| class WooCommerce_Discount_Badge_Widget extends \Elementor\Widget_Base { | |
| public function get_name() { | |
| return 'woocommerce_discount_badge'; | |
| } | |
| public function get_title() { | |
| return __('WooCommerce Discount Badge', 'text-domain'); | |
| } | |
| public function get_icon() { | |
| return 'eicon-products'; | |
| } | |
| public function get_categories() { | |
| return ['woocommerce-elements']; | |
| } | |
| protected function render() { | |
| // Получение текущего продукта WooCommerce | |
| global $product; | |
| // Если продукт не найден или не в распродаже — ничего не выводить | |
| if (!$product || !$product->is_on_sale()) { | |
| return; // Ничего не делаем | |
| } | |
| // Получение цен продукта | |
| $regular_price = $product->get_regular_price(); // Старая цена | |
| $sale_price = $product->get_sale_price(); // Цена со скидкой | |
| // Проверка наличия цен и расчет процента скидки | |
| if ($regular_price && $sale_price) { | |
| $discount_percentage = round((($regular_price - $sale_price) / $regular_price) * 100); | |
| // Вывод бейджа | |
| echo '<div class="custom-discount-badge">-' . $discount_percentage . '%</div>'; | |
| } | |
| } | |
| } | |
| $widgets_manager->register_widget_type(new WooCommerce_Discount_Badge_Widget()); | |
| }); | |
| // Add form wrapper and styles | |
| add_action('wp_head', 'add_registration_form_styles'); | |
| function add_registration_form_styles() { | |
| ?> | |
| <style> | |
| .registration-grid { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 15px; | |
| margin-bottom: 20px; | |
| } | |
| .registration-grid .woocommerce-form-row { | |
| margin: 0 !important; | |
| } | |
| .registration-grid input { | |
| background-color: #f8f9fa !important; | |
| border: none !important; | |
| padding: 10px !important; | |
| width: 100% !important; | |
| } | |
| .registration-grid .form-row-full { | |
| grid-column: 1 / -1; | |
| } | |
| .form-row-full input { | |
| width: 100% !important; | |
| } | |
| .woocommerce-privacy-policy-text { | |
| grid-column: 1 / -1; | |
| margin-top: 10px; | |
| color: #666; | |
| font-size: 14px; | |
| } | |
| @media (max-width: 768px) { | |
| .registration-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| </style> | |
| <?php | |
| } | |
| // Add custom fields to registration form | |
| add_action('woocommerce_register_form_start', 'add_custom_registration_fields_start'); | |
| function add_custom_registration_fields_start() { | |
| echo '<div class="registration-grid">'; | |
| } | |
| add_action('woocommerce_register_form', 'add_custom_registration_fields'); | |
| function add_custom_registration_fields() { | |
| ?> | |
| <p class="woocommerce-form-row form-row"> | |
| <label for="reg_billing_first_name"><?php _e('სახელი', 'woocommerce'); ?> <span class="required">*</span></label> | |
| <input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="billing_first_name" id="reg_billing_first_name" required /> | |
| </p> | |
| <p class="woocommerce-form-row form-row"> | |
| <label for="reg_billing_last_name"><?php _e('გვარი', 'woocommerce'); ?> <span class="required">*</span></label> | |
| <input type="text" class="woocommerce-Input woocommerce-Input--text input-text" name="billing_last_name" id="reg_billing_last_name" required /> | |
| </p> | |
| <p class="woocommerce-form-row form-row"> | |
| <label for="reg_billing_phone"><?php _e('ტელეფონი', 'woocommerce'); ?> <span class="required">*</span></label> | |
| <input type="tel" class="woocommerce-Input woocommerce-Input--text input-text" name="billing_phone" id="reg_billing_phone" required /> | |
| </p> | |
| <p class="woocommerce-form-row form-row"> | |
| <label for="reg_password"><?php _e('პაროლი', 'woocommerce'); ?> <span class="required">*</span></label> | |
| <input type="password" class="woocommerce-Input woocommerce-Input--text input-text" name="password" id="reg_password" autocomplete="new-password" required /> | |
| </p> | |
| <?php | |
| } | |
| add_action('woocommerce_register_form_end', 'add_custom_registration_fields_end'); | |
| function add_custom_registration_fields_end() { | |
| echo '</div>'; | |
| } | |
| // Validate fields and set password | |
| add_filter('woocommerce_registration_errors', 'validate_custom_registration_fields', 10, 3); | |
| function validate_custom_registration_fields($errors, $username, $email) { | |
| if (empty($_POST['billing_first_name'])) { | |
| $errors->add('billing_first_name_error', __('გთხოვთ შეიყვანოთ სახელი', 'woocommerce')); | |
| } | |
| if (empty($_POST['billing_last_name'])) { | |
| $errors->add('billing_last_name_error', __('გთხოვთ შეიყვანოთ გვარი', 'woocommerce')); | |
| } | |
| if (empty($_POST['billing_phone'])) { | |
| $errors->add('billing_phone_error', __('გთხოვთ შეიყვანოთ ტელეფონის ნომერი', 'woocommerce')); | |
| } | |
| if (empty($_POST['password'])) { | |
| $errors->add('password_error', __('გთხოვთ შეიყვანოთ პაროლი', 'woocommerce')); | |
| } | |
| return $errors; | |
| } | |
| // Save fields and set password | |
| add_action('woocommerce_created_customer', 'save_custom_registration_fields'); | |
| function save_custom_registration_fields($customer_id) { | |
| // Save custom fields | |
| if (isset($_POST['billing_first_name'])) { | |
| update_user_meta($customer_id, 'billing_first_name', sanitize_text_field($_POST['billing_first_name'])); | |
| update_user_meta($customer_id, 'first_name', sanitize_text_field($_POST['billing_first_name'])); | |
| } | |
| if (isset($_POST['billing_last_name'])) { | |
| update_user_meta($customer_id, 'billing_last_name', sanitize_text_field($_POST['billing_last_name'])); | |
| update_user_meta($customer_id, 'last_name', sanitize_text_field($_POST['billing_last_name'])); | |
| } | |
| if (isset($_POST['billing_phone'])) { | |
| update_user_meta($customer_id, 'billing_phone', sanitize_text_field($_POST['billing_phone'])); | |
| } | |
| // Set user password | |
| if (isset($_POST['password']) && !empty($_POST['password'])) { | |
| wp_set_password($_POST['password'], $customer_id); | |
| } | |
| } | |
| // Enable password field in registration | |
| add_filter('woocommerce_registration_generate_password', '__return_false'); | |
| function display_product_brand_shortcode() { | |
| global $product; | |
| // Проверяем, существует ли объект $product и метод get_id() | |
| if ( ! is_object($product) || ! method_exists($product, 'get_id') ) { | |
| return ''; // Возвращаем пустую строку, если объект недоступен | |
| } | |
| // Получаем термины (бренд) для текущего продукта | |
| $terms = wp_get_post_terms($product->get_id(), 'pa_brand'); | |
| // Проверяем, есть ли термины и нет ли ошибок | |
| if (!empty($terms) && !is_wp_error($terms)) { | |
| // Set the label based on current language | |
| $brand_label = 'ბრენდი: '; // Default Georgian | |
| // Check if WPML functions exist and get current language | |
| if (function_exists('icl_object_id')) { | |
| $current_language = apply_filters('wpml_current_language', NULL); | |
| if ($current_language == 'en') { | |
| $brand_label = 'Brand: '; | |
| } | |
| } | |
| return '<p><strong>' . $brand_label . '</strong>' . esc_html($terms[0]->name) . '</p>'; | |
| } | |
| return ''; // Возвращаем пустую строку, если бренды не найдены | |
| } | |
| add_shortcode('product_brand', 'display_product_brand_shortcode'); | |
| // Кнопка "ყიდვა" (Покупка) | |
| function black_button_shortcode() { | |
| global $product; | |
| if (!$product) { | |
| return ''; | |
| } | |
| $product_id = $product->get_id(); | |
| $checkout_url = wc_get_checkout_url(); | |
| // Get the current language and set button text accordingly | |
| $button_text = 'ყიდვა'; // Default Georgian | |
| if (function_exists('icl_object_id')) { | |
| $current_language = apply_filters('wpml_current_language', NULL); | |
| if ($current_language == 'en') { | |
| $button_text = 'Buy'; | |
| } | |
| } | |
| return '<a href="#" class="black-button" onclick="addToCartAndRedirect(' . $product_id . ', \'' . $checkout_url . '\'); return false;">' . $button_text . '</a>'; | |
| } | |
| add_shortcode('black_button', 'black_button_shortcode'); | |
| // Кнопка "განვადება" (Рассрочка) | |
| function red_button_shortcode() { | |
| global $product; | |
| if (!$product) { | |
| return ''; | |
| } | |
| $product_id = $product->get_id(); | |
| $checkout_url = wc_get_checkout_url(); | |
| // Get the current language and set button text accordingly | |
| $button_text = 'განვადება'; // Default Georgian | |
| if (function_exists('icl_object_id')) { | |
| $current_language = apply_filters('wpml_current_language', NULL); | |
| if ($current_language == 'en') { | |
| $button_text = 'Installment'; | |
| } | |
| } | |
| return '<a href="#" class="red-button" onclick="addToCartAndRedirect(' . $product_id . ', \'' . $checkout_url . '\'); return false;">' . $button_text . '</a>'; | |
| } | |
| add_shortcode('red_button', 'red_button_shortcode'); | |
| // Добавляем стили и скрипты | |
| function add_button_styles_and_scripts() { | |
| ?> | |
| <style> | |
| .black-button, | |
| .red-button { | |
| display: inline-block; | |
| width: 100%; | |
| padding: 12px 20px; | |
| text-align: center; | |
| text-decoration: none; | |
| border-radius: 5px; | |
| font-weight: 500; | |
| transition: background-color 0.3s; | |
| cursor: pointer; | |
| } | |
| .black-button { | |
| background-color: #000000; | |
| color: #ffffff; | |
| } | |
| .red-button { | |
| background-color: #FF0000; | |
| color: #ffffff; | |
| } | |
| .black-button:hover { | |
| background-color: #000000; | |
| color: #ffffff; | |
| text-decoration: none; | |
| } | |
| .red-button:hover { | |
| background-color: #FF0000; | |
| color: #ffffff; | |
| text-decoration: none; | |
| } | |
| </style> | |
| <script> | |
| function addToCartAndRedirect(productId, checkoutUrl) { | |
| // Добавляем загрузчик или блокируем кнопку | |
| jQuery.ajax({ | |
| type: 'POST', | |
| url: wc_add_to_cart_params.ajax_url, | |
| data: { | |
| 'action': 'add_to_cart', | |
| 'product_id': productId, | |
| 'quantity': 1 | |
| }, | |
| success: function(response) { | |
| // После успешного добавления в корзину перенаправляем на чекаут | |
| window.location.href = checkoutUrl; | |
| }, | |
| error: function(error) { | |
| console.log(error); | |
| alert('Error adding to cart'); | |
| } | |
| }); | |
| } | |
| </script> | |
| <?php | |
| } | |
| add_action('wp_head', 'add_button_styles_and_scripts'); | |
| // Обработчик AJAX запроса для добавления в корзину | |
| function ajax_add_to_cart() { | |
| $product_id = isset($_POST['product_id']) ? intval($_POST['product_id']) : 0; | |
| $quantity = isset($_POST['quantity']) ? intval($_POST['quantity']) : 1; | |
| if ($product_id > 0) { | |
| WC()->cart->empty_cart(); // Очищаем корзину перед добавлением нового товара | |
| $added = WC()->cart->add_to_cart($product_id, $quantity); | |
| if ($added) { | |
| wp_send_json_success('Product added to cart'); | |
| } else { | |
| wp_send_json_error('Failed to add product to cart'); | |
| } | |
| } else { | |
| wp_send_json_error('Invalid product ID'); | |
| } | |
| wp_die(); | |
| } | |
| add_action('wp_ajax_add_to_cart', 'ajax_add_to_cart'); | |
| add_action('wp_ajax_nopriv_add_to_cart', 'ajax_add_to_cart'); | |
| function get_same_category_products() { | |
| global $post; | |
| if (!is_product()) { | |
| return ''; | |
| } | |
| $product_cats = wp_get_post_terms($post->ID, 'product_cat'); | |
| if (empty($product_cats)) { | |
| return ''; | |
| } | |
| $primary_cat = $product_cats[0]; | |
| $args = array( | |
| 'post_type' => 'product', | |
| 'posts_per_page' => -1, | |
| 'post__not_in' => array($post->ID), | |
| 'tax_query' => array( | |
| array( | |
| 'taxonomy' => 'product_cat', | |
| 'field' => 'term_id', | |
| 'terms' => $primary_cat->term_id, | |
| ), | |
| ), | |
| 'orderby' => 'rand' | |
| ); | |
| $products = new WP_Query($args); | |
| $output = ''; | |
| if ($products->have_posts()) { | |
| $output .= '<div class="same-category-products">'; | |
| $output .= '<div class="products-carousel">'; | |
| while ($products->have_posts()) { | |
| $products->the_post(); | |
| $output .= '<div class="carousel-item">'; | |
| $output .= do_shortcode('[elementor-template id="3125"]'); | |
| $output .= '</div>'; | |
| } | |
| $output .= '</div>'; | |
| $output .= '<button class="carousel-prev">❮</button>'; | |
| $output .= '<button class="carousel-next">❯</button>'; | |
| $output .= '</div>'; | |
| } | |
| wp_reset_postdata(); | |
| return $output; | |
| } | |
| function register_same_category_products_shortcode() { | |
| add_shortcode('same_category_products', 'get_same_category_products'); | |
| } | |
| add_action('init', 'register_same_category_products_shortcode'); | |
| function add_same_category_products_styles() { | |
| ?> | |
| <style> | |
| .same-category-products { | |
| margin: 2em 0; | |
| position: relative; | |
| overflow: hidden; | |
| padding-bottom: 20px; | |
| } | |
| .products-carousel { | |
| display: flex; | |
| transition: transform 0.3s ease-in-out; | |
| min-height: 450px; | |
| } | |
| .carousel-item { | |
| flex: 0 0 25%; | |
| max-width: 25%; | |
| padding: 0 10px; | |
| box-sizing: border-box; | |
| } | |
| .carousel-prev, | |
| .carousel-next { | |
| position: absolute; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| background: #000000; | |
| color: white; | |
| border: none; | |
| padding: 10px 15px; | |
| cursor: pointer; | |
| z-index: 2; | |
| } | |
| .carousel-prev { | |
| left: 0; | |
| } | |
| .carousel-next { | |
| right: 0; | |
| } | |
| .carousel-prev:hover, | |
| .carousel-next:hover { | |
| background: #000000; | |
| color: white; | |
| } | |
| /* Mobile styles */ | |
| @media screen and (max-width: 767px) { | |
| .carousel-item { | |
| flex: 0 0 50%; | |
| max-width: 50%; | |
| padding: 0 5px; /* Уменьшенные отступы для мобильной версии */ | |
| } | |
| .products-carousel { | |
| min-height: 350px; | |
| } | |
| .same-category-products { | |
| padding: 0 15px; /* Добавляем отступы по бокам */ | |
| } | |
| .carousel-prev, | |
| .carousel-next { | |
| padding: 8px 12px; /* Уменьшаем размер кнопок */ | |
| font-size: 14px; /* Уменьшаем размер стрелок */ | |
| } | |
| .carousel-prev { | |
| left: 5px; /* Приближаем кнопки к краю */ | |
| } | |
| .carousel-next { | |
| right: 5px; | |
| } | |
| } | |
| </style> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const carousel = document.querySelector('.products-carousel'); | |
| const prevBtn = document.querySelector('.carousel-prev'); | |
| const nextBtn = document.querySelector('.carousel-next'); | |
| let position = 0; | |
| function updateCarousel() { | |
| const isMobile = window.innerWidth <= 767; | |
| const itemsPerView = isMobile ? 2 : 4; | |
| const itemWidth = 100 / itemsPerView; | |
| const totalItems = carousel.children.length; | |
| const maxPosition = Math.max(0, totalItems - itemsPerView); | |
| carousel.style.transform = `translateX(-${position * itemWidth}%)`; | |
| // Update position if needed when switching between mobile and desktop | |
| position = Math.min(position, maxPosition); | |
| } | |
| prevBtn.addEventListener('click', () => { | |
| position = Math.max(0, position - 1); | |
| updateCarousel(); | |
| }); | |
| nextBtn.addEventListener('click', () => { | |
| const isMobile = window.innerWidth <= 767; | |
| const itemsPerView = isMobile ? 2 : 4; | |
| const totalItems = carousel.children.length; | |
| const maxPosition = Math.max(0, totalItems - itemsPerView); | |
| position = Math.min(maxPosition, position + 1); | |
| updateCarousel(); | |
| }); | |
| // Handle window resize | |
| window.addEventListener('resize', updateCarousel); | |
| // Initial update | |
| updateCarousel(); | |
| }); | |
| </script> | |
| <?php | |
| } | |
| add_action('wp_head', 'add_same_category_products_styles'); | |
| // Шорткод для отображения размеров продукта на грузинском языке | |
| add_shortcode('product_dimensions_georgian', 'product_dimensions_georgian_shortcode'); | |
| // Добавляем стили для блока размеров на грузинском | |
| add_action('wp_head', 'add_georgian_product_dimensions_styles'); | |
| function add_georgian_product_dimensions_styles() { | |
| ?> | |
| <style> | |
| .product-dimensions { | |
| margin: 0; | |
| font-size: 0.9em; | |
| color: #333; | |
| } | |
| </style> | |
| <?php | |
| } | |
| function product_dimensions_georgian_shortcode($atts) { | |
| global $product; | |
| // Если мы не на странице продукта, пытаемся получить ID из атрибутов | |
| if (!is_a($product, 'WC_Product') && isset($atts['id'])) { | |
| $product = wc_get_product($atts['id']); | |
| } | |
| // Если продукт не найден, возвращаем пустую строку | |
| if (!is_a($product, 'WC_Product')) { | |
| return ''; | |
| } | |
| // Получаем размеры продукта | |
| $weight = $product->get_weight(); | |
| $dimensions = $product->get_dimensions(false); | |
| // Переводим единицы измерения на грузинский | |
| $weight_unit = get_option('woocommerce_weight_unit'); | |
| $dimension_unit = get_option('woocommerce_dimension_unit'); | |
| // Замена единиц измерения на грузинские | |
| $weight_unit_georgian = str_replace( | |
| ['kg', 'g', 'lbs', 'oz'], | |
| ['კგ', 'გ', 'ფნტ', 'უნც'], | |
| $weight_unit | |
| ); | |
| $dimension_unit_georgian = str_replace( | |
| ['cm', 'm', 'mm', 'in', 'yd'], | |
| ['სმ', 'მ', 'მმ', 'ინ', 'იარდ'], | |
| $dimension_unit | |
| ); | |
| // Формируем массив с параметрами | |
| $dimensions_parts = array(); | |
| if (!empty($weight)) { | |
| $dimensions_parts[] = "წონა: " . $weight . ' ' . $weight_unit_georgian; | |
| } | |
| if (!empty($dimensions['length'])) { | |
| $dimensions_parts[] = "სიგრძე: " . $dimensions['length'] . ' ' . $dimension_unit_georgian; | |
| } | |
| if (!empty($dimensions['width'])) { | |
| $dimensions_parts[] = "სიგანე: " . $dimensions['width'] . ' ' . $dimension_unit_georgian; | |
| } | |
| if (!empty($dimensions['height'])) { | |
| $dimensions_parts[] = "სიმაღლე: " . $dimensions['height'] . ' ' . $dimension_unit_georgian; | |
| } | |
| // Если есть хотя бы один параметр, выводим блок | |
| if (!empty($dimensions_parts)) { | |
| return '<div class="product-dimensions"><strong>მიწოდება:</strong> <strong>' . | |
| implode(' | ', $dimensions_parts) . | |
| '</strong></div>'; | |
| } | |
| return ''; | |
| } | |
| // Шорткод для отображения размеров продукта на английском языке | |
| add_shortcode('product_dimensions_english', 'product_dimensions_english_shortcode'); | |
| // Добавляем стили для блока размеров на английском | |
| add_action('wp_head', 'add_english_product_dimensions_styles'); | |
| function add_english_product_dimensions_styles() { | |
| ?> | |
| <style> | |
| .product-dimensions { | |
| margin: 0; | |
| font-size: 0.9em; | |
| color: #333; | |
| } | |
| </style> | |
| <?php | |
| } | |
| function product_dimensions_english_shortcode($atts) { | |
| global $product; | |
| // Если мы не на странице продукта, пытаемся получить ID из атрибутов | |
| if (!is_a($product, 'WC_Product') && isset($atts['id'])) { | |
| $product = wc_get_product($atts['id']); | |
| } | |
| // Если продукт не найден, возвращаем пустую строку | |
| if (!is_a($product, 'WC_Product')) { | |
| return ''; | |
| } | |
| // Получаем размеры продукта | |
| $weight = $product->get_weight(); | |
| $dimensions = $product->get_dimensions(false); | |
| // Формируем массив с параметрами | |
| $dimensions_parts = array(); | |
| if (!empty($weight)) { | |
| $dimensions_parts[] = "Weight: " . $weight . ' ' . get_option('woocommerce_weight_unit'); | |
| } | |
| if (!empty($dimensions['length'])) { | |
| $dimensions_parts[] = "Length: " . $dimensions['length'] . ' ' . get_option('woocommerce_dimension_unit'); | |
| } | |
| if (!empty($dimensions['width'])) { | |
| $dimensions_parts[] = "Width: " . $dimensions['width'] . ' ' . get_option('woocommerce_dimension_unit'); | |
| } | |
| if (!empty($dimensions['height'])) { | |
| $dimensions_parts[] = "Height: " . $dimensions['height'] . ' ' . get_option('woocommerce_dimension_unit'); | |
| } | |
| // Если есть хотя бы один параметр, выводим блок | |
| if (!empty($dimensions_parts)) { | |
| return '<div class="product-dimensions"><strong>Delivery:</strong> <strong>' . | |
| implode(' | ', $dimensions_parts) . | |
| '</strong></div>'; | |
| } | |
| return ''; | |
| } | |
| // Функция для вывода перекрестных продаж с помощью шорткода | |
| function custom_cross_sells_shortcode() { | |
| ob_start(); // Начинаем буферизацию вывода | |
| global $product; | |
| // Проверяем, определен ли объект товара | |
| if (!is_a($product, 'WC_Product')) { | |
| return ''; | |
| } | |
| // Получаем ID перекрестных продаж | |
| $cross_sell_ids = $product->get_cross_sell_ids(); | |
| if (empty($cross_sell_ids)) { | |
| return ''; | |
| } | |
| // Заголовок секции | |
| echo '<div class="ertad-qidva-section">'; | |
| echo '<h3 class="ertad-qidva-title">ერთად ყიდვა</h3>'; | |
| // Получаем объекты товаров | |
| $cross_sell_products = array_map('wc_get_product', $cross_sell_ids); | |
| echo '<div class="ertad-qidva-products">'; | |
| // Выводим основной товар | |
| echo '<div class="ertad-qidva-main-product">'; | |
| echo '<div class="product-image"><a href="' . esc_url(get_permalink($product->get_id())) . '">' . $product->get_image('thumbnail') . '</a></div>'; | |
| echo '<div class="product-info">'; | |
| echo '<div class="product-title"><a href="' . esc_url(get_permalink($product->get_id())) . '">' . $product->get_name() . '</a></div>'; | |
| echo '<div class="product-price">' . $product->get_price_html() . '</div>'; | |
| echo '</div>'; | |
| echo '</div>'; | |
| // Выводим знак плюса только если есть перекрестные продажи | |
| if (!empty($cross_sell_products)) { | |
| echo '<div class="ertad-qidva-plus">+</div>'; | |
| } | |
| // Выводим перекрестные продажи | |
| foreach ($cross_sell_products as $cross_sell_product) { | |
| if (!is_a($cross_sell_product, 'WC_Product') || !$cross_sell_product->is_in_stock()) { | |
| continue; | |
| } | |
| $product_permalink = get_permalink($cross_sell_product->get_id()); | |
| echo '<div class="ertad-qidva-item" data-product-id="' . esc_attr($cross_sell_product->get_id()) . '">'; | |
| echo '<div class="product-image"><a href="' . esc_url($product_permalink) . '">' . $cross_sell_product->get_image('thumbnail') . '</a></div>'; | |
| echo '<div class="product-info">'; | |
| echo '<div class="product-title"><a href="' . esc_url($product_permalink) . '">' . $cross_sell_product->get_name() . '</a></div>'; | |
| echo '<div class="product-price">' . $cross_sell_product->get_price_html() . '</div>'; | |
| echo '</div>'; | |
| echo '<div class="product-remove"><span class="remove-cross-sell" data-product-id="' . esc_attr($cross_sell_product->get_id()) . '">×</span></div>'; | |
| echo '</div>'; | |
| if (next($cross_sell_products)) { | |
| echo '<div class="ertad-qidva-plus">+</div>'; | |
| } | |
| } | |
| echo '</div>'; | |
| // Итоговая секция и кнопка добавления в корзину | |
| echo '<div class="ertad-qidva-summary">'; | |
| echo '<div class="ertad-qidva-total">'; | |
| echo '<div class="total-label">ჯამში:</div>'; | |
| echo '<div class="total-price" id="ertad-qidva-total-price">' . wc_price($product->get_price()) . '</div>'; | |
| echo '</div>'; | |
| echo '<button type="button" class="button ertad-qidva-add-to-cart">ყიდვა</button>'; | |
| echo '</div>'; | |
| echo '</div>'; | |
| // JavaScript для обработки выбора товаров и обновления цены | |
| ?> | |
| <script type="text/javascript"> | |
| jQuery(document).ready(function($) { | |
| // Хранение выбранных товаров | |
| var selectedProducts = {}; | |
| var mainProductPrice = <?php echo (float)$product->get_price(); ?>; | |
| // Инициализация выбранных товаров | |
| <?php foreach ($cross_sell_products as $cross_sell_product): | |
| if (!is_a($cross_sell_product, 'WC_Product') || !$cross_sell_product->is_in_stock()) continue; ?> | |
| selectedProducts[<?php echo $cross_sell_product->get_id(); ?>] = { | |
| id: <?php echo $cross_sell_product->get_id(); ?>, | |
| price: <?php echo (float)$cross_sell_product->get_price(); ?>, | |
| selected: true | |
| }; | |
| <?php endforeach; ?> | |
| // Обновление общей суммы | |
| function updateTotalPrice() { | |
| var totalPrice = mainProductPrice; | |
| // Добавляем цены выбранных товаров | |
| $.each(selectedProducts, function(id, product) { | |
| if (product.selected) { | |
| totalPrice += product.price; | |
| } | |
| }); | |
| $('#ertad-qidva-total-price').html('<?php echo get_woocommerce_currency_symbol(); ?>' + totalPrice.toFixed(2)); | |
| } | |
| // Обработка удаления товара | |
| $('.remove-cross-sell').click(function(e) { | |
| e.preventDefault(); // Предотвращаем переход по ссылке при клике на крестик | |
| e.stopPropagation(); // Останавливаем всплытие события | |
| var productId = $(this).data('product-id'); | |
| var item = $(this).closest('.ertad-qidva-item'); | |
| var plus = item.next('.ertad-qidva-plus'); | |
| if (plus.length === 0) { | |
| plus = item.prev('.ertad-qidva-plus'); | |
| } | |
| // Удаляем товар и знак плюса из интерфейса | |
| item.remove(); | |
| plus.remove(); | |
| // Помечаем товар как неактивный | |
| if (selectedProducts[productId]) { | |
| selectedProducts[productId].selected = false; | |
| } | |
| // Проверяем, остались ли выбранные товары | |
| var anySelected = false; | |
| $.each(selectedProducts, function(id, product) { | |
| if (product.selected) { | |
| anySelected = true; | |
| return false; // Прерываем цикл $.each | |
| } | |
| }); | |
| // Если не осталось выбранных товаров, удаляем знак плюса после основного товара | |
| if (!anySelected) { | |
| $('.ertad-qidva-main-product').next('.ertad-qidva-plus').remove(); | |
| } | |
| updateTotalPrice(); | |
| }); | |
| // Добавление всех выбранных товаров в корзину и переход на чекаут | |
| $('.ertad-qidva-add-to-cart').click(function(e) { | |
| e.preventDefault(); | |
| var products = []; | |
| var $button = $(this); | |
| // Добавляем индикатор загрузки | |
| $button.addClass('loading').prop('disabled', true); | |
| $button.html('<span class="loading-spinner"></span> ყიდვა'); | |
| // Добавляем основной товар | |
| products.push({ | |
| product_id: <?php echo $product->get_id(); ?>, | |
| quantity: 1 | |
| }); | |
| // Добавляем выбранные перекрестные продажи | |
| $.each(selectedProducts, function(id, product) { | |
| if (product.selected) { | |
| products.push({ | |
| product_id: product.id, | |
| quantity: 1 | |
| }); | |
| } | |
| }); | |
| // Очищаем корзину перед добавлением | |
| $.ajax({ | |
| type: 'POST', | |
| url: wc_add_to_cart_params.ajax_url, | |
| data: { | |
| action: 'woocommerce_clear_cart_ajax', | |
| security: '<?php echo wp_create_nonce('clear-cart'); ?>' | |
| }, | |
| success: function() { | |
| // После очистки корзины, добавляем товары | |
| addProducts(0); | |
| }, | |
| error: function() { | |
| // Обработка ошибки | |
| $button.removeClass('loading').prop('disabled', false); | |
| $button.html('ყიდვა'); | |
| alert('Ошибка при очистке корзины.'); | |
| } | |
| }); | |
| // Поочередно добавляем товары в корзину | |
| function addProducts(index) { | |
| if (index >= products.length) { | |
| // Все товары добавлены, перенаправляем на страницу оформления заказа | |
| window.location.href = '<?php echo wc_get_checkout_url(); ?>'; | |
| return; | |
| } | |
| var product = products[index]; | |
| $.ajax({ | |
| type: 'POST', | |
| url: wc_add_to_cart_params.ajax_url, | |
| data: { | |
| action: 'woocommerce_ajax_add_to_cart', | |
| product_id: product.product_id, | |
| quantity: product.quantity, | |
| security: '<?php echo wp_create_nonce('add-to-cart'); ?>' | |
| }, | |
| success: function(response) { | |
| if (response.error) { | |
| $button.removeClass('loading').prop('disabled', false); | |
| $button.html('ყიდვა'); | |
| alert('Ошибка при добавлении товара в корзину.'); | |
| return; | |
| } | |
| addProducts(index + 1); | |
| }, | |
| error: function() { | |
| $button.removeClass('loading').prop('disabled', false); | |
| $button.html('ყიდვა'); | |
| alert('Ошибка при добавлении товара в корзину.'); | |
| } | |
| }); | |
| } | |
| }); | |
| // Инициализация цены | |
| updateTotalPrice(); | |
| }); | |
| </script> | |
| <?php | |
| // Добавляем CSS для стилизации | |
| ?> | |
| <style type="text/css"> | |
| .ertad-qidva-section { | |
| border: 1px solid #e0e0e0; | |
| border-radius: 8px; | |
| padding: 20px; | |
| margin: 30px 0; | |
| box-shadow: 0 2px 5px rgba(0,0,0,0.05); | |
| } | |
| .ertad-qidva-title { | |
| font-size: 20px; | |
| margin-bottom: 20px; | |
| font-weight: 600; | |
| color: #333; | |
| } | |
| .ertad-qidva-products { | |
| display: flex; | |
| flex-wrap: wrap; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .ertad-qidva-main-product, | |
| .ertad-qidva-item { | |
| display: flex; | |
| align-items: center; | |
| padding: 15px; | |
| border: 1px solid #f0f0f0; | |
| border-radius: 5px; | |
| position: relative; | |
| width: 100%; | |
| max-width: 350px; | |
| transition: all 0.3s ease; | |
| } | |
| .ertad-qidva-main-product { | |
| background-color: #f9f9f9; | |
| } | |
| .ertad-qidva-item:hover { | |
| border-color: #ddd; | |
| box-shadow: 0 2px 8px rgba(0,0,0,0.1); | |
| } | |
| .product-remove { | |
| margin-left: 10px; | |
| cursor: pointer; | |
| } | |
| .remove-cross-sell { | |
| display: inline-block; | |
| width: 22px; | |
| height: 22px; | |
| line-height: 20px; | |
| text-align: center; | |
| border-radius: 50%; | |
| background-color: #f0f0f0; | |
| color: #666; | |
| font-size: 16px; | |
| font-weight: bold; | |
| transition: all 0.2s ease; | |
| } | |
| .remove-cross-sell:hover { | |
| background-color: #e84c3d; | |
| color: white; | |
| } | |
| .product-image { | |
| width: 70px; | |
| height: 70px; | |
| flex-shrink: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .product-image img { | |
| width: 100%; | |
| height: auto; | |
| max-height: 70px; | |
| object-fit: contain; | |
| display: block; | |
| } | |
| .product-info { | |
| margin-left: 15px; | |
| flex-grow: 1; | |
| overflow: hidden; | |
| } | |
| .product-title { | |
| font-weight: bold; | |
| margin-bottom: 8px; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .product-title a { | |
| color: inherit; | |
| text-decoration: none; | |
| } | |
| .product-title a:hover { | |
| text-decoration: underline; | |
| color: #e84c3d; | |
| } | |
| .ertad-qidva-plus { | |
| font-size: 22px; | |
| font-weight: bold; | |
| color: #999; | |
| padding: 0 5px; | |
| } | |
| .ertad-qidva-summary { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-top: 25px; | |
| padding-top: 20px; | |
| border-top: 1px solid #e0e0e0; | |
| } | |
| .ertad-qidva-total { | |
| display: flex; | |
| align-items: center; | |
| } | |
| .total-label { | |
| font-weight: bold; | |
| margin-right: 10px; | |
| font-size: 16px; | |
| } | |
| .total-price { | |
| font-size: 20px; | |
| font-weight: bold; | |
| color: #e84c3d; | |
| } | |
| .ertad-qidva-add-to-cart { | |
| background-color: #e84c3d; | |
| color: white; | |
| border: none; | |
| padding: 12px 25px; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| font-size: 16px; | |
| font-weight: bold; | |
| transition: background-color 0.3s ease; | |
| } | |
| .ertad-qidva-add-to-cart:hover { | |
| background-color: #d43c2d; | |
| } | |
| .ertad-qidva-add-to-cart.loading { | |
| opacity: 0.8; | |
| cursor: wait; | |
| } | |
| .loading-spinner { | |
| display: inline-block; | |
| width: 14px; | |
| height: 14px; | |
| border: 2px solid rgba(255,255,255,0.3); | |
| border-radius: 50%; | |
| border-top-color: #fff; | |
| animation: spin 1s ease-in-out infinite; | |
| margin-right: 5px; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| @media (max-width: 768px) { | |
| .ertad-qidva-products { | |
| flex-direction: column; | |
| align-items: stretch; | |
| } | |
| .ertad-qidva-main-product, | |
| .ertad-qidva-item { | |
| max-width: 100%; | |
| } | |
| .ertad-qidva-plus { | |
| align-self: center; | |
| margin: 5px 0; | |
| } | |
| .ertad-qidva-summary { | |
| flex-direction: column; | |
| gap: 15px; | |
| } | |
| .ertad-qidva-add-to-cart { | |
| width: 100%; | |
| } | |
| } | |
| </style> | |
| <?php | |
| $output = ob_get_clean(); // Получаем содержимое буфера и очищаем его | |
| return $output; | |
| } | |
| // Регистрируем шорткод [ertad_qidva] | |
| add_shortcode('ertad_qidva', 'custom_cross_sells_shortcode'); | |
| // Функция для AJAX добавления товаров в корзину | |
| add_action('wp_ajax_woocommerce_ajax_add_to_cart', 'custom_ajax_add_to_cart'); | |
| add_action('wp_ajax_nopriv_woocommerce_ajax_add_to_cart', 'custom_ajax_add_to_cart'); | |
| function custom_ajax_add_to_cart() { | |
| check_ajax_referer('add-to-cart', 'security'); | |
| $product_id = apply_filters('woocommerce_add_to_cart_product_id', absint($_POST['product_id'])); | |
| $quantity = empty($_POST['quantity']) ? 1 : wc_stock_amount($_POST['quantity']); | |
| $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $quantity); | |
| if ($passed_validation && WC()->cart->add_to_cart($product_id, $quantity)) { | |
| do_action('woocommerce_ajax_added_to_cart', $product_id); | |
| if ('yes' === get_option('woocommerce_cart_redirect_after_add')) { | |
| wc_add_to_cart_message(array($product_id => $quantity), true); | |
| } | |
| WC_AJAX::get_refreshed_fragments(); | |
| } else { | |
| $data = array( | |
| 'error' => true, | |
| 'product_url' => apply_filters('woocommerce_cart_redirect_after_error', get_permalink($product_id), $product_id) | |
| ); | |
| wp_send_json($data); | |
| } | |
| wp_die(); | |
| } | |
| // Функция для очистки корзины через AJAX | |
| add_action('wp_ajax_woocommerce_clear_cart_ajax', 'custom_clear_cart_ajax'); | |
| add_action('wp_ajax_nopriv_woocommerce_clear_cart_ajax', 'custom_clear_cart_ajax'); | |
| function custom_clear_cart_ajax() { | |
| check_ajax_referer('clear-cart', 'security'); | |
| WC()->cart->empty_cart(); | |
| wp_send_json_success(); | |
| wp_die(); | |
| } | |
| // WooCommerce AJAX Add to Cart კლასი და data-product_id დამატება Elementor-ის გენერირებულ ბმულებზე | |
| add_filter( 'elementor/frontend/the_content', function( $content ) { | |
| return preg_replace_callback( | |
| '/<a([^>]+href=["\'][^"\']*add-to-cart=(\d+)[^>]*)>/i', | |
| function( $matches ) { | |
| $tag = $matches[0]; | |
| $product_id = (int) $matches[2]; | |
| // class დამატება | |
| if ( strpos( $tag, 'ajax_add_to_cart' ) === false ) { | |
| if ( preg_match('/class=["\']([^"\']*)["\']/', $tag) ) { | |
| $tag = preg_replace( | |
| '/class=["\']([^"\']*)["\']/', | |
| 'class="$1 add_to_cart_button ajax_add_to_cart"', | |
| $tag | |
| ); | |
| } else { | |
| $tag = str_replace('<a ', '<a class="add_to_cart_button ajax_add_to_cart" ', $tag); | |
| } | |
| } | |
| // data-product_id დამატება | |
| if ( strpos( $tag, 'data-product_id' ) === false ) { | |
| $tag = str_replace('<a ', '<a data-product_id="' . esc_attr( $product_id ) . '" ', $tag); | |
| } | |
| return $tag; | |
| }, | |
| $content | |
| ); | |
| }, 20 ); | |
| // გადამისამართების გათიშვა | |
| add_filter( 'woocommerce_add_to_cart_redirect', '__return_false' ); | |
| // WooCommerce-ის საჭირო JS-ის ჩატვირთვა | |
| add_action( 'wp_enqueue_scripts', function() { | |
| wp_enqueue_script( 'wc-add-to-cart' ); | |
| wp_enqueue_script( 'wc-cart-fragments' ); | |
| }, 20 ); | |
| /* ენების შემცვლელი კოდი / | |
| */ | |
| if (!defined('ABSPATH')) exit; | |
| /** | |
| * Shortcode: [custom_lang_switcher] | |
| * Minimal, JS/HTML/CSS-based language switcher with a flag and toggle. | |
| */ | |
| add_shortcode('custom_lang_switcher', function () { | |
| ob_start(); ?> | |
| <div class="lsw" data-current="" data-ka-url="" data-en-url=""></div> | |
| <style> | |
| .lsw { display: inline-block; } | |
| /* 1px-ით ნაკლები დაშორება დროშასა და ღილაკს შორის */ | |
| .lsw-wrap { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .lsw-flag { width: 26px; height: auto; display: block; } | |
| .lsw-toggle { | |
| position: relative; | |
| width: 56px; | |
| height: 30px; | |
| padding: 0; | |
| border: 0; | |
| background: transparent; | |
| cursor: pointer; | |
| outline: none; | |
| border-radius: 999px; /* აქედანვე დავამრგვალეთ კონტეინერი */ | |
| } | |
| /* ჰოვერზე იგივე ყვითელი, outline/box-shadow უცვლელი */ | |
| .lsw-toggle:hover .lsw-track { | |
| background: #F2F2F2 !important; | |
| box-shadow: inset 0 0 0 2px rgba(0,0,0,.6); | |
| } | |
| /* ფოკუსზე მხოლოდ შიდა outline */ | |
| .lsw-toggle:focus-visible { | |
| outline: none; | |
| box-shadow: 0 0 0 2px #111 inset; | |
| border-radius: 999px; | |
| } | |
| .lsw-track { | |
| position: absolute; | |
| inset: 0; | |
| background: #F2F2F2; | |
| border-radius: 999px; | |
| box-shadow: inset 0 0 0 2px rgba(0,0,0,.6); | |
| } | |
| .lsw-thumb { | |
| position: absolute; | |
| top: 4px; | |
| left: 4px; | |
| width: 22px; | |
| height: 22px; | |
| background: #111; | |
| border-radius: 50%; | |
| transition: left .25s ease; | |
| } | |
| .lsw-wrap.is-en .lsw-thumb { left: 30px; } | |
| @media (max-width: 420px){ | |
| .lsw-flag { width: 22px; } | |
| .lsw-toggle { width: 50px; height: 22px; } | |
| .lsw-thumb { width: 14px; height: 14px; top: 4px; left: 4px; } | |
| .lsw-wrap.is-en .lsw-thumb { left: 26px; } | |
| .lsw-wrap { | |
| gap: 2px !important; /* მობილურზე კიდევ უფრო ახლოს */ | |
| } | |
| } | |
| </style> | |
| <script> | |
| (function () { | |
| const CONFIG = { | |
| flags: { | |
| ka: 'https://upload.wikimedia.org/wikipedia/commons/0/0f/Flag_of_Georgia.svg', | |
| en: 'https://upload.wikimedia.org/wikipedia/commons/8/83/Flag_of_the_United_Kingdom_%283-5%29.svg' | |
| }, | |
| homes: { | |
| ka: '/', | |
| en: '/en/' | |
| }, | |
| pairs: [ | |
| ['/', '/en/'], | |
| ['/კონტაქტი/', '/en/contact/'], | |
| ['/ენა-შეცვლა/', '/en/change-language/'] | |
| ], | |
| inferLanguage: () => { | |
| const htmlLang = (document.documentElement.lang || '').toLowerCase(); | |
| if (htmlLang.startsWith('ka')) return 'ka'; | |
| if (htmlLang.startsWith('en')) return 'en'; | |
| const p = location.pathname.toLowerCase(); | |
| if (p === '/en' || p.startsWith('/en/')) return 'en'; | |
| return 'ka'; | |
| } | |
| }; | |
| function normalizePath(p) { | |
| try { | |
| const u = new URL(p, location.origin); | |
| let path = u.pathname; | |
| if (!path.endsWith('/')) path += '/'; | |
| return path.toLowerCase(); | |
| } catch { | |
| let path = p || '/'; | |
| if (!path.startsWith('/')) path = '/' + path; | |
| if (!path.endsWith('/')) path += '/'; | |
| return path.toLowerCase(); | |
| } | |
| } | |
| function buildMap(pairs) { | |
| const map = new Map(); | |
| pairs.forEach(([ka, en]) => { | |
| const KA = normalizePath(ka); | |
| const EN = normalizePath(en); | |
| map.set(KA, EN); | |
| map.set(EN, KA); | |
| }); | |
| return map; | |
| } | |
| function resolveTarget(currentLang, map) { | |
| const here = normalizePath(location.pathname); | |
| if (map.has(here)) return map.get(here); | |
| return currentLang === 'ka' ? CONFIG.homes.en : CONFIG.homes.ka; | |
| } | |
| function render(container, currentLang, targetUrl) { | |
| const wrap = document.createElement('div'); | |
| wrap.className = 'lsw-wrap ' + (currentLang === 'en' ? 'is-en' : 'is-ka'); | |
| wrap.setAttribute('role', 'group'); | |
| wrap.setAttribute('aria-label', 'Language switcher'); | |
| const flag = document.createElement('img'); | |
| flag.className = 'lsw-flag'; | |
| flag.alt = currentLang === 'en' ? 'English' : 'ქართული'; | |
| flag.src = CONFIG.flags[currentLang]; | |
| flag.referrerPolicy = 'no-referrer'; | |
| flag.decoding = 'async'; | |
| flag.loading = 'lazy'; | |
| flag.onerror = function(){ this.style.display='none'; }; | |
| const btn = document.createElement('button'); | |
| btn.className = 'lsw-toggle'; | |
| btn.type = 'button'; | |
| btn.setAttribute('role', 'switch'); | |
| btn.setAttribute('aria-checked', currentLang === 'en' ? 'true' : 'false'); | |
| btn.setAttribute('aria-label', 'Switch language'); | |
| const track = document.createElement('span'); | |
| track.className = 'lsw-track'; | |
| const thumb = document.createElement('span'); | |
| thumb.className = 'lsw-thumb'; | |
| btn.appendChild(track); | |
| btn.appendChild(thumb); | |
| btn.addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| const url = targetUrl.startsWith('http') ? targetUrl : (location.origin + targetUrl); | |
| window.location.href = url; | |
| }); | |
| wrap.appendChild(flag); | |
| wrap.appendChild(btn); | |
| container.innerHTML = ''; | |
| container.appendChild(wrap); | |
| } | |
| function initOne(container, map) { | |
| const dataKa = (container.getAttribute('data-ka-url') || '').trim(); | |
| const dataEn = (container.getAttribute('data-en-url') || '').trim(); | |
| let current = (container.getAttribute('data-current') || '').toLowerCase(); | |
| if (current !== 'ka' && current !== 'en') current = CONFIG.inferLanguage(); | |
| let target = ''; | |
| if (dataKa && dataEn) { | |
| target = current === 'ka' ? dataEn : dataKa; | |
| } else { | |
| target = resolveTarget(current, map); | |
| } | |
| render(container, current, target); | |
| } | |
| function init() { | |
| const map = buildMap(CONFIG.pairs); | |
| document.querySelectorAll('.lsw').forEach(el => initOne(el, map)); | |
| } | |
| if (document.readyState !== 'loading') init(); | |
| else document.addEventListener('DOMContentLoaded', init); | |
| })(); | |
| </script> | |
| <?php | |
| return ob_get_clean(); | |
| }); | |
| function wpb_search_sku( $where, $query ) { | |
| global $wpdb; | |
| if ( is_admin() || ! $query->is_search() || ! isset( $_GET['s'] ) ) { | |
| return $where; | |
| } | |
| $search_term = esc_sql( like_escape( $_GET['s'] ) ); | |
| // SKU ძიება | |
| $where .= " OR ( {$wpdb->posts}.ID IN ( | |
| SELECT post_id FROM {$wpdb->postmeta} | |
| WHERE meta_key='_sku' AND meta_value LIKE '%{$search_term}%' | |
| ) )"; | |
| // თუ WPML ან Polylang გაქვს, დავამატოთ ენის ფილტრი | |
| if ( defined( 'ICL_LANGUAGE_CODE' ) ) { | |
| global $sitepress; | |
| $current_lang = $sitepress->get_current_language(); | |
| $where .= $wpdb->prepare( | |
| " AND {$wpdb->posts}.ID IN ( | |
| SELECT element_id FROM {$wpdb->prefix}icl_translations | |
| WHERE language_code = %s | |
| )", | |
| $current_lang | |
| ); | |
| } | |
| return $where; | |
| } | |
| add_filter( 'posts_where', 'wpb_search_sku', 10, 2 ); | |
| /** | |
| * Limit JetEngine Listing (Query ID(s)) to the latest 50 products (9 per page pagination). | |
| * WPML-aware, language-separated cache, debug logs and clear-cache helper. | |
| * | |
| * Usage: | |
| * - Give your Georgian Listing Grid Query ID = 'productNOOO' | |
| * - Give your English Listing Grid Query ID = 'productNOOO_en' | |
| * - Paste this into functions.php or a site-specific plugin (remove previous attempts) | |
| * | |
| * Note: clear object cache and transients after installing. | |
| */ | |
| add_filter( 'posts_clauses', 'je_limit_listing_to_latest_50_products_multilang', 10, 2 ); | |
| function je_limit_listing_to_latest_50_products_multilang( $clauses, $query ) { | |
| global $wpdb; | |
| // ---------- Safety bailouts ---------- | |
| // Don't change admin screens (unless it's an AJAX frontend call) | |
| if ( is_admin() && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) { | |
| return $clauses; | |
| } | |
| // Don't affect REST API calls | |
| if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { | |
| return $clauses; | |
| } | |
| // ---------- Which Query IDs should be limited? ---------- | |
| // Set the Query IDs you use in Elementor/JetEngine for each language | |
| $possible_ids = array( 'productNOOO', 'productNOOO_en' ); | |
| $possible_keys = array( 'jet_ajax', 'jet_ajax_listing', 'listing_id', 'query_id', 'query_name', 'jet_listing' ); | |
| $apply = false; | |
| // 1) If Elementor/JetEngine sets the query name on the WP_Query object | |
| $elementor_name = $query->get( 'elementor_query_name' ); | |
| if ( $elementor_name && in_array( $elementor_name, $possible_ids, true ) ) { | |
| $apply = true; | |
| } | |
| // 2) If JetEngine sends the Query ID via request args (AJAX) | |
| if ( ! $apply ) { | |
| foreach ( $possible_keys as $k ) { | |
| if ( isset( $_REQUEST[ $k ] ) && in_array( $_REQUEST[ $k ], $possible_ids, true ) ) { | |
| $apply = true; | |
| break; | |
| } | |
| } | |
| } | |
| // 3) Fallback (only if nothing else matched) — very conservative: | |
| // If this is a frontend product loop with posts_per_page = 9, assume it's ours. | |
| if ( ! $apply ) { | |
| if ( ! is_admin() && $query->get( 'post_type' ) === 'product' && (int) $query->get( 'posts_per_page' ) === 9 ) { | |
| $apply = true; | |
| } | |
| } | |
| if ( ! $apply ) { | |
| return $clauses; | |
| } | |
| // ---------- Determine current language (WPML) ---------- | |
| $wpml_active = ( defined( 'ICL_SITEPRESS_VERSION' ) || function_exists( 'icl_object_id' ) ); | |
| $lang = null; | |
| if ( $wpml_active ) { | |
| // WPML-provided current language (eg: 'en', 'ka') | |
| $lang = apply_filters( 'wpml_current_language', null ); | |
| } | |
| if ( ! $lang ) { | |
| // fallback: first two letters of locale or 'default' | |
| $locale = get_locale(); | |
| $lang = $locale ? substr( $locale, 0, 2 ) : 'default'; | |
| } | |
| // ---------- Build / reuse cached list of latest 50 product IDs for this language ---------- | |
| $transient_key = 'je_latest_50_product_ids_' . $lang; | |
| $ids = get_transient( $transient_key ); | |
| if ( false === $ids ) { | |
| $ids = array(); | |
| // 1) If WPML is active and icl_translations table exists, use direct SQL for reliable language-specific IDs | |
| if ( $wpml_active ) { | |
| $icl_table = $wpdb->prefix . 'icl_translations'; | |
| $table_exists = $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $wpdb->esc_like( $wpdb->prefix . 'icl_translations' ) ) ); | |
| if ( $table_exists ) { | |
| // element_type example: 'post_product' | |
| $element_type = 'post_' . 'product'; | |
| $sql = $wpdb->prepare( | |
| "SELECT p.ID | |
| FROM {$wpdb->posts} p | |
| INNER JOIN {$icl_table} t ON p.ID = t.element_id AND t.element_type = %s | |
| WHERE p.post_type = %s | |
| AND p.post_status = 'publish' | |
| AND t.language_code = %s | |
| ORDER BY p.post_date DESC | |
| LIMIT 50", | |
| $element_type, | |
| 'product', | |
| $lang | |
| ); | |
| $ids = $wpdb->get_col( $sql ); | |
| } | |
| } | |
| // 2) Fallback: WP_Query with WPML filters enabled (suppress_filters => false) | |
| if ( empty( $ids ) ) { | |
| $latest = new WP_Query( array( | |
| 'post_type' => 'product', | |
| 'posts_per_page' => 50, | |
| 'orderby' => 'date', | |
| 'order' => 'DESC', | |
| 'fields' => 'ids', | |
| 'no_found_rows' => true, | |
| 'suppress_filters' => false, // let WPML and other filters run | |
| ) ); | |
| if ( ! empty( $latest->posts ) ) { | |
| $ids = $latest->posts; | |
| } | |
| } | |
| // 3) Final fallback: WP_Query without filters (default language products) | |
| if ( empty( $ids ) ) { | |
| $latest = new WP_Query( array( | |
| 'post_type' => 'product', | |
| 'posts_per_page' => 50, | |
| 'orderby' => 'date', | |
| 'order' => 'DESC', | |
| 'fields' => 'ids', | |
| 'no_found_rows' => true, | |
| ) ); | |
| if ( ! empty( $latest->posts ) ) { | |
| $ids = $latest->posts; | |
| } | |
| } | |
| // Cache the result per-language (short TTL for safety) | |
| set_transient( $transient_key, $ids, HOUR_IN_SECONDS ); | |
| } | |
| // If we couldn't find any IDs — bail | |
| if ( empty( $ids ) || ! is_array( $ids ) ) { | |
| if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { | |
| error_log( '[JE_LIMIT] No product IDs found for lang: ' . $lang ); | |
| } | |
| return $clauses; | |
| } | |
| // Sanitize and add WHERE clause to limit main query to those IDs only | |
| $ids = array_map( 'absint', $ids ); | |
| $ids_list = implode( ',', $ids ); | |
| // Inject limitation so WP's COUNT and Pagination honor the reduced dataset | |
| $clauses['where'] .= " AND {$wpdb->posts}.ID IN ( {$ids_list} )"; | |
| // Ensure results ordered by date (latest) | |
| $clauses['orderby'] = "{$wpdb->posts}.post_date DESC"; | |
| if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { | |
| error_log( "[JE_LIMIT] Applied for lang: {$lang}. IDs: {$ids_list}" ); | |
| } | |
| return $clauses; | |
| } | |
| // Helper: clear per-language transients via URL (for testing) | |
| // Visit: https://your-site.example/?je_clear_latest_50=1 | |
| add_action( 'init', function() { | |
| if ( isset( $_GET['je_clear_latest_50'] ) && $_GET['je_clear_latest_50'] == '1' ) { | |
| $langs = array(); | |
| if ( function_exists( 'icl_get_languages' ) ) { | |
| $wpml_langs = apply_filters( 'wpml_active_languages', null, array( 'skip_missing' => 0 ) ); | |
| if ( $wpml_langs && is_array( $wpml_langs ) ) { | |
| $langs = array_keys( $wpml_langs ); | |
| } | |
| } | |
| // always include fallback key | |
| $langs[] = 'default'; | |
| foreach ( $langs as $l ) { | |
| delete_transient( 'je_latest_50_product_ids_' . $l ); | |
| } | |
| if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { | |
| error_log( '[JE_LIMIT] Transients cleared by ?je_clear_latest_50=1' ); | |
| } | |
| } | |
| }); | |
| // === Copilot Slider === | |
| // 1. Enqueue CSS და JS | |
| function copilot_slider_enqueue_assets() { | |
| // CSS | |
| wp_register_style('copilot-slider-style', false); | |
| wp_enqueue_style('copilot-slider-style'); | |
| wp_add_inline_style('copilot-slider-style', ' | |
| .copilot-slider { | |
| position: relative; | |
| width: 100%; | |
| max-width: 100vw; | |
| aspect-ratio: 16 / 5; /* ოდნავ დაბალი სლაიდი */ | |
| background: #222; | |
| overflow: hidden; | |
| margin: 0 auto; | |
| border-bottom-left-radius: 7px; | |
| border-bottom-right-radius: 7px; | |
| } | |
| .copilot-slides { | |
| display: flex; | |
| height: 100%; | |
| } | |
| .copilot-slide { | |
| min-width: 100%; | |
| height: 100%; | |
| display: none; | |
| justify-content: center; | |
| align-items: center; | |
| position: relative; | |
| } | |
| .copilot-slide.active { display: flex; animation: fadeIn 0.7s; } | |
| @keyframes fadeIn { from {opacity:0;} to {opacity:1;} } | |
| .copilot-slide img { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| border-radius: 7px; | |
| } | |
| /* მობილური და desktop ვერსიების კონტროლი */ | |
| .copilot-slide img.mobile { display: none; } | |
| .copilot-slide img.desktop { display: block; } | |
| @media (max-width: 768px) { | |
| .copilot-slide img.mobile { display: block; } | |
| .copilot-slide img.desktop { display: none; } | |
| } | |
| /* Desktop-ზე სურათის პროპორცია შემცირებული */ | |
| @media (min-width: 769px) { | |
| .copilot-slider { | |
| aspect-ratio: 1600 / 500; /* შემცირებული სიმაღლე */ | |
| height: auto; | |
| } | |
| .copilot-slide img { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: contain; | |
| background: #000; | |
| } | |
| } | |
| /* Navigation Arrows */ | |
| .copilot-prev, .copilot-next { | |
| position: absolute; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| background: rgba(0,0,0,0.4); | |
| color: #fff; | |
| border: none; | |
| width: 50px; | |
| height: 50px; | |
| font-size: 26px; | |
| cursor: pointer; | |
| z-index: 10; | |
| border-radius: 50%; | |
| transition: all 0.3s ease; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| .copilot-prev:hover, .copilot-next:hover { | |
| background: #f7b731; | |
| color: #fff; | |
| } | |
| .copilot-prev { left: 15px; } | |
| .copilot-next { right: 15px; } | |
| /* Dots */ | |
| .copilot-dots { | |
| position: absolute; | |
| bottom: 18px; | |
| left: 0; | |
| width: 100%; | |
| text-align: center; | |
| z-index: 10; | |
| } | |
| .copilot-dots span { | |
| display: inline-block; | |
| height: 13px; | |
| width: 13px; | |
| margin: 0 6px; | |
| background: rgba(255,255,255,0.7); | |
| border-radius: 50%; | |
| cursor: pointer; | |
| } | |
| .copilot-dots span.active { background: #ffd000; } | |
| @media (max-width: 900px) { .copilot-slider { aspect-ratio: 16 / 8; } } | |
| @media (max-width: 600px) { .copilot-slider { aspect-ratio: 16 / 10; } } | |
| '); | |
| // JS | |
| wp_register_script('copilot-slider-script', false); | |
| wp_enqueue_script('copilot-slider-script'); | |
| wp_add_inline_script('copilot-slider-script', ' | |
| document.addEventListener("DOMContentLoaded", function () { | |
| document.querySelectorAll(".copilot-slider").forEach(slider => { | |
| const slides = Array.from(slider.querySelectorAll(".copilot-slide")); | |
| const prev = slider.querySelector(".copilot-prev"); | |
| const next = slider.querySelector(".copilot-next"); | |
| const dotsContainer = slider.querySelector(".copilot-dots"); | |
| let current = 0; | |
| // Dots | |
| slides.forEach((_, i) => { | |
| const dot = document.createElement("span"); | |
| dot.addEventListener("click", () => goTo(i)); | |
| dotsContainer.appendChild(dot); | |
| }); | |
| const dots = Array.from(dotsContainer.querySelectorAll("span")); | |
| function update() { | |
| slides.forEach((s, i) => s.classList.toggle("active", i === current)); | |
| dots.forEach((d, i) => d.classList.toggle("active", i === current)); | |
| } | |
| function goTo(i) { current = (i+slides.length)%slides.length; update(); } | |
| function nextSlide() { goTo(current+1); } | |
| function prevSlide() { goTo(current-1); } | |
| prev.addEventListener("click", prevSlide); | |
| next.addEventListener("click", nextSlide); | |
| // Auto Slide | |
| setInterval(nextSlide, 5000); | |
| update(); | |
| }); | |
| }); | |
| '); | |
| } | |
| add_action('wp_enqueue_scripts', 'copilot_slider_enqueue_assets'); | |
| // 2. ქართული შორტკოდი | |
| function copilot_slider_shortcode_ka() { | |
| ob_start(); ?> | |
| <div class="copilot-slider"> | |
| <div class="copilot-slides"> | |
| <div class="copilot-slide active"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/03/WhatsApp-Image-2025-03-25-at-12.56.37.jpeg" alt="სლაიდი 1 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/მაინ-ტანტან1.jpg" alt="სლაიდი 1 Mobile"> | |
| </div> | |
| <div class="copilot-slide"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/მოტობლოკი.jpg" alt="სლაიდი 2 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/მმოტობლოკი1.jpg" alt="სლაიდი 2 Mobile"> | |
| </div> | |
| <div class="copilot-slide"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/აქსესუარები-1.jpg" alt="სლაიდი 3 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/აქსესუარები.jpg" alt="სლაიდი 3 Mobile"> | |
| </div> | |
| <div class="copilot-slide"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/შემადუღებელი-ხელსაწყოები-1.jpg" alt="სლაიდი 4 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/შემადუღებელი-ხელსაწყოები.jpg" alt="სლაიდი 4 Mobile"> | |
| </div> | |
| <div class="copilot-slide"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/ბაღის-ხელსაწყოები-2.jpg" alt="სლაიდი 5 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/ბაღის-ხელსაწყოები-1.jpg" alt="სლაიდი 5 Mobile"> | |
| </div> | |
| <div class="copilot-slide"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/ავტოფარეხის-ხელსაწყოები.jpg" alt="სლაიდი 6 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/აცტოფარეხის-ხელსაწყოები-1.jpg" alt="სლაიდი 6 Mobile"> | |
| </div> | |
| <div class="copilot-slide"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/ხელის-იარაღები-1.jpg" alt="სლაიდი 7 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/ხელის-იარაღები.jpg" alt="სლაიდი 7 Mobile"> | |
| </div> | |
| </div> | |
| <button class="copilot-prev">❮</button> | |
| <button class="copilot-next">❯</button> | |
| <div class="copilot-dots"></div> | |
| </div> | |
| <?php | |
| return ob_get_clean(); | |
| } | |
| add_shortcode('copilot_slider_ka', 'copilot_slider_shortcode_ka'); | |
| // 3. ინგლისური შორტკოდი | |
| function copilot_slider_shortcode_en() { | |
| ob_start(); ?> | |
| <div class="copilot-slider"> | |
| <div class="copilot-slides"> | |
| <div class="copilot-slide active"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/03/Facebook-banner-1.jpg" alt="Slide 1 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/მაინ-ტანტან1-ENG.jpg" alt="Slide 1 Mobile"> | |
| </div> | |
| <div class="copilot-slide"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/03/POWER-TILLER.jpg" alt="Slide 2 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/მმოტობლოკი1-ENG.jpg" alt="Slide 2 Mobile"> | |
| </div> | |
| <div class="copilot-slide"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/03/ACCESSORIES.jpg" alt="Slide 3 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/აქსესუარები-ENG.jpg" alt="Slide 3 Mobile"> | |
| </div> | |
| <div class="copilot-slide"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/03/WELDING-MACHINES.jpg" alt="Slide 4 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/შემადუღებელი-ხელსაწყოები-ENG.jpg" alt="Slide 4 Mobile"> | |
| </div> | |
| <div class="copilot-slide"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/03/GARAGE-TOOLS.jpg" alt="Slide 5 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/აცტოფარეხის-ხელსაწყოები-ENG.jpg" alt="Slide 5 Mobile"> | |
| </div> | |
| <div class="copilot-slide"> | |
| <img class="desktop" src="https://tantangeorgia.ge/wp-content/uploads/2025/03/HAND-TOOLS.jpg" alt="Slide 6 Desktop"> | |
| <img class="mobile" src="https://tantangeorgia.ge/wp-content/uploads/2025/09/ხელის-იარაღები-ENG.jpg" alt="Slide 6 Mobile"> | |
| </div> | |
| </div> | |
| <button class="copilot-prev">❮</button> | |
| <button class="copilot-next">❯</button> | |
| <div class="copilot-dots"></div> | |
| </div> | |
| <?php | |
| return ob_get_clean(); | |
| } | |
| add_shortcode('copilot_slider_en', 'copilot_slider_shortcode_en'); | |
| /*BROADCAMP მგონი ასე ქვია ნავიგაციის მენიუა თუ სად იმყოფება მომხმარებელი იმ მომენტში ვუკომერსის ბროადს აუქმებს და რეალურად ამატებს რა სად არის გაწერილი */ | |
| add_filter( 'woocommerce_get_breadcrumb', 'custom_all_categories_breadcrumb', 20, 2 ); | |
| function custom_all_categories_breadcrumb( $crumbs, $breadcrumb ) { | |
| if ( is_product() ) { | |
| global $post; | |
| // ავიღოთ ყველა კატეგორია | |
| $categories = get_the_terms( $post->ID, 'product_cat' ); | |
| if ( $categories && ! is_wp_error( $categories ) ) { | |
| // ამოვწმინდოთ ყველაფერი პროდუქტის გვერდის გარდა (მთავარი) | |
| $new_crumbs = array(); | |
| $new_crumbs[] = $crumbs[0]; // "მთავარი" | |
| // დავამატოთ უნიკალური კატეგორიები (დუბლიკატის გარეშე) | |
| $added = array(); | |
| foreach ( $categories as $cat ) { | |
| if ( !in_array( $cat->term_id, $added ) ) { | |
| $new_crumbs[] = array( | |
| $cat->name, | |
| get_term_link( $cat ) | |
| ); | |
| $added[] = $cat->term_id; | |
| } | |
| } | |
| // ბოლოს დავამატოთ პროდუქტის სახელი | |
| $new_crumbs[] = array( get_the_title( $post->ID ), '' ); | |
| return $new_crumbs; | |
| } | |
| } | |
| return $crumbs; | |
| } | |
| // functions.php-ში ჩაამატე | |
| add_filter('woocommerce_get_price_html', 'custom_price_html_order', 100, 2); | |
| function custom_price_html_order($price, $product) { | |
| if ($product->is_type('simple') || $product->is_type('variable')) { | |
| $regular_price = wc_get_price_to_display($product, array('price' => $product->get_regular_price())); | |
| $sale_price = wc_get_price_to_display($product, array('price' => $product->get_sale_price())); | |
| if ($product->is_on_sale() && $sale_price) { | |
| $price = '<span class="sale-price">' . wc_price($sale_price) . '</span> '; | |
| $price .= '<del class="regular-price">' . wc_price($regular_price) . '</del>'; | |
| } | |
| } | |
| return $price; | |
| } | |
| add_filter( 'wpml_word_count_enabled', '__return_false' ); | |
| add_action( 'woocommerce_product_query_tax_query', 'force_exclude_subcat_products', 999 ); | |
| function force_exclude_subcat_products( $tax_query ) { | |
| if ( is_product_category() && ! is_admin() ) { | |
| $term = get_queried_object(); | |
| if ( $term && isset( $term->term_id ) ) { | |
| // წავშალოთ ყველა ძველი product_cat ფილტრი და დავტოვოთ მხოლოდ ზუსტი კატეგორია | |
| $new_query = array( | |
| array( | |
| 'taxonomy' => 'product_cat', | |
| 'field' => 'term_id', | |
| 'terms' => array( $term->term_id ), | |
| 'include_children' => false, | |
| ), | |
| ); | |
| return $new_query; | |
| } | |
| } | |
| return $tax_query; | |
| } | |
| // enqueue header css/js properly (put in functions.php ან Code Snippets) | |
| add_action('wp_enqueue_scripts', function() { | |
| if ( ! is_admin() ) { | |
| $ver = file_exists( get_stylesheet_directory() . '/assets/js/header.js' ) ? filemtime( get_stylesheet_directory() . '/assets/js/header.js' ) : false; | |
| wp_enqueue_style( 'tgt-header-css', get_stylesheet_directory_uri() . '/assets/css/header.css', array(), $ver ); | |
| wp_enqueue_script( 'tgt-header-js', get_stylesheet_directory_uri() . '/assets/js/header.js', array(), $ver, true ); // load in footer | |
| // ensure Woo fragments is available | |
| if ( class_exists('WooCommerce') && ! wp_script_is('wc-cart-fragments', 'enqueued') ) { | |
| wp_enqueue_script('wc-cart-fragments'); | |
| } | |
| } | |
| }, 20); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment