How to Integrate OpenMage Image Cleaner with External Backup Systems Before Mass Deletions
You can integrate external backup systems with OpenMage Image Cleaner by extending the admin controller, observing the predispatch event, or exporting the orphaned image list to an external script before triggering mass deletion.
OpenMage Image Cleaner is a Magento-compatible module that scans the media folder, identifies orphaned images, and stores their paths in the fb_imagecleaner_image table. Because the module performs permanent filesystem deletions via unlink() without native backup hooks, integrating external backup systems is critical for safe mass deletion workflows.
Understanding the Deletion Risk
The massDeleteAction() method in app/code/community/Fballiano/ImageCleaner/controllers/Adminhtml/FbimagecleanerController.php (lines 48-76) handles bulk deletions by iterating over selected image IDs, resolving their full paths via getMediaDirByEntityTypeId(), and calling unlink() directly:
$image_path = $helper->getMediaDirByEntityTypeId($image['entity_type_id']) . $image["path"];
if (unlink($image_path)) { … }
As noted in the project's README.md (lines 36-38), the module explicitly warns: "Backup your database and files before launching the cleaning process!!!" The module checks file existence but performs no backup automatically, creating the integration requirement.
Method 1: Extend the Admin Controller
The most direct approach is to create a local module that rewrites Fballiano_ImageCleaner_Adminhtml_FbimagecleanerController and overrides massDeleteAction(). This allows you to execute backup logic before delegating to the parent deletion method.
Create app/code/local/YourVendor/ImageCleaner/controllers/Adminhtml/FbimagecleanerController.php:
<?php
class YourVendor_ImageCleaner_Adminhtml_FbimagecleanerController
extends Fballiano_ImageCleaner_Adminhtml_FbimagecleanerController
{
public function massDeleteAction()
{
$ids = $this->getRequest()->getParam('ids');
if ($ids) {
$helper = Mage::helper('fballiano_imagecleaner');
$resource = Mage::getSingleton('core/resource');
$db = $resource->getConnection('core_read');
$cleanerTable = $resource->getTableName('fb_imagecleaner_image');
// ---- BEGIN BACKUP LOGIC ----
$backupRoot = '/var/backups/openmage-images/' . date('Ymd_His') . '/';
foreach ($ids as $imageId) {
$row = $db->fetchRow("SELECT * FROM {$cleanerTable} WHERE image_id=?", $imageId);
if ($row) {
$src = $helper->getMediaDirByEntityTypeId($row['entity_type_id']) . $row['path'];
$dst = $backupRoot . $row['path'];
@mkdir(dirname($dst), 0755, true);
copy($src, $dst);
}
}
// ---- END BACKUP LOGIC ----
}
return parent::massDeleteAction();
}
}
Activate the rewrite in app/etc/modules/YourVendor_ImageCleaner.xml. This approach preserves the original workflow while ensuring every file exists in your backup location before unlink() executes.
Method 2: Observe the Predispatch Event
Magento fires the adminhtml_controller_action_predispatch_fballiano_imagecleaner_adminhtml_fbimagecleaner_massDelete event before any controller logic runs. Attaching an observer to this event lets you backup files without modifying core or community code.
Define the observer in app/code/local/YourVendor/ImageCleaner/etc/events.xml:
<config>
<global>
<events>
<adminhtml_controller_action_predispatch_fballiano_imagecleaner_adminhtml_fbimagecleaner_massDelete>
<observers>
<yourvendor_imagecleaner_backup>
<class>yourvendor_imagecleaner/observer</class>
<method>backupBeforeMassDelete</method>
</yourvendor_imagecleaner_backup>
</observers>
</adminhtml_controller_action_predispatch_fballiano_imagecleaner_adminhtml_fbimagecleaner_massDelete>
</events>
</global>
</config>
Implement the backup logic in app/code/local/YourVendor/ImageCleaner/Model/Observer.php:
<?php
class YourVendor_ImageCleaner_Model_Observer
{
public function backupBeforeMassDelete(Varien_Event_Observer $observer)
{
$controller = $observer->getEvent()->getControllerAction();
$ids = $controller->getRequest()->getParam('ids');
if (!$ids) {
return;
}
$helper = Mage::helper('fballiano_imagecleaner');
$resource = Mage::getSingleton('core/resource');
$db = $resource->getConnection('core_read');
$table = $resource->getTableName('fb_imagecleaner_image');
$backupRoot = '/mnt/backup/openmage/' . date('Ymd_His') . '/';
foreach ($ids as $imageId) {
$row = $db->fetchRow("SELECT * FROM {$table} WHERE image_id=?", $imageId);
if ($row) {
$src = $helper->getMediaDirByEntityTypeId($row['entity_type_id']) . $row['path'];
$dst = $backupRoot . $row['path'];
@mkdir(dirname($dst), 0755, true);
copy($src, $dst);
}
}
}
}
This method intercepts the request early, queries the fb_imagecleaner_image table using the same logic as the original controller, and copies files to external storage before the controller's massDeleteAction() executes.
Method 3: Export and External Script
For environments where code modifications are restricted, use the module's built-in export functionality. The admin grid provides Export CSV and Export Excel actions (handled by exportCsvAction() and exportExcelAction() in the same controller).
- In the admin panel, select orphaned images and click Export CSV.
- Save the exported list to a temporary location.
- Execute a shell script that reads the CSV and copies files to your backup system:
#!/bin/bash
CSV=/tmp/unused_images.csv
BACKUP=/mnt/backup/openmage/$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP"
while IFS=, read -r image_path; do
src=$(php -r "echo Mage::getBaseDir('media').'/'.$image_path;")
dst="$BACKUP/$image_path"
mkdir -p "$(dirname "$dst")"
cp "$src" "$dst"
done < <(tail -n +2 "$CSV")
This approach requires no PHP code changes—only operational discipline to run the script before clicking the mass-delete button in the admin interface.
Summary
- OpenMage Image Cleaner stores orphaned image paths in the
fb_imagecleaner_imagetable and deletes them viaunlink()inmassDeleteAction()without built-in backup functionality. - Controller extension allows you to wrap backup logic around the existing deletion method by rewriting
Fballiano_ImageCleaner_Adminhtml_FbimagecleanerController. - Event observation leverages the
adminhtml_controller_action_predispatch_fballiano_imagecleaner_adminhtml_fbimagecleaner_massDeleteevent to backup files before the controller executes. - Manual export uses the grid's CSV export feature to feed external backup scripts, requiring no codebase modifications.
Frequently Asked Questions
Does OpenMage Image Cleaner include a native backup feature before mass deletion?
No. According to the source code in FbimagecleanerController.php (lines 48-76), the massDeleteAction() method performs unlink() operations immediately after verifying file existence. The README.md explicitly warns administrators to perform manual backups before cleaning, as the module provides no embedded backup hooks.
Which database table stores the orphaned image paths?
The module uses the fb_imagecleaner_image table to store metadata about orphaned images, including image_id, entity_type_id, and path. Both the controller and your custom integration logic should query this table to resolve full filesystem paths using Mage::helper('fballiano_imagecleaner')->getMediaDirByEntityTypeId().
Can I use AWS S3 or remote NFS mounts for the backup destination?
Yes. In the controller extension or observer examples, replace the copy() function with your preferred transfer mechanism—such as aws s3 cp shell commands, PHP SDK calls, or rsync to NFS mounts. The $backupRoot variable can point to any mounted filesystem or temporary staging directory that your backup pipeline monitors.
Is the predispatch event fired before file deletion is confirmed?
Yes. The adminhtml_controller_action_predispatch_fballiano_imagecleaner_adminhtml_fbimagecleaner_massDelete event fires before the controller's massDeleteAction() method executes, guaranteeing that your observer runs before any unlink() calls. However, the observer cannot prevent the deletion if the user proceeds; it only ensures files are copied to backup storage first.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →