Сортування сортування атрибутів продукту Magento 1.9.1


24

Як я вже згадував, схоже, що проблема magento 1.9.1 та сортування атрибутів настроюваних продуктів є. Варіанти настроюваного продукту ЗАВЖДИ залежать від ідентифікатора продукту простого продукту. Порядок параметрів атрибутів ігнорується.

Я повернувся до magento 1.9.0.1. Можливо, хтось може визначити, як робиться сортування в 1.9.1. Було б чудово для всіх, хто використовує налаштовані продукти, щоб виправити це.

Якщо хтось хоче це побачити, ви можете зробити це тут, у демонстраційному магазині magento. Я не зміг правильно сортувати розміри.

Відповіді:


25

Примітка. Мені було звернено увагу, що це рішення не працює для Magento 1.9.2. Щоб заощадити інші витрачені час, я хотів би вказати на це у верхній частині цієї публікації. Якщо я розроблюю власне рішення або знайду чуже рішення, яке працює для 1.9.2, я оновлю цей пост на той час.

Примітка: Розроблене тут рішення розширює файл класового блоку в основній бібліотеці Magento. Я переглянув вихідний код Magento перед цим підходом і визначив, що не вдалося спостерігати події, щоб уникнути такого підходу. Якщо в майбутній версії Magento ця проблема сортування вирішена, ви можете скасувати ці зміни нижче, просто відключивши розширення в XML-файлі програми / etc / module.

Крок 1: створіть файл- додаток / etc / module / FirstScribe_CatalogOptionSortFix.xml

Зміст:

<?xml version="1.0"?>
<config>
    <modules>
        <FirstScribe_CatalogOptionSortFix>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Catalog />
            </depends>
        </FirstScribe_CatalogOptionSortFix>
    </modules>
</config>

Примітка: Для кроків 2 та 3 створіть каталоги для цих файлів за необхідності. Наприклад, у вас вже може бути додаток / код каталогу / локальний , а ви можете, не залежно від того, які розширення ви вже встановили на своєму сайті.

Крок 2: Створіть додаток файлу / код / ​​локальний / FirstScribe / CatalogOptionSortFix / тощо / config.xml

Зміст:

<?xml version="1.0"?>
<!--
/**
 * Magento 1.9.1.0 has a bug in that the configurable options are sorted by
 * ID rather than position for the Configurable Product's front end view script.
 * This extension addresses this problem.
 *
 * @category    FirstScribe
 * @package     FirstScribe_CatalogOptionSortFix
 * @version     2014.12.15
 */
-->
<config>
    <modules>
        <FirstScribe_CatalogOptionSortFix>
            <version>1.0.0</version>
        </FirstScribe_CatalogOptionSortFix>
    </modules>
    <global>
        <blocks>
            <catalog>
                <rewrite>
                    <product_view_type_configurable>FirstScribe_CatalogOptionSortFix_Block_Product_View_Type_Configurable</product_view_type_configurable>
                </rewrite>
            </catalog>
        </blocks>
    </global>
</config>

Крок 3: Створіть додаток файлу / код / ​​локальний / FirstScribe / CatalogOptionSortFix / Блок / Продукт / Перегляд / Тип / Configurable.php

Зміст:

<?php
/**
 * Magento 1.9.1.0 has a bug in that the configurable options are sorted by
 * ID rather than position for the Configurable Product's front end view script.
 * This extension addresses this problem.
 *
 * @category    FirstScribe
 * @package     FirstScribe_CatalogOptionSortFix
 * @version     2014.12.15
 */
class FirstScribe_CatalogOptionSortFix_Block_Product_View_Type_Configurable extends Mage_Catalog_Block_Product_View_Type_Configurable
{
    /**
     * @var Magento_Db_Adapter_Pdo_Mysql
     */
    protected $_read;

    /**
     * @var string
     */
    protected $_tbl_eav_attribute_option;

    /**
     * Composes configuration for js
     *
     * @version 2014.12.15 - Addition of this line:
     *    $info['options'] = $this->_sortOptions($info['options']);
     *
     * @return string
     */
    public function getJsonConfig()
    {
        $attributes = array();
        $options    = array();
        $store      = $this->getCurrentStore();
        $taxHelper  = Mage::helper('tax');
        $currentProduct = $this->getProduct();

        $preconfiguredFlag = $currentProduct->hasPreconfiguredValues();
        if ($preconfiguredFlag) {
            $preconfiguredValues = $currentProduct->getPreconfiguredValues();
            $defaultValues       = array();
        }

        foreach ($this->getAllowProducts() as $product) {
            $productId  = $product->getId();

            foreach ($this->getAllowAttributes() as $attribute) {
                $productAttribute   = $attribute->getProductAttribute();
                $productAttributeId = $productAttribute->getId();
                $attributeValue     = $product->getData($productAttribute->getAttributeCode());
                if (!isset($options[$productAttributeId])) {
                    $options[$productAttributeId] = array();
                }

                if (!isset($options[$productAttributeId][$attributeValue])) {
                    $options[$productAttributeId][$attributeValue] = array();
                }
                $options[$productAttributeId][$attributeValue][] = $productId;
            }
        }

        $this->_resPrices = array(
            $this->_preparePrice($currentProduct->getFinalPrice())
        );

        foreach ($this->getAllowAttributes() as $attribute) {
            $productAttribute = $attribute->getProductAttribute();
            $attributeId = $productAttribute->getId();
            $info = array(
                    'id'        => $productAttribute->getId(),
                    'code'      => $productAttribute->getAttributeCode(),
                    'label'     => $attribute->getLabel(),
                    'options'   => array()
            );

            $optionPrices = array();
            $prices = $attribute->getPrices();
            if (is_array($prices)) {
                foreach ($prices as $value) {
                    if(!$this->_validateAttributeValue($attributeId, $value, $options)) {
                        continue;
                    }
                    $currentProduct->setConfigurablePrice(
                            $this->_preparePrice($value['pricing_value'], $value['is_percent'])
                    );
                    $currentProduct->setParentId(true);
                    Mage::dispatchEvent(
                            'catalog_product_type_configurable_price',
                            array('product' => $currentProduct)
                    );
                    $configurablePrice = $currentProduct->getConfigurablePrice();

                    if (isset($options[$attributeId][$value['value_index']])) {
                        $productsIndex = $options[$attributeId][$value['value_index']];
                    } else {
                        $productsIndex = array();
                    }

                    $info['options'][] = array(
                            'id'        => $value['value_index'],
                            'label'     => $value['label'],
                            'price'     => $configurablePrice,
                            'oldPrice'  => $this->_prepareOldPrice($value['pricing_value'], $value['is_percent']),
                            'products'  => $productsIndex,
                    );
                    $optionPrices[] = $configurablePrice;
                }
            }

            // CALL SORT ORDER FIX
            $info['options'] = $this->_sortOptions($info['options']);

            /**
             * Prepare formated values for options choose
             */
            foreach ($optionPrices as $optionPrice) {
                foreach ($optionPrices as $additional) {
                    $this->_preparePrice(abs($additional-$optionPrice));
                }
            }
            if($this->_validateAttributeInfo($info)) {
                $attributes[$attributeId] = $info;
            }

            // Add attribute default value (if set)
            if ($preconfiguredFlag) {
                $configValue = $preconfiguredValues->getData('super_attribute/' . $attributeId);
                if ($configValue) {
                    $defaultValues[$attributeId] = $configValue;
                }
            }
        }

        $taxCalculation = Mage::getSingleton('tax/calculation');
        if (!$taxCalculation->getCustomer() && Mage::registry('current_customer')) {
            $taxCalculation->setCustomer(Mage::registry('current_customer'));
        }

        $_request = $taxCalculation->getDefaultRateRequest();
        $_request->setProductClassId($currentProduct->getTaxClassId());
        $defaultTax = $taxCalculation->getRate($_request);

        $_request = $taxCalculation->getRateRequest();
        $_request->setProductClassId($currentProduct->getTaxClassId());
        $currentTax = $taxCalculation->getRate($_request);

        $taxConfig = array(
                'includeTax'        => $taxHelper->priceIncludesTax(),
                'showIncludeTax'    => $taxHelper->displayPriceIncludingTax(),
                'showBothPrices'    => $taxHelper->displayBothPrices(),
                'defaultTax'        => $defaultTax,
                'currentTax'        => $currentTax,
                'inclTaxTitle'      => Mage::helper('catalog')->__('Incl. Tax')
        );

        $config = array(
                'attributes'        => $attributes,
                'template'          => str_replace('%s', '#{price}', $store->getCurrentCurrency()->getOutputFormat()),
                'basePrice'         => $this->_registerJsPrice($this->_convertPrice($currentProduct->getFinalPrice())),
                'oldPrice'          => $this->_registerJsPrice($this->_convertPrice($currentProduct->getPrice())),
                'productId'         => $currentProduct->getId(),
                'chooseText'        => Mage::helper('catalog')->__('Choose an Option...'),
                'taxConfig'         => $taxConfig
        );

        if ($preconfiguredFlag && !empty($defaultValues)) {
            $config['defaultValues'] = $defaultValues;
        }

        $config = array_merge($config, $this->_getAdditionalConfig());    

        return Mage::helper('core')->jsonEncode($config);
    }

    /**
     * Sort the options based off their position.
     *
     * @param array $options
     * @return array
     */
    protected function _sortOptions($options)
    {
        if (count($options)) {
            if (!$this->_read || !$this->_tbl_eav_attribute_option) {
                $resource = Mage::getSingleton('core/resource');

                $this->_read = $resource->getConnection('core_read');
                $this->_tbl_eav_attribute_option = $resource->getTableName('eav_attribute_option');
            }

            // Gather the option_id for all our current options
            $option_ids = array();
            foreach ($options as $option) {
                $option_ids[] = $option['id'];

                $var_name  = 'option_id_'.$option['id'];
                $$var_name = $option;
            }

            $sql    = "SELECT `option_id` FROM `{$this->_tbl_eav_attribute_option}` WHERE `option_id` IN('".implode('\',\'', $option_ids)."') ORDER BY `sort_order`";
            $result = $this->_read->fetchCol($sql);

            $options = array();
            foreach ($result as $option_id) {
                $var_name  = 'option_id_'.$option_id;
                $options[] = $$var_name;
            }
        }

        return $options;
    }
}

Крок 4: Якщо це увімкнено, оновіть кеш-пам'ять типу "Конфігурація" Magento в розділі Система -> Кешування керування панелі адміністратора.

Огляд розширення

  1. Розширіть клас Mage_Catalog_Block_Product_View_Type_Configurable.
  2. Додайте метод для сортування параметрів за їх positionзначенням, витягнувши цю інформацію з бази даних.
  3. Перепишіть метод getJsonConfig, щоб викликати нашу нову функцію після того, як зібрали параметри для атрибута.

2
працює фантастично, і я радий, що рішення не вплине на майбутні оновлення - велике спасибі вам за життєздатне рішення.
dawhoo

Привіт @Meogi, хоча здається, що виправлення ідеально підходить для значень атрибутів, чи ми впевнені, що все в порядку для самих виділених полів продукту? Помітив проблему із впорядкуванням таких способів, як вони встановлені в наборі атрибутів. Наприклад, у нас було "Колір", який перетягується вище "Розмір" в наборі атрибутів, однак 1.9.1 перемикає два (ігнорується впорядкування). Єдиний спосіб виправити редагування самого продукту та перетягування порядку в налаштовуваний. Можливо, це був просто негідний продукт, який раніше випадково перезаписали вручну?
Джо

1
@Joe Якщо я не помиляюсь, перетягування атрибута вище / нижче в наборі атрибутів не впливає на порядок їх відображення на сторінці детальної інформації про продукт. Натомість потрібно зайти в Каталог -> Атрибути -> Керувати атрибутами, знайти свій атрибут та відредагувати значення "Позиція". Це вплине як на порядок відображення настроюваних атрибутів на сторінці продукту, так і на багатошарову навігацію. Порядок параметрів, що настроюються, також можна змінити на основі продукту, перейшовши на вкладку "Асоційовані продукти" в адміністраторі та перетягнувши атрибути вгору / вниз.
Даррен Фелтон

1
@Meogi це лише для багатошарового навігаційного блоку навігації, коли ви включаєте "Використовувати в багатошаровій навігації".
Джо

@Joe Я бачу, ну тоді я не знаю, як змінити налаштування за замовчуванням (можливо, це завжди було розміщенням у наборі атрибутів, я не впевнений). Я можу констатувати, що під час встановлення Magento 1.9.1.0 я все-таки змогла встановити його на моє обране порядку, натиснувши / перетягнувши вгору / вниз на вкладці "Асоційовані продукти" налаштованого продукту. Там, де вони перераховані між формою швидкого створення та сіткою продукту внизу.
Даррен Фелтон

11

Просто, щоб додати два мої центи, інші два відповіді вдало вказали мені на виправлення, але я подумав, що атакую ​​його на джерело, а не на блок подання блоку.

Такого ж результату можна досягти, розширивши метод Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collectionмоделі _loadPrices(), який, незважаючи на назву, змінився (імовірно, для продуктивності), в результаті чого атрибути впорядковуються за ідентифікатором, а не за релевантністю.

Здається, що ці зміни були зроблені, щоб уникнути вкладених foreachвисловлювань, але, в свою чергу, втрачає і правильний порядок. Це рішення трохи змінює оновлену логіку, щоб відстежувати параметри атрибутів, а потім виконує інший цикл, заснований на вихідному порядку, щоб фактично зробити додавання.

Ось скоригований посібник, подібний до відповіді Меогі вище :


Крок 1: Зареєструйте новий модуль

Примітка: якщо у вас вже є, повторно використовуйте існуючий.

# File: app/etc/modules/YourCompany_AttributeFix.xml
<?xml version="1.0"?>
<config>
    <modules>
        <YourCompany_AttributeFix>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Catalog />
            </depends>
        </YourCompany_AttributeFix>
    </modules>
</config>

Крок 2: Створіть конфігурацію модуля

# File: app/code/local/YourCompany/AttributeFix/etc/config.xml
<?xml version="1.0"?>
<config>
    <modules>
        <YourCompany_AttributeFix>
            <version>0.1.0</version>
        </YourCompany_AttributeFix>
    </modules>    
    <global>
        <models>
            <catalog_resource>
                <rewrite>
                    <product_type_configurable_attribute_collection>YourCompany_AttributeFix_Model_Resource_Product_Type_Configurable_Attribute_Collection</product_type_configurable_attribute_collection>
                </rewrite>
            </catalog_resource>
        </models>
    </global>
</config>

Крок 3: Додайте розширення моделі ресурсу

# File: app/code/local/YourCompany/AttributeFix/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
/**
 * Catalog Configurable Product Attribute Collection - overridden to re-enable the attribute option
 * sorting by relevance rather than by ID as changed in the Magento core class
 */
class YourCompany_AttributeFix_Model_Resource_Product_Type_Configurable_Attribute_Collection
    extends Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
{
    /**
     * Load attribute prices information
     *
     * @return Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
     */
    protected function _loadPrices()
    {
        if ($this->count()) {
            $pricings = array(
                0 => array()
            );

            if ($this->getHelper()->isPriceGlobal()) {
                $websiteId = 0;
            } else {
                $websiteId = (int)Mage::app()->getStore($this->getStoreId())->getWebsiteId();
                $pricing[$websiteId] = array();
            }

            $select = $this->getConnection()->select()
                ->from(array('price' => $this->_priceTable))
                ->where('price.product_super_attribute_id IN (?)', array_keys($this->_items));

            if ($websiteId > 0) {
                $select->where('price.website_id IN(?)', array(0, $websiteId));
            } else {
                $select->where('price.website_id = ?', 0);
            }

            $query = $this->getConnection()->query($select);

            while ($row = $query->fetch()) {
                $pricings[(int)$row['website_id']][] = $row;
            }

            $values = array();

            foreach ($this->_items as $item) {
                $productAttribute = $item->getProductAttribute();
                if (!($productAttribute instanceof Mage_Eav_Model_Entity_Attribute_Abstract)) {
                    continue;
                }
                $options = $productAttribute->getFrontend()->getSelectOptions();

                $optionsByValue = array();
                foreach ($options as $option) {
                    $optionsByValue[$option['value']] = $option['label'];
                }

                /**
                 * Modification to re-enable the sorting by relevance for attribute options
                 * @author Robbie Averill <robbie.averill@kathmandu.co.nz>
                 */
                $toAdd = array();
                foreach ($this->getProduct()->getTypeInstance(true)
                             ->getUsedProducts(array($productAttribute->getAttributeCode()), $this->getProduct())
                         as $associatedProduct) {

                    $optionValue = $associatedProduct->getData($productAttribute->getAttributeCode());

                    if (array_key_exists($optionValue, $optionsByValue)) {
                        $toAdd[] = $optionValue;
                    }
                }

                // Add the attribute options, but in the relevant order rather than by ID
                foreach (array_intersect_key($optionsByValue, array_flip($toAdd)) as $optionValueKey => $optionValue) {
                    // If option available in associated product
                    if (!isset($values[$item->getId() . ':' . $optionValue])) {
                        // If option not added, we will add it.
                        $values[$item->getId() . ':' . $optionValueKey] = array(
                            'product_super_attribute_id' => $item->getId(),
                            'value_index'                => $optionValueKey,
                            'label'                      => $optionsByValue[$optionValueKey],
                            'default_label'              => $optionsByValue[$optionValueKey],
                            'store_label'                => $optionsByValue[$optionValueKey],
                            'is_percent'                 => 0,
                            'pricing_value'              => null,
                            'use_default_value'          => true
                        );
                    }
                }
                /**
                 * End attribute option order modification
                 * @author Robbie Averill <robbie.averill@kathmandu.co.nz>
                 */
            }

            foreach ($pricings[0] as $pricing) {
                // Addding pricing to options
                $valueKey = $pricing['product_super_attribute_id'] . ':' . $pricing['value_index'];
                if (isset($values[$valueKey])) {
                    $values[$valueKey]['pricing_value']     = $pricing['pricing_value'];
                    $values[$valueKey]['is_percent']        = $pricing['is_percent'];
                    $values[$valueKey]['value_id']          = $pricing['value_id'];
                    $values[$valueKey]['use_default_value'] = true;
                }
            }

            if ($websiteId && isset($pricings[$websiteId])) {
                foreach ($pricings[$websiteId] as $pricing) {
                    $valueKey = $pricing['product_super_attribute_id'] . ':' . $pricing['value_index'];
                    if (isset($values[$valueKey])) {
                        $values[$valueKey]['pricing_value']     = $pricing['pricing_value'];
                        $values[$valueKey]['is_percent']        = $pricing['is_percent'];
                        $values[$valueKey]['value_id']          = $pricing['value_id'];
                        $values[$valueKey]['use_default_value'] = false;
                    }
                }
            }

            foreach ($values as $data) {
                $this->getItemById($data['product_super_attribute_id'])->addPrice($data);
            }
        }
        return $this;
    }
}

Крок 4: Очистіть кеш


Для довідки , фактична зміна основного класу в a git diffбуде нижче (не редагуйте основні файли!):

diff --git a/app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php b/app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
index 135d9d3..4d2a59b 100644
--- a/app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
+++ b/app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
@@ -254,6 +254,11 @@ class Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
                     $optionsByValue[$option['value']] = $option['label'];
                 }

+                /**
+                 * Modification to re-enable the sorting by relevance for attribute options
+                 * @author Robbie Averill <robbie.averill@kathmandu.co.nz>
+                 */
+                $toAdd = array();
                 foreach ($this->getProduct()->getTypeInstance(true)
                              ->getUsedProducts(array($productAttribute->getAttributeCode()), $this->getProduct())
                          as $associatedProduct) {
@@ -261,22 +266,31 @@ class Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
                     $optionValue = $associatedProduct->getData($productAttribute->getAttributeCode());

                     if (array_key_exists($optionValue, $optionsByValue)) {
-                        // If option available in associated product
-                        if (!isset($values[$item->getId() . ':' . $optionValue])) {
-                            // If option not added, we will add it.
-                            $values[$item->getId() . ':' . $optionValue] = array(
-                                'product_super_attribute_id' => $item->getId(),
-                                'value_index'                => $optionValue,
-                                'label'                      => $optionsByValue[$optionValue],
-                                'default_label'              => $optionsByValue[$optionValue],
-                                'store_label'                => $optionsByValue[$optionValue],
-                                'is_percent'                 => 0,
-                                'pricing_value'              => null,
-                                'use_default_value'          => true
-                            );
-                        }
+                        $toAdd[] = $optionValue;
                     }
                 }
+
+                // Add the attribute options, but in the relevant order rather than by ID
+                foreach (array_intersect_key($optionsByValue, array_flip($toAdd)) as $optionValueKey => $optionValue) {
+                    // If option available in associated product
+                    if (!isset($values[$item->getId() . ':' . $optionValue])) {
+                        // If option not added, we will add it.
+                        $values[$item->getId() . ':' . $optionValueKey] = array(
+                            'product_super_attribute_id' => $item->getId(),
+                            'value_index'                => $optionValueKey,
+                            'label'                      => $optionsByValue[$optionValueKey],
+                            'default_label'              => $optionsByValue[$optionValueKey],
+                            'store_label'                => $optionsByValue[$optionValueKey],
+                            'is_percent'                 => 0,
+                            'pricing_value'              => null,
+                            'use_default_value'          => true
+                        );
+                    }
+                }
+                /**
+                 * End attribute option order modification
+                 * @author Robbie Averill <robbie.averill@kathmandu.co.nz>
+                 */
             }

             foreach ($pricings[0] as $pricing) {

Це також є на GitHub, якщо хтось хоче його для довідки.

Редагувати: Я також записав це як помилку з Magento .


1
Дивовижний внесок мій друг. +1 (так, я не повинен використовувати ці коментарі, щоб сказати спасибі, але ви вбили його, тому я повинен ха-ха)
Даррен Фелтон

Я спробував це з magento 1.9.2 - здається, це спрацює невдало. І я не розумію, чому ця помилка досі не виправлена ​​Magento.
Reinsch

Ви переконалися, що ваш модуль налаштований правильно? Я впевнений, що вони знають про це, але це, мабуть, щось, що потребує часу, щоб перевірити, перш ніж випустити патч, оскільки це дуже важлива частина системи. Редагувати: Ви також можете перевірити виправлення безпосередньо (і тимчасово), але скопіюючи патч безпосередньо в основний клас (тимчасово)
Роббі Аверилл

1
@Reinsch Я щойно отримав електронний лист від несумісності з CE 1.9.2 - надіслав оновлення до мого сховища Github і перевірив його на CE 1.9.2 із зразками даних Magento, і він працює коректно зараз
Robbie Averill

1
Чудова робота @RobbieAverill - велике спасибі Випробуваний та підтверджений на веб-сайті Magento 1.9.2.1.
zigojacko

3

Це насправді не належне виправлення, але це те, що я робив тимчасово, щоб уникнути необхідності повернутися до 1.9.0.1 до наступного випуску Magento, сподіваємось, виправити проблему належним чином. Він буде сортувати значення параметрів за алфавітом, ви, звичайно, можете сортувати все, що завгодно, але я не знаю, як отримати доступ до порядку сортування, встановленого в бекенді, і в алфавіті це досить добре для моїх цілей.

Змініть файл

/app/code/core/Mage/Catalog/Block/Product/View/Type/configurable.php

Зміна лінії 215

if($this->_validateAttributeInfo($info)) {
   $attributes[$attributeId] = $info;
}

до

usort($info['options'], function ($a,$b)
    {
        return strcmp($a['label'],$b['label']);
    }
);
if($this->_validateAttributeInfo($info)) {
   $attributes[$attributeId] = $info;
}

2
Будь ласка, дивіться мою відповідь на відповідь, яка належним чином розширює основну бібліотеку Magento, а не змінює її безпосередньо. Все-таки, кудо Стіву за цю відповідь, оскільки це дуже допомогло мені знати, з чого почати розробляти рішення, яке я придумав.
Даррен Фелтон

Чудово спрацював як шарм у Ентерпрайз, навіть завдяки тоні, що ти врятуєш мій день ..
Бхарат
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.