Bug in Magento 1.4.1.1: Pagination links missing in tagged product list

August 5, 2012  |  1 Comments  |  by Raj (MagePsycho)  |  Latest, Magento

Today, I noticed one bug in Magento 1.4.1.1: Pagination links (Page: 1, 2, 3 …) were missing in tagged product list.

Pagination bug in tagged product list


After looking into later version of Magento 1.5.x, it was found that issue was due to following missing blocks in ‘tag_product_list’ handle of layout file: app/design/frontend/[interface]/[theme]/layout/tag.xml:

<block type="catalog/product_list_toolbar" name="product_list_toolbar" template="catalog/product/list/toolbar.phtml">
	<block type="page/html_pager" name="product_list_toolbar_pager"/>
</block>
<action method="setToolbarBlockName"><name>product_list_toolbar</name></action>

Existing XML code in Magento 1.4.1.1

<tag_product_list translate="label">
	<label>Tagged Products List</label>
	<!-- Mage_Tag -->
	<reference name="content">
		<block type="tag/product_result" name="tag_products" template="catalogsearch/result.phtml">
			<block type="catalog/product_list" name="search_result_list" template="catalog/product/list.phtml"></block>
			<action method="setListOrders"/>
			<action method="setListModes"/>
			<action method="setListCollection"/>
		</block>
	</reference>
</tag_product_list>

Fixed XML code

<tag_product_list translate="label">
	<label>Tagged Products List</label>
	<!-- Mage_Tag -->
	<reference name="content">
		<block type="tag/product_result" name="tag_products" template="catalogsearch/result.phtml">
			<block type="catalog/product_list" name="search_result_list" template="catalog/product/list.phtml">
				<block type="catalog/product_list_toolbar" name="product_list_toolbar" template="catalog/product/list/toolbar.phtml">
					<block type="page/html_pager" name="product_list_toolbar_pager"/>
				</block>
				<action method="setToolbarBlockName"><name>product_list_toolbar</name></action>
			</block>
			<action method="setListOrders"/>
			<action method="setListModes"/>
			<action method="setListCollection"/>
		</block>
	</reference>
</tag_product_list>

Using above xml code instead, will fix the issue of missing pagination links in tagged product list.

Fixed Pagination

Hope this helps somebody.

Importing regular, special, tier & group prices using ‘Mass Importer Pro: Price Importer’ Extension

July 1, 2012  |  4 Comments  |  by Raj (MagePsycho)  |  Latest, Magento

Recently, the MagePsycho team developed Mass Importer Pro: Price Importer Extension, the fastest price import tool for Magento. It provides for importing/updating different prices, such as regular, special, tier, and group prices. This article will serve as documentation for the Mass Importer Pro: Price Importer Extension.

1. INSTALLATION

0. Before installation, make sure that Cache(System > Cache Management) / Compilation(System > Tools > Compilation) is disabled.
1. After the purchase of the extension, you can immediately download the extension from www.magepsycho.com > My Account > My Downloadable Products.
2. Unzipping it, you will get ‘app’ & ‘var’ folders and ReadMe.txt file.
Upload the ‘app’ & ‘var’ folder to the root of your Magento installation.
Note that upload of ‘var’ folder is optional as it contains some sample csv files only.
4. Installation is done!
5. After installation, you can enable the Cache / Compilation if required.

2. CONFIGURATION

After installation, login to the admin. If you get 404 error page in the extension page then try to logout and re-login.
You will now see the top level menu called ‘Mass Importer Pro’ in the admin:

Mass Importer Pro Menu

Extension Activation

0. Go to ‘Mass Importer Pro’ > ‘Price Importer’ menu, you will see the following form (with disabled ‘Run Import’ button) which means you need to enable the extension and activate it with provided license key.

Disabled Run Import Button


1. Go to ‘Mass Importer Pro’ > ‘Manage Settings’ menu, you will see the following section for configuration:

Configuration Section for Mass Importer Pro: Price Importer


And configure as:
[General Settings]
Enabled = Yes
Domain Type = Production / Development
License Key = [will be provided in the email shortly after the purchase of the extension]

[Price Settings]
Tier Price Import Type = Merge / Replace (Group / All)
Group Price Import Type = Merge / Replace (Group / All)
Re-Index Product Price After Import = Yes / No
[This setting will be used as default for importing prices]
2. Go to ‘Mass Importer Pro’ > ‘Price Importer’ menu, You will see the activated ‘Run Import’ button which means you are ready to go with unlimited price imports.

Enabled / Activated Import Form


3. Configuration is done!

3. IMPORTING PRICES

After the extension gets installed and configured properly, you should be able to import prices using csv file. Before importing, you need to know the supported csv fields and valid data format required for the extension.

Preparing CSV Fields
You can refer to the [Sample CSV File] in order to find all the supported fields, which are:
“sku”,”store_id”,”cost”,”price”,”special_price”,”special_from_date”,”special_to_date”,”website_id”,”tier_price:_all_”
,”tier_price:Wholesale”,”tier_price:[Any-Customer-Group]”,”group_price:Wholesale”,”group_price:[Any-Customer-Group]”

FieldDescription
skuSKU of the product
store_idStore Id which can be found in ‘core_store’ table with ‘store_id’ field, If you want to update for all stores then simply use store_id = 0
costCost
priceRegular/Unit Price
special_priceSpecial Price, If ‘special_from_date’ & ‘special_to_date’ fields are left empty then price is special for unlimited time period
special_from_dateSpecial From Date, Date field should be in [YYYY-mm-dd] format. For example: 2012-06-15
special_to_dateSpecial To Date, Date field should be in [YYYY-mm-dd] format. For example: 2012-08-25
website_idWebsite Id, Used for Website wise tier / group pricing import.
tier_price:_all_Tier price for all groups, Values for tier_price should be in tierQty:tierPrice format separated by semi-colon(;). For example: 10:100;20:90;50:70
If you want to delete the tier price then you can mark it with value ‘x’. For example: 10:x;20:x;50:70
tier_price:[CustomerGroupName]If you want tier price for specific groups then you need to add new column in the csv with name = tier_price:[CustomerGroupName]
For example: tier_price:Wholesale if customer group is Wholesale, tier_price:Retailer if customer group is Retailer.
You can find the customer group name from backend: Customers > Customer Groups.
Values should be in same format as above field(tier_price:_all_).
group_price:[CustomerGroupName]Group Price is new feature available in Magento since 1.7.0.0 version. If you want customer group wise price then you need to add new column in the csv with name = group_price:[CustomerGroupName]
For example: group_price:Wholesale if customer group is Wholesale, group_price:Retailer if customer group is Retailer.
Field value should contain the price only.

Among above fields, only sku is the compulsory field.
You can use the other fields depending on your requirement. You can either leave the column value empty or remove the column itself if you don’t want to update that field.

EDIT
Mass Importer Pro: Price Importer (version > 1.1.0) now supports price export feature in CSV format which reduces the complexity of manual process of CSV fields preparation. Now you can easily export the required price types of the selected products using this extension. Refer the following snapshot for more:

Mass Importer Pro: Export Price Feature

Note: Currently Price Importer supports only the CSV file type, using a comma (,) as delimiter and double quotes(“) as enclosure. We recommend you to use OpenOffice tool for formatting CSV file.

When CSV file is ready, go to ‘Mass Importer Pro’ > ‘Price Updater’ menu and go ahead for importing with the following three steps:
1. Selecting CSV File:
Select the required CSV file from ‘Select File To Import’ dropdown. If required csv file is not in the dropdown list then you can either manually upload via FTP to path ./var/massimporterpro/price_updater/ OR via form using ‘Upload File’ button.

Step 1: Select CSV file to import


2. Configuring Settings:
You can configure the Price Settings from ‘Price Settings’ tab. If not configured the settings will be taken from System > Configuration.

Step 2: Configure Import Settings


3. Hit ‘Run Import’ button
After submitting the form, you will see the message mentioning how many rows were imported, how many rows were skipped etc.

Result Message After Import


To view more information about the import you can go to ‘Import History’ tab and can view the log data after clicking on the ‘View Log’ link, which will give you the data used during import operation and result of success, failure or skipped.

Import History Tab

Import Log Data in readable form

That’s all about installation, configuration & importing regarding Mass Importer Pro: Price Importer extension.
Once you have used the import operation you will find it to be one of the fastest of its type (as it imports/updates thousands of product prices within few seconds). This is not just a claim on my part, the plugin has proven itself.

Happy Importing with Mass Importer Pro: Price Importer!

Usage of image preloading for color swatch operation

June 24, 2012  |  1 Comments  |  by Raj (MagePsycho)  |  jQuery, Latest, Magento

Color Swatch is an often requirement for Magento Stores. You can find lots of Color Swatch Extensions and tutorials in the internet. Most of them are slow in speed in terms of swatching the image . So i am sharing some tips on overcoming that.

You just have to place the following code in app/design/frontend/*/*/catalog/product/view/media.phtml:

<script type="text/javascript">
jQuery(function(){
	<?php
	$count = 1;
	foreach ($this->getGalleryImages() as $_image): ?>
		var image<?php echo $count; ?> = jQuery('<img />').attr('src', '<?php echo $this->helper('catalog/image')->init($this->getProduct(), 'image', $_image->getFile()); ?>');
	<?php
		$count++;
	endforeach;
	?>
});
</script>

Notes: We have used the concept of image preloading using jQuery:

$('<img />').attr('src', $imageUrl)

By Preloading an image means loading an image in browsers cache so that when that image is requested it will be immediately displayed from the cache and hence making the mouseover, swatch operation etc extremely fast.

How to fix the issue: Product images missing in backend but not in frontend

June 22, 2012  |  54 Comments  |  by Raj (MagePsycho)  |  Latest, Magento

Introduction

Recently i have to fix a strange issue for one of my client. Issue was that product images were missing from ‘Images’ tab of product edit form (as depicted below), though they were displaying fine in the frontend.

Missing Product Images In Backend

After going through the table relationship for catalog product images (as shown below), it was found that required rows were missing in table ‘catalog_product_entity_media_gallery’. Note that table ‘catalog_product_entity_media_gallery_value’ is not necessary unless you want to sort or label the product images.

Schema For Product Images

By inspecting the html code of Images tab of product edit form via firebug, it was seen that ‘value’ attribute had empty json:

<input type="hidden" value="[]" name="product[media_gallery][images]" id="media_gallery_content_save">

Further Debugging:
Using template path hints extension for admin: Easy Template Path Hints, it was found that the responsible template and block class were: ‘app/design/adminhtml/default/default/template/catalog/product/helper/gallery.phtml’ and ‘Mage_Adminhtml_Block_Catalog_Product_Helper_Form_Gallery_Content’ respectively.

Looking at the block class and .phtml code it was found that Input element has empty json because of Mage_Adminhtml_Block_Catalog_Product_Helper_Form_Gallery_Content::getImagesJson() method.

When populated the images from table ‘catalog_product_entity_varchar’ to ‘catalog_product_entity_media_gallery’ for those products, products images were seen at the backend(refer to the code below for more info).
Here goes the steps for fixing:

Steps

1> Prepare CSV file(update_missing_images.csv) with only one field: sku and upload to the root of Magento installation.
Note: This will contain the sku of those products whose images are missing at the backend.

2> Create a file: update_missing_images.php and upload to the root of magento installation and paste the following code:

<?php
/**
 * @author		MagePsycho <info@magepsycho.com>
 * @website		http://www.magepsycho.com
 * @category	Export / Import
 */
$mageFilename = 'app/Mage.php';
require_once $mageFilename;
Mage::setIsDeveloperMode(true);
ini_set('display_errors', 1);
umask(0);
Mage::app('admin');
Mage::register('isSecureArea', 1);
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);

set_time_limit(0);
ini_set('memory_limit','1024M');

/***************** UTILITY FUNCTIONS ********************/
function _log($message, $file = 'update_missing_images.log'){
	Mage::log($message, null, $file);
}

function _getIndex($field) {
	global $fields;
	$result = array_search($field, $fields);
	if($result === false){
		$result = -1;
	}
	return $result;
}

function _getConnection($type = 'core_read'){
	return Mage::getSingleton('core/resource')->getConnection($type);
}

function _getTableName($tableName){
	return Mage::getSingleton('core/resource')->getTableName($tableName);
}

function _getAttributeId($attribute_code = 'price'){
	$connection = _getConnection('core_read');
	$sql = "SELECT attribute_id
				FROM " . _getTableName('eav_attribute') . "
			WHERE
				entity_type_id = ?
				AND attribute_code = ?";
	$entity_type_id = _getEntityTypeId();
	return $connection->fetchOne($sql, array($entity_type_id, $attribute_code));
}

function _getEntityTypeId($entity_type_code = 'catalog_product'){
	$connection = _getConnection('core_read');
	$sql		= "SELECT entity_type_id FROM " . _getTableName('eav_entity_type') . " WHERE entity_type_code = ?";
	return $connection->fetchOne($sql, array($entity_type_code));
}

function _getIdFromSku($sku){
	$connection = _getConnection('core_read');
	$sql		= "SELECT entity_id FROM " . _getTableName('catalog_product_entity') . " WHERE sku = ?";
	return $connection->fetchOne($sql, array($sku));
}

function _checkIfSkuExists($sku){
	$connection = _getConnection('core_read');
	$sql		= "SELECT COUNT(*) AS count_no FROM " . _getTableName('catalog_product_entity') . "	WHERE sku = ?";
	$count		= $connection->fetchOne($sql, array($sku));
	if($count > 0){
		return true;
	}else{
		return false;
	}
}

function _checkIfRowExists($productId, $attributeId, $value){
	$tableName  = _getTableName('catalog_product_entity_media_gallery');
	$connection = _getConnection('core_read');
	$sql		= "SELECT COUNT(*) AS count_no FROM " . _getTableName($tableName) . " WHERE entity_id = ? AND attribute_id = ?  AND value = ?";
	$count		= $connection->fetchOne($sql, array($productId, $attributeId, $value));
	if($count > 0){
		return true;
	}else{
		return false;
	}
}

function _insertRow($productId, $attributeId, $value){
	$connection				= _getConnection('core_write');
	$tableName				= _getTableName('catalog_product_entity_media_gallery');

	$sql = "INSERT INTO " . $tableName . " (attribute_id, entity_id, value) VALUES (?, ?, ?)";
	$connection->query($sql, array($attributeId, $productId, $value));
}

function _updateMissingImages($count, $productId, $data){
	$connection				= _getConnection('core_read');
	$smallImageId			= _getAttributeId('small_image');
	$imageId				= _getAttributeId('image');
	$thumbnailId			= _getAttributeId('thumbnail');
	$mediaGalleryId			= _getAttributeId('media_gallery');

	//getting small, base, thumbnail images from catalog_product_entity_varchar for a product
	$sql	= "SELECT * FROM " . _getTableName('catalog_product_entity_varchar') . " WHERE attribute_id IN (?, ?, ?) AND entity_id = ? AND `value` != 'no_selection'";
	$rows	= $connection->fetchAll($sql, array($imageId, $smallImageId, $thumbnailId, $productId));
	if(!empty($rows)){
		foreach($rows as $_image){
			//check if that images exist in catalog_product_entity_media_gallery table or not
			if(!_checkIfRowExists($productId, $mediaGalleryId, $_image['value'])){
				//insert that image in catalog_product_entity_media_gallery if it doesn't exist
				_insertRow($productId, $mediaGalleryId, $_image['value']);
				/* Output / Logs */
				$missingImageUpdates = $count . '> Updated:: $productId=' . $productId . ', $image=' . $_image['value'];
				echo $missingImageUpdates.'<br />';
				_log($missingImageUpdates);
			}
		}
		$separator = str_repeat('=', 100);
		_log($separator);
		echo $separator . '<br />';
	}
}
/***************** UTILITY FUNCTIONS ********************/

$messages			= array();
$csv				= new Varien_File_Csv();
$data				= $csv->getData('update_missing_images.csv'); //path to csv
$fields				= array_shift($data);
#print_r($fields); print_r($data); exit;

$message = '<hr />';
$count   = 1;
foreach($data as $_data){
	$sku									= isset($_data[_getIndex('sku')]) ? trim($_data[_getIndex('sku')]) : '';
	if(_checkIfSkuExists($sku)){
		try{
			$productId = _getIdFromSku($sku);
			_updateMissingImages($count, $productId, $_data);
			$message .= $count . '> Success:: While Updating Images of Sku (' . $sku . '). <br />';

		}catch(Exception $e){
			$message .=  $count .'> Error:: While Upating Images of Sku (' . $sku . ') => '.$e->getMessage().'<br />';
		}
	}else{
		$message .=  $count .'> Error:: Product with Sku (' . $sku . ') does\'t exist.<br />';
	}
	$count++;
}
echo $message;

3> Open your browser and run the following url:
http://your-magento-url/update_missing_images.php

4> That’s all. Try to browse the Images tab, now you will see those missing images.

Hope this helps somebody!

How to find current url in Magento

May 27, 2012  |  6 Comments  |  by Raj (MagePsycho)  |  Latest, Magento

You must be wondering we can simply use:

$currentUrl = Mage::helper('core/url')->getCurrentUrl()

or

$currentUrl = Mage::getUrl('*/*/*', array('_current' => true));

in order to find the current url.

But it does’t always work as expected.
Try to use the above code in a page which has some missing css, js, images etc.(You can use Firebug in order to find such error which are so called Network error.). You will see that above code captures the url for missing file(js, css, images etc.) which is obviously not the required current url.

You can simply fix above error by using the following code:

if (!in_array(Mage::app()->getFrontController()->getAction()->getFullActionName(), array('cms_index_noRoute', 'cms_index_defaultNoRoute'))) {
	$currentUrl = Mage::helper('core/url')->getCurrentUrl();
}

Cheers!