Инструмент кастомизации цвета SVG для WooCommerce

Вопрос или проблема

Мне нужна помощь относительно магазина WooCommerce, который я создаю. Это для дизайнера текстиля на заказ, идея заключается в том, что клиенты могут просматривать пустой шаблон продукта, раскрашивать его по своему вкусу и отправлять его для изготовления на заказ.

Основная идея:

  1. Администратор загружает файл SVG в пользовательское поле продукта (ACF) — работает
  2. На пользовательском шаблоне single-product.php WC, если поле SVG не пустое, загружается соответствующий файл SVG и скрипт раскраски. — работает
  3. Клиент раскрашивает SVG с помощью скрипта раскраски по своему усмотрению. — работает
  4. Реализована кнопка «Добавить в корзину», чтобы выполнить AJAX $_POST запрос с элементом SVG DOM. Он отправляется на сервер. — работает
  5. Отображение SVG как в корзине, так и на странице «Спасибо». — работает
  6. Пользовательский SVG также должен отображаться на странице Админ «Просмотр заказа», чтобы дизайнер знал, как был раскрашен шаблон. — эта часть не работает.

Я подозреваю, что поскольку он добавляется к метаданным элемента заказа с помощью $item->update_meta_data('custom_svg_content', $values['custom_svg_content']);
SVG очищается, и он не отображается правильно на странице просмотра заказа администратором. Он удаляет все теги, поэтому у меня остается только встроенный CSS SVG. В других местах это работает, однако я считаю, что мое решение не идеальное, так как я пропускаю очистку.

Мои вопросы: Где мне нужно добавить очистку, и какой метод следует использовать для отображения SVG на странице «Просмотр заказа» администратора? Интересно также по поводу уязвимостей и того, есть ли лучший способ сделать это (конвертировать SVG в PNG через Imagick и работать с ним?)

Я действительно ценю любые комментарии или помощь, которые могли бы направить меня в правильном направлении. Спасибо!

Соответствующая часть functions.php:

function enqueue_custom_ajax_add_to_cart_script() {
    wp_enqueue_script( 'custom-ajax-add-to-cart', get_template_directory_uri() . '/js/custom-ajax-add-to-cart.js', array( 'jquery' ), null, true );

    // Передаем URL AJAX и nonce в скрипт
    wp_localize_script( 'custom-ajax-add-to-cart', 'custom_ajax_params', array(
        'ajax_url' => admin_url( 'admin-ajax.php' ),
        'nonce'    => wp_create_nonce( 'custom_ajax_nonce' ),
    ));
}
add_action( 'wp_enqueue_scripts', 'enqueue_custom_ajax_add_to_cart_script' );

add_action('wp_ajax_add_to_cart', 'custom_ajax_add_to_cart');
add_action('wp_ajax_nopriv_add_to_cart', 'custom_ajax_add_to_cart');

function custom_ajax_add_to_cart() {
    // Проверка nonce и других мер безопасности
    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'custom_ajax_nonce')) {
        wp_send_json_error('Неверный nonce');
        wp_die();
    }

    // Получение ID продукта и содержимого SVG из POST запроса
    $product_id = absint($_POST['product_id']);
    $svg_content = wp_unslash($_POST['svg_content']); // Очистка содержимого SVG

    // Логирование полученных данных
    error_log('ID продукта: ' . $product_id);
    error_log('Содержимое SVG: ' . $svg_content); // Логируем содержимое SVG

    // Создание уникального ключа для обеспечения отдельных строк для каждого экземпляра
    $unique_key = md5($product_id . $svg_content); // Уникальный ключ для цвета SVG

    // Подготовка данных элемента корзины с пользовательским содержимым SVG
    $cart_item_data = array(
        'custom_svg_content' => $svg_content,
        'unique_key' => $unique_key, // Создание уникального ключа
    );

    // Добавление продукта в корзину с пользовательскими данными
    $added = WC()->cart->add_to_cart($product_id, 1, 0, array(), $cart_item_data);

    if ($added) {
        wp_send_json_success();
    } else {
        wp_send_json_error('Не удалось добавить продукт в корзину');
    }
}


function display_custom_svg_in_cart($item_data, $cart_item) {
    // Проверка, существует ли содержимое SVG в элементе корзины
    if (isset($cart_item['custom_svg_content']) && !empty($cart_item['custom_svg_content'])) {
        // Добавление содержимого SVG к данным элемента
        echo $cart_item['custom_svg_content']; //используя этот код для отображения только SVG без очистки
    }
    return $item_data;
}
add_filter('woocommerce_get_item_data', 'display_custom_svg_in_cart', 10, 2);


// Сохранение пользовательского содержимого SVG в элементах заказа без очистки
add_action('woocommerce_checkout_create_order_line_item', 'save_custom_svg_to_order_item', 10, 4);
function save_custom_svg_to_order_item($item, $cart_item_key, $values, $order) {
    if (isset($values['custom_svg_content'])) {
        // Сохранение необработанного содержимого SVG в метаданных заказа, рассматривая его как необработанный HTML
        $item->update_meta_data('custom_svg_content', $values['custom_svg_content']);
    }
}

add_action('woocommerce_order_item_meta_end', 'display_custom_svg_on_order_page', 10, 4);
function display_custom_svg_on_order_page($item_id, $item, $order, $plain_text) {
    // Получение пользовательского содержимого SVG из метаданных элемента заказа
    $custom_svg = $item->get_meta('custom_svg_content');

    if ($custom_svg) {
        // Обеспечить вывод содержимого SVG как необработанного HTML (обойти экранирование)
        echo '<div class="custom-svg-content" style="margin-top: 10px;">';
        echo $custom_svg; // Вывод SVG как содержимого необработанного HTML
        echo '</div>';
    }
}

Соответствующая часть single-product.php:

            
<!---------------- РАСКРАСКА ------------------>                 
                    <?php               
                    if(get_field('svg')) {
                        $svg_path = get_field('svg'); 
                        $svg_content = file_get_contents( $svg_path );
                        echo '<center><div id="svgContainer" style="max-height:100vh;">' . $svg_content . '</div></center>';
                    ?>
                    
<style>
    .selected { border: 3px solid black; }
    #color-circle-container {
        width: 100%;
        display: grid;
        grid-template-columns: repeat(16, 1.5rem);
        gap: 1rem;
        justify-content: center;
    }
    .color-circle {
        display: block;
        width: 1.5rem;
        height: 1.5rem;
        border-radius: 100%;
        margin: 0.2rem;
    }
    @media (max-width:960px) {
        #color-circle-container{grid-template-columns:repeat(8,1.5rem);}
    }
</style>
<div id="color-circle-container">
    <div class="color-circle" style="background-color: #ff0000;" data-color="#ff0000"></div>
    <div class="color-circle" style="background-color: #0000ff;" data-color="#0000ff"></div>
    <div class="color-circle" style="background-color: #ffff00;" data-color="#ffff00"></div>
    <div class="color-circle" style="background-color: #6600cc;" data-color="#6600cc"></div>
    <div class="color-circle" style="background-color: #ff99ff;" data-color="#ff99ff"></div>
    <div class="color-circle" style="background-color: #00ff00;" data-color="#00ff00"></div>
    <div class="color-circle" style="background-color: #339933;" data-color="#339933"></div>
    <div class="color-circle" style="background-color: #9999ff;" data-color="#9999ff"></div>
    <div class="color-circle" style="background-color: #cc66ff;" data-color="#cc66ff"></div>
    <div class="color-circle" style="background-color: #ff9900;" data-color="#ff9900"></div>
    <div class="color-circle" style="background-color: #ff6600;" data-color="#ff6600"></div>
    <div class="color-circle" style="background-color: #cc0000;" data-color="#cc0000"></div>
    <div class="color-circle" style="background-color: #993300;" data-color="#993300"></div>
    <div class="color-circle" style="background-color: #663300;" data-color="#663300"></div>
    <div class="color-circle" style="background-color: #ffccff;" data-color="#ffccff"></div>
    <div class="color-circle" style="background-color: #ffcc99;" data-color="#ffcc99"></div>
    <div class="color-circle" style="background-color: #ffffcc;" data-color="#ffffcc"></div>
    <div class="color-circle" style="background-color: #ccffcc;" data-color="#ccffcc"></div>
    <div class="color-circle" style="background-color: #ccffff;" data-color="#ccffff"></div>
    <div class="color-circle" style="background-color: #cc9900;" data-color="#cc9900"></div>
    <div class="color-circle" style="background-color: #666633;" data-color="#666633"></div>
    <div class="color-circle" style="background-color: #333300;" data-color="#333300"></div>
    <div class="color-circle" style="background-color: #4d004d;" data-color="#4d004d"></div>
    <div class="color-circle" style="background-color: #ff33cc;" data-color="#ff33cc"></div>
    <div class="color-circle" style="background-color: #ffffff;" data-color="#ffffff"></div>
    <div class="color-circle" style="background-color: #336699;" data-color="#336699"></div>
    <div class="color-circle" style="background-color: #333399;" data-color="#333399"></div>
    <div class="color-circle" style="background-color: #99ccff;" data-color="#99ccff"></div>
    <div class="color-circle" style="background-color: #999966;" data-color="#999966"></div>
    <div class="color-circle" style="background-color: #D3D3D3;" data-color="#D3D3D3"></div>
    <div class="color-circle" style="background-color: #808080;" data-color="#808080"></div>
    <div class="color-circle" style="background-color: #000000;" data-color="#000000"></div>
</div>
                    
                    
                    
<!-- Скрипт раскраски -->
<script>
                        let selectedColor="#ff0000";

                        document.addEventListener('DOMContentLoaded', function() {
                            const svgContainer = document.getElementById('svgContainer');
                            const paths = svgContainer.querySelectorAll('path');

                            paths.forEach(path => {
                                path.addEventListener('click', function() {
                                    this.style.fill = selectedColor;
                                });
                            });
                        });

                        const colorCircles = document.querySelectorAll('.color-circle');
                        colorCircles[0].classList.add('selected');

                        colorCircles.forEach(circle => {
                            circle.addEventListener('click', function() {
                                colorCircles.forEach(c => c.classList.remove('selected'));
                                this.classList.add('selected');
                                selectedColor = this.getAttribute('data-color');
                            });
                        });
                    </script>
                    
<!---------------- КОНЕЦ РАСКРАСКИ ------------------>
        
<!--- ПОЛЬЗОВАТЕЛЬСКАЯ КНОПКА ДОБАВЛЕНИЯ В КОРЗИНУ --->
<button class="custom-ajax-add-to-cart" data-product-id="<?php echo esc_attr( $product->get_id() ); ?>">
    Добавить в корзину (пользовательская кнопка)
</button>               
                    
                    
<!---- КОНЕЦ ПОЛЬЗОВАТЕЛЬСКОЙ КНОПКИ ДОБАВЛЕНИЯ В КОРЗИНУ --->
                    
                    
                    <?php
                    } else {
                        // Кнопка добавления в корзину
                        woocommerce_template_single_add_to_cart();  
                    }
                    
                    ?>

custom-ajax-add-to-cart.js:

jQuery(document).ready(function($) {
    $('.custom-ajax-add-to-cart').on('click', function(e) {
        e.preventDefault(); // Предотвратить стандартное действие

        const button = $(this);
        button.prop('disabled', true); // Отключить кнопку, чтобы предотвратить множественные клики

        var productId = button.data('product-id');
        var svgContent = $('#svgContainer').html(); // Захватить содержимое SVG из контейнера


        console.log('ID продукта:', productId);
        console.log('Данные SVG:', svgContent);

        if (productId && svgContent) {
            $.ajax({
                url: custom_ajax_params.ajax_url,
                type: 'POST',
                data: {
                    action: 'add_to_cart',
                    product_id: productId,
                    svg_content: svgContent,
                    nonce: custom_ajax_params.nonce
                },
                success: function(response) {
                    console.log('AJAX Ответ:', response);
                    // При необходимости обновить интерфейс или сообщить пользователю об успешном выполнении здесь
                },
                error: function(xhr, status, error) {
                    console.error('AJAX Ошибка:', error);
                },
                complete: function() {
                    button.prop('disabled', false); // Снова включить кнопку после завершения AJAX вызова
                }
            });
        } else {
            console.error('Отсутствует ID продукта или данные SVG');
            button.prop('disabled', false); // Снова включить кнопку, если произошла ошибка
        }
    });

});

.

Ответ или решение

Инструмент настройки цвета SVG для WooCommerce: Решение проблемы с отображением на странице управления заказами

Вам удалось успешно реализовать множество важных функций для вашего интернет-магазина WooCommerce, связанного с кастомизированным дизайном текстиля. Однако, вы столкнулись с проблемой при отображении цветного SVG на странице просмотра заказа в админ-панели. Давайте подробно разберем предлагаемые решения, чтобы исправить данную ситуацию.

Актуальная Проблема

При сохранении SVG-контента в метаданных заказа через метод $item->update_meta_data('custom_svg_content', $values['custom_svg_content']), происходит автоматическая процесс-санитация, в результате чего все теги SVG удаляются. Однако для вашего случая это нежелательно, так как вам нужно сохранить весь SVG для корректного отображения.

Рекомендации по Решению

  1. Сохранение SVG в оригинальном формате
    При сохранении SVG-контента в метаданных заказа, важно сохранить его без проектирования. Вы можете использовать метод wp_kses() для указания разрешенных тегов и атрибутов, что поможет избежать уязвимостей без полного удаления SVG:

    $allowed_tags = array(
       'svg' => array(
           'xmlns' => array(),
           'width' => array(),
           'height' => array(),
           'viewBox' => array(),
           'path' => array(
               'd' => array(),
               'fill' => array(),
               'stroke' => array(),
               // Добавьте дополнительные атрибуты по мере необходимости
           ),
           // Разрешите другие теги по мере необходимости
       ),
       // Добавьте другие реже используемые теги (например, `g`, `circle`)
    );
    
    // При сохранении SVG
    $item->update_meta_data('custom_svg_content', wp_kses($values['custom_svg_content'], $allowed_tags));
  2. Отображение на странице Управления Заказами
    При выводе SVG на странице управления заказами, используйте функции htmlspecialchars или htmlentities, чтобы избежать потенциальных проблем с выводом:

    function display_custom_svg_on_order_page($item_id, $item, $order, $plain_text) {
       $custom_svg = wp_kses_post($item->get_meta('custom_svg_content'));
    
       if ($custom_svg) {
           echo '<div class="custom-svg-content" style="margin-top: 10px;">';
           echo $custom_svg;
           echo '</div>';
       }
    }
  3. Безопасность и Уязвимости
    Всегда учитывайте безопасность при работе с пользовательским вводом SVG. Использование функции wp_kses() позволит вам ступенчато управлять тем, что действительно может быть использовано в SVG. Инструмент Imagick, о котором вы упомянули, хорош для преобразования SVG в другие форматы, такие как PNG, но в этом случае вы теряете возможность редактирования цвета на этапе заказа.

Заключение

Функциональность вашего интернет-магазина Woocommerce может быть значительно улучшена с помощью правильной обработки и отображения SVG. Использование wp_kses() не только обеспечит защиту, но и позволит надлежащим образом сохранить данные для будущего использования. Убедитесь, что каждый шаг, который вы реализуете, имеет продуманную безопасность, ведь это главное в индустрии электронной коммерции.

Если у вас возникнут дополнительные вопросы или потребуется помощь по интеграции этого решения, не стесняйтесь обращаться за помощью. Успехов в развитии вашего WooCommerce магазина!

Оцените материал
Добавить комментарий

Капча загружается...