Index: branches/5.0.x/units/products/products_event_handler.php
===================================================================
--- branches/5.0.x/units/products/products_event_handler.php	(revision 13285)
+++ branches/5.0.x/units/products/products_event_handler.php	(revision 13286)
@@ -1,1292 +1,1316 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Commerce
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license	Commercial License
 * This software is protected by copyright law and international treaties.
 * Unauthorized reproduction or unlicensed usage of the code of this program,
 * or any portion of it may result in severe civil and criminal penalties,
 * and will be prosecuted to the maximum extent possible under the law
 * See http://www.in-portal.org/commercial-license for copyright notices and details.
 */
 
 defined('FULL_PATH') or die('restricted access!');
 
 class ProductsEventHandler extends kCatDBEventHandler {
 
 	/**
 	 * Allows to override standart permission mapping
 	 *
 	 */
 	function mapPermissions()
 	{
 		parent::mapPermissions();
 		$permissions = Array(
 								// front
 								'OnCancelAction'		=>	Array('self' => true),
 								'OnRateProduct'			=>	Array('self' => true),
 								'OnClearRecent'			=>	Array('self' => true),
 								'OnRecommendProduct'	=>	Array('self' => true),
 
 								// admin
 								'OnQtyAdd'			=>	Array('self' => 'add|edit'),
 								'OnQtyRemove'		=>	Array('self' => 'add|edit'),
 								'OnQtyOrder'		=>	Array('self' => 'add|edit'),
 								'OnQtyReceiveOrder'	=>	Array('self' => 'add|edit'),
 								'OnQtyCancelOrder'	=>	Array('self' => 'add|edit'),
 
 						);
 		$this->permMapping = array_merge($this->permMapping, $permissions);
 	}
 
 	function mapEvents()
 	{
 		parent::mapEvents();	// ensure auto-adding of approve/decine and so on events
 		$product_events = Array(	'OnQtyAdd'=>'InventoryAction',
 									'OnQtyRemove'=>'InventoryAction',
 									'OnQtyOrder'=>'InventoryAction',
 									'OnQtyReceiveOrder'=>'InventoryAction',
 									'OnQtyCancelOrder'=>'InventoryAction',);
 
 		$this->eventMethods = array_merge($this->eventMethods, $product_events);
 	}
 
 	/**
 	 * Sets default processing data for subscriptions
 	 *
 	 * @param kEvent $event
 	 */
 	function OnBeforeItemCreate(&$event)
 	{
 		$object =& $event->getObject();
 
 		$product_approve_events = Array(2 => 'p:OnSubscriptionApprove',
 										4 => 'p:OnDownloadableApprove',
 										5 => 'p:OnPackageApprove');
 
 		$product_type = $object->GetDBField('Type');
 
 		$type_found = in_array($product_type, array_keys($product_approve_events) );
 
 		if($type_found && !$object->GetDBField('ProcessingData') )
 		{
 			$processing_data = Array('ApproveEvent' => $product_approve_events[$product_type] );
 			$object->SetDBField( 'ProcessingData', serialize($processing_data) );
 		}
 	}
 
 	/**
 	 * Process product count manipulations
 	 *
 	 * @param kEvent $event
 	 * @access private
 	 */
 	function InventoryAction(&$event)
 	{
 		$object =& $event->getObject();
 
 		$object->SetFieldsFromHash( $this->getSubmittedFields($event) );
 
 		if ($object->GetDBField('InventoryStatus') == 2) {
 			// inventory by options (use first selected combination in grid)
 			$combination_id = array_shift( array_keys( $this->Application->GetVar('poc_grid') ) );
 		}
 		else {
 			// inventory by product
 			$combination_id = 0;
 		}
 
 		// save id of selected option combination & preselect it in grid
 		$this->Application->SetVar('combination_id', $combination_id);
 
 		$this->ScheduleInventoryAction($event->Name, $object->GetId(), $object->GetDBField('Qty'), $combination_id);
 
 		$object->Validate();
 
 		if (!isset($object->FieldErrors['Qty']['pseudo'])){
 			$this->modifyInventory($event->Name, $object, $object->GetDBField('Qty'), $combination_id);
 		}
 
 		$object->SetDBField('Qty', null);
 		$event->redirect = false;
 	}
 
 	/**
 	 * Perform inventory action on supplied object
 	 *
 	 * @param string $action event name which is actually called by user
 	 * @param ProductsItem $product
 	 * @param int $qty
 	 * @param int $combination_id
 	 */
 	function modifyInventory($action, &$product, $qty, $combination_id)
 	{
 		if ($product->GetDBField('InventoryStatus') == 2) {
 			// save inventory changes to option combination instead of product
 			$object =& $this->Application->recallObject('poc.-item', null, Array('skip_autoload' => true));
 			$object->Load($combination_id);
 		}
 		elseif ($combination_id > 0) {
 			// combination id present, but not inventory by combinations => skip
 			return false;
 		}
 		elseif ($product->GetDBField('InventoryStatus') == 1) {
 			// save inventory changes to product
 			$object =& $product;
 		}
 		else {
 			// product has inventory actions, but don't use inventory => skip
 			return false;
 		}
 
 		if (!$object->isLoaded()) {
 			// product/combination in action doesn't exist in database by now
 			return false;
 		}
 
 		switch ($action) {
 			case 'OnQtyAdd':
 				$object->SetDBField('QtyInStock', $object->GetDBField('QtyInStock') + $qty);
 				break;
 
 			case 'OnQtyRemove':
 				if ($object->GetDBField('QtyInStock') < $qty) {
 					$qty = $object->GetDBField('QtyInStock');
 				}
 				$object->SetDBField('QtyInStock', $object->GetDBField('QtyInStock') - $qty);
 				break;
 
 			case 'OnQtyOrder':
 				$object->SetDBField('QtyOnOrder', $object->GetDBField('QtyOnOrder') + $qty);
 				break;
 
 			case 'OnQtyReceiveOrder':
 				$object->SetDBField('QtyOnOrder', $object->GetDBField('QtyOnOrder') - $qty);
 				$object->SetDBField('QtyInStock', $object->GetDBField('QtyInStock') + $qty);
 				break;
 
 			case 'OnQtyCancelOrder':
 				$object->SetDBField('QtyOnOrder', $object->GetDBField('QtyOnOrder') - $qty);
 				break;
 		}
 
 		return $object->Update();
 	}
 
 	function ScheduleInventoryAction($action, $prod_id, $qty, $combination_id = 0)
 	{
 		$inv_actions = $this->Application->RecallVar('inventory_actions');
 		if (!$inv_actions) {
 			$inv_actions = Array();
 		}
 		else {
 			$inv_actions = unserialize($inv_actions);
 		}
 
 		array_push($inv_actions, Array('action' => $action, 'product_id' => $prod_id, 'combination_id' => $combination_id, 'qty' => $qty));
 
 		$this->Application->StoreVar('inventory_actions', serialize($inv_actions));
 	}
 
 	function RealInventoryAction($action, $prod_id, $qty, $combination_id)
 	{
 		$product =& $this->Application->recallObject('p.liveitem', null, Array('skip_autoload' => true));
 		$product->SwitchToLive();
 		$product->Load($prod_id);
 
 		$this->modifyInventory($action, $product, $qty, $combination_id);
 	}
 
 	function RunScheduledInventoryActions(&$event)
 	{
 		$inv_actions = $this->Application->GetVar('inventory_actions');
 		if (!$inv_actions) {
 			return;
 		}
 		$inv_actions = unserialize($inv_actions);
 
 		$products = array();
 		foreach($inv_actions as $an_action) {
 			$this->RealInventoryAction($an_action['action'], $an_action['product_id'], $an_action['qty'], $an_action['combination_id']);
 			array_push($products, $an_action['product_id'].'_'.$an_action['combination_id']);
 		}
 
 		$products = array_unique($products);
 		if ($products) {
 			$product_obj =& $this->Application->recallObject('p.liveitem', null, Array('skip_autoload' => true));
 			$product_obj->SwitchToLive();
 			foreach ($products as $product_key) {
 				list($prod_id, $combination_id) = explode('_', $product_key);
 			$product_obj->Load($prod_id);
 				$this->FullfillBackOrders($product_obj, $combination_id);
 			}
 		}
 	}
 
 	/**
 	 * In case if products arrived into inventory and they are required by old (non processed) orders, then use them (products) in that orders
 	 *
 	 * @param ProductsItem $product
 	 * @param int $combination_id
 	 */
 	function FullfillBackOrders(&$product, $combination_id)
 	{
 		if ( !$this->Application->ConfigValue('Comm_Process_Backorders_Auto') ) return;
 
 		if ($combination_id && ($product->GetDBField('InventoryStatus') == 2)) {
 			// if combination id present and inventory by combinations
 			$poc_idfield = $this->Application->getUnitOption('poc', 'IDField');
 			$poc_tablename = $this->Application->getUnitOption('poc', 'TableName');
 			$sql = 'SELECT QtyInStock
 					FROM '.$poc_tablename.'
 					WHERE '.$poc_idfield.' = '.$combination_id;
 			$stock_qty = $this->Conn->GetOne($sql);
 		}
 		else {
 			// inventory by product
 			$stock_qty = $product->GetDBField('QtyInStock');
 		}
 
 		$qty = (int) $stock_qty - $product->GetDBField('QtyInStockMin');
 		$prod_id = $product->GetID();
 		if ($prod_id <= 0 || !$prod_id || $qty <= 0) return;
 
 		//selecting up to $qty backorders with $prod_id where full qty is not reserved
 		$query = 'SELECT '.TABLE_PREFIX.'Orders.OrderId
 							FROM '.TABLE_PREFIX.'OrderItems
 					LEFT JOIN '.TABLE_PREFIX.'Orders ON '.TABLE_PREFIX.'Orders.OrderId = '.TABLE_PREFIX.'OrderItems.OrderId
 					WHERE (ProductId = '.$prod_id.') AND (Quantity > QuantityReserved) AND (Status = '.ORDER_STATUS_BACKORDERS.')
 							GROUP BY '.TABLE_PREFIX.'Orders.OrderId
 							ORDER BY OrderDate ASC
 							LIMIT 0,'.$qty; //assuming 1 item per order - minimum possible
 
 		$orders = $this->Conn->GetCol($query);
 
 		if (!$orders) return;
 
 		$order =& $this->Application->recallObject('ord.-inv', null, Array('skip_autoload' => true));
 		foreach ($orders as $ord_id) {
 			$order->Load($ord_id);
 
 			$email_event_admin =& $this->Application->EmailEventAdmin('BACKORDER.FULLFILL');
 
 			//reserve what's possible in any case
 			$this->Application->HandleEvent( $event, 'ord:OnReserveItems' );
 			if ($event->status == erSUCCESS) { //
 				//in case the order is ready to process - process it
 				$this->Application->HandleEvent( $event, 'ord:OnOrderProcess' );
 			}
 		}
 	}
 
 	function OnBeforeDeleteFromLive(&$event)
 	{
 		$id = $event->getEventParam('id');
 		$product =& $this->Application->recallObject($event->Prefix.'.itemlive', null, Array('skip_autoload' => true));
 		$product->SwitchToLive();
 		if (!$product->Load($id)) return ; // this will make sure New product will not be overwritten with empty data
 
 		$temp =& $this->Application->recallObject($event->Prefix.'.itemtemp', null, Array('skip_autoload' => true));
 		$temp->SwitchToTemp();
 		$temp->Load($id);
 
 		$temp->SetDBFieldsFromHash($product->FieldValues, Array('QtyInStock','QtyReserved','QtyBackOrdered','QtyOnOrder'));
 		$temp->Update();
 	}
 
 	function clearSelectedIDs(&$event)
 	{
 		parent::clearSelectedIDs($event);
 		$this->Application->SetVar('inventory_actions', $this->Application->RecallVar('inventory_actions'));
 		$this->Application->RemoveVar('inventory_actions');
 	}
 
 	function OnSave(&$event)
 	{
 		$res = parent::OnSave($event);
 		if ($event->status == erSUCCESS) {
 			$this->RunScheduledInventoryActions($event);
 		}
 		return $res;
 	}
 
 	function OnPreCreate(&$event)
 	{
 		parent::onPreCreate($event);
 		$object =& $event->GetObject();
 		$object->SetDBField('Type', $this->Application->GetVar( $event->getPrefixSpecial(true).'_new_type' ));
 	}
 
 
 	function OnPreSaveAndGo(&$event) {
 
 		$event->CallSubEvent('OnPreSave');
 		$this->LoadItem($event);
 		$object =& $event->getObject();
 		$from_type = $object->GetDBField('Type');
 		if ($event->status==erSUCCESS) {
 			$this->Application->SetVar($event->Prefix_Special.'_id', $this->Application->GetVar($event->getPrefixSpecial(true).'_GoId'));
 			$this->LoadItem($event);
 			$to_type = $object->GetDBField('Type');
 
 			if ($from_type != $to_type) {
 				$from_tabs = $this->GetTabs($from_type);
 				$from_tab_i = array_search($this->Application->GetVar('t'), $from_tabs);
 
 				$to_tabs = $this->GetTabs($to_type);
 				$to_tab = $this->Application->GetVar('t');
 
 				$found = false;
 				while ( !isset($to_tabs[$from_tab_i]) && $from_tab_i < count($to_tabs)) {
 					$from_tab_i++;
 				}
 				if ( !isset($to_tabs[$from_tab_i]) ) $from_tab_i = 0;
 				$to_tab = $to_tabs[$from_tab_i];
 
 				$event->redirect = $to_tab;
 			}
 		}
 	}
 
 	function GetTabs($type)
 	{
 		switch($type)
 		{
 			case 1:
 				return Array(
 					0 => 'in-commerce/products/products_edit',
 					1 => 'in-commerce/products/products_inventory',
 					2 => 'in-commerce/products/products_pricing',
 					3 => 'in-commerce/products/products_categories',
 					4 => 'in-commerce/products/products_images',
 					5 => 'in-commerce/products/products_reviews',
 					6 => 'in-commerce/products/products_custom',
 				);
 
 			case 2:
 				return Array(
 					0 => 'in-commerce/products/products_edit',
 					1 => 'in-commerce/products/products_access',
 					/*2 => 'in-commerce/products/products_access_pricing',*/
 					3 => 'in-commerce/products/products_categories',
 					4 => 'in-commerce/products/products_images',
 					5 => 'in-commerce/products/products_reviews',
 					6 => 'in-commerce/products/products_custom',
 				);
 
 			case 3:
 				return Array(
 					0 => 'in-commerce/products/products_edit',
 
 					2 => 'in-commerce/products/products_access_pricing',
 					3 => 'in-commerce/products/products_categories',
 					4 => 'in-commerce/products/products_images',
 					5 => 'in-commerce/products/products_reviews',
 					6 => 'in-commerce/products/products_custom',
 				);
 
 			case 4:
 				return Array(
 					0 => 'in-commerce/products/products_edit',
 
 					2 => 'in-commerce/products/products_files',
 					3 => 'in-commerce/products/products_categories',
 					4 => 'in-commerce/products/products_images',
 					5 => 'in-commerce/products/products_reviews',
 					6 => 'in-commerce/products/products_custom',
 				);
 		}
 	}
 
 	/**
 	 * Return type clauses for list bulding on front
 	 *
 	 * @param kEvent $event
 	 * @return Array
 	 */
 	function getTypeClauses(&$event)
 	{
 		$types=$event->getEventParam('types');
 		$except_types=$event->getEventParam('except');
 		$object =& $event->getObject();
 		$type_clauses = parent::getTypeClauses($event);
 
 		$type_clauses['featured']['include']='%1$s.Featured=1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1';
 		$type_clauses['featured']['except']='%1$s.Featured!=1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1';
 		$type_clauses['featured']['having_filter']=false;
 
 		$type_clauses['onsale']['include']='%1$s.OnSale=1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1';
 		$type_clauses['onsale']['except']='%1$s.OnSale!=1 AND '.TABLE_PREFIX.'CategoryItems.PrimaryCat = 1';
 		$type_clauses['onsale']['having_filter']=false;
 
 		// products from selected manufacturer: begin
 		$manufacturer = $event->getEventParam('manufacturer');
 		if ( !$manufacturer ) {
 			$manufacturer = $this->Application->GetVar('manuf_id');
 		}
 		if ( $manufacturer ) {
 			$type_clauses['manufacturer']['include'] = '%1$s.ManufacturerId='.$manufacturer.' AND PrimaryCat = 1';
 			$type_clauses['manufacturer']['except'] = '%1$s.ManufacturerId!='.$manufacturer.' AND PrimaryCat = 1';
 			$type_clauses['manufacturer']['having_filter'] = false;
 		}
 		// products from selected manufacturer: end
 
 		// recent products: begin
 		$recent = $this->Application->RecallVar('recent_products');
 		if ($recent) {
 			$recent = unserialize($recent);
 			$type_clauses['recent']['include'] = '%1$s.ProductId IN ('.implode(',', $recent).') AND PrimaryCat = 1';
 			$type_clauses['recent']['except'] = '%1$s.ProductId NOT IN ('.implode(',', $recent).') AND PrimaryCat = 1';
 		}
 		else {
 			$type_clauses['recent']['include']='0';
 			$type_clauses['recent']['except']='1';
 		}
 		$type_clauses['recent']['having_filter']=false;
 		// recent products: end
 
 		// products already in shopping cart: begin
 		if (strpos($types, 'in_cart') !== false || strpos($except_types, 'in_cart') !== false) {
 			$order_id = $this->Application->RecallVar('ord_id');
 			if ($order_id) {
 				$in_cart = $this->Conn->GetCol('SELECT ProductId FROM '.TABLE_PREFIX.'OrderItems WHERE OrderId = '.$order_id);
 				if ($in_cart) {
 					$type_clauses['in_cart']['include'] = '%1$s.ProductId IN ('.implode(',', $in_cart).') AND PrimaryCat = 1';
 					$type_clauses['in_cart']['except'] = '%1$s.ProductId NOT IN ('.implode(',', $in_cart).') AND PrimaryCat = 1';
 				}
 				else {
 					$type_clauses['in_cart']['include']='0';
 					$type_clauses['in_cart']['except']='1';
 				}
 			}
 			else {
 				$type_clauses['in_cart']['include']='0';
 				$type_clauses['in_cart']['except']='1';
 			}
 			$type_clauses['in_cart']['having_filter']=false;
 		}
 		// products already in shopping cart: end
 
 		// my downloadable products: begin
 		if (strpos($types, 'my_downloads') !== false || strpos($except_types, 'my_downloads') !== false)
 		{
 			$user_id = $this->Application->RecallVar('user_id');
 			$my_downloads = ($user_id > 0) ? $this->Conn->GetCol('SELECT ProductId FROM '.TABLE_PREFIX.'UserFileAccess WHERE PortalUserId = '.$user_id) : false;
 			if ($my_downloads)
 			{
 				$type_clauses['my_downloads']['include'] = '%1$s.ProductId IN ('.implode(',', $my_downloads).') AND PrimaryCat = 1';
 				$type_clauses['my_downloads']['except'] = '%1$s.ProductId NOT IN ('.implode(',', $my_downloads).') AND PrimaryCat = 1';
 			}
 			else
 			{
 				$type_clauses['my_downloads']['include'] = '0';
 				$type_clauses['my_downloads']['except'] = '1';
 			}
 
 			$type_clauses['my_downloads']['having_filter'] = false;
 		}
 		// my downloadable products: end
 
 		// my favorite products: begin
 		if (strpos($types, 'wish_list') !== false || strpos($except_types, 'wish_list') !== false) {
 			$sql = 'SELECT ResourceId FROM '.$this->Application->getUnitOption('fav', 'TableName').'
 					WHERE PortalUserId = '.(int)$this->Application->RecallVar('user_id');
 			$wishlist_ids = $this->Conn->GetCol($sql);
 			if ($wishlist_ids) {
 				$type_clauses['wish_list']['include'] = '%1$s.ResourceId IN ('.implode(',', $wishlist_ids).') AND PrimaryCat = 1';
 				$type_clauses['wish_list']['except'] = '%1$s.ResourceId NOT IN ('.implode(',', $wishlist_ids).') AND PrimaryCat = 1';
 			}
 			else {
 				$type_clauses['wish_list']['include']='0';
 				$type_clauses['wish_list']['except']='1';
 			}
 			$type_clauses['wish_list']['having_filter']=false;
 		}
 		// my favorite products: end
 
 		// products from package: begin
 		if (strpos($types, 'content') !== false) {
 			$object->removeFilter('category_filter');
 			$object->AddGroupByField('%1$s.ProductId');
 
 			$item_type = $this->Application->getUnitOption('p', 'ItemType');
 
 			$object_product = &$this->Application->recallObject($event->Prefix);
 
 			$content_ids_array = $object_product->GetPackageContentIds();
 
 			if (sizeof($content_ids_array)==0) {
 				$content_ids_array = array('-1');
 			}
 
 			if (sizeof($content_ids_array)>0) {
 				$type_clauses['content']['include'] = '%1$s.ProductId IN ('.implode(',', $content_ids_array).')';
 			}
 			else {
 				$type_clauses['content']['include']='0';
 			}
 			$type_clauses['related']['having_filter']=false;
 		}
 		// products from package: end
 
 		$object->addFilter('not_virtual', '%1$s.Virtual = 0');
 
 		if (!$this->Application->isAdminUser) {
 			$object->addFilter('expire_filter', '%1$s.Expire IS NULL OR %1$s.Expire > UNIX_TIMESTAMP()');
 		}
 
 		return $type_clauses;
 	}
 
 	function OnClearRecent(&$event)
 	{
 		$this->Application->RemoveVar('recent_products');
 	}
 
 	/**
 	 * Occurs, when user rates a product
 	 *
 	 * @param kEvent $event
 	 */
 	function OnRateProduct(&$event)
 	{
 		$event->redirect_params = Array('pass' => 'all,p');
 		$event->redirect = $this->Application->GetVar('success_template');
 
 		$object =& $event->getObject();
 		$user_id = ($this->Application->RecallVar('user_id') == 0) ? -2 : $this->Application->RecallVar('user_id');
 
 		$sql = '	SELECT * FROM '.TABLE_PREFIX.'SpamControl
 					WHERE ItemResourceId='.$object->GetDBField('ResourceId').'
 					AND IPaddress="'.$_SERVER['REMOTE_ADDR'].'"
 					AND PortalUserId='.$user_id.'
 					AND DataType="Rating"';
 		$res = $this->Conn->GetRow($sql);
 
 		if( $res && $res['Expire'] < adodb_mktime() )
 		{
 			$sql = '	DELETE FROM '.TABLE_PREFIX.'SpamControl
 						WHERE ItemResourceId='.$object->GetDBField('ResourceId').'
 						AND IPaddress="'.$_SERVER['REMOTE_ADDR'].'"
 						AND PortalUserId='.$user_id.'
 						AND DataType="Rating"';
 			$this->Conn->Query($sql);
 			unset($res);
 		}
 
 		$new_rating = $this->Application->GetVar('rating');
 
 		if($new_rating !== false && !$res)
 		{
 			$rating = $object->GetDBField('CachedRating');
 			$votes = $object->GetDBField('CachedVotesQty');
 			$new_votes = $votes + 1;
 
 			$rating = (($rating * $votes) + $new_rating) / $new_votes;
 			$object->SetDBField('CachedRating', $rating);
 			$object->SetDBField('CachedVotesQty', $new_votes);
 			$object->Update();
 
 			$expire = adodb_mktime() + $this->Application->ConfigValue('product_ReviewDelay_Value') * $this->Application->ConfigValue('product_ReviewDelay_Interval');
 			$sql = '	INSERT INTO '.TABLE_PREFIX.'SpamControl
 							(ItemResourceId, IPaddress, PortalUserId, DataType, Expire)
 						VALUES ('.$object->GetDBField('ResourceId').',
 								"'.$_SERVER['REMOTE_ADDR'].'",
 								'.$user_id.',
 								"Rating",
 								'.$expire.')';
 			$this->Conn->Query($sql);
 		}
 		else
 		{
 			$event->status == erFAIL;
 			$event->redirect=false;
 			$object->FieldErrors['CachedRating']['pseudo'] = 'too_frequent';
 			$object->ErrorMsgs['too_frequent'] = $this->Application->Phrase('lu_ferror_rate_duplicate');
 		}
 	}
 
 	function OnCancelAction(&$event)
 	{
 		$event->redirect_params = Array('pass' => 'all,p');
 		$event->redirect = $this->Application->GetVar('cancel_template');
 	}
 
 	function OnRecommendProduct(&$event)
 	{
 		// used for error reporting only -> rewrite code + theme (by Alex)
 		$object =& $this->Application->recallObject('u', null, Array('skip_autoload' => true)); // TODO: change theme too
 
 		$friend_email = $this->Application->GetVar('friend_email');
 		$friend_name = $this->Application->GetVar('friend_name');
 		$my_email = $this->Application->GetVar('your_email');
 		$my_name = $this->Application->GetVar('your_name');
 		$my_message = $this->Application->GetVar('your_message');
 
 		$send_params = array();
 		$send_params['to_email']=$friend_email;
 		$send_params['to_name']=$friend_name;
 		$send_params['from_email']=$my_email;
 		$send_params['from_name']=$my_name;
 		$send_params['message']=$my_message;
 
 		if (preg_match('/'.REGEX_EMAIL_USER.'@'.REGEX_EMAIL_DOMAIN.'/', $friend_email)) {
 			$user_id = $this->Application->RecallVar('user_id');
 			$email_event = &$this->Application->EmailEventUser('PRODUCT.SUGGEST', $user_id, $send_params);
 			$email_event = &$this->Application->EmailEventAdmin('PRODUCT.SUGGEST');
 
 			if ($email_event->status == erSUCCESS){
 				$event->redirect_params = array('opener' => 's', 'pass' => 'all');
 				$event->redirect = $this->Application->GetVar('template_success');
 			}
 			else {
 //					$event->redirect_params = array('opener' => 's', 'pass' => 'all');
 //					$event->redirect = $this->Application->GetVar('template_fail');
 
 				$object->ErrorMsgs['send_error'] = $this->Application->Phrase('lu_email_send_error');
 				$object->FieldErrors['Email']['pseudo'] = 'send_error';
 				$event->status = erFAIL;
 			}
 	    }
 	    else {
 			$object->ErrorMsgs['invalid_email'] = $this->Application->Phrase('lu_InvalidEmail');
 			$object->FieldErrors['Email']['pseudo'] = 'invalid_email';
 			$event->status = erFAIL;
 	    }
 	}
 
 	/**
 	 * Creates/updates virtual product based on listing type data
 	 *
 	 * @param kEvent $event
 	 */
 	function OnSaveVirtualProduct(&$event)
 	{
 		$object =& $event->getObject( Array('skip_autoload' => true) );
 		$listing_type =& $this->Application->recallObject('lst', null, Array('skip_autoload' => true));
 		$listing_type->Load($event->MasterEvent->getEventParam('id'));
 
 		$product_id = $listing_type->GetDBField('VirtualProductId');
 
 		if ($product_id) {
 			$object->Load($product_id);
 		}
 
 		if (!$listing_type->GetDBField('EnableBuying')) {
 			if ($product_id) {
 				// delete virtual product here
 				$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
 				$temp_handler->DeleteItems($event->Prefix, $event->Special, Array($product_id));
 
 				$listing_type->SetDBField('VirtualProductId', 0);
 				$listing_type->Update();
 			}
 			return true;
 		}
 
 		$ml_formatter =& $this->Application->recallObject('kMultiLanguage');
 		$object->SetDBField($ml_formatter->LangFieldName('Name'), $listing_type->GetDBField('ShopCartName') );
 		$object->SetDBField($ml_formatter->LangFieldName('Description'), $listing_type->GetDBField('Description'));
 		$object->SetDBField('SKU', 'ENHANCE_LINK_'.abs( crc32( $listing_type->GetDBField('Name') ) ) );
 
 		if ($product_id) {
 			$object->Update();
 		}
 		else {
 			$object->SetDBField('Type', 2);
 			$object->SetDBField('Status', 1);
 			$object->SetDBField('HotItem', 0);
 			$object->SetDBField('PopItem', 0);
 			$object->SetDBField('NewItem', 0);
 			$object->SetDBField('Virtual', 1);
 
 //			$processing_data = Array('ApproveEvent' => 'ls:EnhanceLinkAfterOrderApprove', 'ExpireEvent' => 'ls:ExpireLink');
 			$processing_data = Array(	'ApproveEvent'			=>	'ls:EnhanceLinkAfterOrderApprove',
 										'DenyEvent'				=>	'ls:EnhanceLinkAfterOrderDeny',
 										'CompleteOrderEvent'	=>	'ls:EnhancedLinkOnCompleteOrder',
 										'ExpireEvent'			=>	'ls:ExpireLink',
 										'HasNewProcessing'		=>	1);
 			$object->SetDBField('ProcessingData', serialize($processing_data));
 			$object->Create();
 
 			$listing_type->SetDBField('VirtualProductId', $object->GetID());
 			$listing_type->Update();
 		}
 
 		$additiona_fields = Array(	'AccessDuration'	=>	$listing_type->GetDBField('Duration'),
 									'AccessUnit'		=>	$listing_type->GetDBField('DurationType'),
 							);
 		$this->setPrimaryPrice($object->GetID(), (double)$listing_type->GetDBField('Price'), $additiona_fields);
 	}
 
 	/**
 	 * [HOOK] Deletes virtual product when listing type is deleted
 	 *
 	 * @param kEvent $event
 	 */
 	function OnDeleteListingType(&$event)
 	{
 		$listing_type = $event->MasterEvent->getObject();
 		$product_id = $listing_type->GetDBField('VirtualProductId');
 
 		if ($product_id) {
 			$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
 			$temp_handler->DeleteItems($event->Prefix, $event->Special, Array($product_id));
 		}
 	}
 
 	/**
 	 * Extends user membership in group when his order is approved
 	 *
 	 * @param kEvent $event
 	 */
 	function OnSubscriptionApprove(&$event)
 	{
 		$field_values = $event->getEventParam('field_values');
 		$item_data = unserialize($field_values['ItemData']);
 
 		if (!getArrayValue($item_data,'PortalGroupId')) {
 			// is subscription product, but no group defined in it's properties
 			trigger_error('Invalid product <b>'.$field_values['ProductName'].'</b> (id: '.$field_values['ProductId'].')');
 			return false;
 		}
 
 		$order_table = $this->Application->getUnitOption('ord', 'TableName');
 		$order_idfield = $this->Application->getUnitOption('ord', 'IDField');
 		$sql = 'SELECT PortalUserId FROM %s WHERE %s = %s';
 		$user_id = $this->Conn->GetOne( sprintf($sql, $order_table, $order_idfield, $field_values['OrderId']) );
 
 		$group_id = $item_data['PortalGroupId'];
 		$duration = $item_data['Duration'];
 
 		$sql = 'SELECT * FROM '.TABLE_PREFIX.'UserGroup WHERE PortalUserId = %s';
 		$user_groups = $this->Conn->Query( sprintf($sql, $user_id), 'GroupId' );
 
 		$sql = 'REPLACE INTO '.TABLE_PREFIX.'UserGroup (PortalUserId,GroupId,MembershipExpires,PrimaryGroup) VALUES (%s,%s,%s,%s)';
 		if( !isset($user_groups[$group_id]) )
 		{
 			$primary_group = count($user_groups) == 0 ? 1 : 0;
 			$expire = adodb_mktime() + $duration;
 		}
 		else {
 			$primary_group = $user_groups[$group_id]['PrimaryGroup'];
 			$expire = $user_groups[$group_id]['MembershipExpires'];
 			$expire = $expire < adodb_mktime() ? adodb_mktime() + $duration : $expire + $duration;
 		}
 
 /*
 		// Customization healtheconomics.org
 		if ($item_data['DurationType'] == 2) {
 			$expire = $item_data['AccessExpiration'];
 		}
 		// Customization healtheconomics.org --
 */
 		$this->Conn->Query( sprintf($sql, $user_id, $group_id, $expire, $primary_group) );
 
 		$sub_order =& $this->Application->recallObject('ord.-sub'.$event->getEventParam('next_sub_number'), 'ord');
 		$sub_order->SetDBField('IsRecurringBilling', getArrayValue($item_data, 'IsRecurringBilling') ? 1 : 0);
 		$sub_order->SetDBField('GroupId', $group_id);
 		$sub_order->SetDBField('NextCharge_date', $expire);
 		$sub_order->SetDBField('NextCharge_time', $expire);
 	}
 
 
 
 	function OnDownloadableApprove(&$event)
 	{
 		$field_values = $event->getEventParam('field_values');
 		$product_id = $field_values['ProductId'];
 		$sql = 'SELECT PortalUserId FROM '.$this->Application->getUnitOption('ord', 'TableName').'
 				WHERE OrderId = '.$field_values['OrderId'];
 		$user_id = $this->Conn->GetOne($sql);
 		$sql = 'INSERT INTO '.TABLE_PREFIX.'UserFileAccess VALUES("", '.$product_id.', '.$user_id.')';
 		$this->Conn->Query($sql);
 	}
 
 	function OnPackageApprove(&$event){
 		$field_values = $event->getEventParam('field_values');
 		$item_data = unserialize($field_values['ItemData']);
 		$package_content_ids = $item_data['PackageContent'];
 
 
 
 		$object_item = &$this->Application->recallObject('p.packageitem', null, array('skip_autoload'=>true));
 		foreach ($package_content_ids as $package_item_id) {
 			$object_field_values = array();
 					// query processing data from product and run approve event
 			$sql = 'SELECT ProcessingData FROM '.TABLE_PREFIX.'Products WHERE ProductId = '.$package_item_id;
 			$processing_data = $this->Conn->GetOne($sql);
 			if($processing_data)
 			{
 				$processing_data = unserialize($processing_data);
 				$approve_event = new kEvent($processing_data['ApproveEvent']);
 
 				//$order_item_fields = $this->Conn->GetRow('SELECT * FROM '.TABLE_PREFIX.'OrderItems WHERE OrderItemId = '.$grouping_data[1]);
 				$object_item->Load($package_item_id);
 
 				$object_field_values['OrderId'] = $field_values['OrderId'];
 				$object_field_values['ProductId'] = $package_item_id;
 
 				$object_field_values['ItemData'] = serialize($item_data['PackageItemsItemData'][$package_item_id]);
 
 				$approve_event->setEventParam('field_values', $object_field_values);
 				$this->Application->HandleEvent($approve_event);
 			}
 
 
 		}
 
 	}
 
 	/**
 	 * Checks, that all required product options are filled in before product is saved
 	 *
 	 * @param kEvent $event
 	 */
 	function OnPreSave(&$event)
 	{
 		$this->CheckRequiredOptions($event);
 
 		parent::OnPreSave($event);
 	}
 
 	/**
 	 * Set new price to ProductsPricing
 	 *
 	 * @param kEvent $event
 	 */
 	function OnAfterItemCreate(&$event)
 	{
 		parent::OnAfterItemCreate($event);
 
 		$this->_updateProductPrice($event);
 	}
 
 	/**
 	 * Set new price to ProductsPricing
 	 *
 	 * @param kEvent $event
 	 */
 	function OnAfterItemUpdate(&$event)
 	{
 		parent::OnAfterItemUpdate($event);
 
 		$this->_updateProductPrice($event);
 	}
 
 	/**
 	 * Updates product's primary price based on Price virtual field value
 	 *
 	 * @param kEvent $event
 	 */
 	function _updateProductPrice(&$event)
 	{
 		$object =& $event->getObject();
 		/* @var $object kDBItem */
 
 		$price = $object->GetDBField('Price');
 
 		// always create primary pricing, to show on Pricing tab (in admin) for tangible products
 		$force_create = ($object->GetDBField('Type') == PRODUCT_TYPE_TANGIBLE) && is_null($price);
 
 		if ($force_create || ($price != $object->GetOriginalField('Price'))) {
 			// new product OR price was changed in virtual field
 			$this->setPrimaryPrice($object->GetID(), (float)$price);
 		}
 	}
 
 	function CheckRequiredOptions(&$event)
 	{
 		$object =& $event->getObject();
 		if ($object->GetDBField('ProductId') == '') return ; // if product does not have ID - it's not yet created
 		$opt_object =& $this->Application->recallObject('po', null, Array('skip_autoload' => true) );
 		$has_required = $this->Conn->GetOne('SELECT COUNT(*) FROM '.$opt_object->TableName.' WHERE Required = 1 AND ProductId = '.$object->GetDBField('ProductId'));
 		//we need to imitate data sumbit, as parent' PreSave sets object values from $items_info
 		$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 		$items_info[$object->GetDBField('ProductId')]['HasRequiredOptions'] = $has_required ? 1:0;
 		$this->Application->SetVar($event->getPrefixSpecial(true), $items_info);
 		$object->SetDBField('HasRequiredOptions', $has_required ? 1:0);
 	}
 
 	/**
 	 * Sets required price in primary price backed, if it's missing, then create it
 	 *
 	 * @param int $product_id
 	 * @param double $price
 	 * @param Array $additional_fields
 	 * @return bool
 	 */
 	function setPrimaryPrice($product_id, $price, $additional_fields = Array())
 	{
 		$pr_object =& $this->Application->recallObject('pr.-item', null, Array('skip_autoload' => true) );
 		/* @var $pr_object kDBItem */
 
 		$pr_object->Load( Array('ProductId' => $product_id, 'IsPrimary' => 1) );
 
 		$sql = 'SELECT COUNT(*) FROM '.$pr_object->TableName.' WHERE ProductId = '.$product_id;
 		$has_pricings = $this->Conn->GetOne($sql);
 
 		if ($additional_fields) {
 			$pr_object->SetDBFieldsFromHash($additional_fields);
 		}
 
 		if( ($price === false) && $has_pricings ) return false;
 
 		if( $pr_object->isLoaded() )
 		{
 			$pr_object->SetField('Price', $price);
 			return $pr_object->Update();
 		}
 		else
 		{
 			$group_id = $this->Application->ConfigValue('User_LoggedInGroup');
 			$field_values = Array('ProductId' => $product_id, 'IsPrimary' => 1, 'MinQty' => 1, 'MaxQty' => -1, 'GroupId'=>$group_id);
 			$pr_object->SetDBFieldsFromHash($field_values);
 			$pr_object->SetField('Price', $price);
 			$ret = $pr_object->Create();
 			if ($pr_object->mode == 't') {
 				$pr_object->setTempID();
 			}
 			return $ret;
 		}
 	}
 
 	/**
 	 * Enter description here...
 	 *
 	 * @param kEvent $event
 	 */
 	function OnAfterItemDelete(&$event)
 	{
 		$product_id = $event->getEventParam('id');
 		if(!$product_id)
 		{
 			return;
 		}
 		$sql = 'DELETE FROM '.TABLE_PREFIX.'UserFileAccess
 				WHERE ProductId = '.$product_id;
 		$this->Conn->Query($sql);
 	}
 
 	/**
 	 * Load price from temp table if product mode is temp table
 	 *
 	 * @param kEvent $event
 	 */
 	function OnAfterItemLoad(&$event)
 	{
 		parent::OnAfterItemLoad($event);
 
 		$object =& $event->getObject();
 		/* @var $object kDBItem */
 
 		$a_pricing = $object->getPrimaryPricing();
 		if (!$a_pricing) {
 			// pricing doesn't exist for new products
 			$price = $cost = null;
 		}
 		else {
 			$price = (float)$a_pricing['Price'];
 			$cost = (float)$a_pricing['Cost'];
 		}
 
 		// set original fields to use them in OnAfterItemCreate/OnAfterItemUpdate later
 		$object->SetDBField('Price', $price);
 		$object->SetOriginalField('Price', $price);
 
 		$object->SetDBField('Cost', $cost);
 		$object->SetOriginalField('Cost', $cost);
 	}
 
 	/**
 	 * Allows to add products to package besides all that parent method does
 	 *
 	 * @param kEvent $event
 	 */
 	function OnProcessSelected(&$event)
 	{
 		$dst_field = $this->Application->RecallVar('dst_field');
 
 		if ($dst_field == 'PackageContent') {
 			$this->OnAddToPackage($event);
 		}
 		elseif ($dst_field == 'AssignedCoupon') {
 			$coupon_id = $this->Application->GetVar('selected_ids');
 			$object =& $event->getObject();
 			$object->SetDBField('AssignedCoupon', $coupon_id);
 			$this->RemoveRequiredFields($object);
 			$object->Update();
 		}
 		else {
 			parent::OnProcessSelected($event);
 		}
 		$this->finalizePopup($event);
 	}
 
 	/**
 	 * Called when some products are selected in products selector for this prefix
 	 *
 	 * @param kEvent $event
 	 */
 	function OnAddToPackage(&$event)
 	{
 		$selected_ids = $this->Application->GetVar('selected_ids');
 
 		// update current package content with selected products
 
 		$object =& $event->getObject();
 		/* @var $object ProductsItem */
 
 		$product_ids = $selected_ids['p'] ? explode(',', $selected_ids['p']) : Array();
 
 		if ($product_ids) {
 			$current_ids = $object->GetPackageContentIds();
 			$current_ids = array_unique(array_merge($current_ids, $product_ids));
 
 			// remove package product from selected list
 			$this_product = array_search($object->GetID(), $current_ids);
 			if ($this_product !== false) {
 				unset($current_ids[$this_product]);
 			}
 
 			$dst_field = $this->Application->RecallVar('dst_field');
 			$object->SetDBField($dst_field, '|'.implode('|', $current_ids).'|');
 
 			$object->Update();
 			$this->ProcessPackageItems($event);
 		}
 
 		$this->finalizePopup($event);
 	}
 
 
 	function ProcessPackageItems(&$event)
 	{
 		//$this->Application->SetVar('p_mode', 't');
 
 		$object =& $event->getObject();
 		/* @var $object ProductsItem */
 
 		$content_ids = $object->GetPackageContentIds();
 
 		if (sizeof($content_ids) > 0) {
 			$total_weight = $this->Conn->GetOne('SELECT SUM(Weight) FROM '.TABLE_PREFIX.'Products WHERE ProductId IN ('.implode(', ', $content_ids).') AND Type=1');
 
 			if (!$total_weight) $total_weight = 0;
 
 			$this->Conn->Query('UPDATE '.$object->TableName.' SET Weight='.$total_weight.' WHERE ProductId='.$object->GetID());
 		}
 
 		/*
 		$this->Application->SetVar('p_mode', false);
 
 		$list = &$this->Application->recallObject('p.content', 'p_List', array('types'=>'content'));
 
 		$this->Application->SetVar('p_mode', 't');
 
 		$list->Query();
 
 		$total_weight_a = 0;
 		$total_weight_b = 0;
 
 		$list->GoFirst();
 
 		while (!$list->EOL())
 		{
 			if ($list->GetDBField('Type')==1){
 				$total_weight_a += $list->GetField('Weight_a');
 				$total_weight_b += $list->GetField('Weight_b');
 			}
 			$list->GoNext();
 		}
 
 		$object->SetField('Weight_a', $total_weight_a);
 		$object->SetField('Weight_b', $total_weight_b);
 		*/
 		//$object->Update();
 
 
 	}
 
 	/**
 	 * Enter description here...
 	 *
 	 * @param kEvent $event
 	 */
 
 	function OnSaveItems(&$event)
 	{
 		//$event->CallSubEvent('OnUpdate');
 		$event->redirect = false;
 		//$event->redirect_params = Array('opener'=>'s','pass'=>'all,p');
 
 
 	}
 
 	/**
 	 * Removes product from package
 	 *
 	 * @param kEvent $event
 	 */
 	function OnRemovePackageItem(&$event) {
 
 		$this->Application->SetVar('p_mode', 't');
 
 		$object =& $event->getObject();
 
 		$items_info = $this->Application->GetVar('p_content');
 
 		if($items_info)
 		{
 			$product_ids = array_keys($items_info);
 
 			$current_ids = $object->GetPackageContentIds();
 
 			$current_ids_flip = array_flip($current_ids);
 			foreach($product_ids as $key=>$val){
 				unset($current_ids_flip[$val]);
 			}
 			$current_ids = array_keys($current_ids_flip);
 			$current_ids_str = '|'.implode('|', array_unique($current_ids)).'|';
 			$object->SetDBField('PackageContent', $current_ids_str);
 		}
 
 		$object->Update();
 		$this->ProcessPackageItems($event);
 	}
 
 	/**
 	 * Enter description here...
 	 *
 	 * @param kEvent $event
 	 */
 
 	function OnBeforeItemDelete(&$event){
 
 		$object = &$event->getObject();
 
 		$product_includes_in = $this->Conn->GetOne('SELECT COUNT(*) FROM '.TABLE_PREFIX.'Products WHERE PackageContent LIKE "%|'.$object->GetID().'%"');
 		if ($product_includes_in > 0){
 			$event->status=erFAIL;
 		}
 	}
 
 	/**
 	 * Returns specific to each item type columns only
 	 *
 	 * @param kEvent $event
 	 * @return Array
 	 */
 	function getCustomExportColumns(&$event)
 	{
 		$columns = parent::getCustomExportColumns($event);
 		$new_columns = Array(
 			'__VIRTUAL__Price' => 'Price',
 			'__VIRTUAL__Cost' => 'Cost',
 		);
 		return array_merge_recursive2($columns, $new_columns);
 	}
 
 /**
 	 * Sets non standart virtual fields (e.g. to other tables)
 	 *
 	 * @param kEvent $event
 	 */
 	function setCustomExportColumns(&$event)
 	{
 		parent::setCustomExportColumns($event);
 
 		$object =& $event->getObject();
 		$this->setPrimaryPrice($object->GetID(), (double)$object->GetDBField('Price'), Array('Cost' => (double)$object->GetDBField('Cost')) );
 	}
 
 	function OnPreSaveAndOpenPopup(&$event)
 	{
 		$object =& $event->getObject();
 		$this->RemoveRequiredFields($object);
 		$event->CallSubEvent('OnPreSave');
 
 		$event->redirect = $this->Application->GetVar('t');
 		// pass ID too, in case if product is created by OnPreSave call to ensure proper editing
 		$event->redirect_params =	Array(
 											'pass' => 'all',
 											$event->getPrefixSpecial(true).'_id' => $object->GetID(),
 									);
 	}
 
 
 	function getPassedID(&$event)
 	{
 		$event->setEventParam('raise_warnings', 0);
 		$passed = parent::getPassedID($event);
 
 		if ($passed) {
 			return $passed;
 		}
 
 		if ($this->Application->isAdminUser) {
 			// we may get product id out of OrderItem, if it exists
 			$ord_item =& $this->Application->recallObject('orditems', null, Array ('raise_warnings' => 0));
 
 			if ($ord_item->GetDBField('ProductId')) {
 				$passed = $ord_item->GetDBField('ProductId');
 			}
 		}
 
 		return $passed;
 	}
 
 	function OnAfterConfigRead(&$event)
 	{
 		parent::OnAfterConfigRead($event);
 
 		if (!$this->Application->LoggedIn()) {
 			return ;
 		}
 
 		$user_id = $this->Application->RecallVar('user_id');
 
 		$calculated_fields = $this->Application->getUnitOption($event->Prefix, 'CalculatedFields');
 
 		$sql = 'SELECT GroupId
 				FROM ' . TABLE_PREFIX . 'UserGroup
 				WHERE PrimaryGroup = 1 AND PortalUserId = ' . $user_id;
 		$primary_group = $this->Conn->GetOne($sql);
 
 		if (!$primary_group) {
 			return;
 		}
 
 		$sub_select = '	SELECT pp.Price
 						FROM ' . TABLE_PREFIX . 'ProductsPricing AS pp
 			 			WHERE pp.ProductId = %1$s.ProductId AND GroupId = ' . $primary_group . '
 			 			ORDER BY MinQty
 			 			LIMIT 0,1';
 		$calculated_fields['']['Price'] = 'IFNULL((' . $sub_select . '), ' . $calculated_fields['']['Price'] . ')';
 		$this->Application->setUnitOption($event->Prefix, 'CalculatedFields', $calculated_fields);
 	}
 
 	/**
 	 * Starts product editing, remove any pending inventory actions
 	 *
 	 * @param kEvent $event
 	 */
 	function OnEdit(&$event)
 	{
 		$this->Application->RemoveVar('inventory_actions');
 		parent::OnEdit($event);
 	}
 
 	/**
 	 * Adds "Shop Cart" tab on paid listing type editing tab
 	 *
 	 * @param kEvent $event
 	 */
 	function OnModifyPaidListingConfig(&$event)
 	{
 		$edit_tab_presets = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'EditTabPresets');
 		$edit_tab_presets['Default']['shopping_cart'] = Array ('title' => 'la_tab_ShopCartEntry', 't' => 'in-commerce/paid_listings/paid_listing_type_shopcart', 'priority' => 2);
 		$this->Application->setUnitOption($event->MasterEvent->Prefix, 'EditTabPresets', $edit_tab_presets);
 	}
+
+	/**
+	 * [HOOK] Allows to add cloned subitem to given prefix
+	 *
+	 * @param kEvent $event
+	 */
+	function OnCloneSubItem(&$event)
+	{
+		parent::OnCloneSubItem($event);
+
+		if ($event->MasterEvent->Prefix == 'rev') {
+			$clones = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'Clones');
+			$subitem_prefix = $event->Prefix . '-' . $event->MasterEvent->Prefix;
+
+			$clones[$subitem_prefix]['ConfigMapping'] = Array (
+				'PerPage'				=>	'Comm_Perpage_Reviews',
+
+				'ReviewDelayInterval'	=>	'product_ReviewDelay_Value',
+				'ReviewDelayValue'		=>	'product_ReviewDelay_Interval',
+			);
+
+			$this->Application->setUnitOption($event->MasterEvent->Prefix, 'Clones', $clones);
+		}
+	}
 }
\ No newline at end of file
Index: branches/5.0.x/units/products/products_config.php
===================================================================
--- branches/5.0.x/units/products/products_config.php	(revision 13285)
+++ branches/5.0.x/units/products/products_config.php	(revision 13286)
@@ -1,654 +1,687 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Commerce
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license	Commercial License
 * This software is protected by copyright law and international treaties.
 * Unauthorized reproduction or unlicensed usage of the code of this program,
 * or any portion of it may result in severe civil and criminal penalties,
 * and will be prosecuted to the maximum extent possible under the law
 * See http://www.in-portal.org/commercial-license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	$config =	Array (
 					'Prefix'			=>	'p',
 					'ItemClass'			=>	Array ('class' => 'ProductsItem', 'file' => 'products_item.php', 'require_classes' => Array ('kCatDBItem'), 'build_event' => 'OnItemBuild'),
 					'ListClass'			=>	Array ('class' => 'kCatDBList', 'file' => '', 'build_event' => 'OnListBuild'),
 					'EventHandlerClass'	=>	Array ('class' => 'ProductsEventHandler', 'file' => 'products_event_handler.php', 'require_classes' => Array ('kCatDBEventHandler'), 'build_event' => 'OnBuild'),
 					'TagProcessorClass' =>	Array ('class' => 'ProductsTagProcessor', 'file' => 'products_tag_processor.php', 'require_classes' => Array ('kCatDBTagProcessor'), 'build_event' => 'OnBuild'),
 					'AutoLoad'			=>	true,
 					'QueryString'		=>	Array (
 												1	=>	'id',
 												2	=>	'Page',
 												3	=>	'event',
 												4 	=>	'mode',
 											),
 
 					'CatalogItem'			=>	true,
 					'AdminTemplatePath'		=>	'products',
 					'AdminTemplatePrefix'	=>	'products_',
 
 					'SearchConfigPostfix' => 'products',
 
 					'ConfigPriority' => 0,
 
 					'RewritePriority' => 104,
 					'RewriteListener' => 'ModRewriteHelper:CategoryItemRewriteListener',
 
 					'Hooks' => Array (
 						// for subscription products: access group is saved before changing pricings
 						Array (
 							'Mode' => hAFTER,
 							'Conditional' => true,
 							'HookToPrefix' => 'pr',
 							'HookToSpecial' => '*',
 							'HookToEvent' => Array ('OnNew', 'OnAfterItemLoad'),
 							'DoPrefix' => '',
 							'DoSpecial' => '*',
 							'DoEvent' => 'OnPreSave',
 						),
 
 						Array (
 							'Mode' => hAFTER,
 							'Conditional' => false,
 							'HookToPrefix' => 'lst',
 							'HookToSpecial' => '',
 							'HookToEvent' => Array ( 'OnBeforeCopyToLive' ),
 							'DoPrefix' => '',
 							'DoSpecial' => '',
 							'DoEvent' => 'OnSaveVirtualProduct',
 						),
 
 						Array (
 							'Mode' => hAFTER,
 							'Conditional' => false,
 							'HookToPrefix' => 'lst',
 							'HookToSpecial' => '*',
 							'HookToEvent' => Array ('OnAfterItemDelete'),
 							'DoPrefix' => '',
 							'DoSpecial' => '',
 							'DoEvent' => 'OnDeleteListingType',
 						),
 
 						Array (
 							'Mode' => hAFTER,
 							'Conditional' => false,
 							'HookToPrefix' => 'lst',
 							'HookToSpecial' => '*',
 							'HookToEvent' => Array ('OnAfterConfigRead'),
 							'DoPrefix' => '',
 							'DoSpecial' => '',
 							'DoEvent' => 'OnModifyPaidListingConfig',
 						),
 
 						Array (
 							'Mode' => hBEFORE,
 							'Conditional' => false,
 							'HookToPrefix' => 'file',
 							'HookToSpecial' => '',
 							'HookToEvent' => Array ( 'OnNew', 'OnEdit' ),
 							'DoPrefix' => '',
 							'DoSpecial' => '',
 							'DoEvent' => 'OnPreSave',
 						),
 
 						Array (
 							'Mode' => hBEFORE,
 							'Conditional' => false,
 							'HookToPrefix' => '',
 							'HookToSpecial' => '*',
 							'HookToEvent' => Array ('OnAfterConfigRead'),
 							'DoPrefix' => 'cdata',
 							'DoSpecial' => '*',
 							'DoEvent' => 'OnDefineCustomFields',
 						),
+
+						Array (
+							'Mode' => hBEFORE,
+							'Conditional' => false,
+							'HookToPrefix' => 'rev',
+							'HookToSpecial' => '*',
+							'HookToEvent' => Array ('OnAfterConfigRead'),
+							'DoPrefix' => '',
+							'DoSpecial' => '*',
+							'DoEvent' => 'OnCloneSubItem',
+						),
+
+						Array (
+							'Mode' => hBEFORE,
+							'Conditional' => false,
+							'HookToPrefix' => 'fav',
+							'HookToSpecial' => '*',
+							'HookToEvent' => Array ('OnAfterConfigRead'),
+							'DoPrefix' => '',
+							'DoSpecial' => '*',
+							'DoEvent' => 'OnCloneSubItem',
+						),
+
+						Array (
+							'Mode' => hBEFORE,
+							'Conditional' => false,
+							'HookToPrefix' => 'ci',
+							'HookToSpecial' => '*',
+							'HookToEvent' => Array ('OnAfterConfigRead'),
+							'DoPrefix' => '',
+							'DoSpecial' => '*',
+							'DoEvent' => 'OnCloneSubItem',
+						),
 					),
 
 					'IDField'			=>	'ProductId',
 					'StatusField'		=>	Array ('Status'),	// field, that is affected by Approve/Decline events
 
 					'TitleField'		=>	'Name',		// field, used in bluebar when editing existing item
 					'ItemType'			=>	11,	// this is used when relation to product is added from in-portal and via-versa
 
 					'ViewMenuPhrase'	=>	'la_text_Products',
 					'CatalogTabIcon' => 'in-commerce:icon16_products.png',
 
 					'ItemPropertyMappings'	=>	Array (
 													'NewDays'		=>	'Product_NewDays',		// number of days item to be NEW
 													'MinPopVotes'	=>	'Product_MinPopVotes',	// minimum number of votes for an item to be POP
 													'MinPopRating'	=>	'Product_MinPopRating',	// minimum rating for an item to be POP
 													'MaxHotNumber'	=>	'Product_MaxHotNumber',	// maximum number of HOT (top seller) items
 
 													'HotLimit'		=>	'Product_HotLimit',		// variable name in inp_Cache table
 													'ClickField'	=>	'Hits',					// item click count is stored here (in item table)
 												),
 
 					'TitlePhrase'		=> 'la_text_Product',
 
 					'TitlePresets'		=>	Array (
 												'default'	=>	Array (	'new_status_labels'		=> Array ('p' => '!la_title_Adding_Product!'),
 																		'edit_status_labels'	=> Array ('p' => '!la_title_Editing_Product!'),
 																		'new_titlefield'		=> Array ('p' => '!la_title_NewProduct!'),
 																),
 												'product_list' =>Array (	'prefixes'				=> Array ('c_List', 'p_List'),
 																		'tag_params'			=> Array ('c' => Array ('per_page' =>-1)),
 																		'format'				=>	"!la_title_Categories! (#c_recordcount#) - !la_title_Products! (#p_recordcount#)",
 																),
 												'products_edit' =>Array (	'prefixes'				=> Array ('p'),
 																		'new_titlefield'		=> Array ('p' => '!la_title_NewProduct!'),
 																		'format'				=> "#p_status# '#p_titlefield#' - !la_title_General!",
 																),
 												'inventory'	=>	Array ('prefixes' => Array ('p'), 'format' => "#p_status# - '#p_titlefield#' - !la_title_Product_Inventory!"),
 												'pricing'	=>	Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Product_Pricing!"),
 												'access_pricing'	=>	Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Product_AccessPricing!"),
 												'access'	=>	Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Product_Access!"),
 												'files'		=>	Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Product_Files!"),
 												'options' =>	Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Product_Options!"),
 												'categories' =>	Array ('prefixes' => Array ('p', 'p-ci_List'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Categories!"),
 												'relations'	=>	Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Relations!"),
 												'content'	=>	Array ('prefixes' => Array ('p', 'p.content_List'), 'tag_params' => Array ('p.content' => Array ('types' => 'content', 'live_table' =>true)), 'format' => "#p_status# '#p_titlefield#' - !la_title_Product_PackageContent!"),
 												'images'	=>	Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Images!"),
 												'reviews'	=>	Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Reviews!"),
 												'products_custom'	=>	Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_Custom!"),
 												'images_edit'		=>	Array (	'prefixes'				=>	Array ('p', 'img'),
 																				'new_status_labels'		=>	Array ('img' => '!la_title_Adding_Image!'),
 																				'edit_status_labels'	=>	Array ('img' => '!la_title_Editing_Image!'),
 																				'new_titlefield'		=>	Array ('img' => '!la_title_New_Image!'),
 																				'format'				=>	"#p_status# '#p_titlefield#' - #img_status# '#img_titlefield#'",
 																		),
 												'pricing_edit'	=>	Array (		'prefixes'				=>	Array ('p', 'pr'),
 																				'new_status_labels'		=>	Array ('pr' =>"!la_title_Adding_PriceBracket! '!la_title_New_PriceBracket!'"),
 																				'edit_status_labels'	=>	Array ('pr' => '!la_title_Editing_PriceBracket!'),
 																				'format'				=>	"#p_status# '#p_titlefield#' - #pr_status#",
 																		),
 												'options_edit'	=>	Array (	'prefixes'				=>	Array ('p', 'po'),
 																				'new_status_labels'		=>	Array ('po' =>"!la_title_Adding_Option!"),
 																				'edit_status_labels'	=>	Array ('po' => '!la_title_Editing_Option!'),
 																				'new_titlefield'		=>	Array ('po' => '!la_title_New_Option!'),
 																				'format'				=>	"#p_status# '#p_titlefield#' - #po_status# '#po_titlefield#'",
 																		),
 
 												'options_combinations'	=>	Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_ManagingOptionCombinations!"),
 												'shipping_options'	=>	Array ('prefixes' => Array ('p'), 'format' => "#p_status# '#p_titlefield#' - !la_title_ManagingShippingOptions!"),
 
 												'file_edit'		=>	Array (		'prefixes'				=>	Array ('p', 'file'),
 																				'new_status_labels'		=>	Array ('file' =>"!la_title_Adding_File!"),
 																				'edit_status_labels'	=>	Array ('file' => '!la_title_Editing_File!'),
 																				'new_titlefield'		=>	Array ('file' => '!la_title_New_File!'),
 																				'format'				=>	"#p_status# '#p_titlefield#' - #file_status# '#file_titlefield#'",
 																		),
 												'relations_edit'	=>	Array (	'prefixes'				=>	Array ('p', 'rel'),
 																				'new_status_labels'		=>	Array ('rel' =>"!la_title_Adding_Relationship! '!la_title_New_Relationship!'"),
 																				'edit_status_labels'	=>	Array ('rel' => '!la_title_Editing_Relationship!'),
 																				'format'				=>	"#p_status# '#p_titlefield#' - #rel_status#",
 																		),
 												'reviews_edit'	=>	Array (		'prefixes'				=>	Array ('p', 'rev'),
 																				'new_status_labels'		=>	Array ('rev' =>"!la_title_Adding_Review! '!la_title_New_Review!'"),
 																				'edit_status_labels'	=>	Array ('rev' => '!la_title_Editing_Review!'),
 																				'format'				=>	"#p_status# '#p_titlefield#' - #rev_status#",
 																		),
 
 												'products_export' => Array ('format' => '!la_title_ProductsExport!'),
 
 												'products_import' => Array ('format' => '!la_title_ImportProducts!'),
 
 												'tree_in-commerce'	=>	Array ('format' => '!la_Text_Version! '.$this->Application->findModule('Name', 'In-Commerce', 'Version')),
 											),
 
 					'EditTabPresets' => Array (
 						'Default' => Array (
 							'general' => Array ('title' => 'la_tab_General', 't' => 'in-commerce/products/products_edit', 'priority' => 1),
 							'inventory' => Array ('title' => 'la_tab_Inventory', 't' => 'in-commerce/products/products_inventory', 'priority' => 2),
 							'access_and_pricing' => Array ('title' => 'la_tab_AccessAndPricing', 't' => 'in-commerce/products/products_access', 'priority' => 3),
 							'pricing' => Array ('title' => 'la_tab_Pricing', 't' => 'in-commerce/products/products_pricing', 'priority' => 4),
 
 //							'pricing2' => Array ('title' => 'la_tab_Pricing', 't' => 'in-commerce/products/products_access_pricing', 'priority' => 5),
 
 							'files_and_pricing' => Array ('title' => 'la_tab_FilesAndPricing', 't' => 'in-commerce/products/products_files', 'priority' => 6),
 							'options' => Array ('title' => 'la_tab_Options', 't' => 'in-commerce/products/products_options', 'priority' => 7),
 							'categories' => Array ('title' => 'la_tab_Categories', 't' => 'in-commerce/products/products_categories', 'priority' => 8),
 							'relations' => Array ('title' => 'la_tab_Relations', 't' => 'in-commerce/products/products_relations', 'priority' => 9),
 							'package_content' => Array ('title' => 'la_tab_PackageContent', 't' => 'in-commerce/products/products_packagecontent', 'priority' => 10),
 							'images' => Array ('title' => 'la_tab_Images', 't' => 'in-commerce/products/products_images', 'priority' => 11),
 							'reviews' => Array ('title' => 'la_tab_Reviews', 't' => 'in-commerce/products/products_reviews', 'priority' => 12),
 							'custom' => Array ('title' => 'la_tab_Custom', 't' => 'in-commerce/products/products_custom', 'priority' => 13),
 						),
 					),
 
 					'PermItemPrefix'	=>	'PRODUCT',
 
 					'PermTabText'		=>	'In-Commerce',
 					'PermSection'		=>	Array ('main' => 'CATEGORY:in-commerce:products_list', 'search' => 'in-commerce:search', 'email' => 'in-commerce:incommerce_configemail', 'custom' => 'in-commerce:configuration_custom'),
 
 					'Sections'			=>	Array (
 						'in-commerce'	=>	Array (
 							'parent'		=>	'in-portal:root',
 							'icon'			=>	'ecommerce',
 							'label'			=>	'la_title_In-Commerce',
 							'url'			=>	Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'),
 							'permissions'	=>	Array ('view'),
 							'priority'		=>	2.1,
 							'container'		=>	true,
 							'type'			=>	stTREE,
 						),
 
 						'in-commerce:products'	=>	Array (
 							'parent'		=>	'in-portal:site',
 							'icon'			=>	'products',
 							'label'			=>	'la_tab_Products',
 							'url'			=>	Array ('t' => 'catalog/advanced_view', 'anchor' => 'tab-p.showall', 'pass' => 'm'),
 							'onclick'		=>	'setCatalogTab(\'p.showall\')',
 							'permissions'	=>	Array ('view'),
 							'priority'		=>	3.2,
 							'type'			=>	stTREE,
 						),
 
 						// product settings
 						'in-commerce:setting_folder' => Array (
 							'parent'		=>	'in-portal:system',
 							'icon'			=>	'conf_ecommerce',
 							'label'			=>	'la_title_In-Commerce',
 							'use_parent_header' => 1,
 							'url'			=>	Array ('t' => 'index', 'pass_section' => true, 'pass' => 'm'),
 							'permissions'	=>	Array ('view'),
 							'priority'		=>	3.1,
 							'container'		=>	true,
 							'type'			=>	stTREE,
 						),
 
 						'in-commerce:general'	=>	Array (
 							'parent'		=>	'in-commerce:setting_folder',
 							'icon'			=>	'conf_ecommerce_general',
 							'label'			=>	'la_tab_GeneralSettings',
 							'url'			=>	Array ('t' => 'config/config_general', 'pass_section' => true, 'pass' => 'm'),
 							'permissions'	=>	Array ('view', 'edit'),
 							'priority'		=>	1,
 							'type'			=>	stTREE,
 						),
 
 						'in-commerce:output'	=>	Array (
 							'parent'		=>	'in-commerce:setting_folder',
 							'icon'			=>	'core:conf_output',
 							'label'			=>	'la_tab_ConfigOutput',
 							'url'			=>	Array ('t' => 'config/config_universal', 'pass_section' => true, 'pass' => 'm'),
 							'permissions'	=>	Array ('view', 'edit'),
 							'priority'		=>	2,
 							'type'			=>	stTREE,
 						),
 
 						'in-commerce:search'	=>	Array (
 							'parent'		=>	'in-commerce:setting_folder',
 							'icon'			=>	'core:conf_search',
 							'label'			=>	'la_tab_ConfigSearch',
 							'url'			=>	Array ('t' => 'config/config_search', 'module_key' => 'products', 'pass_section' => true, 'pass' => 'm'),
 							'permissions'	=>	Array ('view', 'edit'),
 							'priority'		=>	7,
 							'type'			=>	stTREE,
 						),
 
 						'in-commerce:incommerce_configemail'	=>	Array (
 							'parent'		=>	'in-commerce:setting_folder',
 							'icon'			=>	'core:conf_email',
 							'label'			=>	'la_tab_ConfigE-mail',
 							'url'			=>	Array ('t' => 'config/config_email', 'pass_section' => true, 'pass' => 'm'),
 							'permissions'	=>	Array ('view', 'edit'),
 							'priority'		=>	8,
 							'type'			=>	stTREE,
 						),
 
 						'in-commerce:configuration_custom'	=>	Array (
 							'parent'		=>	'in-commerce:setting_folder',
 							'icon'			=>	'core:conf_customfields',
 							'label'			=>	'la_tab_ConfigCustom',
 							'url'			=>	Array ('t' => 'custom_fields/custom_fields_list', 'cf_type' => 11, 'pass_section' => true, 'pass' => 'm,cf'),
 							'permissions'	=>	Array ('view', 'add', 'edit', 'delete'),
 							'priority'		=>	9,
 							'type'			=>	stTREE,
 						),
 
 						'in-commerce:contacts'	=>	Array (
 							'parent'		=>	'in-commerce:setting_folder',
 							'icon'			=>	'conf_contact_info',
 							'label'			=>	'la_tab_ConfigContacts',
 							'url'			=>	Array ('t' => 'config/config_universal', 'pass_section' => true, 'pass' => 'm'),
 							'permissions'	=>	Array ('view', 'edit'),
 							'priority'		=>	10,
 							'type'			=>	stTREE,
 						),
 					),
 
 					'FilterMenu'		=>	Array (
 												'Groups' => Array (
 													Array ('mode' => 'AND', 'filters' => Array ('show_active', 'show_pending', 'show_disabled'), 'type' => WHERE_FILTER),
 													Array ('mode' => 'AND', 'filters' => Array ('show_tang', 'show_sub', 'show_serv', 'show_download', 'show_package'), 'type' => WHERE_FILTER),
 													Array ('mode' => 'AND', 'filters' => Array ('show_new'), 'type' => HAVING_FILTER),
 													Array ('mode' => 'AND', 'filters' => Array ('show_hot'), 'type' => HAVING_FILTER),
 													Array ('mode' => 'AND', 'filters' => Array ('show_pop'), 'type' => HAVING_FILTER),
 													Array ('mode' => 'AND', 'filters' => Array ('show_pick'), 'type' => WHERE_FILTER),
 												),
 												'Filters' => Array (
 													'show_active'	=>	Array ('label' => 'la_Active', 'on_sql' => '', 'off_sql' => '%1$s.Status != 1' ),
 													'show_pending'	=>	Array ('label' => 'la_Pending', 'on_sql' => '', 'off_sql' => '%1$s.Status != 2'  ),
 													'show_disabled'	=>	Array ('label' => 'la_Disabled', 'on_sql' => '', 'off_sql' => '%1$s.Status != 0'  ),
 													's1'	=>	Array (),
 													'show_tang'	=>		Array ('label' => 'la_product_tangible', 'on_sql' => '', 'off_sql' => '%1$s.Type != 1'  ),
 													'show_sub'	=>		Array ('label' => 'la_product_subscription', 'on_sql' => '', 'off_sql' => '%1$s.Type != 2'  ),
 													'show_serv'	=>		Array ('label' => 'la_product_service', 'on_sql' => '', 'off_sql' => '%1$s.Type != 3'  ),
 													'show_download'	=>	Array ('label' => 'la_product_downloadable', 'on_sql' => '', 'off_sql' => '%1$s.Type != 4'  ),
 													'show_package'	=>	Array ('label' => 'la_product_package', 'on_sql' => '', 'off_sql' => '%1$s.Type != 5'  ),
 													's2'	=>	Array (),
 													'show_new'	=>	Array ('label' => 'la_Text_New', 'on_sql' => '', 'off_sql' => '`IsNew` != 1'  ),
 													'show_hot'	=>	Array ('label' => 'la_Text_TopSellers', 'on_sql' => '', 'off_sql' => '`IsHot` != 1'  ),
 													'show_pop'	=>	Array ('label' => 'la_Text_Pop', 'on_sql' => '', 'off_sql' => '`IsPop` != 1'  ),
 													'show_pick'	=>	Array ('label' => 'la_prompt_EditorsPick', 'on_sql' => '', 'off_sql' => '%1$s.`EditorsPick` != 1'  ),
 												)
 											),
 
 					'TableName'			=>	TABLE_PREFIX.'Products',
 					'CalculatedFields' => Array (
 						''	=>	Array (
 							'SameImages'	=>	'img.SameImages',
 							'LocalThumb'	=>	'img.LocalThumb',
 							'ThumbPath'		=>	'img.ThumbPath',
 							'ThumbUrl'		=>	'img.ThumbUrl',
 							'LocalImage'	=>	'img.LocalImage',
 							'LocalPath'		=>	'img.LocalPath',
 							'FullUrl'		=>	'img.Url',
 
 							'Price'			=>	'COALESCE(pricing.Price, 0)',
 							'Cost'			=>	'COALESCE(pricing.Cost, 0)',
 							'PrimaryCat'	=> TABLE_PREFIX.'%3$sCategoryItems.PrimaryCat',
 							'CategoryId'	=> TABLE_PREFIX.'%3$sCategoryItems.CategoryId',
 							'ParentPath'	=> TABLE_PREFIX.'Category.ParentPath',
 							'Manufacturer'	=> TABLE_PREFIX.'Manufacturers.Name',
 							'Filename' => TABLE_PREFIX.'%3$sCategoryItems.Filename',
 							'FileSize'		=>	'files.Size',
 							'FilePath'		=>	'files.FilePath',
 							'FileVersion'	=>	'files.Version',
 						),
 
 						'showall' => Array (
 							'Price'			=>	'COALESCE(pricing.Price, 0)',
 							'Manufacturer'	=> TABLE_PREFIX.'Manufacturers.Name',
 							'PrimaryCat'	=> TABLE_PREFIX.'%3$sCategoryItems.PrimaryCat',
 							'CategoryId'	=> TABLE_PREFIX.'%3$sCategoryItems.CategoryId',
 							'FileSize'		=>	'files.Size',
 							'FilePath'		=>	'files.FilePath',
 							'FileVersion'	=>	'files.Version',
 							'Filename' 		=>	TABLE_PREFIX.'%3$sCategoryItems.Filename',
 						),
 					),
 
 					'ListSQLs'			=>	Array (
 													''	=>	'	SELECT %1$s.* %2$s
 																FROM %1$s
 																LEFT JOIN '.TABLE_PREFIX.'PortalGroup ON '.TABLE_PREFIX.'PortalGroup.GroupId = %1$s.AccessGroupId
 																LEFT JOIN '.TABLE_PREFIX.'%3$sCategoryItems ON '.TABLE_PREFIX.'%3$sCategoryItems.ItemResourceId = %1$s.ResourceId
 																LEFT JOIN '.TABLE_PREFIX.'Category ON '.TABLE_PREFIX.'Category.CategoryId = '.TABLE_PREFIX.'%3$sCategoryItems.CategoryId
 																LEFT JOIN '.TABLE_PREFIX.'Images img ON img.ResourceId = %1$s.ResourceId AND img.DefaultImg = 1
 																LEFT JOIN '.TABLE_PREFIX.'ProductFiles files ON files.ProductId = %1$s.ProductId AND files.IsPrimary = 1
 																LEFT JOIN '.TABLE_PREFIX.'ProductsPricing pricing ON pricing.ProductId = %1$s.ProductId AND pricing.IsPrimary = 1
 																LEFT JOIN '.TABLE_PREFIX.'Manufacturers ON '.TABLE_PREFIX.'Manufacturers.ManufacturerId = %1$s.ManufacturerId
 																LEFT JOIN '.TABLE_PREFIX.'PermCache perm ON perm.CategoryId = '.TABLE_PREFIX.'%3$sCategoryItems.CategoryId
 																LEFT JOIN '.TABLE_PREFIX.'%3$sProductsCustomData cust ON %1$s.ResourceId = cust.ResourceId',
 
 													'showall' => 'SELECT %1$s.* %2$s FROM %1$s
 																LEFT JOIN '.TABLE_PREFIX.'ProductsPricing pricing ON pricing.ProductId = %1$s.ProductId AND pricing.IsPrimary = 1
 																LEFT JOIN '.TABLE_PREFIX.'ProductFiles files ON files.ProductId = %1$s.ProductId AND files.IsPrimary = 1
 																LEFT JOIN '.TABLE_PREFIX.'Manufacturers ON '.TABLE_PREFIX.'Manufacturers.ManufacturerId = %1$s.ManufacturerId
 																LEFT JOIN '.TABLE_PREFIX.'%3$sCategoryItems ON '.TABLE_PREFIX.'%3$sCategoryItems.ItemResourceId = %1$s.ResourceId
 																LEFT JOIN '.TABLE_PREFIX.'Category ON '.TABLE_PREFIX.'Category.CategoryId = '.TABLE_PREFIX.'%3$sCategoryItems.CategoryId
 																LEFT JOIN '.TABLE_PREFIX.'PermCache perm ON perm.CategoryId = '.TABLE_PREFIX.'%3$sCategoryItems.CategoryId
 																LEFT JOIN '.TABLE_PREFIX.'%3$sProductsCustomData cust ON %1$s.ResourceId = cust.ResourceId',
 																		), // key - special, value - list select sql
 					'ListSortings'	=> 	Array (
 																'' => Array (
 																	'ForcedSorting' => Array ('Priority' => 'desc'),
 																	'Sorting' => Array ('Name' => 'asc'),
 																)
 															),
 					'ItemSQLs'			=>	Array (	'' => '	SELECT %1$s.* %2$s
 															FROM %1$s
 															LEFT JOIN '.TABLE_PREFIX.'PortalGroup pg ON pg.GroupId = %1$s.AccessGroupId
 															LEFT JOIN '.TABLE_PREFIX.'%3$sCategoryItems ON '.TABLE_PREFIX.'%3$sCategoryItems.ItemResourceId = %1$s.ResourceId
 															LEFT JOIN '.TABLE_PREFIX.'Category ON '.TABLE_PREFIX.'Category.CategoryId = '.TABLE_PREFIX.'%3$sCategoryItems.CategoryId
 															LEFT JOIN '.TABLE_PREFIX.'Images img ON img.ResourceId = %1$s.ResourceId AND img.DefaultImg = 1
 															LEFT JOIN '.TABLE_PREFIX.'ProductFiles files ON files.ProductId = %1$s.ProductId AND files.IsPrimary = 1
 															LEFT JOIN '.TABLE_PREFIX.'ProductsPricing pricing ON pricing.ProductId = %1$s.ProductId AND pricing.IsPrimary = 1
 															LEFT JOIN '.TABLE_PREFIX.'Manufacturers ON '.TABLE_PREFIX.'Manufacturers.ManufacturerId = %1$s.ManufacturerId
 															LEFT JOIN '.TABLE_PREFIX.'%3$sProductsCustomData cust ON %1$s.ResourceId = cust.ResourceId',
 																		),
 					'SubItems'		=> Array ('pr', 'rev', 'img', 'po', 'poc', 'p-ci', 'rel', 'file', 'p-cdata', 'p-fav'),
 					'Fields'		=>	Array (
 					    'ProductId'				=>	Array ('type' => 'int', 'not_null' => 1, 'default' => 0,),
 					    'Name'					=>	Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'required' => 1, 'max_len' =>255, 'default' => ''),
     					'AutomaticFilename'		=>	Array (
-					    	'type' => 'int', 
-					    	'formatter' => 'kOptionsFormatter', 
+					    	'type' => 'int',
+					    	'formatter' => 'kOptionsFormatter',
 					    	'options' => Array (0 => 'la_No', 1 => 'la_Yes'),
-					    	'use_phrases' => 1, 'not_null' => 1, 'default' => 1, 
+					    	'use_phrases' => 1, 'not_null' => 1, 'default' => 1,
 						),
 					    'SKU'					=>	Array ('type' => 'string', 'required' => 1, 'max_len' =>255, 'error_msgs' => Array ('required' => 'Please fill in'), 'default' => NULL),
 					    'Description'			=>	Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'using_fck' => 1, 'default' => NULL),
 					    'DescriptionExcerpt'	=>	Array ('type' => 'string', 'formatter' => 'kMultiLanguage', 'using_fck' => 1, 'default' => NULL),
 					    'Weight'				=>	Array ('type' => 'float', 'min_value_exc' => 0, 'formatter' => 'kUnitFormatter', 'format' => '%0.2f', 'default' => NULL),
 					    'MSRP'					=>	Array ('type' => 'float', 'min_value_inc' => 0, 'formatter' => 'kFormatter', 'format' => '%0.2f', 'default' => NULL),
 					    'ManufacturerId'		=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' =>Array (0 => ''), 'options_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'Manufacturers ORDER BY Name', 'option_key_field' => 'ManufacturerId', 'option_title_field' => 'Name', 'not_null' => 1, 'default' => 0),
 					    'Status'				=>	Array (
 					    	'type' => 'int',
-					    	'formatter' => 'kOptionsFormatter', 
+					    	'formatter' => 'kOptionsFormatter',
 					    	'options' => Array (1 => 'la_Active', 2 => 'la_Pending', 0 => 'la_Disabled'), 'use_phrases' => 1,
 					    	'default' => 2, 'not_null' => 1,
 						),
 					    'BackOrder'				=>	Array ('type' => 'int', 'not_null' => 1, 'options' => Array ( 2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never' ), 'use_phrases' => 1, 'default' => 2 ),
 					    'BackOrderDate'			=>	Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'error_msgs' => Array ('bad_date_format' => 'Please use the following date format: %s'), 'default' => NULL),
 					    'NewItem'				=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( 2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never' ), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2 ),
 					    'HotItem'				=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( 2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never' ), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2 ),
 					    'PopItem'				=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( 2 => 'la_Auto', 1 => 'la_Always', 0 => 'la_Never' ), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2 ),
 					    'EditorsPick'			=>	Array (
 					    	'type' => 'int',
 					    	'formatter' => 'kOptionsFormatter',
 					    	'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
 					    	'not_null' => 1, 'default' => 0,
 						),
 					    'Featured'				=>	Array (
 					    	'type' => 'int',
 					    	'formatter' => 'kOptionsFormatter',
 					    	'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
 					    	'not_null' => 1, 'default' => 0,
 						),
 					    'OnSale'				=>	Array (
 					    	'type' => 'int',
 					    	'formatter' => 'kOptionsFormatter',
 					    	'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
 					    	'not_null' => 1, 'default' => 0,
 						),
 					    'Priority'				=>	Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 					    'CachedRating'			=>	Array ('not_null' => 1, 'default' => 0, 'type' => 'float', 'formatter' => 'kFormatter', 'format' => '%0.2f'),
 					    'CachedVotesQty'		=>	Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 					    'Hits'					=>	Array ('type' => 'double', 'formatter' => 'kFormatter', 'format' => '%d', 'not_null' => 1, 'default' => 0),
 					    'CreatedOn'				=>	Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => '#NOW#'),
 					    'Expire'				=>	Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' =>null),
 					    'Type'					=>	Array ('type' => 'int', 'not_null' => 1, 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' =>Array (1=> 'la_product_tangible',  2=> 'la_product_subscription', 4=> 'la_product_downloadable', 3=> 'la_product_service'/*, 6=> 'la_gift_certificate', 5=> 'la_product_package'*/), 'not_null' => 1, 'default' => 1 ),
 					    'Modified'				=>	Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => '#NOW#'),
 					    'ModifiedById'			=>	Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 					    'CreatedById'			=>	Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 					    'ResourceId'			=>	Array ('type' => 'int', 'default' => 0),
 					    'CachedReviewsQty'		=>	Array ('type' => 'int', 'formatter' => 'kFormatter', 'format' => '%d', 'not_null' => 1, 'default' => 0),
 					    'InventoryStatus' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_Disabled', 1 => 'la_by_product', 2 => 'la_by_options'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0),
 					    'QtyInStock'			=>	Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 					    'QtyInStockMin'			=>	Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 					    'QtyReserved'			=>	Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 					    'QtyBackOrdered'		=>	Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 					    'QtyOnOrder'			=>	Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 					    'InventoryComment'		=>	Array ('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => null),
 					    'Qty'					=>	Array ('type' => 'int', 'formatter' => 'kFormatter', 'regexp' => '/^[\d]+$/', 'error_msgs' => Array ('invalid_format' => '!la_invalid_integer!')),
 					    'AccessGroupId'			=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'PortalGroup WHERE System!=1 AND Personal !=1 ORDER BY Name', 'option_key_field' => 'GroupId', 'option_title_field' => 'Name', 'default' => NULL),
 					    'AccessDuration'		=>	Array ('type' => 'int', 'default' => NULL),
 					    'AccessDurationType'	=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' =>Array (1 => 'la_opt_sec', 2 => 'la_opt_min', 3 => 'la_opt_hour', 4 => 'la_opt_day', 5 => 'la_opt_week', 6 => 'la_opt_month', 7 => 'la_opt_year' ), 'default' => NULL,),
 					    'AccessStart'			=>	Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL),
 					    'AccessEnd'				=>	Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL,),
 					    'OptionsSelectionMode'	=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' =>Array (0 => 'la_opt_Selection', 1 => 'la_opt_List'), 'default' => 0),
 					    'HasRequiredOptions'	=>	Array ('type' => 'int', 'default' => 0, 'not_null' => 1),
 					    'Virtual'				=>	Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 						'ProcessingData'		=>	Array ('type' => 'string', 'default' => ''),
         				'PackageContent'		=>	Array ('type' => 'string', 'default' => NULL),
         				'IsRecurringBilling'	=>	Array (
-					    	'type' => 'int', 
-					    	'formatter' => 'kOptionsFormatter', 
+					    	'type' => 'int',
+					    	'formatter' => 'kOptionsFormatter',
 					    	'options' => Array (0 => 'la_No', 1 => 'la_Yes'),
-					    	'use_phrases' => 1, 'not_null' => 1, 'default' => 0, 
+					    	'use_phrases' => 1, 'not_null' => 1, 'default' => 0,
 						),
 						//'PayPalRecurring'		=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'not_null' => '1', 'default' => '0'),
         				'ShippingMode'			=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1, 'options' =>Array (0 => 'la_shipping_AnyAndSelected', 1 => 'la_shipping_Limited'), 'not_null' => 1, 'default' =>0),
 
         				'ProcessingData'		=>	Array ('type' => 'string', 'default' => null),
 
 						'ShippingLimitation'	=>	Array ('type' => 'string', 'default' => NULL),
 						'AssignedCoupon'	=>
 								Array ('type' => 'int', 'not_null' => 1, 'default' => 0,
 											'formatter' => 'kLEFTFormatter',
 											'options' => Array (0 => 'None'),
 											'left_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'ProductsCoupons WHERE `%s` = \'%s\'',
 											'left_key_field' => 'CouponId',
 											'left_title_field' => 'Name'),
 						'MinQtyFreePromoShipping'	=>	Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 						'MetaKeywords'			=>	Array ('type' => 'string', 'default' => null),
 	            		'MetaDescription'		=>	Array ('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => null),
 					),
-					
+
 					'VirtualFields'	=> 	Array (
 						'Qty'					=>	1,
 						'Price'					=>	Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%.2f', 'default' => NULL),
 						'Cost'					=>	Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%.2f', 'default' => NULL),
 						'IsHot'					=>	Array ('type' => 'int'),
 						'IsNew'					=>	Array ('type' => 'int'),
 						'IsPop'					=>	Array ('type' => 'int'),
 						'Manufacturer'			=>	Array (),
 
 						// export related fields: begin
 						'CategoryId'			=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (), 'default' => 0),
 						'ExportFormat'			=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'CSV', /*2 => 'XML'*/), 'default' => 1),
 						'ExportFilename'		=>	Array ('type' => 'string', 'default' => ''),
 						'FieldsSeparatedBy'		=>	Array ('type' => 'string', 'default' => ', '),
 						'FieldsEnclosedBy'		=>	Array ('type' => 'string', 'default' => '"'),
 						'LineEndings'			=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'Windows', 2 => 'UNIX'), 'default' => 1),
 						'LineEndingsInside'		=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'CRLF', 2 => 'LF'), 'default' => 2),
 						'IncludeFieldTitles'	=>	Array (
-					    	'type' => 'int', 
-					    	'formatter' => 'kOptionsFormatter', 
+					    	'type' => 'int',
+					    	'formatter' => 'kOptionsFormatter',
 					    	'options' => Array (0 => 'la_No', 1 => 'la_Yes'),
 					    	'use_phrases' => 1, 'not_null' => 1, 'default' => 1,
 						),
 						'ExportColumns'			=>	Array ('type' => 'string', 'formatter' => 'kOptionsFormatter', 'options' => Array ()),
 						'AvailableColumns'		=>	Array ('type' => 'string', 'formatter' => 'kOptionsFormatter', 'options' => Array ()),
 						'CategoryFormat'		=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_MixedCategoryPath', 2 => 'la_SeparatedCategoryPath'), 'use_phrases' => 1, 'default' => 1),
 						'CategorySeparator'		=>	Array ('type' => 'string', 'default' => ':'),
 						'IsBaseCategory'		=>	Array (
-					    	'type' => 'int', 
-					    	'formatter' => 'kOptionsFormatter', 
+					    	'type' => 'int',
+					    	'formatter' => 'kOptionsFormatter',
 					    	'options' => Array (0 => 'la_No', 1 => 'la_Yes'),
 					    	'use_phrases' => 1, 'not_null' => 1, 'default' => 0,
 						),
 						// export related fields: end
 
 						// import related fields: begin
 						'FieldTitles'			=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Automatic', 2 => 'la_Manual'), 'use_phrases' => 1, 'default' => 1),
 						'ImportSource'			=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Upload', 2 => 'la_Local'), 'use_phrases' => 1, 'default' => 2),
 						'ImportFilename'		=>	Array ('type' => 'string', 'formatter' => 'kUploadFormatter', 'max_size' => MAX_UPLOAD_SIZE, 'upload_dir' => (defined('EXPORT_BASE_PATH') ? EXPORT_BASE_PATH : '/admin/export') . '/'),
 						'ImportLocalFilename'	=>	Array ('type' => 'string', 'formatter' => 'kOptionsFormatter', 'default' => ''),
 						'CheckDuplicatesMethod'	=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_IDField', 2 => 'la_OtherFields'), 'use_phrases' => 1, 'default' => 1),
 						'ReplaceDuplicates'		=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1, 'default' => 0),
 						'DuplicateCheckFields'	=>	Array ('type' => 'string', 'formatter' => 'kOptionsFormatter', 'options' => Array ('Name' => 'NAME'), 'default' => '|Name|'),
 						'SkipFirstRow'			=>	Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1, 'default' => 1),
 						// import related fields: end
 
 						'ThumbnailImage'		=>	Array ('type' => 'string', 'default' => ''),
 						'FullImage'				=>	Array ('type' => 'string', 'default' => ''),
 						'ImageAlt'				=>	Array ('type' => 'string', 'default' => ''),
 
 						'Filename' 				=>	Array ('type' => 'string', 'not_null' => '1', 'default' => ''),
 						'CachedNavbar'			=>	Array ('type' => 'string', 'default' => ''),
 						'ParentPath'			=>	Array ('type' => 'string', 'default' => ''),
 
 						'FileSize'				=>	Array ('type' => 'int', 'formatter' => 'kFilesizeFormatter', 'not_null' => 1, 'default' => 0),
 						'FilePath'				=>	Array (),
 						'FileVersion'			=>	Array (),
 
 						// for primary image
 						'SameImages'	=>	Array ('type' => 'string', 'default' => ''),
 						'LocalThumb'	=>	Array ('type' => 'string', 'default' => ''),
 						'ThumbPath'		=>	Array ('type' => 'string', 'default' => ''),
 						'ThumbUrl'		=>	Array ('type' => 'string', 'default' => ''),
 						'LocalImage'	=>	Array ('type' => 'string', 'default' => ''),
 						'LocalPath'		=>	Array ('type' => 'string', 'default' => ''),
 						'FullUrl'		=>	Array ('type' => 'string', 'default' => ''),
 					),
-					
+
 					'Grids'	=> Array (
 						'Default' => Array (
 							'Icons' => Array (
 								'default' => 'icon16_product.png',
 								0 => 'icon16_product_disabled.png',
 								1 => 'icon16_product.png',
 								2 => 'icon16_product_pending.png',
 								'NEW' => 'icon16_product_new.png',
 							),
 							'Fields' => Array (
 								'ProductId' => Array ( 'title' => 'la_col_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 60, ),
 								'SKU' => Array ( 'title' => 'la_col_ProductSKU', 'filter_block' => 'grid_like_filter', 'width' => 100, ),
 								'Name' => Array ( 'title' => 'la_col_ProductName', 'data_block' => 'grid_catitem_td', 'filter_block' => 'grid_like_filter', 'width' => 150, ),
 								'Type'	=>	Array ('title' => 'la_col_ProductType', 'filter_block' => 'grid_options_filter', 'width' => 80, ),
 								'Manufacturer'	=>	Array ('title' => 'la_col_Manufacturer', 'filter_block' => 'grid_like_filter', 'width' => 100, ),
 								'Price'	=>	Array ('title' => 'la_col_Price', 'filter_block' => 'grid_range_filter', 'width' => 70, ),
 								'Status' => Array ('title' => 'la_col_Status', 'filter_block' => 'grid_options_filter', 'width' => 70, ),
 								'QtyInStock'	=>	Array ('title' => 'la_col_Qty', 'data_block' => 'qty_td', 'filter_block' => 'grid_range_filter', 'width' => 80, ),
 								'QtyBackOrdered'	=>	Array ('title' => 'la_col_QtyBackOrdered', 'filter_block' => 'grid_range_filter', 'width' => 80, ),
 								'OnSale'	=>	Array ('title' => 'la_col_OnSale', 'filter_block' => 'grid_options_filter', 'width' => 70, ),							/*
 								'Weight' => Array ( 'title' => 'la_col_ProductWeight', 'filter_block' => 'grid_float_range_filter', 'width' => 150, ),
 								'CreatedOn' => Array ( 'title' => 'la_col_ProductCreatedOn', 'filter_block' => 'grid_date_range_filter', 'width' => 150, ),
 								'BackOrderDate' => Array ( 'title' => 'la_col_ProductBackOrderDate', 'filter_block' => 'grid_date_range_filter', 'width' => 150, ),
 							*/
 							),
 						),
 
 						'Radio' => Array (
 							'Icons' => Array (
 								'default' => 'icon16_product.png',
 								0 => 'icon16_product_disabled.png',
 								1 => 'icon16_product.png',
 								2 => 'icon16_product_pending.png',
 								'NEW' => 'icon16_product_new.png',
 							),
 							'Selector' => 'radio',
 							'Fields' => Array (
 								'ProductId' => Array ( 'title' => 'la_col_Id', 'data_block' => 'grid_radio_td', 'filter_block' => 'grid_range_filter', 'width' => 60, ),
 								'SKU' => Array ( 'title' => 'la_col_ProductSKU', 'filter_block' => 'grid_like_filter', 'width' => 100, ),
 								'Name' => Array ( 'title' => 'la_col_ProductName', 'data_block' => 'grid_catitem_td', 'filter_block' => 'grid_like_filter', 'width' => 150, ),
 								'Type'	=>	Array ('title' => 'la_col_ProductType', 'filter_block' => 'grid_options_filter', 'width' => 80, ),
 								'Manufacturer'	=>	Array ('title' => 'la_col_Manufacturer', 'filter_block' => 'grid_like_filter', 'width' => 100, ),
 								'Price'	=>	Array ('title' => 'la_col_Price', 'filter_block' => 'grid_range_filter', 'width' => 70, ),
 								'Status' => Array ('title' => 'la_col_Status', 'filter_block' => 'grid_options_filter', 'width' => 70, ),
 								'QtyInStock'	=>	Array ('title' => 'la_col_Qty', 'data_block' => 'qty_td', 'filter_block' => 'grid_range_filter', 'width' => 80, ),
 								'QtyBackOrdered'	=>	Array ('title' => 'la_col_QtyBackOrdered', 'filter_block' => 'grid_range_filter', 'width' => 80, ),
 
 							),
 						),
 					),
 
 					'ConfigMapping' => 	Array (
 												'PerPage'				=>	'Comm_Perpage_Products',
 												'ShortListPerPage'		=>	'Comm_Perpage_Products_Short',
 												'ForceEditorPick'		=>	'products_EditorPicksAboveRegular',
 												'DefaultSorting1Field'	=>	'product_OrderProductsBy',
 												'DefaultSorting2Field'	=>	'product_OrderProductsThenBy',
 												'DefaultSorting1Dir'	=>	'product_OrderProductsByDir',
 												'DefaultSorting2Dir'	=>	'product_OrderProductsThenByDir',
 
 												'RatingDelayValue'		=>	'product_RatingDelay_Value',
 												'RatingDelayInterval'	=>	'product_RatingDelay_Interval',
 										),
 	);