first commit

This commit is contained in:
Konstantin
2026-05-30 09:27:58 +03:00
commit de0344d218
2371 changed files with 661486 additions and 0 deletions
@@ -0,0 +1,146 @@
<?php
class ControllerExtensionFeedGoogleBase extends Controller {
public function index() {
if ($this->config->get('feed_google_base_status')) {
$output = '<?xml version="1.0" encoding="UTF-8" ?>';
$output .= '<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">';
$output .= ' <channel>';
$output .= ' <title>' . $this->config->get('config_name') . '</title>';
$output .= ' <description>' . $this->config->get('config_meta_description') . '</description>';
$output .= ' <link>' . $this->config->get('config_url') . '</link>';
$this->load->model('extension/feed/google_base');
$this->load->model('catalog/category');
$this->load->model('catalog/product');
$this->load->model('tool/image');
$product_data = array();
$google_base_categories = $this->model_extension_feed_google_base->getCategories();
foreach ($google_base_categories as $google_base_category) {
$filter_data = array(
'filter_category_id' => $google_base_category['category_id'],
'filter_filter' => false
);
$products = $this->model_catalog_product->getProducts($filter_data);
foreach ($products as $product) {
if (!in_array($product['product_id'], $product_data) && $product['description']) {
$product_data[] = $product['product_id'];
$output .= '<item>';
$output .= '<title><![CDATA[' . $product['name'] . ']]></title>';
$output .= '<link>' . $this->url->link('product/product', 'product_id=' . $product['product_id']) . '</link>';
$output .= '<description><![CDATA[' . strip_tags(html_entity_decode($product['description'], ENT_QUOTES, 'UTF-8')) . ']]></description>';
$output .= '<g:brand><![CDATA[' . html_entity_decode($product['manufacturer'], ENT_QUOTES, 'UTF-8') . ']]></g:brand>';
$output .= '<g:condition>new</g:condition>';
$output .= '<g:id>' . $product['product_id'] . '</g:id>';
if ($product['image']) {
$output .= ' <g:image_link>' . $this->model_tool_image->resize($product['image'], 500, 500) . '</g:image_link>';
} else {
$output .= ' <g:image_link></g:image_link>';
}
$output .= ' <g:model_number>' . $product['model'] . '</g:model_number>';
if ($product['mpn']) {
$output .= ' <g:mpn><![CDATA[' . $product['mpn'] . ']]></g:mpn>' ;
} else {
$output .= ' <g:identifier_exists>false</g:identifier_exists>';
}
if ($product['upc']) {
$output .= ' <g:upc>' . $product['upc'] . '</g:upc>';
}
if ($product['ean']) {
$output .= ' <g:ean>' . $product['ean'] . '</g:ean>';
}
$currencies = array(
'USD',
'EUR',
'GBP'
);
if (in_array($this->session->data['currency'], $currencies)) {
$currency_code = $this->session->data['currency'];
$currency_value = $this->currency->getValue($this->session->data['currency']);
} else {
$currency_code = 'USD';
$currency_value = $this->currency->getValue('USD');
}
if ((float)$product['special']) {
$output .= ' <g:price>' . $this->currency->format($this->tax->calculate($product['special'], $product['tax_class_id']), $currency_code, $currency_value, false) . '</g:price>';
} else {
$output .= ' <g:price>' . $this->currency->format($this->tax->calculate($product['price'], $product['tax_class_id']), $currency_code, $currency_value, false) . '</g:price>';
}
$output .= ' <g:google_product_category>' . $google_base_category['google_base_category_id'] . '</g:google_product_category>';
$categories = $this->model_catalog_product->getCategories($product['product_id']);
foreach ($categories as $category) {
$path = $this->getPath($category['category_id']);
if ($path) {
$string = '';
foreach (explode('_', $path) as $path_id) {
$category_info = $this->model_catalog_category->getCategory($path_id);
if ($category_info) {
if (!$string) {
$string = $category_info['name'];
} else {
$string .= ' &gt; ' . $category_info['name'];
}
}
}
$output .= '<g:product_type><![CDATA[' . $string . ']]></g:product_type>';
}
}
$output .= ' <g:quantity>' . $product['quantity'] . '</g:quantity>';
$output .= ' <g:weight>' . $this->weight->format($product['weight'], $product['weight_class_id']) . '</g:weight>';
$output .= ' <g:availability><![CDATA[' . ($product['quantity'] ? 'in stock' : 'out of stock') . ']]></g:availability>';
$output .= '</item>';
}
}
}
$output .= ' </channel>';
$output .= '</rss>';
$this->response->addHeader('Content-Type: application/rss+xml');
$this->response->setOutput($output);
}
}
protected function getPath($parent_id, $current_path = '') {
$category_info = $this->model_catalog_category->getCategory($parent_id);
if ($category_info) {
if (!$current_path) {
$new_path = $category_info['category_id'];
} else {
$new_path = $category_info['category_id'] . '_' . $current_path;
}
$path = $this->getPath($category_info['parent_id'], $new_path);
if ($path) {
return $path;
} else {
return $new_path;
}
}
}
}
@@ -0,0 +1,107 @@
<?php
class ControllerExtensionFeedGoogleSitemap extends Controller {
public function index() {
if ($this->config->get('feed_google_sitemap_status')) {
$output = '<?xml version="1.0" encoding="UTF-8"?>';
$output .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">';
$this->load->model('catalog/product');
$this->load->model('tool/image');
$products = $this->model_catalog_product->getProducts();
foreach ($products as $product) {
if ($product['image']) {
$output .= '<url>';
$output .= ' <loc>' . $this->url->link('product/product', 'product_id=' . $product['product_id']) . '</loc>';
$output .= ' <changefreq>weekly</changefreq>';
$output .= ' <lastmod>' . date('Y-m-d\TH:i:sP', strtotime($product['date_modified'])) . '</lastmod>';
$output .= ' <priority>1.0</priority>';
$output .= ' <image:image>';
$output .= ' <image:loc>' . $this->model_tool_image->resize($product['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_popup_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_popup_height')) . '</image:loc>';
$output .= ' <image:caption>' . $product['name'] . '</image:caption>';
$output .= ' <image:title>' . $product['name'] . '</image:title>';
$output .= ' </image:image>';
$output .= '</url>';
}
}
$this->load->model('catalog/category');
$output .= $this->getCategories(0);
$this->load->model('catalog/manufacturer');
$manufacturers = $this->model_catalog_manufacturer->getManufacturers();
foreach ($manufacturers as $manufacturer) {
$output .= '<url>';
$output .= ' <loc>' . $this->url->link('product/manufacturer/info', 'manufacturer_id=' . $manufacturer['manufacturer_id']) . '</loc>';
$output .= ' <changefreq>weekly</changefreq>';
$output .= ' <priority>0.7</priority>';
$output .= '</url>';
$products = $this->model_catalog_product->getProducts(array('filter_manufacturer_id' => $manufacturer['manufacturer_id']));
foreach ($products as $product) {
$output .= '<url>';
$output .= ' <loc>' . $this->url->link('product/product', 'manufacturer_id=' . $manufacturer['manufacturer_id'] . '&product_id=' . $product['product_id']) . '</loc>';
$output .= ' <changefreq>weekly</changefreq>';
$output .= ' <priority>1.0</priority>';
$output .= '</url>';
}
}
$this->load->model('catalog/information');
$informations = $this->model_catalog_information->getInformations();
foreach ($informations as $information) {
$output .= '<url>';
$output .= ' <loc>' . $this->url->link('information/information', 'information_id=' . $information['information_id']) . '</loc>';
$output .= ' <changefreq>weekly</changefreq>';
$output .= ' <priority>0.5</priority>';
$output .= '</url>';
}
$output .= '</urlset>';
$this->response->addHeader('Content-Type: application/xml');
$this->response->setOutput($output);
}
}
protected function getCategories($parent_id, $current_path = '') {
$output = '';
$results = $this->model_catalog_category->getCategories($parent_id);
foreach ($results as $result) {
if (!$current_path) {
$new_path = $result['category_id'];
} else {
$new_path = $current_path . '_' . $result['category_id'];
}
$output .= '<url>';
$output .= ' <loc>' . $this->url->link('product/category', 'path=' . $new_path) . '</loc>';
$output .= ' <changefreq>weekly</changefreq>';
$output .= ' <priority>0.7</priority>';
$output .= '</url>';
$products = $this->model_catalog_product->getProducts(array('filter_category_id' => $result['category_id']));
foreach ($products as $product) {
$output .= '<url>';
$output .= ' <loc>' . $this->url->link('product/product', 'path=' . $new_path . '&product_id=' . $product['product_id']) . '</loc>';
$output .= ' <changefreq>weekly</changefreq>';
$output .= ' <priority>1.0</priority>';
$output .= '</url>';
}
$output .= $this->getCategories($result['category_id'], $new_path);
}
return $output;
}
}
@@ -0,0 +1,98 @@
<?php
class ControllerExtensionFeedGoogleSitemapFast extends Controller {
protected $categories = array();
public function index() {
if($this->config->get('feed_google_sitemap_fast_status')) {
$time_start = microtime(true);
$output = '<?xml version="1.0" encoding="UTF-8"?>';
$output .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
$this->load->model('tool/sitemap');
$products = $this->model_tool_sitemap->getProducts();
foreach($products as $product) {
$output .= '<url>';
$output .= '<loc>' . str_replace('&', '&amp;', str_replace('&amp;', '&', $this->url->link('product/product', 'product_id=' . $product['product_id']))) . '</loc>';
$output .= '<lastmod>' . $product['date'] . '</lastmod>';
$output .= '<changefreq>weekly</changefreq>';
$output .= '<priority>1.0</priority>';
$output .= '</url>';
}
$this->categories = $this->model_tool_sitemap->getAllCategories();
$output .= $this->getAllCategories(0);
$manufacturers = $this->model_tool_sitemap->getManufacturers();
foreach($manufacturers as $manufacturer) {
$output .= '<url>';
$output .= '<loc>' . str_replace('&', '&amp;', str_replace('&amp;', '&', $this->url->link('product/manufacturer/info', 'manufacturer_id=' . $manufacturer['manufacturer_id']))) . '</loc>';
$output .= '<changefreq>weekly</changefreq>';
$output .= '<priority>0.7</priority>';
$output .= '</url>';
}
$informations = $this->model_tool_sitemap->getInformations();
foreach($informations as $information) {
$output .= '<url>';
$output .= '<loc>' . str_replace('&', '&amp;', str_replace('&amp;', '&', $this->url->link('information/information', 'information_id=' . $information['information_id']))) . '</loc>';
$output .= '<changefreq>weekly</changefreq>';
$output .= '<priority>0.5</priority>';
$output .= '</url>';
}
$output .= '</urlset>';
$this->log->write(sprintf("Fast Sitemap Execution Time: %05.5f", (microtime(true) - $time_start)));
$this->response->addHeader('Content-Type: application/xml');
$this->response->setOutput($output);
}
}
protected function getAllCategories($parent_id = 0, $current_path = '') {
$output = '';
if(array_key_exists($parent_id, $this->categories)) {
$results = $this->categories[$parent_id];
foreach($results as $result) {
if(!$current_path) {
$new_path = $result['category_id'];
} else {
$new_path = $current_path . '_' . $result['category_id'];
}
$output .= '<url>';
$output .= '<loc>' . str_replace('&', '&amp;', str_replace('&amp;', '&', $this->url->link('product/category', 'path=' . $new_path))) . '</loc>';
$output .= '<lastmod>' . $result['date'] . '</lastmod>';
$output .= '<changefreq>weekly</changefreq>';
$output .= '<priority>0.7</priority>';
$output .= '</url>';
if(!$current_path) {
$new_path = $result['category_id'];
} else {
$new_path = $current_path . '_' . $result['category_id'];
}
$children = '';
if(array_key_exists($result['category_id'], $this->categories)) {
$children = $this->getAllCategories($result['category_id'], $new_path);
}
$output .= $children;
}
}
return $output;
}
}
?>
@@ -0,0 +1,109 @@
<?php
/**
* Unisender subscriber for OpenCart (ocStore) 2.3.x
*
* Main class subscribe/unsubscribe Unisender maillists
*
* @author Alexander Toporkov <toporchillo@gmail.com>
* @copyright (C) 2013- Alexander Toporkov
* @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
*
* Version of this module: http://opencartforum.ru/files/file/1258-unisender-eksport-kontakov/
*/
class ControllerExtensionFeedUnisender extends Controller {
public function update() {
$newsletter = $this->customer->getNewsletter();
$data = array('email'=>$this->customer->getEmail());
$key = $this->config->get('feed_unisender_key');
if (!$this->config->get('feed_unisender_status') || !$key) return;
$field_names = array(0=>'email', 1=>'email_status');
$dat = array(0=>array(0=>$data['email'], 1=>($newsletter ? 'active' : 'inactive')));
if ($newsletter) {
$subscribtions = $this->config->get('feed_unisender_subscribtion');
$field_names[2] = 'email_list_ids';
$dat[0][2] = implode(',', $subscribtions);
}
return $this->send($field_names, $dat);
}
public function subscribe_customer() {
$customer_id = $this->session->data['customer_id'];
$this->load->model('account/customer');
$data = $this->model_account_customer->getCustomer($customer_id);
$this->subscribe($data);
}
public function subscribe_guest() {
$order_id = $this->session->data['order_id'];
$this->load->model('checkout/order');
$data = $this->model_checkout_order->getOrder($order_id);
if ($data['customer_id'] > 0) return;
$this->subscribe($data);
}
public function subscribe($data) {
$key = $this->config->get('feed_unisender_key');
if (!$this->config->get('feed_unisender_status') || !$key) return;
$subscribtions = $this->config->get('feed_unisender_subscribtion');
$field_names = array(0=>'email');
$dat = array(0=>array(0=>$data['email']));
$double_optin = $this->config->get('feed_unisender_ignore') ? 1 : 0;
if (((isset($data['newsletter']) && $data['newsletter']) || $this->config->get('feed_unisender_ignore')) && is_array($subscribtions) && count($subscribtions) > 0) {
$field_names[1] = 'email_request_ip';
$dat[0][1] = $this->request->server['REMOTE_ADDR'];
$field_names[2] = 'email_confirm_ip';
$dat[0][2] = $this->request->server['REMOTE_ADDR'];
$field_names[3] = 'email_add_time';
$dat[0][3] = gmdate('Y-m-d h:i:s', time()-20);
$field_names[4] = 'email_confirm_time';
$dat[0][4] = gmdate('Y-m-d h:i:s', time()-10);
$field_names[5] = 'email_list_ids';
$dat[0][5] = implode(',', $subscribtions);
}
if (isset($data['telephone']) && $data['telephone']) {
$field_names[6] = 'phone';
$dat[0][6] = $data['telephone'];
}
$field_names[7] = 'Name';
$dat[0][7] = trim($data['firstname'].' '.$data['lastname']);
$res = $this->send($field_names, $dat, $double_optin);
return $res;
}
private function send($field_names, $dat, $double_optin=0) {
$key = $this->config->get('feed_unisender_key');
$exp = array(
'api_key' => $key,
'double_optin' => $double_optin
);
foreach($field_names as $n=>$v) {
$exp['field_names['.$n.']'] = $v;
}
foreach($dat[0] as $n=>$v) {
$exp['data[0]['.$n.']'] = $v;
}
$ch = curl_init ('https://api.unisender.com/ru/api/importContacts?format=json');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1) ;
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $exp);
$res = curl_exec ($ch) ;
curl_close ($ch);
return json_decode($res);
}
}
?>
@@ -0,0 +1,542 @@
<?php
// * @source See SOURCE.txt for source and other copyright.
// * @license GNU General Public License version 3; see LICENSE.txt
/**
* Класс YML экспорта
* YML (Yandex Market Language) - стандарт, разработанный "Яндексом"
* для принятия и публикации информации в базе данных Яндекс.Маркет
* YML основан на стандарте XML (Extensible Markup Language)
* описание формата YML http://partner.market.yandex.ru/legal/tt/
*/
class ControllerExtensionFeedYandexMarket extends Controller {
private $shop = array();
private $currencies = array();
private $categories = array();
private $offers = array();
private $from_charset = 'utf-8'; // UTF-8, windows-1251
private $eol = "\n";
public function index() {
if ($this->config->get('feed_yandex_market_status')) {
// Защитный ключ
$secret_key = $this->config->get('feed_yandex_market_secret_key');
if ($secret_key) {
if (isset($this->request->get['secret_key'])) {
if ($this->request->get['secret_key'] != $secret_key) exit();
} else {
exit();
}
}
// Выборка категорий и производителей
$allowed_categories = $this->config->get('feed_yandex_market_categories');
$allowed_manufacturers = $this->config->get('feed_yandex_market_manufacturers');
//if (!$allowed_categories && !$allowed_manufacturers) exit();
$this->load->model('extension/feed/yandex_market');
$this->load->model('localisation/currency');
$this->load->model('tool/image');
// Магазин
$this->setShop('name', $this->config->get('feed_yandex_market_shopname'));
$this->setShop('company', $this->config->get('feed_yandex_market_company'));
$this->setShop('url', HTTP_SERVER);
$this->setShop('phone', $this->config->get('config_telephone'));
$this->setShop('platform', 'OCSTORE.COM');
$this->setShop('version', VERSION);
// Валюты
// TODO: Добавить возможность настраивать проценты в админке.
$offers_currency = $this->config->get('feed_yandex_market_currency');
if (!$this->currency->has($offers_currency)) exit();
$decimal_place = $this->currency->getDecimalPlace($offers_currency);
$shop_currency = $this->config->get('config_currency');
$this->setCurrency($offers_currency, 1);
$currencies = $this->model_localisation_currency->getCurrencies();
$supported_currencies = array('RUR', 'RUB', 'USD', 'BYN', 'BYR', 'KZT', 'EUR', 'UAH');
$currencies = array_intersect_key($currencies, array_flip($supported_currencies));
foreach ($currencies as $currency) {
if ($currency['code'] != $offers_currency && $currency['status'] == 1) {
$this->setCurrency($currency['code'], number_format(1/$this->currency->convert($currency['value'], $offers_currency, $shop_currency), 4, '.', ''));
}
}
// Категории <categories></categories>
$categories = $this->model_extension_feed_yandex_market->getCategory();
foreach ($categories as $category) {
$this->setCategory($category['name'], $category['category_id'], $category['parent_id']);
}
// Параметры товарного предложения <offer></offer>
$bus_id = $this->config->get('feed_yandex_market_id'); // Идентификатор товара - "id"
$bus_type = $this->config->get('feed_yandex_market_type'); // Тип предложений - "type"
$bus_name = $this->config->get('feed_yandex_market_name'); // Название товара - "name"
$bus_model = $this->config->get('feed_yandex_market_model'); // Код товара - "model"
$bus_vendorCode = $this->config->get('feed_yandex_market_vendorCode'); // Артикул товара - "SKU"
$bus_image = $this->config->get('feed_yandex_market_image'); // Статус товара без изображений
$bus_image_width = $this->config->get('feed_yandex_market_image_width'); // Ширина изображения товара
$bus_image_height = $this->config->get('feed_yandex_market_image_height'); // Высота изображения товара
$bus_image_quantity = $this->config->get('feed_yandex_market_image_quantity'); // Количество изображений товара
$bus_main_category = $this->config->get('feed_yandex_market_main_category'); // Статус товара без главной категории
$in_stock_id = $this->config->get('feed_yandex_market_in_stock'); // id статуса товара "В наличии"
$out_of_stock_id = $this->config->get('feed_yandex_market_out_of_stock'); // id статуса товара "Нет на складе"
$bus_quantity_status = $this->config->get('feed_yandex_market_quantity_status'); // Статус товара "количество равное 0"
$vendor_required = false; // true - только товары у которых задан производитель, необходимо для 'vendor.model'
$products = $this->model_extension_feed_yandex_market->getProduct($allowed_categories, $allowed_manufacturers, $out_of_stock_id, $vendor_required, $bus_image, $bus_image_quantity, $bus_main_category, $bus_quantity_status);
foreach ($products as $product) {
$data = array();
// Атрибуты товарного предложения
if (!empty($product[$bus_id])) {
$data['id'] = $product[$bus_id];
} else {
$data['id'] = $product['product_id'];
}
// $data['type'] = $bus_type;
// $data['type'] = 'vendor.model';
$data['available'] = ($product['quantity'] > 0 || $product['stock_status_id'] == $in_stock_id);
// $data['bid'] = 10;
// $data['cbid'] = 15;
// Параметры товарного предложения
$data['url'] = $this->url->link('product/product', 'path=' . $this->getPath($product['category_id']) . '&product_id=' . $product['product_id']);
$data['price'] = number_format($this->currency->convert($this->tax->calculate($product['price'], $product['tax_class_id']), $shop_currency, $offers_currency), $decimal_place, '.', '');
$data['currencyId'] = $offers_currency;
$data['categoryId'] = $product['category_id'];
$data['delivery'] = 'true';
// $data['local_delivery_cost'] = 100;
if (!empty($product[$bus_name])) {
$data['name'] = $product[$bus_name];
} else {
$data['name'] = $product['name'];
}
if (!empty($product['manufacturer'])) {
$data['vendor'] = $product['manufacturer'];
} else {
$data['vendor'] = '';
}
if (!empty($product[$bus_vendorCode])) {
$data['vendorCode'] = $product[$bus_vendorCode];
} else {
$data['vendorCode'] = '';
}
if (!empty($product[$bus_model])) {
$data['model'] = $product[$bus_model];
} else {
$data['model'] = '';
}
if (!empty($product['description'])) {
$data['description'] = $product['description'];
} else {
$data['description'] = '';
}
// $data['manufacturer_warranty'] = 'true';
// $data['barcode'] = $product['sku'];
if (!empty($product['image'])) {
$data['picture'] = $this->model_tool_image->resize($product['image'], $bus_image_width, $bus_image_height);
}
if (isset($product['images'])) {
foreach (explode(',', $product['images']) as $image) {
$data['picture'] .= ',' . $this->model_tool_image->resize($image, $bus_image_width, $bus_image_height);
}
}
/*
// пример структуры массива для вывода параметров
$data['param'] = array(
array(
'name'=>'Wi-Fi',
'value'=>'есть'
), array(
'name'=>'Размер экрана',
'unit'=>'дюйм',
'value'=>'20'
), array(
'name'=>'Вес',
'unit'=>'кг',
'value'=>'4.6'
)
);
*/
$this->setOffer($data);
}
$this->categories = array_filter($this->categories, array($this, "filterCategory"));
if (!$this->categories) exit();
$this->response->addHeader('Content-Type: application/xml');
$this->response->setOutput($this->getYml());
}
}
/**
* Методы формирования YML
*/
/**
* Формирование массива для элемента shop описывающего магазин
*
* @param string $name - Название элемента
* @param string $value - Значение элемента
*/
private function setShop($name, $value) {
$allowed = array('name', 'company', 'url', 'phone', 'platform', 'version', 'agency', 'email');
if (in_array($name, $allowed)) {
$this->shop[$name] = $this->prepareField($value);
}
}
/**
* Валюты
*
* @param string $id - код валюты (RUR, RUB, USD, BYN, BYR, KZT, EUR, UAH)
* @param float|string $rate - курс этой валюты к валюте, взятой за единицу.
* Параметр rate может иметь так же следующие значения:
* CBRF - курс по Центральному банку РФ.
* NBU - курс по Национальному банку Украины.
* NBK - курс по Национальному банку Казахстана.
* СВ - курс по банку той страны, к которой относится интернет-магазин
* по Своему региону, указанному в Партнерском интерфейсе Яндекс.Маркета.
* @param float $plus - используется только в случае rate = CBRF, NBU, NBK или СВ
* и означает на сколько увеличить курс в процентах от курса выбранного банка
* @return bool
*/
private function setCurrency($id, $rate = 'CBRF', $plus = 0) {
$allow_id = array('RUR', 'RUB', 'USD', 'BYN', 'BYR', 'KZT', 'EUR', 'UAH');
if (!in_array($id, $allow_id)) {
return false;
}
$allow_rate = array('CBRF', 'NBU', 'NBK', 'CB');
if (in_array($rate, $allow_rate)) {
$plus = str_replace(',', '.', $plus);
if (is_numeric($plus) && $plus > 0) {
$this->currencies[] = array(
'id'=>$this->prepareField(strtoupper($id)),
'rate'=>$rate,
'plus'=>(float)$plus
);
} else {
$this->currencies[] = array(
'id'=>$this->prepareField(strtoupper($id)),
'rate'=>$rate
);
}
} else {
$rate = str_replace(',', '.', $rate);
if (!(is_numeric($rate) && $rate > 0)) {
return false;
}
$this->currencies[] = array(
'id'=>$this->prepareField(strtoupper($id)),
'rate'=>(float)$rate
);
}
return true;
}
/**
* Категории товаров
*
* @param string $name - название рубрики
* @param int $id - id рубрики
* @param int $parent_id - id родительской рубрики
* @return bool
*/
private function setCategory($name, $id, $parent_id = 0) {
$id = (int)$id;
if ($id < 1 || trim($name) == '') {
return false;
}
if ((int)$parent_id > 0) {
$this->categories[$id] = array(
'id'=>$id,
'parentId'=>(int)$parent_id,
'name'=>$this->prepareField($name)
);
} else {
$this->categories[$id] = array(
'id'=>$id,
'name'=>$this->prepareField($name)
);
}
return true;
}
/**
* Товарные предложения
*
* @param array $data - массив параметров товарного предложения
*/
private function setOffer($data) {
$offer = array();
$attributes = array('id', 'type', 'available', 'bid', 'cbid', 'param');
$attributes = array_intersect_key($data, array_flip($attributes));
foreach ($attributes as $key => $value) {
switch ($key)
{
case 'id':
case 'bid':
case 'cbid':
$value = (int)$value;
if ($value > 0) {
$offer[$key] = $value;
}
break;
case 'type':
if (in_array($value, array('vendor.model', 'book', 'audiobook', 'artist.title', 'tour', 'ticket', 'event-ticket'))) {
$offer['type'] = $value;
}
break;
case 'available':
$offer['available'] = ($value ? 'true' : 'false');
break;
case 'param':
if (is_array($value)) {
$offer['param'] = $value;
}
break;
default:
break;
}
}
$type = isset($offer['type']) ? $offer['type'] : '';
$allowed_tags = array('url'=>0, 'buyurl'=>0, 'price'=>1, 'wprice'=>0, 'currencyId'=>1, 'xCategory'=>0, 'categoryId'=>1, 'picture'=>0, 'store'=>0, 'pickup'=>0, 'delivery'=>0, 'deliveryIncluded'=>0, 'local_delivery_cost'=>0, 'orderingTime'=>0);
switch ($type) {
case 'vendor.model':
$allowed_tags = array_merge($allowed_tags, array('typePrefix'=>0, 'vendor'=>1, 'vendorCode'=>0, 'model'=>1, 'provider'=>0, 'tarifplan'=>0));
break;
case 'book':
$allowed_tags = array_merge($allowed_tags, array('author'=>0, 'name'=>1, 'publisher'=>0, 'series'=>0, 'year'=>0, 'ISBN'=>0, 'volume'=>0, 'part'=>0, 'language'=>0, 'binding'=>0, 'page_extent'=>0, 'table_of_contents'=>0));
break;
case 'audiobook':
$allowed_tags = array_merge($allowed_tags, array('author'=>0, 'name'=>1, 'publisher'=>0, 'series'=>0, 'year'=>0, 'ISBN'=>0, 'volume'=>0, 'part'=>0, 'language'=>0, 'table_of_contents'=>0, 'performed_by'=>0, 'performance_type'=>0, 'storage'=>0, 'format'=>0, 'recording_length'=>0));
break;
case 'artist.title':
$allowed_tags = array_merge($allowed_tags, array('artist'=>0, 'title'=>1, 'year'=>0, 'media'=>0, 'starring'=>0, 'director'=>0, 'originalName'=>0, 'country'=>0));
break;
case 'tour':
$allowed_tags = array_merge($allowed_tags, array('worldRegion'=>0, 'country'=>0, 'region'=>0, 'days'=>1, 'dataTour'=>0, 'name'=>1, 'hotel_stars'=>0, 'room'=>0, 'meal'=>0, 'included'=>1, 'transport'=>1, 'price_min'=>0, 'price_max'=>0, 'options'=>0));
break;
case 'event-ticket':
$allowed_tags = array_merge($allowed_tags, array('name'=>1, 'place'=>1, 'hall'=>0, 'hall_part'=>0, 'date'=>1, 'is_premiere'=>0, 'is_kids'=>0));
break;
default:
$allowed_tags = array_merge($allowed_tags, array('name'=>1, 'vendor'=>0, 'vendorCode'=>0, 'model'=>1));
break;
}
$allowed_tags = array_merge($allowed_tags, array('aliases'=>0, 'additional'=>0, 'description'=>0, 'sales_notes'=>0, 'promo'=>0, 'manufacturer_warranty'=>0, 'country_of_origin'=>0, 'downloadable'=>0, 'adult'=>0, 'barcode'=>0));
$required_tags = array_filter($allowed_tags);
if (sizeof(array_intersect_key($data, $required_tags)) != sizeof($required_tags)) {
return;
}
$data = array_intersect_key($data, $allowed_tags);
// if (isset($data['tarifplan']) && !isset($data['provider'])) {
// unset($data['tarifplan']);
// }
$allowed_tags = array_intersect_key($allowed_tags, $data);
// Стандарт XML учитывает порядок следования элементов,
// поэтому важно соблюдать его в соответствии с порядком описанным в DTD
$offer['data'] = array();
foreach ($allowed_tags as $key => $value) {
$offer['data'][$key] = $this->prepareField($data[$key], $key);
}
$this->offers[] = $offer;
}
/**
* Формирование YML файла
*
* @return string
*/
private function getYml() {
$yml = '<?xml version="1.0" encoding="' . $this->from_charset . '"?>' . $this->eol;
$yml .= '<!DOCTYPE yml_catalog SYSTEM "shops.dtd">' . $this->eol;
$yml .= '<yml_catalog date="' . date('Y-m-d H:i') . '">' . $this->eol;
$yml .= '<shop>' . $this->eol;
// информация о магазине
$yml .= $this->array2Tag($this->shop);
// валюты
$yml .= '<currencies>' . $this->eol;
foreach ($this->currencies as $currency) {
$yml .= $this->getElement($currency, 'currency');
}
$yml .= '</currencies>' . $this->eol;
// категории
$yml .= '<categories>' . $this->eol;
foreach ($this->categories as $category) {
$category_name = $category['name'];
unset($category['name'], $category['export']);
$yml .= $this->getElement($category, 'category', $category_name);
}
$yml .= '</categories>' . $this->eol;
// товарные предложения
$yml .= '<offers>' . $this->eol;
foreach ($this->offers as $offer) {
$tags = $this->array2Tag($offer['data']);
unset($offer['data']);
if (isset($offer['param'])) {
$tags .= $this->array2Param($offer['param']);
unset($offer['param']);
}
$yml .= $this->getElement($offer, 'offer', $tags);
}
$yml .= '</offers>' . $this->eol;
$yml .= '</shop>';
$yml .= '</yml_catalog>';
return $yml;
}
/**
* Фрмирование элемента
*
* @param array $attributes
* @param string $element_name
* @param string $element_value
* @return string
*/
private function getElement($attributes, $element_name, $element_value = '') {
$retval = '<' . $element_name . ' ';
foreach ($attributes as $key => $value) {
$retval .= $key . '="' . $value . '" ';
}
$retval .= $element_value ? '>' . $this->eol . $element_value . '</' . $element_name . '>' : '/>';
$retval .= $this->eol;
return $retval;
}
/**
* Преобразование массива в теги
*
* @param array $tags
* @return string
*/
private function array2Tag($tags) {
$retval = '';
foreach ($tags as $key => $value) {
if ($key == 'picture') {
foreach (explode(',', $value) as $val) {
$retval .= '<' . $key . '>' . $val . '</' . $key . '>' . $this->eol;
}
} else {
$retval .= '<' . $key . '>' . $value . '</' . $key . '>' . $this->eol;
}
}
return $retval;
}
/**
* Преобразование массива в теги параметров
*
* @param array $params
* @return string
*/
private function array2Param($params) {
$retval = '';
foreach ($params as $param) {
$retval .= '<param name="' . $this->prepareField($param['name']);
if (isset($param['unit'])) {
$retval .= '" unit="' . $this->prepareField($param['unit']);
}
$retval .= '">' . $this->prepareField($param['value']) . '</param>' . $this->eol;
}
return $retval;
}
/**
* Подготовка текстового поля в соответствии с требованиями Яндекса
* Запрещаем любые html-тэги, стандарт XML не допускает использования в текстовых данных
* непечатаемых символов с ASCII-кодами в диапазоне значений от 0 до 31 (за исключением
* символов с кодами 9, 10, 13 - табуляция, перевод строки, возврат каретки). Также этот
* стандарт требует обязательной замены некоторых символов на их символьные примитивы.
* @param string $text
* @return string
*/
private function prepareField($field, $key = false) {
if ($field) {
$field = htmlspecialchars_decode($field);
$field = strip_tags($field);
$from = array('"', '&', '>', '<', '\'');
$to = array('&quot;', '&amp;', '&gt;', '&lt;', '&apos;');
$field = str_replace($from, $to, $field);
if ($key == 'description') {
$field = "<![CDATA[" . mb_substr($field, 0, 3000) . "]]>";
}
if ($this->from_charset == 'windows-1251') {
$field = iconv($this->from_charset, 'windows-1251//TRANSLIT//IGNORE', $field);
}
$field = preg_replace('#[\x00-\x08\x0B-\x0C\x0E-\x1F]+#is', ' ', $field);
}
return trim($field);
}
protected function getPath($category_id, $current_path = '') {
if (isset($this->categories[$category_id])) {
$this->categories[$category_id]['export'] = 1;
if (!$current_path) {
$new_path = $this->categories[$category_id]['id'];
} else {
$new_path = $this->categories[$category_id]['id'] . '_' . $current_path;
}
if (isset($this->categories[$category_id]['parentId'])) {
return $this->getPath($this->categories[$category_id]['parentId'], $new_path);
} else {
return $new_path;
}
}
}
function filterCategory($category) {
return isset($category['export']);
}
}
@@ -0,0 +1,180 @@
<?php
/**
* Класс YML экспорта
* YML (Yandex Market Language) - стандарт, разработанный "Яндексом"
* для принятия и публикации информации в базе данных Яндекс.Маркет
* YML основан на стандарте XML (Extensible Markup Language)
* описание формата YML http://partner.market.yandex.ru/legal/tt/
*/
class ControllerExtensionFeedYandexTurbo extends Controller {
private $currencies = array();
private $categories = array();
private $eol = "";
public function index() {
if ($this->config->get('feed_yandex_turbo_status')) {
$this->load->model('extension/feed/yandex_turbo');
$this->load->model('tool/image');
$this->eol = "\n";
$output = '<?xml version="1.0" encoding="UTF-8"?>' . $this->eol;
$output .= '<!DOCTYPE yml_catalog SYSTEM "shops.dtd">' . $this->eol;
$output .= '<yml_catalog date="' . date('Y-m-d H:i') . '">' . $this->eol;
$output .= '<shop>' . $this->eol;
$output .= '<name>' . $this->config->get('config_name') . '</name>' . $this->eol ;
$output .= '<company>' . $this->config->get('config_owner') . '</company>' . $this->eol ;
$output .= '<url>' . HTTPS_SERVER . '</url>' . $this->eol ;
$output .= '<phone>' . $this->config->get('config_telephone') . '</phone>' . $this->eol ;
$output .= '<platform>Opencart</platform>' . $this->eol ;
$output .= '<version>' . VERSION . '</version>' . $this->eol ;
$offers_currency = $this->config->get('feed_yandex_turbo_currency');
$this->load->model('localisation/currency');
$this->load->model('tool/image');
if (!$this->currency->has($offers_currency)) exit();
$decimal_place = $this->currency->getDecimalPlace($offers_currency);
$shop_currency = $this->config->get('config_currency');
$this->setCurrency($offers_currency, 1);
$currencies = $this->model_localisation_currency->getCurrencies();
$supported_currencies = array('RUR', 'RUB', 'USD', 'BYR', 'KZT', 'EUR', 'UAH');
$currencies = array_intersect_key($currencies, array_flip($supported_currencies));
foreach ($currencies as $currency) {
if ($currency['code'] != $offers_currency && $currency['status'] == 1) {
$this->setCurrency($currency['code'], number_format(1/$this->currency->convert($currency['value'], $offers_currency, $shop_currency), 4, '.', ''));
}
}
$output .= '<currencies>' . $this->eol;
foreach ($this->currencies as $currency) {
$output .= $this->getElement($currency, 'currency');
}
$output .= '</currencies>' . $this->eol;
$decimal = (int)$this->currency->getDecimalPlace($offers_currency);
$categories = $this->model_extension_feed_yandex_turbo->getCategories();
foreach ($categories as $category) {
$this->setCategory($category['name'], $category['category_id'], $category['parent_id']);
}
$output .= '<categories>' . $this->eol;
foreach ($this->categories as $category) {
$category_name = $category['name'];
unset($category['name'], $category['export']);
$output .= $this->getElement($category, 'category', $category_name);
}
$output .= '</categories>' . $this->eol;
$output .= '<offers>' . $this->eol;
$products = $this->model_extension_feed_yandex_turbo->getProducts();
foreach ($products as $product) {
$output .= '<offer id="' . $product['product_id'] . '" available="' . ($product['quantity'] > 0 ? 'true' : 'false') . '">' . $this->eol;
$output .= '<url>' . $this->url->link('product/product', 'product_id=' . $product['product_id']) . '</url>' . $this->eol;
$output .= '<price>' . number_format($this->currency->convert($this->tax->calculate($product['price'], $product['tax_class_id']), $shop_currency, $offers_currency), $decimal, '.', '') . '</price>';
$output .= '<currencyId>' . $offers_currency . '</currencyId>' . $this->eol;
$output .= '<categoryId>' . $product['category_id'] . '</categoryId>' . $this->eol;
$output .= '<picture>' . $this->model_tool_image->resize($product['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_popup_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_popup_height')) . '</picture>' . $this->eol;
$output .= '<name><![CDATA[' . $this->prepareField($product['name']) . ']]></name>' . $this->eol;
$output .= '<description><![CDATA[' . $this->prepareField($product['description']) . ']]></description>' . $this->eol;
$output .= '</offer>' . $this->eol;
}
$output .= '</offers>' . $this->eol;
$output .= '</shop>'. $this->eol;
$output .= '</yml_catalog>';
$this->response->addHeader('Content-Type: application/xml');
$this->response->setOutput($output);
}
}
private function setCurrency($id, $rate = 'CBRF', $plus = 0) {
$allow_id = array('RUR', 'RUB', 'USD', 'BYR', 'KZT', 'EUR', 'UAH');
if (!in_array($id, $allow_id)) {
return false;
}
$allow_rate = array('CBRF', 'NBU', 'NBK', 'CB');
if (in_array($rate, $allow_rate)) {
$plus = str_replace(',', '.', $plus);
if (is_numeric($plus) && $plus > 0) {
$this->currencies[] = array(
'id'=>$this->prepareField(strtoupper($id)),
'rate'=>$rate,
'plus'=>(float)$plus
);
} else {
$this->currencies[] = array(
'id'=>$this->prepareField(strtoupper($id)),
'rate'=>$rate
);
}
} else {
$rate = str_replace(',', '.', $rate);
if (!(is_numeric($rate) && $rate > 0)) {
return false;
}
$this->currencies[] = array(
'id'=>$this->prepareField(strtoupper($id)),
'rate'=>(float)$rate
);
}
return true;
}
private function setCategory($name, $id, $parent_id = 0) {
$id = (int)$id;
if ($id < 1 || trim($name) == '') {
return false;
}
if ((int)$parent_id > 0) {
$this->categories[$id] = array(
'id'=>$id,
'parentId'=>(int)$parent_id,
'name'=>$this->prepareField($name)
);
} else {
$this->categories[$id] = array(
'id'=>$id,
'name'=>$this->prepareField($name)
);
}
return true;
}
private function getElement($attributes, $element_name, $element_value = '') {
$retval = '<' . $element_name . ' ';
foreach ($attributes as $key => $value) {
$retval .= $key . '="' . $value . '" ';
}
$retval .= $element_value ? '>' . $this->eol . $element_value . '</' . $element_name . '>' : '/>';
$retval .= $this->eol;
return $retval;
}
private function prepareField($field) {
$field = htmlspecialchars_decode($field);
$field = strip_tags($field);
$from = array('"', '&', '>', '<', '°', '\'');
$to = array('&quot;', '&amp;', '&gt;', '&lt;', '&#176;', '&apos;');
$field = str_replace($from, $to, $field);
$field = preg_replace('#[\x00-\x08\x0B-\x0C\x0E-\x1F]+#is', ' ', $field);
return trim($field);
}
}