Files
kupshop/bundles/KupShop/FeedsBundle/Admin/feeds.php
2025-08-02 16:30:27 +02:00

370 lines
15 KiB
PHP

<?php
use KupShop\AdminBundle\Admin\ProductsFilter;
use KupShop\AdminBundle\Util\Filter\OrdersFilter;
use KupShop\FeedGeneratorBundle\ContextPropertiesLocator;
use KupShop\FeedGeneratorBundle\Exception\XMLFeedReader\XMLFeedReaderException;
use KupShop\FeedGeneratorBundle\Exception\XSDParser\XSDParsingException;
use KupShop\FeedGeneratorBundle\Expressions\ExpressionInterpreterException;
use KupShop\FeedGeneratorBundle\V8ErrorException;
use KupShop\FeedsBundle\FeedLocator;
use KupShop\FeedsBundle\Utils\FeedUtils;
use KupShop\KupShopBundle\Context\CountryContext;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Context\DomainContext;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
$main_class = 'Feeds';
class Feeds extends Window
{
/** @var FeedLocator */
private $feedLocator;
/** @var LanguageContext */
private $languageContext;
/** @var CurrencyContext */
private $currencyContext;
/** @var CountryContext */
private $countryContext;
/** @var DomainContext */
private $domainContext;
/** @var ContextPropertiesLocator */
private $contextPropertiesLocator;
private FeedUtils $feedUtils;
/** @var string */
private static $defaultTemplate = '{"beginning": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<catalog>", "end": "</catalog>", "nodes": []}';
public function __construct()
{
$this->feedLocator = ServiceContainer::getService(FeedLocator::class);
$this->feedUtils = ServiceContainer::getService(FeedUtils::class);
$this->languageContext = ServiceContainer::getService(LanguageContext::class);
$this->currencyContext = ServiceContainer::getService(CurrencyContext::class);
$this->countryContext = ServiceContainer::getService(CountryContext::class);
$this->domainContext = ServiceContainer::getService(DomainContext::class);
if (findModule(Modules::FEED_GENERATOR)) {
$this->contextPropertiesLocator = ServiceContainer::getService(ContextPropertiesLocator::class);
}
}
public function get_vars()
{
$vars = parent::get_vars();
$ID = $this->getID();
$vars['body']['feedTypes'] = $this->feedLocator->getAliases();
$vars['body']['languages']['null'] = translate('languages_null', 'feeds');
foreach ($this->languageContext->getSupported() as $language) {
$vars['body']['languages'][$language->getId()] = $language->getName();
}
$vars['body']['countries']['null'] = translate('countries_null', 'feeds');
foreach ($this->countryContext->getAll() as $country) {
$vars['body']['countries'][$country->getId()] = $country->getName();
}
$vars['body']['currencies']['null'] = translate('currencies_null', 'feeds');
foreach ($this->currencyContext->getSupported() as $currency) {
$vars['body']['currencies'][$currency->getId()] = $currency->getName();
}
$vars['body']['domains'] = array_combine($this->domainContext->getSupported(), $this->domainContext->getSupported());
$vars['body']['domainsWithScheme'] = $this->domainContext->getSupportedWithScheme();
if (!empty($ID)) {
$vars['body']['data']['active'] = $vars['body']['data']['active'] ? 'Y' : 'N';
$vars['body']['data']['id_language'] = $vars['body']['data']['id_language'] ?? 'null';
$vars['body']['data']['id_currency'] = $vars['body']['data']['id_currency'] ?? 'null';
if ($vars['body']['data']['settings']) {
$settings = json_decode_strict($vars['body']['data']['settings'], true);
$vars['filter'] = $settings['filter'] ?? null;
}
}
if (empty($vars['body']['data']['template'])) {
$vars['body']['data']['template'] = static::$defaultTemplate;
}
$vars['body']['configurableFeedTemplates'] = array_map(fn ($x) => $x['name'], $this->getFeedTemplates());
if (findModule(Modules::FEED_GENERATOR) && $this->feedLocator->isTypeConfigurable($vars['body']['data']['type'] ?? false)) {
$vars['body']['enableConfigurator'] = true;
$vars['body']['contextPropertiesLocator'] = $this->contextPropertiesLocator;
}
if ($vars['body']['data']['type'] ?? false) {
$vars['body']['feedService'] = $this->feedLocator->getServiceByType($vars['body']['data']['type']);
}
$vars['body']['objectID'] = getVal('objectID', null, '');
$this->unserializeCustomData($vars['body']['data']);
$report = [];
foreach (json_decode($vars['body']['data']['report'] ?? '') ?? [] as $key => $item) {
if (str_starts_with($key, 'feed_')) {
$report[$key] = $item;
} else {
$report['last_generated_feed'][$key] = $item;
}
}
$vars['body']['data']['report_decoded'] = $report;
if (($vars['body']['feedService'] ?? false) instanceof \KupShop\FeedsBundle\Feed\IHasCustomData) {
$template = createSmarty(false, true);
$template->assign($vars);
$vars['body']['customDataForm'] = $template->fetch($vars['body']['feedService']->getCustomDataFormTemplate());
}
$formats = $this->feedUtils->getFeedFormats();
$vars['body']['additionalFormats'] = array_combine($formats, $formats);
$vars['body']['formatExtension'] = ($vars['body']['data']['data']['format'] ?? false)
&& !in_array($vars['body']['data']['data']['format'], ['xml', 'xml_pretty'], true) ? $vars['body']['data']['data']['format'] : '';
return $vars;
}
public function checkRequired($data)
{
// Allow to load all fields from template, skip validity check
if ($data['fromTemplate'] ?? null) {
return [];
}
return parent::checkRequired($data);
}
public function getSQLFields($data = null, $fields = null, $defaults = null, $types = null)
{
$fields = parent::getSQLFields($data, $fields, $defaults);
$fromTemplate = getVal('data')['fromTemplate'] ?? null;
if ($this->getAction() == 'add' && !empty($fromTemplate)) {
$templates = $this->getFeedTemplates();
if (isset($templates[$fromTemplate])) {
$feedTemplate = $templates[$fromTemplate];
$feedTemplate['row']['name'] ??= $feedTemplate['name'];
foreach ($feedTemplate['row'] as $key => $value) {
// handle merging custom data maybe?
$fields[$key] = $value;
}
}
}
$fields['active'] = !empty($fields['active']) && $fields['active'] === 'Y';
$fields['expensive'] = !empty($fields['expensive']) && $fields['expensive'] === 'Y';
$fields['id_language'] = $fields['id_language'] === 'null' ? null : $fields['id_language'];
if (array_key_exists('id_country', $fields)) {
$fields['id_country'] = $fields['id_country'] === 'null' ? null : $fields['id_country'];
}
$fields['id_currency'] = $fields['id_currency'] === 'null' ? null : $fields['id_currency'];
$ID = $this->getID();
if (!empty($ID)) {
$fields['updated'] = (new DateTime())->format('Y-m-d H:i:s');
}
$filter = getVal('filter');
if ($filter) {
$feedRow = $this->selectSQL('feeds', ['id' => $this->getID()])->fetch();
$feedService = $this->feedLocator->getServiceByType($feedRow['type']);
$settings = [];
if ($feedService && $feedService->getObjectType() === 'order') {
$settings['filter'] = OrdersFilter::cleanFilter($filter);
} else {
$settings['filter'] = ProductsFilter::cleanFilter($filter);
}
$settings_json = json_encode($settings);
$fields['settings'] = $settings_json;
}
return $fields;
}
public function getData()
{
$data = parent::getData();
$this->serializeCustomData($data);
return $data;
}
public function handleUpdate()
{
$acn = $this->getAction();
$SQL = null;
if ($acn == 'add') {
$sqlFields = $this->getSQLFields();
$this->insertSQL($this->getTableName(), $sqlFields);
$SQL = true;
if (empty($ID)) {
if (isset($sqlFields['id'])) {
$this->setID($sqlFields['id']);
} else {
$this->setID(sqlInsertID());
}
}
$this->insertHash();
return $SQL;
} else {
return parent::handleUpdate();
}
}
private function insertHash()
{
$feed = $this->selectSQL('feeds', ['id' => $this->getID()])->fetch();
if ($feed) {
$hash = FeedUtils::createHash([
$feed['id'],
$feed['created'],
]);
$this->updateSQL('feeds', ['hash' => $hash], ['id' => $feed['id']]);
}
}
public function handleConfiguratorPreview()
{
increaseMemoryLimit(1024);
$feedID = $this->getID();
$feedRow = $this->selectSQL('feeds', ['id' => $feedID])->fetch();
$feedRow['data'] = json_decode($feedRow['data'] ?? '{}', true);
$response = [];
// set language context
$language = getVal('language');
if ($language && $language !== 'null') {
$this->languageContext->activate($language);
} elseif (isset($feedRow['id_language'])) {
$this->languageContext->activate($feedRow['id_language']);
}
// set currency context
$currency = getVal('currency');
if ($currency && $currency !== 'null') {
$this->currencyContext->activate($currency);
} elseif (isset($feedRow['id_currency'])) {
$this->currencyContext->activate($feedRow['id_currency']);
}
// set country context
$this->countryContext->activate($this->countryContext->getDefaultId());
$country = getVal('country');
if ($country && $country !== 'null') {
$this->countryContext->activate($country);
} elseif (isset($feedRow['id_country'])) {
$this->countryContext->activate($feedRow['id_country']);
}
// set domain context
if ($domain = getVal('domain')) {
$this->domainContext->activate($domain);
}
if ($feedRow && $this->feedLocator->isTypeConfigurable($feedRow['type'])) {
$this->filterByObjectID($feedRow['type']);
$feedRow['template'] = getVal('wipTemplate', $_POST, static::$defaultTemplate);
$feedService = $this->feedLocator->getServiceByType($feedRow['type']);
while (ob_get_level()) {
ob_end_clean();
}
try {
ob_start();
$feedService->renderStrict($feedRow, 1);
$result = ob_get_contents();
ob_end_clean();
// format output
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
@$dom->loadXML($result);
$dom->formatOutput = true;
$response['xmlPreview'] = $dom->saveXML();
} catch (ExpressionInterpreterException|V8ErrorException|XSDParsingException|XMLFeedReaderException $e) {
$rc = new \ReflectionClass($e);
$response['xmlPreview'] = $rc->getShortName().': '.$e->getMessage();
$response['xmlPreview'] .= PHP_EOL.PHP_EOL.'----------'.PHP_EOL.ob_get_contents();
ob_end_clean();
}
$smarty = createSmarty(true, true);
$smarty->assign('body', ['feedService' => $feedService, 'data' => $feedRow]);
$response['dataInfo'] = $smarty->fetch('window/feeds.dataInfo.tpl');
if ($feedService instanceof \KupShop\FeedGeneratorBundle\Feed\IConfigurableFeed) {
$response['consoleLogs'] = $feedService->getReport()['consoleLogs'] ?? [];
}
}
header('Content-Type: application/json');
echo json_encode($response);
exit;
}
public function handleLoadTemplate()
{
$feedID = $this->getID();
$feedRow = $this->selectSQL('feeds', ['id' => $feedID])->fetch();
$service = $this->feedLocator->getServiceByType($feedRow['type']);
if (!$service instanceof \KupShop\FeedGeneratorBundle\Feed\IConfigurableFeed) {
$this->returnError('Pro nastavení šablony musí být vybrán konfigurovatelný typ feedu');
}
$feedTemplate = getVal('feedTemplate', null, '');
$templates = $this->getFeedTemplates();
if (!preg_match('/^[a-zA-Z\_\-]+$/', $feedTemplate) || !isset($templates[$feedTemplate])) {
$this->returnError('Šablona konfigurovatelného feedu nebyla nalezena');
}
$feedTemplate = $templates[$feedTemplate];
$this->updateSQL('feeds', [
'template' => $feedTemplate['row']['template'],
], ['id' => $feedID]);
$this->returnOK(translate('templateLoaded'));
}
protected function getFeedTemplates(): array
{
$configurableFeedTemplates = getCache('configurableFeedTemplates');
if (!$configurableFeedTemplates) {
$configurableFeedTemplates = ['' => ['name' => translate('selectTemplate', 'feeds')]];
$bundleFinder = ServiceContainer::getService(\KupShop\KupShopBundle\Util\System\BundleFinder::class);
foreach ($bundleFinder->getExistingBundlesPath('Admin/ConfigurableFeedTemplates') as $bundlePath) {
foreach (glob($bundlePath.'/*.json') as $filePath) {
try {
$feedTemplate = json_decode_strict(file_get_contents($filePath), true);
$configurableFeedTemplates[mb_substr(basename($filePath), 0, -5)] = $feedTemplate;
} catch (Exception $e) {
if (isLocalDevelopment()) {
throw $e;
}
}
}
}
setCache('configurableFeedTemplates', $configurableFeedTemplates);
}
return $configurableFeedTemplates;
}
protected function filterByObjectID($feedType)
{
$ID = $this->getID();
$objectID = getVal('objectID');
$feed = $this->feedLocator->getServiceByType($feedType);
if ($objectID && !empty($ID) && $feed instanceof \KupShop\FeedGeneratorBundle\Feed\IConfigurableFeed) {
$feed->filterByObjectID($objectID);
}
}
}