Files
kupshop/admin/cleaning.php
2025-08-02 16:30:27 +02:00

550 lines
16 KiB
PHP

<?php
use KupShop\AdminBundle\Util\Script\ScriptLocator;
use KupShop\CatalogBundle\Search\FulltextInterface;
use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Context\ContextManager;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Event\CronEvent;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
$main_class = 'Cleaning';
class Cleaning extends Window
{
public $success = 0;
public $failed = 0;
// Cache
public function handleCacheImages()
{
global $cfg;
foreach (glob($cfg['Path']['data'].'tmp/[0-9]*/*/*.*') as $file) {
if (@unlink($file)) {
$this->success++;
} else {
$this->failed++;
}
}
$path = realpath($cfg['Path']['data'].'tmp/');
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
$folders = [];
foreach ($objects as $path => $object) {
$folder_name = substr($path, strrpos($path, '/') + 1, strlen($path));
if (ctype_digit($folder_name) && strlen($folder_name) == 1) {
$folders[] = $path;
}
}
$folders = array_reverse($folders);
foreach ($folders as $folder) {
@rmdir($folder);
}
}
public function handleImageVersionUpgrade()
{
$dbcfg = Settings::getDefault();
$lastImageVersion = $dbcfg->loadValue('image_version');
$dbcfg->saveValue('image_version', $lastImageVersion ? $lastImageVersion + 1 : 2);
$dbcfg->clearCache();
$this->success++;
}
public function handleCacheSections()
{
clearCache('sections', true);
$this->success++;
}
public function handleCacheRuntime()
{
clearCache('', true);
$this->success++;
}
public function handleCacheTemplates()
{
global $cfg;
foreach (glob($cfg['Path']['data'].'tmp/templates/*') as $file) {
if (@unlink($file)) {
$this->success++;
} else {
$this->failed++;
}
}
}
public function handleCacheFeeds()
{
global $cfg;
foreach (glob($cfg['Path']['data'].'tmp/feed_*_*') as $file) {
if (@unlink($file)) {
$this->success++;
} else {
$this->failed++;
}
}
}
public function handleCacheSitemap()
{
$cfg = Config::get();
foreach (glob($cfg['Path']['data'].'tmp/sitemap*') as $file) {
if (@unlink($file)) {
$this->success++;
} else {
$this->failed++;
}
}
}
public function handleCacheStatic()
{
global $cfg;
foreach (glob($cfg['Path']['data'].'tmp/cache/*') as $file) {
if (@unlink($file)) {
$this->success++;
} else {
$this->failed++;
}
}
$this->handleCacheRuntime();
}
// Photos
public function getImagesUnused()
{
$SQL = sqlQuery('SELECT id
FROM photos p
LEFT JOIN photos_products_relation ppr ON p.id = ppr.id_photo
LEFT JOIN photos_articles_relation ppa ON p.id = ppa.id_photo
LEFT JOIN photos_menu_relation par ON p.id = par.id_photo
LEFT JOIN photos_blocks_relation blo ON p.id = blo.id_photo
LEFT JOIN photos_blocks_new_relation bln ON p.id = bln.id_photo
LEFT JOIN photos_sections_relation psr ON p.id = psr.id_photo
WHERE ppr.id_product IS NULL AND ppa.id_art IS NULL AND par.id_menu IS NULL
AND blo.id_block IS NULL AND bln.id_block IS NULL AND psr.id_section IS NULL');
$files = sqlFetchAll($SQL);
return $files;
}
public function handleCacheImagesType()
{
if (($type = getVal('type')) !== null) {
$this->clearImageTypeCache($type);
}
}
// clear cache only of defined photo type
public function clearImageTypeCache($type)
{
$cfg = Config::get();
if (!isset($cfg['Photo']['id_to_type'][$type])) {
return;
}
foreach (glob($cfg['Path']['data'].'tmp/'.$type.'/*/*.*') as $file) {
if (@unlink($file)) {
$this->success++;
} else {
$this->failed++;
}
}
$path = realpath($cfg['Path']['data'].'tmp/'.$type.'/');
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
$folders = [];
foreach ($objects as $path => $object) {
$folder_name = substr($path, strrpos($path, '/') + 1, strlen($path));
if (ctype_digit($folder_name) && strlen($folder_name) == 1) {
$folders[] = $path;
}
}
$folders = array_reverse($folders);
foreach ($folders as $folder) {
@rmdir($folder);
}
}
public function handleImagesUnused()
{
$files = $this->getImagesUnused();
$img = new Photos('product');
foreach ($files as $file) {
if ($img->erasePhoto($file['id'])) {
$this->success++;
} else {
$this->failed++;
}
}
}
public function getImagesFiles()
{
global $cfg;
$dir = $cfg['Path']['photos'];
$dirIterator = new RecursiveDirectoryIterator($dir, FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_PATHNAME | FilesystemIterator::SKIP_DOTS);
$dirIterator = new RecursiveCallbackFilterIterator($dirIterator, function ($current, $key, $iterator) use ($dir) {
// Avoid sliders folder
if ($key === $dir.'sliders') {
return false;
}
return true;
});
$files = iterator_to_array(new RecursiveIteratorIterator($dirIterator));
$db_images = sqlQuery('SELECT CONCAT(:dir, source, image_2) AS path FROM `photos`', ['dir' => $dir]);
$db_images = sqlFetchAll($db_images, ['path' => 'path']);
$files = array_filter($files, function ($file) use ($db_images) {
return !isset($db_images[$file]);
});
return $files;
}
public function handleImagesFiles()
{
$files = $this->getImagesFiles();
foreach ($files as $file) {
if (@unlink($file)) {
$this->success++;
} else {
$this->failed++;
}
}
}
public function handleImagesCorrupted()
{
global $cfg;
$db_images = sqlQuery('SELECT CONCAT(:dir, source, image_2) AS path, id FROM `photos`', ['dir' => $cfg['Path']['photos']]);
$corrupted_id = [];
foreach ($db_images as $file) {
if (!file_exists($file['path']) || filesize($file['path']) <= 0/* && wpj_open_image($source) */) {
$corrupted_id[] = $file['id'];
$this->success++;
}
}
if (!empty($corrupted_id)) {
sqlQuery('DELETE FROM `photos` WHERE id IN ('.join(', ', $corrupted_id).')');
}
}
public function getImagesMissingMain()
{
return sqlFetchAll(sqlQuery('SELECT id_product FROM photos_products_relation GROUP BY id_product HAVING MAX(show_in_lead)=\'N\''), 'id_product');
}
public function handleImagesMissingMain()
{
$IDs = $this->getImagesMissingMain();
foreach ($IDs as $id_product => $_) {
Photos::checkLeadPhoto('photos_products_relation', 'id_product', $id_product);
}
}
// Products
public function handleProductsAll()
{
$this->success = $this->deleteSQL('products', ['1' => '1']);
}
public function handleProductsAnnotation()
{
foreach (sqlQuery('SELECT id, short_descr FROM products') as $product) {
$product['short_descr'] = trim(html_entity_decode(strip_tags($product['short_descr'])));
$this->success += $this->updateSQL('products', $product, ['id' => $product['id']], ['id']);
}
if (findModule(\Modules::TRANSLATIONS)) {
foreach (sqlQuery('SELECT id, short_descr FROM products_translations') as $product) {
$product['short_descr'] = trim(html_entity_decode(strip_tags($product['short_descr'])));
$this->success += $this->updateSQL('products_translations', $product, ['id' => $product['id']], ['id']);
}
}
}
public function handleProductsDescriptions()
{
foreach (sqlQuery('SELECT id, short_descr, long_descr FROM products') as $product) {
$product['short_descr'] = trim(html_entity_decode(strip_tags($product['short_descr'])));
$product['long_descr'] = trim(html_entity_decode(strip_tags($product['long_descr'])));
$this->success += $this->updateSQL('products', $product, ['id' => $product['id']], ['id']);
}
}
// Parameters
public function handleParameters()
{
$this->success = $this->deleteSQL('parameters', ['1' => '1']);
}
public function handleParametersValues()
{
$this->success = $this->deleteSQL('parameters_list', ['1' => '1']);
}
// Sections
public function handleSections()
{
$this->success = $this->deleteSQL('sections', ['(id = 0)' => 'FALSE']);
clearCache('sections', true);
}
public function handleSectionsEmpty()
{
$SQL = sqlQuery('DELETE s FROM sections s
LEFT JOIN products_in_sections ps ON ps.id_section = s.id
LEFT JOIN sections_relation sr ON sr.id_topsection = s.id
WHERE ps.id_product IS NULL AND sr.id_section IS NULL
AND s.id <> 0 ');
$this->success = sqlNumRows($SQL);
MenuSectionTree::invalidateCache();
clearCache('sections', true);
}
// Producers
public function handleProducers()
{
$this->success = $this->deleteSQL('producers', ['1' => '1']);
}
// Orders
public function handleOrders()
{
$this->success = $this->deleteSQL('orders', ['1' => '1']);
}
// Articles
public function handleArticles()
{
$this->success = $this->deleteSQL('articles', ['1' => '1']);
}
// Users
public function handleUsers()
{
$this->success = $this->deleteSQL('users', ['1' => '1']);
}
// Variations
public function handleVariationsDuplicates()
{
$duplicates = sqlQuery('SELECT t.id_product, GROUP_CONCAT(t.id ORDER BY t.id SEPARATOR \',\') id_variations
FROM (
SELECT pv.id, pv.id_product, GROUP_CONCAT(pvc.id_value ORDER BY pvc.id_value SEPARATOR \'-\') vals
FROM products_variations pv
JOIN products_variations_combination pvc ON pv.id = pvc.id_variation
GROUP BY pv.id
) AS t
GROUP BY t.id_product, t.vals
HAVING COUNT(*) > 1');
foreach ($duplicates as $duplicate) {
$variations = explode(',', $duplicate['id_variations']);
array_shift($variations);
$this->success += sqlQueryBuilder()->delete('products_variations')->where(\Query\Operator::inIntArray($variations, 'id'))->execute();
}
}
public function handleRegenerateVariationsTitles()
{
Variations::updateTitle();
}
public function handleFulltextUpdate()
{
$fulltext = ServiceContainer::getService(FulltextInterface::class);
$contextManager = ServiceContainer::getService(ContextManager::class);
$languages = $fulltext->getFulltextLanguages();
if ($lang = getVal('fulltext_language')) {
$languages = [$lang => $languages[$lang]];
}
foreach ($languages as $language => $_) {
$contextManager->activateContexts([LanguageContext::class => $language], function () use ($fulltext) {
$fulltext->updateIndex(getVal('fulltext_index'));
$this->success++;
});
}
}
// Reviews
public function handleReviewsImport()
{
$reviews = ServiceContainer::getService(\KupShop\CatalogBundle\Util\ReviewsUtil::class);
$this->success = $reviews->importReviews();
if ($reviews->getError()) {
$this->returnError($reviews->getError());
}
}
public function handleReviewsAutoConfirm()
{
$reviews = ServiceContainer::getService(\KupShop\CatalogBundle\Util\ReviewsUtil::class);
$this->success = $reviews->autoConfirm();
}
public function handleCronFrequent()
{
$this->handleCron('FREQUENT');
}
public function handleCronNormal()
{
$this->handleCron('NORMAL');
}
public function handleCronExpensive()
{
$this->handleCron('EXPENSIVE');
}
protected function handleCron(string $type)
{
/** @var \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher */
$dispatcher = ServiceContainer::getService('event_dispatcher');
$event = new CronEvent($type);
$dispatcher->dispatch($event, constant('KupShop\KupShopBundle\Event\CronEvent::RUN_'.$type));
}
public function handleRunScript()
{
$script = getVal('script');
if ($script) {
ServiceContainer::getService(ScriptLocator::class)->runScript($script, getVal('script_parameters'));
exit("\n============ DONE ==============");
}
}
public function getCounts()
{
global $cfg;
$counts = [];
// Cache
$counts['cacheImages'] = ['count' => count(glob($cfg['Path']['data'].'tmp/*/*/*.*'))];
$counts['cacheSections'] = ['count' => 1];
$counts['cacheTemplates'] = ['count' => count(glob($cfg['Path']['data'].'tmp/templates/*'))];
$counts['cacheStatic'] = ['count' => count(glob($cfg['Path']['data'].'tmp/cache/*'))];
// Photos
$files = $this->getImagesUnused();
$counts['imagesUnused'] = ['count' => count($files), 'links' => array_map(function ($file) {
return "javascript:nw('photos', {$file['id']})";
}, array_slice($files, 0, 10, true) + array_slice($files, -10, 10, true))];
$files = $this->getImagesFiles();
$counts['imagesFiles'] = ['count' => count($files), 'links' => array_slice($files, 0, 10, true) + array_slice($files, -10, 10, true)];
$images = $this->getImagesMissingMain();
$counts['imagesMissingMain'] = ['count' => count($images)];
return $counts;
}
public function get_vars()
{
$vars = parent::get_vars();
if (getVal('calculate')) {
$vars['counts'] = $this->getCounts();
}
$vars['scripts'] = ServiceContainer::getService(ScriptLocator::class)->getScripts();
$indexes = ServiceContainer::getService(FulltextInterface::class)->getIndexTypes();
$vars['fulltext']['indexes'] = array_combine($indexes, $indexes);
return $vars;
}
public function handle()
{
ini_set('max_execution_time', 600);
ini_set('memory_limit', '512M');
parent::handle();
if (getVal('acn') != 'edit' && getVal('acn') != 'add') {
$this->returnOK("Upraveno {$this->success} položek. Selhalo {$this->failed} položek");
}
}
// Do not try to handle exceptions - hides some DB errors
public function handleException($e)
{
throw $e;
}
public function hasRights($name = null)
{
switch ($name) {
case Window::RIGHT_SAVE:
case Window::RIGHT_DELETE:
case Window::RIGHT_DUPLICATE:
return false;
default:
return parent::hasRights($name);
}
}
public function handleDiscounts()
{
$SQL = sqlQuery('DELETE FROM discounts
WHERE condition_type != \'generate_coupon\'');
$this->success = sqlNumRows($SQL);
}
public function handleAllDiscounts()
{
$this->success = $this->deleteSQL('discounts', ['1' => '1']);
}
// Variation labels
public function handleLabels()
{
$this->success = $this->deleteSQL('products_variations_choices_labels', ['1' => '1']);
}
public function handleLabelsEmpty()
{
$SQL = sqlQuery('DELETE vl FROM products_variations_choices_labels vl
LEFT JOIN products_variations_combination pvc ON pvc.id_label = vl.id
WHERE pvc.id_variation IS NULL');
$this->success = sqlNumRows($SQL);
}
}