-
-
Save LuizSantos1/0b8ba118f35479ec3d439657125f0bb8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | |
<title>Remove Magento's orphan images web console</title> | |
<link href='https://fonts.googleapis.com/css?family=Open+Sans+Condensed:300,700' rel='stylesheet' type='text/css'> | |
<link href='https://fonts.googleapis.com/css?family=Inconsolata:400,700&subset=latin,latin-ext' rel='stylesheet' | |
type='text/css'> | |
<style type="text/css"> | |
body { | |
font-family: "Open Sans", Arial, sans-serif; | |
margin-left: 25px; | |
margin-right: 30px; | |
padding: 0 0 0 0; | |
} | |
pre { | |
font-family: Inconsolata, "Courier New", monospace; | |
font-size: 15px; | |
clear: both; | |
margin: 0 -0px 0 0; | |
background: #000; | |
border: 1px groove #ccc; | |
color: #ccc; | |
display: block; | |
width: 100%; | |
min-height: 600px; | |
padding: 5px 5px 5px 5px; | |
} | |
.logo { | |
float: left; | |
margin-right: 25px;; | |
} | |
.scriptInfo { | |
float: left; | |
} | |
h1 { | |
font-size: 16px; | |
margin-top: 0; | |
line-height: 49px; | |
margin-bottom: 0; | |
} | |
h1 span { | |
font-family: Inconsolata, "Courier New", monospace; | |
} | |
.header { | |
width: 100%; | |
height: auto; | |
display: table; | |
margin-top: 25px; | |
margin-bottom: 25px;; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="header"> | |
<div class="logo"> | |
<img src="skin/frontend/default/modern/images/logo.gif" alt="Magento's Logo" width="173" height="49"> | |
</div> | |
<div class="scriptInfo"> | |
<h1>Delete Catalog Orphan Images <span>1.1</span></h1> | |
</div> | |
</div> | |
<pre> | |
<?php | |
require 'app/Mage.php'; | |
/** | |
* NOTE: Set this to false if you want to actually delete files. | |
*/ | |
$dryRun = true; | |
$job = new OrphanImagesCleaner(); | |
$job->run($dryRun); | |
/** | |
* Class OrphanImagesCleaner | |
* | |
* @author Jeroen Boersma <[email protected]> | |
* @author Adriano Cataluddi <[email protected]> | |
*/ | |
class OrphanImagesCleaner | |
{ | |
/** | |
* @var bool | |
*/ | |
protected $dryRun = true; | |
/** | |
* Executes the tools | |
* @param bool $dryRun | |
*/ | |
public function run($dryRun = true) | |
{ | |
$this->dryRun = $dryRun; | |
$duplicateCount = 0; | |
$duplicateTotalSize = 0; | |
$orphansCount = 0; | |
$orphansTotalSize = 0; | |
if (!Mage::isInstalled()) { | |
$this->wl('Application is not installed yet, please complete install wizard first.'); | |
exit; | |
} | |
set_time_limit(0); | |
session_write_close(); | |
umask(0); | |
Mage::app('admin')->setUseSessionInUrl(false); | |
$mediaPath = $this->getRootPath() . '/media/catalog/product'; | |
if ($this->dryRun) | |
$this->wl('DRY RUN mode: the script will NOT modify any file or record.'); | |
$this->wl(); | |
$this->wl(' Magento release: ' . Mage::getVersion()); | |
$this->wl(' Media path: ' . $mediaPath); | |
$this->wl(); | |
$this->wl(); | |
$this->wl('[Phase 1/2] Looking for duplicate products images...'); | |
$this->wl(str_repeat('-', 80)); | |
$connection = Mage::getSingleton('core/resource') | |
->getConnection('core_write'); | |
$sql = 'select distinct ' | |
. 'cp.entity_id, ' | |
. 'cpg.value_id, ' | |
. 'cpv.value as default_value, ' | |
. 'cpg.value ' | |
. 'from catalog_product_entity as cp ' | |
. 'join catalog_product_entity_varchar as cpv on cp.entity_id = cpv.entity_id ' | |
. 'join catalog_product_entity_media_gallery as cpg on cp.entity_id = cpg.entity_id ' | |
. 'WHERE ' | |
. 'cpv.attribute_id in(select attribute_id from eav_attribute where frontend_input = \'media_image\') ' | |
. 'and ' | |
. 'cpv.value != cpg.value;'; | |
$results = $connection->fetchAll($sql); | |
$this->wl(sprintf('Found %s items to process.', sizeof($results)), true); | |
$lastEntityId = null; | |
$origSums = array(); | |
foreach ($results as $row) { | |
if ($row['entity_id'] != $lastEntityId) { | |
$lastEntityId = $row['entity_id']; | |
$origSums = array(); | |
} | |
$origFile = $mediaPath . $row['default_value']; | |
if (!file_exists($origFile)) { | |
continue; | |
} | |
$file = $mediaPath . $row['value']; | |
if (file_exists($file)) { | |
if (!isset($origSums[$origFile])) { | |
$origSums[$origFile] = md5_file($origFile); | |
} | |
$sum = md5_file($file); | |
if (!in_array($sum, $origSums)) { | |
$origSums[$file] = $sum; | |
} | |
else { | |
$this->wl(sprintf('Deleting image "%s" (#%s)', $file, $row['entity_id']), true); | |
$duplicateCount++; | |
$duplicateTotalSize += filesize($file); | |
if (!$this->dryRun) unlink($file); | |
} | |
} | |
if (!file_exists($file)) { | |
$this->wl(sprintf('Deleting record for "%s" (#%s)', $file, $row['entity_id']), true); | |
$deleteSql = 'delete from catalog_product_entity_media_gallery where value_id = ' . $row['value_id'] . ';'; | |
if (!$this->dryRun) $connection->query($deleteSql); | |
} | |
} | |
// Find files on filesystem which aren't listed in the database | |
$this->wl(); | |
$this->wl('[Phase 1/2] Finding files on filesystem which aren\'t listed in the database...'); | |
$this->wl(str_repeat('-', 80)); | |
$files = glob($mediaPath . '/[A-z0-9]/*/*'); | |
foreach ($files as $file) { | |
$searchFile = str_replace($mediaPath, '', $file); | |
// Lookup | |
$mediaSql = "select count(*) as records from catalog_product_entity_media_gallery where value = '{$searchFile}'"; | |
$mediaCount = $connection->fetchOne($mediaSql); | |
if ($mediaCount < 1) { | |
$orphansCount++; | |
$orphansTotalSize += filesize($file); | |
$this->wl(sprintf('Deleting image "%s"', $file), true); | |
if (!$this->dryRun) unlink($file); | |
} | |
} | |
$this->wl(); | |
$this->wl('Done.'); | |
$this->wl(str_repeat('-', 80)); | |
$this->wl(sprintf(' Total duplicate images: %s (%s)', $duplicateCount, $this->formatBytes($duplicateTotalSize))); | |
$this->wl(sprintf(' Total orphan images: %s (%s)', $orphansCount, $this->formatBytes($orphansTotalSize))); | |
$this->wl(str_repeat('-', 80)); | |
} | |
/** | |
* @param boolean $dryRun | |
*/ | |
public function setDryRunEnabled($dryRun) | |
{ | |
$this->dryRun = $dryRun; | |
} | |
/** | |
* @return boolean | |
*/ | |
public function isDryRunEnabled() | |
{ | |
return $this->dryRun; | |
} | |
/** | |
* Writes a line in console. | |
* @param $line | |
* @param bool $notifyDryRun | |
*/ | |
protected function wl($line = null, $notifyDryRun = false) | |
{ | |
($notifyDryRun && $this->dryRun && ($line !== null)) ? | |
$dryLabel = 'DRY RUN | ' : | |
$dryLabel = ''; | |
print $dryLabel . $line . "\n"; | |
} | |
/** | |
* Returns the script root path | |
* @return string | |
*/ | |
protected function getRootPath() | |
{ | |
return (dirname(__FILE__)); | |
} | |
/** | |
* Format bytes | |
* @author MrCaspan (https://github.com/MrCaspan) | |
* @param $bytes | |
* @return string | |
*/ | |
protected function formatBytes($bytes) | |
{ | |
$i = floor(log($bytes, 1024)); | |
return round($bytes / pow(1024, $i), [0, 0, 2, 2, 3][$i]) . ['B', 'kB', 'MB', 'GB', 'TB'][$i]; | |
} | |
} | |
?> | |
</pre> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment