public function catalogAction()
 {
     /**
      * @var shopYandexmarketPlugin $plugin
      */
     $plugin = wa()->getPlugin('yandexmarket');
     $profile_helper = new shopImportexportHelper('yandexmarket');
     list($path, $profile_id) = $plugin->getInfoByHash(waRequest::param('hash'));
     if ($profile_id) {
         $profile = $profile_helper->getConfig($profile_id);
         if (!$profile) {
             throw new waException('Profile not found', 404);
         }
         $lifetime = ifset($profile['config']['lifetime'], 0);
         if ($lifetime && (!file_exists($path) || time() - filemtime($path) > $lifetime)) {
             waRequest::setParam('profile_id', $profile_id);
             $runner = new shopYandexmarketPluginRunController();
             $_POST['processId'] = null;
             $moved = false;
             $ready = false;
             do {
                 ob_start();
                 if (empty($_POST['processId'])) {
                     $_POST['processId'] = $runner->processId;
                 } else {
                     sleep(1);
                 }
                 if ($ready) {
                     $_POST['cleanup'] = true;
                     $moved = true;
                 }
                 $runner->execute();
                 $out = ob_get_clean();
                 $result = json_decode($out, true);
                 $ready = !empty($result) && is_array($result) && ifempty($result['ready']);
             } while (!$ready || !$moved);
             //TODO check errors
         }
     }
     waFiles::readFile($path, waRequest::get('download') ? 'yandexmarket.xml' : null);
 }
    protected function init()
    {
        try {
            $backend = wa()->getEnv() == 'backend';
            $profiles = new shopImportexportHelper('yandexmarket');
            switch ($this->encoding) {
                case 'windows-1251':
                    setlocale(LC_CTYPE, 'ru_RU.CP-1251', 'ru_RU.CP1251', 'ru_RU.win');
                    break;
            }
            $this->data['offset'] = array('offers' => 0);
            $this->data['timestamp'] = time();
            $default_export_config = array('zero_stock' => 0, 'compare_price' => 0, 'sku' => 0, 'sku_group' => '', 'hidden_categories' => 0);
            if ($backend) {
                $hash = shopImportexportHelper::getCollectionHash();
                $profile_config = array('hash' => $hash['hash'], 'domain' => waRequest::post('domain'), 'map' => array(), 'types' => array_filter((array) waRequest::post('types')), 'export' => (array) waRequest::post('export', array()) + $default_export_config, 'company' => waRequest::post('company'), 'company_name' => waRequest::post('company_name'), 'shop' => waRequest::post('shop'), 'lifetime' => waRequest::post('lifetime', 0, waRequest::TYPE_INT), 'utm_source' => waRequest::post('utm_source'), 'utm_medium' => waRequest::post('utm_medium'), 'utm_campaign' => waRequest::post('utm_campaign'));
                $this->data['map'] = $this->plugin()->map(waRequest::post('map', array()), $profile_config['types']);
                foreach ($this->data['map'] as $type => $offer_map) {
                    foreach ($offer_map['fields'] as $field => $info) {
                        if (!empty($info['source']) && preg_match('@^\\w+:(.+)$@', $info['source'], $matches) && $matches[1] != '%s') {
                            $profile_config['map'][$type][$field] = $info['source'];
                        }
                    }
                    if (empty($profile_config['map'][$type])) {
                        unset($profile_config['map'][$type]);
                    }
                }
                $profile_id = $profiles->setConfig($profile_config);
                $this->plugin()->getHash($profile_id);
            } else {
                $profile_id = waRequest::param('profile_id');
                if (!$profile_id || !($profile = $profiles->getConfig($profile_id))) {
                    throw new waException('Profile not found', 404);
                }
                $profile_config = $profile['config'];
                $profile_config['export'] += $default_export_config;
                $this->data['map'] = $this->plugin()->map($profile_config['map'], $profile_config['types']);
                foreach ($this->data['map'] as $type => &$offer_map) {
                    foreach ($offer_map['fields'] as $field => &$info) {
                        $info['source'] = ifempty($profile_config['map'][$type][$field], 'skip:');
                    }
                    unset($offer_map);
                }
            }
            foreach ($this->data['map'] as $type => &$offer_map) {
                if ($type != 'simple') {
                    $offer_map['fields']['type'] = array('source' => 'value:' . $type, 'attribute' => true);
                }
                unset($offer_map);
            }
            $feature_model = new shopFeatureModel();
            foreach ($this->data['map'] as $type => &$offer_map) {
                foreach ($offer_map['fields'] as $field => &$info) {
                    if (strpos($field, 'param.') === 0 && isset($info['source'])) {
                        switch (preg_replace('@:.+$@', '', $info['source'])) {
                            case 'feature':
                                if ($feature = $feature_model->getByCode(preg_replace('@^[^:]+:@', '', $info['source']))) {
                                    $info['source_name'] = $feature['name'];
                                }
                                break;
                        }
                    }
                }
                unset($info);
                unset($offer_map);
            }
            $this->data['hash'] = $profile_config['hash'];
            if (!isset($this->data['categories'])) {
                $this->data['categories'] = array();
            }
            $this->data['export'] = $profile_config['export'];
            $this->data['domain'] = $profile_config['domain'];
            $this->data['utm'] = array();
            foreach (array('utm_source', 'utm_medium', 'utm_campaign') as $field) {
                if (!empty($profile_config[$field])) {
                    $this->data['utm'][$field] = $profile_config[$field];
                }
            }
            if ($this->data['utm']) {
                $this->data['utm'] = http_build_query(array_map('rawurlencode', $this->data['utm']));
            }
            $this->data['types'] = array();
            foreach ($profile_config['types'] as $type => $type_map) {
                $this->data['types'] += array_fill_keys(array_filter(array_map('intval', $type_map)), $type);
            }
            $this->initRouting();
            $model = new shopCategoryModel();
            if (empty($this->data['export']['hidden_categories'])) {
                $sql = <<<SQL
SELECT COUNT(1) as `cnt`
FROM shop_category c
LEFT JOIN shop_category_routes cr ON (c.id = cr.category_id)
WHERE
  ((cr.route IS NULL) OR (cr.route = s:route))
   AND
  (`c`.`type`=i:type)
  AND
  (`c`.`status`=1)

SQL;
            } else {
                $sql = <<<SQL
SELECT COUNT(1) as `cnt`
FROM shop_category c
LEFT JOIN shop_category_routes cr ON (c.id = cr.category_id)
WHERE
  ((cr.route IS NULL) OR (cr.route = s:route))
   AND
  (`c`.`type`=i:type)

SQL;
            }
            $params = array('route' => $this->data['domain'], 'type' => shopCategoryModel::TYPE_STATIC);
            $this->data['count'] = array('category' => (int) $model->query($sql, $params)->fetchField('cnt'), 'product' => $this->getCollection()->count());
            $stages = array_keys($this->data['count']);
            $this->data['current'] = array_fill_keys($stages, 0);
            $this->data['processed_count'] = array_fill_keys($stages, 0);
            $this->data['stage'] = reset($stages);
            $this->data['stage_name'] = $this->getStageName($this->data['stage']);
            $this->data['memory'] = memory_get_peak_usage();
            $this->data['memory_avg'] = memory_get_usage();
            if (!class_exists('DOMDocument')) {
                throw new waException('PHP extension DOM required');
            }
            $this->dom = new DOMDocument("1.0", $this->encoding);
            $this->dom->encoding = $this->encoding;
            $this->dom->preserveWhiteSpace = false;
            $this->dom->formatOutput = true;
            /**
             * @var shopConfig $config
             */
            $config = wa('shop')->getConfig();
            $xml = <<<XML
<?xml version="1.0" encoding="{$this->encoding}"?>
<!DOCTYPE yml_catalog SYSTEM "shops.dtd">
<yml_catalog  date="%s">
</yml_catalog>
XML;
            $original = shopYandexmarketPlugin::path('shops.dtd');
            $target = $this->getTempPath('shops.dtd');
            $ft = filesize($target);
            $fo = filesize($original);
            if (!file_exists($target) || filesize($target) != filesize($original) && waFiles::delete($target)) {
                waFiles::copy($original, $target);
            }
            $this->dom->loadXML(sprintf($xml, date("Y-m-d H:i")));
            $this->dom->lastChild->appendChild($shop = $this->dom->createElement("shop"));
            $name = ifempty($profile_config['company_name'], $config->getGeneralSettings('name'));
            $name = str_replace('&', '&amp;', $name);
            $name = str_replace("'", '&apos;', $name);
            $this->addDomValue($shop, 'name', $name);
            $company = str_replace('&', '&amp;', $profile_config['company']);
            $company = str_replace("'", '&apos;', $company);
            $this->addDomValue($shop, 'company', $company);
            $this->addDomValue($shop, 'url', preg_replace('@^https@', 'http', wa()->getRouteUrl('shop/frontend', array(), true)));
            if ($phone = $config->getGeneralSettings('phone')) {
                $shop->appendChild($this->dom->createElement('phone', $phone));
            }
            $this->addDomValue($shop, 'platform', 'Shop-Script');
            $this->addDomValue($shop, 'version', wa()->getVersion('shop'));
            $currencies = $this->dom->createElement('currencies');
            $model = new shopCurrencyModel();
            $this->data['currency'] = array();
            $available_currencies = shopYandexmarketPlugin::settingsPrimaryCurrencies();
            if (empty($available_currencies)) {
                throw new waException('Экспорт не может быть выполнен: не задано ни одной валюты, которая могла бы использоваться в качестве основной.');
            }
            unset($available_currencies['auto']);
            $primary_currency = $this->plugin()->getSettings('primary_currency');
            $this->data['default_currency'] = $config->getCurrency();
            if (!isset($available_currencies[$primary_currency])) {
                $primary_currency = $this->data['default_currency'];
                if (!isset($available_currencies[$primary_currency])) {
                    reset($available_currencies);
                    $primary_currency = key($available_currencies);
                }
            }
            $this->data['primary_currency'] = $primary_currency;
            $rate = $available_currencies[$primary_currency]['rate'];
            $available_currencies = $model->getCurrencies(shopYandexmarketPlugin::getConfigParam('currency'));
            foreach ($available_currencies as $info) {
                if ($info['rate'] > 0) {
                    $info['rate'] = $info['rate'] / $rate;
                    $this->data['currency'][] = $info['code'];
                    if (abs(round($info['rate'], 4) - $info['rate']) / $info['rate'] > 0.01) {
                        $info['rate'] = 'CB';
                    }
                    $value = array('id' => $info['code'], 'rate' => $this->format('rate', $info['rate']));
                    $this->addDomValue($currencies, 'currency', $value);
                }
            }
            $shop->appendChild($currencies);
            $shop->appendChild($this->dom->createElement('categories'));
            $fields = array('store' => true, 'pickup' => true, 'delivery' => true, 'deliveryIncluded' => false, 'local_delivery_cost' => '%0.2f', 'adult' => true);
            foreach ($fields as $field => $include_value) {
                $value = ifset($profile_config['shop'][$field], '');
                if ($value || $value !== '') {
                    if ($include_value) {
                        $value = $include_value === true ? $value : $this->format($field, $value, array('format', $include_value));
                        $this->addDomValue($shop, $field, $value);
                    } else {
                        $shop->appendChild($this->dom->createElement($field));
                    }
                }
            }
            $shop->appendChild($this->dom->createElement('offers'));
            if (!$this->data['currency']) {
                throw new waException('Не задано ни одной поддерживаемой валюты');
            }
            if (!in_array($this->data['primary_currency'], $this->data['currency'])) {
                $this->data['primary_currency'] = reset($this->data['currency']);
            }
            $this->data['path'] = array('offers' => shopYandexmarketPlugin::path($profile_id . '.xml'));
            $this->save();
            $this->dom = null;
            $this->loadDom();
        } catch (waException $ex) {
            $this->error($ex->getMessage());
            echo json_encode(array('error' => $ex->getMessage()));
            exit;
        }
    }
 public function execute()
 {
     $direction = waRequest::request('direction', 'import') == 'export' ? 'export' : 'import';
     $profile_helper = new shopImportexportHelper('csv:product:' . $direction);
     $this->view->assign('profiles', $profile_helper->getList());
     $profile = $profile_helper->getConfig();
     if ($direction == 'export') {
         //export section TODO
         $profile['config'] += array('encoding' => 'UTF-8', 'images' => true, 'features' => true, 'domain' => null, 'delimiter' => ';', 'hash' => '');
         $info = array();
         if (!empty($profile['id'])) {
             $path = wa()->getTempPath('csv/download/' . $profile['id']);
             $files = waFiles::listdir($path);
             foreach ($files as $file) {
                 $file_path = $path . '/' . $file;
                 $info[] = array('name' => $file, 'mtime' => filemtime($file_path), 'size' => filesize($file_path));
             }
             usort($info, create_function('$a, $b', 'return (max(-1, min(1, $a["mtime"] - $b["mtime"])));'));
         }
         $this->view->assign('info', array_slice($info, -5, 5, true));
         $set_model = new shopSetModel();
         $this->view->assign('sets', $set_model->getAll());
         $routing = wa()->getRouting();
         $settlements = array();
         $current_domain = null;
         $domain_routes = $routing->getByApp('shop');
         foreach ($domain_routes as $domain => $routes) {
             foreach ($routes as $route) {
                 $settlement = $domain . '/' . $route['url'];
                 if ($current_domain === null) {
                     $current_domain = $settlement;
                 } elseif ($settlement == $profile['config']['domain']) {
                     $current_domain = $settlement;
                 }
                 $settlements[] = $settlement;
             }
         }
         $this->view->assign('current_domain', $current_domain);
         $this->view->assign('settlements', $settlements);
     } else {
         $profile['config'] += array('encoding' => 'UTF-8', 'delimiter' => ';', 'map' => array());
         $base_path = wa()->getConfig()->getPath('root');
         $app_path = array('shop' => wa()->getDataPath(null, false, 'shop'), 'site' => wa()->getDataPath(null, true, 'site'));
         foreach ($app_path as &$path) {
             $path = preg_replace('@^([\\\\/])@', '', str_replace($base_path, '', $path . '/'));
         }
         unset($path);
         $this->view->assign('upload_path', waSystem::getSetting('csv.upload_path', 'path/to/folder/with/source/images/'));
         $this->view->assign('upload_app', waSystem::getSetting('csv.upload_app', 'shop'));
         $this->view->assign('app_path', $app_path);
     }
     $this->view->assign('profile', $profile);
     $type_model = new shopTypeModel();
     $this->view->assign('types', $type_model->getTypes());
     $encoding = array_diff(mb_list_encodings(), array('pass', 'wchar', 'byte2be', 'byte2le', 'byte4be', 'byte4le', 'BASE64', 'UUENCODE', 'HTML-ENTITIES', 'Quoted-Printable', '7bit', '8bit', 'auto'));
     $popular = array_intersect(array('UTF-8', 'Windows-1251', 'ISO-8859-1'), $encoding);
     asort($encoding);
     $encoding = array_unique(array_merge($popular, $encoding));
     $this->view->assign('encoding', $encoding);
     $plugins = wa()->getConfig()->getPlugins();
     $this->view->assign('direction', $direction);
     $this->view->assign('plugin', ifempty($plugins['csvproducts'], array()));
 }
 protected function save(waRequestFile $file)
 {
     $path = wa()->getTempPath('csv/upload/');
     waFiles::create($path);
     $original_name = $file->name;
     if ($name = tempnam($path, 'csv')) {
         unlink($name);
         if (($ext = pathinfo($original_name, PATHINFO_EXTENSION)) && preg_match('/^\\w+$/', $ext)) {
             $name .= '.' . $ext;
         }
         $file->moveTo($name);
     } else {
         throw new waException(_w('Error file upload'));
     }
     $encoding = waRequest::post('encoding', 'UTF-8');
     $delimiter = waRequest::post('delimiter');
     try {
         $this->reader = new shopCsvReader($name, $delimiter, $encoding);
         $delimiters = array(';', ',', 'tab');
         $used_delimiters = array($delimiter);
         while (count($this->reader->header()) < 2 && ($delimiter = array_diff($delimiters, $used_delimiters))) {
             $delimiter = reset($delimiter);
             $used_delimiters[] = $delimiter;
             $this->reader->delete();
             $this->reader = new shopCsvReader($name, $delimiter, $encoding);
         }
         if (count($this->reader->header()) < 2) {
             $this->reader->delete();
             $delimiter = waRequest::post('delimiter');
             $this->reader = new shopCsvReader($name, $delimiter, $encoding);
         }
         $encodings = array('UTF-8', 'Windows-1251', 'ISO-8859-1');
         $used_encodings = array($encoding);
         while (in_array(false, (array) $this->reader->header(), true) && ($encoding = array_diff($encodings, $used_encodings))) {
             $encoding = reset($encoding);
             $used_encodings[] = $encoding;
             $this->reader->delete();
             $this->reader = new shopCsvReader($name, $delimiter, $encoding);
         }
         if (in_array(false, (array) $this->reader->header(), true) || count($this->reader->header()) < 2) {
             throw new waException($this->reader->header() ? _w('No data columns were located in the uploaded file. Make sure right separator and encoding were chosen for this upload.') : _w('Unsupported CSV file structure'));
         }
         $profile_helper = new shopImportexportHelper('csv:product:import');
         $profile = $profile_helper->getConfig();
         $profile['config'] += array('encoding' => $encoding, 'delimiter' => ';', 'map' => array());
         $params = array();
         $params['id'] = 'csvproducts';
         $params['title_wrapper'] = '%s';
         $params['description_wrapper'] = '<br><span class="hint">%s</span>';
         $params['control_wrapper'] = '<div class="field"><div class="name">%s</div><div class="value">%s %s</div></div>';
         $params['options'] = $this->options();
         $control = true ? shopCsvReader::TABLE_CONTROL : shopCsvReader::MAP_CONTROL;
         switch ($control) {
             case shopCsvReader::TABLE_CONTROL:
                 $params['preview'] = 50;
                 $params['columns'] = array(array('shopCsvProductviewController', 'tableRowHandler'), '&nbsp;');
                 $params['control_wrapper'] = '<div class="field"><div class="value" style="overflow-x:auto;margin-left:0;">%s %s</div></div>';
                 $params['title_wrapper'] = false;
                 $params['row_handler'] = 'csv_product/rows/';
                 $params['row_handler_string'] = true;
                 $params['autocomplete_handler'] = 'csv_product/autocomplete/reset/';
                 break;
             case shopCsvReader::MAP_CONTROL:
             default:
                 $control = shopCsvReader::MAP_CONTROL;
                 break;
         }
         return array('name' => htmlentities(basename($this->reader->file()), ENT_QUOTES, 'utf-8'), 'original_name' => htmlentities(basename($original_name), ENT_QUOTES, 'utf-8'), 'size' => waFiles::formatSize($this->reader->size()), 'original_size' => waFiles::formatSize($file->size), 'controls' => waHtmlControl::getControl($control, 'csv_map', $params), 'control' => $control, 'header' => $this->reader->header(), 'columns_offset' => count(ifset($params['columns'], array())), 'delimiter' => $delimiter, 'encoding' => $encoding);
     } catch (waException $ex) {
         if ($this->reader) {
             $this->reader->delete(true);
         }
         throw $ex;
     }
 }
 public function execute()
 {
     $routing = wa()->getRouting();
     $settlements = array();
     $profile_helper = new shopImportexportHelper($this->plugin_id);
     $this->view->assign('profiles', $list = $profile_helper->getList());
     $profile = $profile_helper->getConfig();
     $profile['config'] += array('hash' => '', 'domain' => '', 'lifetime' => 0);
     $current_domain =& $profile['config']['domain'];
     $this->view->assign('current_domain', $current_domain);
     $domain_routes = $routing->getByApp('shop');
     foreach ($domain_routes as $domain => $routes) {
         foreach ($routes as $route) {
             $settlement = $domain . '/' . $route['url'];
             if ($settlement == $current_domain || $current_domain === '') {
                 $current_domain = $settlement;
                 $routing->setRoute($route, $domain);
                 waRequest::setParam($route);
             }
             $settlements[] = $settlement;
         }
     }
     $this->view->assign('profile', $profile);
     $info = array();
     $this->view->assign('settlements', $settlements);
     if (!empty($profile['id'])) {
         $path = shopYandexmarketPlugin::path($profile['id'] . '.xml');
         $info['exists'] = file_exists($path);
         $info['mtime'] = $info['exists'] ? filemtime($path) : null;
     } else {
         $info['mtime'] = $info['exists'] = null;
     }
     if ($info['exists']) {
         $route_params = array('plugin' => $this->plugin_id, 'hash' => $this->plugin()->getHash($profile['id']));
         $info['url'] = $routing->getUrl('shop/frontend/catalog', $route_params, true);
     } else {
         $info['url'] = null;
     }
     $this->view->assign('info', $info);
     /**
      * @var shopConfig $config ;
      */
     $config = wa('shop')->getConfig();
     $this->view->assign('primary_currency', $config->getCurrency());
     $this->view->assign('company', ifempty($profile['config']['company'], $config->getGeneralSettings('name')));
     $this->view->assign('company_name', ifempty($profile['config']['company_name'], $config->getGeneralSettings('name')));
     $type_model = new shopTypeModel();
     $this->view->assign('types', $type_model->getAll());
     $profile_map = ifset($profile['config']['map'], array());
     $export = ifset($profile['config']['export'], array());
     $set_model = new shopSetModel();
     $map = $this->plugin()->map(array(), null, true);
     $params = array();
     if ($profile_map) {
         foreach ($map as $type => &$type_map) {
             foreach ($type_map['fields'] as $field => &$info) {
                 $info['source'] = ifempty($profile_map[$type][$field], 'skip:');
                 unset($profile_map[$type][$field]);
                 unset($info);
             }
             if (!empty($type_map['fields']['param.*'])) {
                 $params[$type] = -1;
             }
             unset($type_map);
         }
         foreach ($profile_map as $type => $fields) {
             foreach ($fields as $field => $source) {
                 $info_field = strpos($field, 'param.') === 0 ? 'param.*' : $field;
                 if (isset($map[$type]['fields'][$info_field])) {
                     $info = $map[$type]['fields'][$info_field];
                     $info['source'] = ifempty($source, 'skip:');
                     $map[$type]['fields'][$field] = $info;
                     $params[$type] = max(ifset($params[$type], -1), intval(preg_replace('@\\D+@', '', $field)));
                 }
             }
         }
     }
     $this->view->assign('sets', $set_model->getAll());
     $this->view->assign('type_map', $map);
     $this->view->assign('params', array('params' => $params));
     $this->view->assign('export', $export);
     $this->view->assign('types_map', ifset($profile['config']['types'], array()));
     $app_settings_model = new waAppSettingsModel();
     $app_settings = array('ignore_stock_count' => $app_settings_model->get('shop', 'ignore_stock_count', 0));
     $this->view->assign('app_settings', $app_settings);
     $feature_model = new shopFeatureModel();
     $config = wa('shop')->getConfig();
     /**
      * @var shopConfig $config
      */
     $limit = $config->getOption('features_per_page');
     if ($feature_model->countByField(array('parent_id' => null)) < $limit) {
         $features = $feature_model->getFeatures(true);
         /*, true*/
         foreach ($features as $id => $feature) {
             if ($feature['type'] == shopFeatureModel::TYPE_DIVIDER) {
                 unset($features[$id]);
             }
         }
     } else {
         $this->view->assign('features_autocomplete', true);
         $features = array();
         foreach ($map as $type_map) {
             foreach ($type_map['fields'] as $info) {
                 if (!empty($info['source']) && preg_match('@^feature:([\\w\\d_\\-]+)$@', $info['source'], $matches)) {
                     $features[] = $matches[1];
                 }
             }
         }
         if ($features = array_unique($features)) {
             $features = $feature_model->getFeatures('code', $features);
         } else {
             $features = array();
         }
     }
     foreach ($features as $id => &$feature) {
         if (strpos($feature['type'], shopFeatureModel::TYPE_DIMENSION . '.') === 0) {
             $units = shopDimension::getUnits($feature['type']);
             $feature['units'] = array();
             foreach ($units as $unit) {
                 $feature['units'][] = $unit['title'];
             }
             $feature['units'] = implode(', ', $feature['units']);
         } elseif (preg_match('@\\(([^\\)]+)\\)$@', $feature['name'], $matches)) {
             $feature['units'] = trim($matches[1]);
         }
         unset($feature);
     }
     $this->view->assign('features', $features);
     $fields = array('name' => _w('Product name'), 'description' => _w('Description'), 'summary' => _w('Summary'), 'count' => _w('In stock'), 'sku' => _w('SKU code'));
     $this->view->assign('fields', $fields);
 }