<?php
if( !defined( '_VALID_MOS' ) && !defined( '_JEXEC' ) ) die( 'Direct Access to '.basename(__FILE__).' is not allowed.' );
/**
*
* @version $Id: ps_product.php 2615 2010-10-31 12:07:48Z zanardi $
* @package VirtueMart
* @subpackage classes
* @copyright Copyright (C) 2004-2009 soeren - All rights reserved.
* @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.php
* VirtueMart is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See /administrator/components/com_virtuemart/COPYRIGHT.php for copyright notices and details.
*
* http://virtuemart.net
*/

/**
 * The class is is used to manage product repository.
 * @package virtuemart
 * @author pablo, jep, gday, soeren
 * 
 */
class vm_ps_product extends vmAbstractObject {
	var $_key = 'product_id';
	var $_table_name = '#__{vm}_product';
	
	/**
	 * Validates product fields and uploaded image files.
	 *
	 * @param array $d The input vars
	 * @return boolean True when validation successful, false when not
	 */
	function validate(&$d) {
		global $vmLogger, $database, $perm, $VM_LANG;
		require_once(CLASSPATH . 'imageTools.class.php' );
		
		$valid = true;
		$db = new ps_DB;

		$q = "SELECT product_id,product_thumb_image,product_full_image FROM #__{vm}_product WHERE product_sku='";
		$q .= $d["product_sku"] . "'";
		$db->setQuery($q); $db->query();
		if ($db->next_record()&&($db->f("product_id") != $d["product_id"])) {
			$vmLogger->err( "A Product with the SKU ".$d['product_sku']." already exists." );
			$valid = false;
		}
		if( !empty( $d['product_discount_id'] )) {
			if( $d['product_discount_id'] == "override" ) {

				$d['is_percent'] = "0";
				
				// If discount are applied before tax then base the discount on the untaxed price
				if( PAYMENT_DISCOUNT_BEFORE == '1' ) {
					$d['amount'] = (float)$d['product_price'] - (float)$d['discounted_price_override'];
				}
				// Otherwise, base the discount on the taxed price
				else {
					$d['amount'] = (float)$d['product_price_incl_tax'] - (float)$d['discounted_price_override'];
				}
				
				// Set the discount start date as today
				$d['start_date'] = date( 'Y-m-d' );

				require_once( CLASSPATH. 'ps_product_discount.php' );
				$ps_product_discount = new ps_product_discount;
				$ps_product_discount->add( $d );
				$d['product_discount_id'] = $database->insertid();
				vmRequest::setVar( 'product_discount_id', $d['product_discount_id'] );
			}
		}
		if (empty($d['manufacturer_id'])) {
			$d['manufacturer_id'] = "1";
		}
		if (empty( $d["product_sku"])) {
			$vmLogger->err( $VM_LANG->_('VM_PRODUCT_MISSING_SKU',false) );
			$valid = false;
		}
		if (!$d["product_name"]) {
			$vmLogger->err( $VM_LANG->_('VM_PRODUCT_MISSING_NAME',false) );
			$valid = false;
		}
		if (empty($d["product_available_date"])) {
			$vmLogger->err( $VM_LANG->_('VM_PRODUCT_MISSING_AVAILDATE',false) );
			$valid = false;
		}
		else {
			$day = (int) substr ( $d["product_available_date"], 8, 2);
			$month= (int) substr ( $d["product_available_date"], 5, 2);
			$year = (int) substr ( $d["product_available_date"], 0, 4);
			$d["product_available_date_timestamp"] = mktime(0,0,0,$month, $day, $year);
		}

		/** Validate Product Specific Fields **/
		if (!$d["product_parent_id"]) {
			if( empty( $d['product_categories']) || !is_array(@$d['product_categories'])) {
				$d['product_categories'] = explode('|', $d['category_ids'] );
			}
			if (sizeof(@$d["product_categories"]) < 1) {
				$vmLogger->err( $VM_LANG->_('VM_PRODUCT_MISSING_CATEGORY') );
				$valid = false;
			}
		}
		/** Image Upload Validation **/

		// do we have an image URL or an image File Upload?
		if (!empty( $d['product_thumb_image_url'] )) {
			// Image URL
			if (substr( $d['product_thumb_image_url'], 0, 4) != "http") {
				$vmLogger->err( $VM_LANG->_('VM_PRODUCT_IMAGEURL_MUSTBEGIN',false) );
				$valid =  false;
			}

			// if we have an uploaded image file, prepare this one for deleting.
			if( $db->f("product_thumb_image") && substr( $db->f("product_thumb_image"), 0, 4) != "http") {
				$_REQUEST["product_thumb_image_curr"] = $db->f("product_thumb_image");
				$d["product_thumb_image_action"] = "delete";
				if (!vmImageTools::validate_image($d,"product_thumb_image","product")) {
					return false;
				}
			}
			$d["product_thumb_image"] = $d['product_thumb_image_url'];
		}
		else {
			// File Upload
			if (!vmImageTools::validate_image($d,"product_thumb_image","product")) {
				$valid = false;
			}
		}

		if (!empty( $d['product_full_image_url'] )) {
			// Image URL
			if (substr( $d['product_full_image_url'], 0, 4) != "http") {
				$vmLogger->err( $VM_LANG->_('VM_PRODUCT_IMAGEURL_MUSTBEGIN',false) );
				return false;
			}
			// if we have an uploaded image file, prepare this one for deleting.
			if( $db->f("product_full_image") && substr( $db->f("product_full_image"), 0, 4) != "http") {
				$_REQUEST["product_full_image_curr"] = $db->f("product_full_image");
				$d["product_full_image_action"] = "delete";
				if (!vmImageTools::validate_image($d,"product_full_image","product")) {
					return false;
				}
			}
			$d["product_full_image"] = $d['product_full_image_url'];
		}
		else {
			// File Upload
			if (!vmImageTools::validate_image($d,"product_full_image","product")) {
				$valid = false;
			}
		}
		
		// validate attribute names
		foreach ( $d["attributeX"] as $attributeX ) {
			// if we only have one attribute it can be left empty
			if ( ( $attributeX["name"] == "" ) and ( count ( $d["attributeX"] ) > 1 ) ) {
				$vmLogger->err( $VM_LANG->_('VM_PRODUCT_MISSING_ATTRIBUTE_NAME',false) );
				$valid = false;	
			}
			if ( 
				strpos( $attributeX["name"], ":" ) or 
				strpos( $attributeX["name"], "." ) or
				strpos( $attributeX["name"], "&" ) or
				strpos( $attributeX["name"], "'" )
			) {
				$vmLogger->err( $VM_LANG->_('VM_PRODUCT_INVALID_ATTRIBUTE_NAME',false) );
				$valid = false;	
			}
		}			
			
		// added for advanced attribute modification
		// strips the trailing semi-colon from an attribute
        if(isset($d["product_advanced_attribute"])) {
		    if (';' == substr($d["product_advanced_attribute"], strlen($d["product_advanced_attribute"])-1,1) ) {
			    $d["product_advanced_attribute"] =substr($d["product_advanced_attribute"], 0, strlen($d["product_advanced_attribute"])-1);
		    }
        }
		// added for custom attribute modification
		// strips the trailing semi-colon from an attribute
        if(isset($d["product_custom_attribute"])) {
		    if (';' == substr($d["product_custom_attribute"], strlen($d["product_custom_attribute"])-1,1) ) {
			    $d["product_custom_attribute"] =substr($d["product_custom_attribute"], 0, strlen($d["product_custom_attribute"])-1);
		    }
        }
		$d["clone_product"] = empty($d["clone_product"]) ? "N" : "Y";
		$d["product_publish"] = empty($d["product_publish"]) ? "N" : "Y";
		$d["product_special"] = empty($d["product_special"]) ? "N" : "Y";
        //parse quantity and child options
        $d['display_headers'] = vmGet($d,'display_headers', 'Y') =='Y' ? 'Y' : 'N';
        $d['product_list_child'] = vmGet($d,'product_list_child', 'Y') =='Y' ? 'Y' : 'N';
        $d['display_use_parent'] = vmGet($d,'display_use_parent', 'Y') =='Y' ? 'Y' : 'N';
        $d['product_list_type'] = vmGet($d,'product_list_type', 'Y') =='Y' ? 'Y' : 'N';
        $d['display_desc'] = vmGet($d,'display_desc', 'Y') =='Y' ? 'Y' : 'N';
        
        if (@$d['product_list'] =="Y") {
            if($d['list_style'] == "one")
                $d['product_list'] = "Y";
            else
                $d['product_list'] = "YM";            
        }
        else {
            $d['product_list'] = "N";
        }
        
        $d['quantity_options'] = ps_product::set_quantity_options($d);
		$d['child_options'] = ps_product::set_child_options($d);
		
        $d['order_levels'] = vmRequest::getInt('min_order_level').",".vmRequest::getInt('max_order_level');

		return $valid;
	}

	/**
	 * Validates that a product can be deleted
	 *
	 * @param array $d The input vars
	 * @return boolean Validation sucessful?
	 */
	function validate_delete( $product_id, &$d ) {
		global $vmLogger, $VM_LANG;
		require_once(CLASSPATH . 'imageTools.class.php' );
		/* Check that ps_vendor_id and product_id match
		if (!$this->check_vendor($d)) {
		$d["error"] = "ERROR: Cannot delete product. Wrong product or vendor." ;
		return false;
		}*/
		if (empty($product_id)) {
			$vmLogger->err( $VM_LANG->_('VM_PRODUCT_SPECIFY_DELETE',false) );
			return false;
		}
		/* Get the image filenames from the database */
		$db = new ps_DB;
		$q  = "SELECT product_thumb_image,product_full_image ";
		$q .= "FROM #__{vm}_product ";
		$q .= "WHERE product_id='$product_id'";
		$db->setQuery($q); $db->query();
		$db->next_record();

		/* Prepare product_thumb_image for Deleting */
		if( !stristr( $db->f("product_thumb_image"), "http") ) {
			$_REQUEST["product_thumb_image_curr"] = $db->f("product_thumb_image");
			$d["product_thumb_image_action"] = "delete";
			if (!vmImageTools::validate_image($d,"product_thumb_image","product")) {
				$vmLogger->err( $VM_LANG->_('VM_PRODUCT_IMGDEL_FAILED',false) );
				return false;
			}
		}
		/* Prepare product_full_image for Deleting */
		if( !stristr( $db->f("product_full_image"), "http") ) {
			$_REQUEST["product_full_image_curr"] = $db->f("product_full_image");
			$d["product_full_image_action"] = "delete";
			if (!vmImageTools::validate_image($d,"product_full_image","product")) {
				return false;
			}
		}
		return true;

	}

	/**
	 * Function to add a new product into the product table
	 *
	 * @param array $d The input vars
	 * @return boolean True, when the product was added, false when not
	 */
	function add( &$d ) {
		global $perm, $vmLogger, $VM_LANG;
		$database = new ps_DB();

		if (!$this->validate($d)) {
			return false;
		}

		if (!vmImageTools::process_images($d)) {
			return false;
		}

		$timestamp = time();
		$db = new ps_DB;

		$ps_vendor_id = $_SESSION["ps_vendor_id"];
		if( $perm->check( 'admin' )) {
			$vendor_id = $d['vendor_id'];
		}
		else {
			$vendor_id = $ps_vendor_id;
		}
        // Insert into DB
		$fields = array ( 'vendor_id' => $vendor_id,
						'product_parent_id' => vmRequest::getInt('product_parent_id'),
						'product_sku' => vmGet($d,'product_sku'),
						'product_name' => vmGet($d,'product_name'),
						'product_desc' => vmRequest::getVar('product_desc', '', 'default', '', VMREQUEST_ALLOWHTML),
						'product_s_desc' => vmRequest::getVar('product_s_desc', '', 'default', '', VMREQUEST_ALLOWHTML),
						'product_thumb_image' => vmGet($d,'product_thumb_image'),
						'product_full_image' => vmGet($d,'product_full_image'),
						'product_publish' => $d['product_publish'],
						'product_weight' => vmRequest::getFloat('product_weight'),
						'product_weight_uom' => vmGet($d,'product_weight_uom'),
						'product_length' => vmRequest::getFloat('product_length'),
						'product_width' => vmRequest::getFloat('product_width'),
						'product_height' => vmRequest::getFloat('product_height'),
						'product_lwh_uom' => vmGet($d,'product_lwh_uom'),
						'product_unit' => vmGet($d,'product_unit'),
						'product_packaging' => (($d["product_box"] << 16) | ($d["product_packaging"]&0xFFFF)),
						'product_url' => vmGet($d,'product_url'),
						'product_in_stock' => vmRequest::getInt('product_in_stock'),
						'attribute' => ps_product_attribute::formatAttributeX(),
						'custom_attribute' => vmGet($d,'product_custom_attribute'),
						'product_available_date' => $d['product_available_date_timestamp'],
						'product_availability' => vmGet($d,'product_availability'),
						'product_special' => $d['product_special'],
						'child_options' => $d['child_options'],
						'quantity_options' => $d['quantity_options'],
						'product_discount_id' => vmRequest::getInt('product_discount_id'),
						'cdate' => $timestamp,
						'mdate' => $timestamp,
						'product_tax_id' => vmRequest::getInt('product_tax_id'),
						'child_option_ids' => vmGet($d,'included_product_id'),
						'product_order_levels' => $d['order_levels'] );

		$db->buildQuery('INSERT', '#__{vm}_product', $fields );
		if( $db->query() === false ) {
			$vmLogger->err( $VM_LANG->_('VM_PRODUCT_ADDING_FAILED',false) );
			return false;
		}

		$d["product_id"] = $_REQUEST['product_id'] = $db->last_insert_id();

		// If is Item, add attributes from parent //
		if ($d["product_parent_id"]) {
			$q  = "SELECT attribute_name FROM #__{vm}_product_attribute_sku ";
			$q .= "WHERE product_id='" . vmRequest::getInt('product_parent_id') . "' ";
			$q .= "ORDER BY attribute_list,attribute_name";
			$db->query($q);

			$db2 = new ps_DB;
			$i = 0;
			while($db->next_record()) {
				$i++;
                
				$q = "INSERT INTO #__{vm}_product_attribute (`product_id`,`attribute_name`,`attribute_value`) VALUES ";
				$q .= "('".$d["product_id"]."', '".$db->f("attribute_name", false)."', '".vmGet($d,'attribute_'.$i )."')";
				$db2->query( $q );
			}
		}
		else {
			// If is Product, Insert category ids
			if( empty( $d['product_categories']) || !is_array(@$d['product_categories'])) {
				$d['product_categories'] = explode('|', $d['category_ids'] );
			}
			foreach( $d["product_categories"] as $category_id ) {
				$db->query('SELECT MAX(`product_list`) as list_order FROM `#__{vm}_product_category_xref` WHERE `category_id`='.$category_id );
				$db->next_record();

				$q  = "INSERT INTO #__{vm}_product_category_xref ";
				$q .= "(category_id,product_id,product_list) ";
				$q .= "VALUES ('$category_id','". $d["product_id"] . "', ".intval($db->f('max') +1 ) . ")";
				$db->setQuery($q); $db->query();
			}
		}
		$q = "INSERT INTO #__{vm}_product_mf_xref VALUES (";
		$q .= "'".$d['product_id']."', '".vmRequest::getInt('manufacturer_id')."')";
		$db->setQuery($q); $db->query();

		if( !empty($d["related_products"])) {
			/* Insert Pipe separated Related Product IDs */
			$related_products = vmGet( $d, "related_products" );
			$q  = "INSERT INTO #__{vm}_product_relations ";
			$q .= "(product_id, related_products) ";
			$q .= "VALUES ('".$d["product_id"]."','".$db->getEscaped($related_products)."')";
			$db->setQuery($q); $db->query();

		}
		// ADD A PRICE, IF NOT EMPTY ADD 0
		if (!empty($d['product_price'])) {

			if(empty($d['product_currency'])) {
				$d['product_currency'] = $_SESSION['vendor_currency'];
			}

			$d["price_quantity_start"] = 0;
			$d["price_quantity_end"] = "";
			require_once ( CLASSPATH. 'ps_product_price.php');
			$my_price = new ps_product_price;
			$my_price->add($d);
		}
		
		if( !empty( $d['product_type_id'])) {
			require_once( CLASSPATH.'ps_product_product_type.php' );
			$ps_product_product_type = new ps_product_product_type();
			$ps_product_product_type->add( $d );
				
			// Product Type Parameters!
			$this->handleParameters( $d );
		}
		
		// CLONE PRODUCT additional code
		if( $d["clone_product"] == "Y" ) {

			// Clone Parent Product's Attributes
			$q  = "INSERT INTO #__{vm}_product_attribute_sku
              SELECT '".$d["product_id"]."', attribute_name, attribute_list 
              FROM #__{vm}_product_attribute_sku WHERE product_id='" . (int)$d["old_product_id"] . "' ";
			$db->query( $q );
			if( !empty( $d["child_items"] )) {

				$database->query( "SHOW COLUMNS FROM #__{vm}_product" );
				$rows = $database->record;
				while(list(,$Field) = each( $rows) ) {
					$product_fields[$Field->Field] = $Field->Field;
				}
				// Change the Field Names
				// leave empty for auto_increment
				$product_fields["product_id"] = "''";
				// Update Product Parent ID to the new one
				$product_fields["product_parent_id"] = "'".$d["product_id"]."'";
				// Rename the SKU
				$product_fields["product_sku"] = "CONCAT(product_sku,'_".$d["product_id"]."')";

				$rows = Array();
				$database->query( "SHOW COLUMNS FROM #__{vm}_product_price" );
				$rows = $database->record;
				while(list(,$Field) = each( $rows) ) {
					$price_fields[$Field->Field] = $Field->Field;
				}

				foreach( $d["child_items"] as $child_id ) {
					$q = "INSERT INTO #__{vm}_product ";
					$q .= "SELECT ".implode(",", $product_fields )." FROM #__{vm}_product WHERE product_id='$child_id'";
					$db->query( $q );
					$new_product_id = $db->last_insert_id();

					$q = "INSERT INTO #__{vm}_product_attribute
			                  SELECT NULL, '$new_product_id', attribute_name, attribute_value
			                  FROM #__{vm}_product_attribute WHERE product_id='$child_id'";
					$db->query( $q );

					$price_fields["product_price_id"] = "''";
					$price_fields["product_id"] = "'$new_product_id'";

					$q = "INSERT INTO #__{vm}_product_price ";
					$q .= "SELECT ".implode(",", $price_fields )." FROM #__{vm}_product_price WHERE product_id='$child_id'";
					$db->query( $q );
				}
			}

			// End Cloning

		}
		if( $d['clone_product'] == 'Y') {
			$vmLogger->info( $VM_LANG->_('VM_PRODUCT_CLONED',false) );
		}
		else {
			$vmLogger->info( $VM_LANG->_('VM_PRODUCT_ADDED',false) );
		}
		return true;
	}

	/**
	 * Function to update product $d['product_id'] in the product table
	 *
	 * @param array $d The input vars
	 * @return boolean True, when the product was updated, false when not
	 */
	function update( &$d ) {
		global $vmLogger, $perm, $VM_LANG;
		require_once(CLASSPATH.'ps_product_attribute.php');
		
		if (!$this->validate($d)) {
			return false;
		}

		if (!vmImageTools::process_images($d)) {
			return false;
		}

		$timestamp = time();
		$db = new ps_DB;
		$ps_vendor_id = $_SESSION["ps_vendor_id"];
		if( $perm->check( 'admin' )) {
			$vendor_id = $d['vendor_id'];
		}
		else {
			$vendor_id = $ps_vendor_id;
		}
		$old_vendor_id = $this->get_field($d['product_id'], 'vendor_id');
		
        // Insert into DB
		$fields = array ( 'vendor_id' => $vendor_id,
						'product_sku' => vmGet($d,'product_sku'),
						'product_name' => vmGet($d,'product_name'),
						'product_desc' => vmRequest::getVar('product_desc', '', 'default', '', VMREQUEST_ALLOWHTML),
						'product_s_desc' => vmRequest::getVar('product_s_desc', '', 'default', '', VMREQUEST_ALLOWHTML),
						'product_thumb_image' => vmGet($d,'product_thumb_image'),
						'product_full_image' => vmGet($d,'product_full_image'),
						'product_publish' => $d['product_publish'],
						'product_weight' => vmRequest::getFloat('product_weight'),
						'product_weight_uom' => vmGet($d,'product_weight_uom'),
						'product_length' => vmRequest::getFloat('product_length'),
						'product_width' => vmRequest::getFloat('product_width'),
						'product_height' => vmRequest::getFloat('product_height'),
						'product_lwh_uom' => vmGet($d,'product_lwh_uom'),
						'product_unit' => vmGet($d,'product_unit'),
						'product_packaging' => (($d["product_box"] << 16) | ($d["product_packaging"]&0xFFFF)),
						'product_url' => vmGet($d,'product_url'),
						'product_in_stock' => vmRequest::getInt('product_in_stock'),
						'attribute' => ps_product_attribute::formatAttributeX(),
						'custom_attribute' => vmGet($d,'product_custom_attribute'),
						'product_available_date' => $d['product_available_date_timestamp'],
						'product_availability' => vmGet($d,'product_availability'),
						'product_special' => $d['product_special'],
						'child_options' => $d['child_options'],
						'quantity_options' => $d['quantity_options'],
						'product_discount_id' => vmRequest::getInt('product_discount_id'),
						'mdate' => $timestamp,
						'product_tax_id' => vmRequest::getInt('product_tax_id'),
						'child_option_ids' => vmGet($d,'included_product_id'),
						'product_order_levels' => $d['order_levels'] );
						
		$db->buildQuery( 'UPDATE', '#__{vm}_product', $fields,  'WHERE product_id='. (int)$d["product_id"] . ' AND vendor_id=' . (int)$old_vendor_id );
		$db->query();

		/* notify the shoppers that the product is here */
		/* see zw_waiting_list */
		if ($d["product_in_stock"] > "0" && @$d['notify_users'] == '1' && $d['product_in_stock_old'] == '0') {
			require_once( CLASSPATH . 'zw_waiting_list.php');
			$zw_waiting_list = new zw_waiting_list;
			$zw_waiting_list->notify_list($d["product_id"]);
		}

		// Check if the Manufacturer XRef is missing
		if($this->get_manufacturer_id($d['product_id'])) {
			$q = "UPDATE #__{vm}_product_mf_xref SET ";
			$q .= 'manufacturer_id='.vmRequest::getInt('manufacturer_id').' ';
			$q .= 'WHERE product_id = '.$d['product_id'];
		} else {
			$q = "INSERT INTO #__{vm}_product_mf_xref (product_id,manufacturer_id) VALUES ('".$d['product_id']."','".vmRequest::getInt('manufacturer_id')."')";
		}
		$db->query($q);

		/* If is Item, update attributes */
		if( !empty($d["product_parent_id"])) {
			$q  = "SELECT attribute_name FROM #__{vm}_product_attribute_sku ";
			$q .= 'WHERE product_id=' .(int)$d["product_parent_id"] . ' ';
			$q .= "ORDER BY attribute_list,attribute_name";
			$db->query($q);

			$db2 = new ps_DB;
			$i = 0;
			while($db->next_record()) {
				$i++;
				$q2  = "UPDATE #__{vm}_product_attribute SET ";
				$q2 .= "attribute_value='" .vmGet($d,'attribute_'.$i ) . "' ";
				$q2 .= "WHERE product_id = '" . $d["product_id"] . "' ";
				$q2 .= "AND attribute_name = '" . $db->f("attribute_name", false ) . "' ";
				$db2->setQuery($q2); $db2->query();
			}
			/* If it is a Product, update Category */
		}
		else {
			// Handle category selection: product_category_xref
			$q  = "SELECT `category_id` FROM `#__{vm}_product_category_xref` ";
			$q .= "WHERE `product_id` = '" . $d["product_id"] . "' ";
			$db->setQuery($q);
			$db->query();
			$old_categories = array();
			while( $db->next_record()) {
				$old_categories[$db->f('category_id')] = $db->f('category_id');
			}
			// NOW Insert new categories
			$new_categories = array();
			
			if( empty( $d['product_categories']) || !is_array(@$d['product_categories'])) {
				$d['product_categories'] = explode('|', $d['category_ids'] );
			}
			
			foreach( $d["product_categories"] as $category_id ) {
				if( !in_array( $category_id, $old_categories ) ) {
					$db->query('SELECT MAX(`product_list`) as list_order FROM `#__{vm}_product_category_xref` WHERE `category_id`='.(int)$category_id );
					$db->next_record();

					$q  = "INSERT INTO #__{vm}_product_category_xref ";
					$q .= "(category_id,product_id,product_list) ";
					$q .= "VALUES ('".(int)$category_id."','". $d["product_id"] . "', ".intval($db->f('max') +1 ) . ")";
					$db->setQuery($q); $db->query();
					$new_categories[$category_id] = $category_id;
				}
				else {
					unset( $old_categories[$category_id]);
				}
			}
			// The rest of the old categories can be deleted
			foreach( $old_categories as $category_id ) {
				$q  = "DELETE FROM `#__{vm}_product_category_xref` ";
				$q .= "WHERE `product_id` = '" . $d["product_id"] . "' ";
				$q .= "AND `category_id` = '" . $category_id . "' ";
				$db->query($q);
			}
		}

		if( !empty($d["related_products"])) {
			/* Insert Pipe separated Related Product IDs */
			$related_products = vmGet( $d, "related_products" );
			$q  = "REPLACE INTO #__{vm}_product_relations (product_id, related_products)";
			$q .= " VALUES( '".$d["product_id"]."', '$related_products') ";
			$db->query($q);

		}
		else{
			$q  = "DELETE FROM #__{vm}_product_relations WHERE product_id='".$d["product_id"]."'";
			$db->query($q);
		}

		// UPDATE THE PRICE, IF EMPTY ADD 0
		if(empty($d['product_currency'])) {
			$d['product_currency'] = $_SESSION['vendor_currency'];
		}

		// look if we have a price for this product
		$q = "SELECT product_price_id, price_quantity_start, price_quantity_end FROM #__{vm}_product_price ";
		$q .= "WHERE shopper_group_id=" . vmRequest::getInt('shopper_group_id');
		$q .= ' AND product_id = ' . $d["product_id"];
		$db->query($q);


		if ($db->next_record()) {

			$d["product_price_id"] = $db->f("product_price_id");
			require_once ( CLASSPATH. 'ps_product_price.php');
			$my_price = new ps_product_price;

			if (@$d['product_price'] != '') {
				// update prices
				$d["price_quantity_start"] = $db->f("price_quantity_start");
				$d["price_quantity_end"] = $db->f("price_quantity_end");

				$my_price->update($d);
			}
			else {
				// delete the price
				$my_price->delete( $d );
			}
		}
		else {
			if ( $d['product_price'] != '' ) {
				// add the price
				$d["price_quantity_start"] = 0;
				$d["price_quantity_end"] = "";
				require_once ( CLASSPATH. 'ps_product_price.php');
				$my_price = new ps_product_price;
				$my_price->add($d);
			}
		}

		// Product Type Parameters!
		$this->handleParameters( $d );
		 
		$vmLogger->info( $VM_LANG->_('VM_PRODUCT_UPDATED',false) );
		return true;
	}
	
	/**
	 * Handles adding or updating parameter values for a product an its product types
	 * @since VirtueMart 1.1.0
	 * @param array $d
	 */
	function handleParameters( &$d ) {
		global $db;
		
		$product_id= intval( $d["product_id"] );

		$q  = "SELECT `product_type_id` FROM `#__{vm}_product_product_type_xref` WHERE ";
		$q .= "`product_id`=$product_id";
		$db->query($q);

		$dbpt = new ps_DB;
		$dbp = new ps_DB;

		// For every Product Type
		while ($db->next_record()) {
			$product_type_id = $db->f("product_type_id");

			$q  = "SELECT * FROM #__{vm}_product_type_parameter WHERE ";
			$q .= "product_type_id='$product_type_id' ";
			$q .= "ORDER BY parameter_list_order";
			$dbpt->query($q);

			$q  = "SELECT COUNT(`product_id`) as num_rows FROM `#__{vm}_product_type_$product_type_id` WHERE ";
			$q .= "product_id='$product_id'";
			$dbp->query($q); $dbp->next_record();
			
			if ( $dbp->f('num_rows') == 0 ) {  // Add record if not exist (Items)
				$q  = "INSERT INTO #__{vm}_product_type_$product_type_id (product_id) ";
				$q .= "VALUES ('$product_id')";
				$dbp->query($q);
			}

			// Update record
			$q  = "UPDATE #__{vm}_product_type_$product_type_id SET ";
			$q .= "product_id='$product_id'";
			while ($dbpt->next_record()) {
				if ($dbpt->f("parameter_type")!="B") { // if it is not breaker
					$value=$d["product_type_".$product_type_id."_".$dbpt->f("parameter_name")];
					if ($dbpt->f("parameter_type")=="V" && is_array($value)) {
						$value = join(';',$value);
					}
					if ($value=="") {
						$value='NULL'; 
					}
					else { 
						$value="'".$dbpt->getEscaped($value)."'"; 
					}
					$q .= ',`'.$dbpt->f('parameter_name', false).'`='.$value;
				}
			}
			$q .= ' WHERE product_id = '.$d['product_id'];
			$dbp->query($q);
		}

	}
	
	/**
	 * Function to delete product(s) $d['product_id'] from the product table
	 *
	 * @param array $d The input vars
	 * @return boolean True, when the product was deleted, false when not
	 */
	function delete(&$d) {

		$product_id = $d["product_id"];

		if( is_array( $product_id)) {
			foreach( $product_id as $product) {
				if( !$this->delete_product( $product, $d ))
				return false;
			}
			return true;
		}
		else {
			return $this->delete_product( $product_id, $d );
		}
	}
	/**
         * Move a product from one category to another
         *
         * @param array $d
         * @return boolean True on sucess, false on failure
         */
	function move( &$d ) {
		global $db, $vmLogger, $VM_LANG;
		if( !is_array( $d['product_id'])) {
			$vmLogger->err( $VM_LANG->_('VM_PRODUCT_MOVE_NOTFOUND',false));
			return false;
		}
		if( empty( $d['category_id'])) {
			$vmLogger->err( $VM_LANG->_('VM_PRODUCT_MUSTSELECT_ONE_CAT',false));
			return false;
		}
		// Loop though each product
		foreach( $d['product_id'] as $product_id ) {
			// check if the product is already assigned to the category it should be moved to
			$db->query( 'SELECT product_id FROM `#__{vm}_product_category_xref` WHERE `product_id`='.intval($product_id).' AND `category_id`='.intval($d['category_id']));
			if( !$db->next_record()) {
				// If the product is not yet in this category, move it!
				$db->query( 'SELECT MAX(`product_list`) as max FROM `#__{vm}_product_category_xref` WHERE `category_id`='.intval($d['category_id']));
				$db->next_record();
				$db->query('INSERT INTO `#__{vm}_product_category_xref` VALUES ('.intval($d['category_id']).', '.intval($product_id).', '.intval( $db->f('max') + 1) .') ');
			}
			$db->query('DELETE FROM `#__{vm}_product_category_xref` WHERE `product_id`='.intval($product_id).' AND `category_id`='.intval($d['old_category_id']));
		}
		return true;
	}

	/**
	 * The function that holds the code for deleting
	 * one product from the database and all related tables
	 * plus deleting files related to the product
	 *
	 * @param int $product_id
	 * @param array $d The input vars
	 * @return boolean True on success, false on error
	 */
	function delete_product( $product_id, &$d ) {
		global $vmLogger, $VM_LANG;
		$db = new ps_DB;
		if (!$this->validate_delete($product_id, $d)) {
			return false;
		}
		/* If is Product */
		if ($this->is_product($product_id)) {
			/* Delete all items first */
			$q  = "SELECT product_id FROM #__{vm}_product WHERE product_parent_id='$product_id'";
			$db->setQuery($q); $db->query();
			while($db->next_record()) {
				$d2["product_id"] = $db->f("product_id");
				if (!$this->delete($d2)) {
					return false;
				}
			}

			/* Delete attributes */
			$q  = "DELETE FROM #__{vm}_product_attribute_sku WHERE product_id='$product_id' ";
			$db->setQuery($q); $db->query();

			/* Delete categories xref */
			$q  = "DELETE FROM #__{vm}_product_category_xref WHERE product_id = '$product_id' ";
			$db->setQuery($q); $db->query();
		}
		/* If is Item */
		else {
			/* Delete attribute values */
			$q  = "DELETE FROM #__{vm}_product_attribute WHERE product_id='$product_id'";
			$db->setQuery($q); $db->query();
		}
		/* For both Product and Item */

		/* Delete product - manufacturer xref */
		$q = "DELETE FROM #__{vm}_product_mf_xref WHERE product_id='$product_id'";
		$db->setQuery($q); $db->query();

		/* Delete Product - ProductType Relations */
		$q  = "DELETE FROM `#__{vm}_product_product_type_xref` WHERE `product_id`=$product_id";
		$db->setQuery($q); $db->query();

		/* Delete product votes */
		$q  = "DELETE FROM #__{vm}_product_votes WHERE product_id='$product_id'";
		$db->setQuery($q); $db->query();

		/* Delete product reviews */
		$q = "DELETE FROM #__{vm}_product_reviews WHERE product_id='$product_id'";
		$db->setQuery($q); $db->query();

		/* Delete Image files */
		if (!vmImageTools::process_images($d)) {
			return false;
		}
		/* Delete other Files and Images files */
		require_once(  CLASSPATH.'ps_product_files.php' );
		$ps_product_files = new ps_product_files();

		$db->query( "SELECT file_id FROM #__{vm}_product_files WHERE file_product_id='$product_id'" );
		while($db->next_record()) {
			$d["file_id"] = $db->f("file_id");
			$ps_product_files->delete( $d );
		}

		/* Delete Product Relations */
		$q  = "DELETE FROM #__{vm}_product_relations WHERE product_id = '$product_id'";
		$db->setQuery($q); $db->query();

		/* Delete Prices */
		$q  = "DELETE FROM #__{vm}_product_price WHERE product_id = '$product_id'";
		$db->setQuery($q); $db->query();

		/* Delete entry FROM #__{vm}_product table */
		$q  = "DELETE FROM #__{vm}_product WHERE product_id = '$product_id'";
		$db->setQuery($q); $db->query();

		/* If only deleting an item, go to the parent product page after
		** the deletion. This had to be done here because the product id
		** of the item to be deleted had to be passed as product_id */
		if (!empty($d["product_parent_id"])) {
			$d["product_id"] = $d["product_parent_id"];
			$d["product_parent_id"] = "";
		}
		$vmLogger->info( str_replace('{product_id}',$product_id,$VM_LANG->_('VM_PRODUCT_DELETED',false)) );
		return true;
	}

	/**
	 * Function to check if the vendor_id of the product
	 * $d['product_id'] matches the vendor_id associated with the
	 * user that calls this function
	 *
	 * @param array $d
	 * @return boolean True, when vendor_id matches, false when not
	 */
	function check_vendor($d) {

		$ps_vendor_id = $_SESSION["ps_vendor_id"];

		$db = new ps_DB;
		$q  = "SELECT vendor_id  FROM #__{vm}_product ";
		$q .= "WHERE vendor_id = '$ps_vendor_id' ";
		$q .= "AND product_id = '" . $d["product_id"] . "' ";
		$db->query($q);
		if ($db->next_record()) {
			return true;
		} else {
			return false;
		}
	}


	/**
	 * Function to create a ps_DB object holding the data of product $d['product_id']
	 * from the table #__{vm}_product
	 *
	 * @param int $product_id
	 * @return ps_DB DB object holding all data for product $product_id
	 */
	function sql($product_id) {
		$db = new ps_DB;
		if( !empty( $product_id )) {
			$q  = 'SELECT * FROM #__{vm}_product WHERE product_id=' . (int)$product_id;
			$db->setQuery($q); $db->query(); 
		}
		return $db;
	}

	/**
	 * Function to create a db object holding the data of all child items of
	 * product $product_id
	 *
	 * @param int $product_id
	 * @return ps_DB object that holds all items of product $product_id
	 */
	function items_sql($product_id) {
		$db = new ps_DB;
		if( !empty($product_id) ) {
			$q  = "SELECT * FROM #__{vm}_product ";
			$q .= "WHERE product_parent_id=".(int)$product_id.' ';
			$q .= "ORDER BY product_name";
	
			$db->setQuery($q); $db->query();
		}

		return $db;
	}

	/**
	 * Function to check whether a product is a parent product or not
	 * If is is a child product, it has a non-empty value for "product_parent_id"
	 *
	 * @param int $product_id
	 * @return boolean True when the product is a parent product, false when product is a child item
	 */
	function is_product($product_id) {
		$product_parent_id = ps_product::get_field($product_id, 'product_parent_id');
		return $product_parent_id == 0;
	}
	/**
	 * Function to check whether a product is published
	 *
	 *
	 * @param int $product_id
	 * @return boolean True when the product is a parent product, false when product is a child item
	 */
	function is_published($product_id, $check_stock=false) {
		if( CHECK_STOCK != '1') $check_stock=false;
		return ps_product::get_field($product_id, 'product_publish') == 'Y';

	}
	/**
	 * Checks if a product is a downloadable product
	 *
	 * @param int $product_id
	 * @return boolean
	 */
	function is_downloadable($product_id) {
		if( empty( $GLOBALS['product_info'][$product_id]['is_downloadable'] )) {
		    $db_check = new ps_DB;
	        $q_dl = "SELECT attribute_name,attribute_value 
	        				FROM #__{vm}_product_attribute WHERE
							product_id=".(int)$product_id." AND attribute_name='download'";
			$db_check->query($q_dl);
			$db_check->next_record();
			if( $db_check->num_rows() > 0 ) {
				$GLOBALS['product_info'][$product_id]['is_downloadable'] = 'Y';
			} else {
				$GLOBALS['product_info'][$product_id]['is_downloadable'] = 'N';
			}
		}
		return $GLOBALS['product_info'][$product_id]['is_downloadable'] == 'Y';
	}
	/**
	 * Function to create a DB object that holds all information
	 * from the attribute tables about item $item_id AND/OR product $product_id
	 *
	 * @param int $item_id The product_id of the item
	 * @param int $product_id The product_id of the parent product
	 * @param string $attribute_name The name of the attribute to filter
	 * @return ps_DB The db object...
	 */
	function attribute_sql($item_id="",$product_id="",$attribute_name="") {
		$db = new ps_DB;
		if ($item_id and $product_id) {
			$q  = "SELECT * FROM #__{vm}_product_attribute,#__{vm}_product_attribute_sku ";
			$q .= "WHERE #__{vm}_product_attribute.product_id = '$item_id' ";
			$q .= "AND #__{vm}_product_attribute_sku.product_id ='$product_id' ";
			if ($attribute_name) {
				$q .= "AND #__{vm}_product_attribute.attribute_name = $attribute_name ";
			}
			$q .= "AND #__{vm}_product_attribute.attribute_name = ";
			$q .=     "#__{vm}_product_attribute_sku.attribute_name ";
			$q .= "ORDER BY attribute_list,#__{vm}_product_attribute.attribute_name";
		} elseif ($item_id) {
			$q  = "SELECT * FROM #__{vm}_product_attribute ";
			$q .= "WHERE product_id=$item_id ";
			if ($attribute_name) {
				$q .= "AND attribute_name = '$attribute_name' ";
			}
		} elseif ($product_id) {
			$q  = "SELECT * FROM #__{vm}_product_attribute_sku ";
			$q .= "WHERE product_id =".(int)$product_id.' ';
			if ($attribute_name) {
				$q .= "AND #__{vm}_product_attribute.attribute_name = $attribute_name ";
			}
			$q .= "ORDER BY attribute_list,attribute_name";
		} else {
			/* Error: no arguments were provided. */
			return 0;
		}

		$db->setQuery($q); $db->query();

		return $db;
	}

	/**
	 * Function to return the product ids of all child items of product $pid
	 *
	 * @param int $pid The ID of the parent product
	 * @return array $list
	 */
	function get_child_product_ids($pid) {
		$db = new ps_DB;
		$q  = "SELECT product_id FROM #__{vm}_product ";
		$q .= "WHERE product_parent_id='$pid' ";

		$db->setQuery($q); $db->query();

		$i = 0;
		$list = Array();
		while($db->next_record()) {
			$list[$i] = $db->f("product_id");
			$i++;
		}
		return $list;
	}

	/**
	 * Function to quickly check whether a product has child products or not
	 *
	 * @param int $pid The id of the product to check
	 * @return boolean True when the product has childs, false when not
	 */
	function parent_has_children($pid) {
		$db = new ps_DB;
		if( empty($GLOBALS['product_info'][$pid]["parent_has_children"] )) {
			$q  = "SELECT COUNT(product_id) as num_rows FROM #__{vm}_product WHERE product_parent_id='$pid' ";
			$db->query($q);
			$db->next_record();
			if( $db->f('num_rows') > 0 ) {
				$GLOBALS['product_info'][$pid]["parent_has_children"] = True;
			}
			else {
				$GLOBALS['product_info'][$pid]["parent_has_children"] = False;
			}
		}
		return $GLOBALS['product_info'][$pid]["parent_has_children"];
	}

	/**
	 * Function to quickly check whether a product has attributes or not
	 *
	 * @param int $pid The id of the product to check
	 * @return boolean True when the product has attributes, false when not
	 */
	function product_has_attributes($pid, $checkSimpleAttributes=false ) {
		if( is_array($pid) || empty($pid)) {
			return false;
		}
		$pid = intval( $pid );
		$db = new ps_DB;
		if( empty($GLOBALS['product_info'][$pid]["product_has_attributes"] )) {
			$db->query( "SELECT `product_id` FROM `#__{vm}_product_attribute_sku` WHERE `product_id`=$pid");
			if ($db->next_record()) {
				$GLOBALS['product_info'][$pid]["product_has_attributes"] = True;
			}
			elseif( $checkSimpleAttributes ) {
				$db->query( "SELECT `attribute`,`custom_attribute` FROM `#__{vm}_product` WHERE `product_id`=$pid");
				$db->next_record();
				if( $db->f('attribute') || $db->f('custom_attribute')) {
					$GLOBALS['product_info'][$pid]["product_has_attributes"] = True;
				}
				else {
					$GLOBALS['product_info'][$pid]["product_has_attributes"] = False;
				}
			}
			else {
				$GLOBALS['product_info'][$pid]["product_has_attributes"] = False;
			}
		}
		return $GLOBALS['product_info'][$pid]["product_has_attributes"];
	}

	/**
	 * Get the value of the field $field_name for product $product_id from the product table
	 *
	 * @param int $product_id
	 * @param string $field_name
	 * @return string The value of the field $field_name for that product
	 */
	function get_field( $product_id, $field_name, $force = false ) {
		if( $product_id == 0 ) return '';
		$db = new ps_DB;
		
		if( !isset($GLOBALS['product_info'][$product_id][$field_name] ) || $force ) {
			$q = 'SELECT product_id, `#__{vm}_product`.* FROM `#__{vm}_product` WHERE `product_id`='.(int)$product_id;
			$db->query($q);
			if ($db->next_record()) {
				$values = get_object_vars( $db->getCurrentRow() );
				
				foreach( $values as $key => $value ) {
					$GLOBALS['product_info'][$product_id][$key] = $value;
				}
				if( !isset( $GLOBALS['product_info'][$product_id][$field_name] ) && !is_null($GLOBALS['product_info'][$product_id][$field_name])) {
					$GLOBALS['vmLogger']->debug( 'The Field '.$field_name. ' does not exist in the product table!');
					$GLOBALS['product_info'][$product_id][$field_name] = true;
				}
			}
			else {
				$GLOBALS['product_info'][$product_id][$field_name] = false;
			}
		}
		return $GLOBALS['product_info'][$product_id][$field_name];
	}

	/**
	 * Sets a global value for a fieldname for a specific product
	 * Is to be used by other scripts to populate a field value for a prodct
	 * that was already fetched from the database - so it doesn't need to e fetched again
	 * Can be also used to override a value
	 *
	 * @param int $product_id
	 * @param string $field_name
	 * @param mixed $value
	 */
	function set_field( $product_id, $field_name, $value ) {

		$GLOBALS['product_info'][$product_id][$field_name] = $value;

	}

	/**
	 * This is a very time consuming function. 
	 * It fetches the category flypage for a specific product id
	 *
	 * @param int $product_id
	 * @return string The flypage value for that product
	 */
	function get_flypage($product_id) {

		if( empty( $_SESSION['product_sess'][$product_id]['flypage'] )) {
			$db = new ps_DB;
			$productParentId = (int)$product_id;
			do {
				$q = "SELECT
                                `#__{vm}_product`.`product_parent_id` AS product_parent_id,
                                `#__{vm}_category`.`category_flypage`
                        FROM
                                `#__{vm}_product`

                        LEFT JOIN `#__{vm}_product_category_xref` ON `#__{vm}_product_category_xref`.`product_id` = `#__{vm}_product`.`product_id`
                        LEFT JOIN `#__{vm}_category` ON `#__{vm}_product_category_xref`.`category_id` = `#__{vm}_category`.`category_id`

                        WHERE `#__{vm}_product`.`product_id`='$productParentId'
                        ";
				$productParentId = $db->f("product_parent_id");
				$db->query($q);
				$db->next_record();
			} 
			while( $db->f("product_parent_id") && !$db->f("category_flypage"));

			if ($db->f("category_flypage")) {
				$_SESSION['product_sess'][$product_id]['flypage'] = $db->f("category_flypage");
			} else {
				$_SESSION['product_sess'][$product_id]['flypage'] = FLYPAGE;
			}
		}
		return $_SESSION['product_sess'][$product_id]['flypage'];
	}

	/**
	 * Function to get the name of the vendor the product is associated with
	 *
	 * @param int $product_id
	 * @return string The name of the vendor
	 */
	function get_vendorname($product_id) {
		$db = new ps_DB;

		$q = "SELECT #__{vm}_vendor.vendor_name FROM #__{vm}_product, #__{vm}_vendor ";
		$q .= "WHERE #__{vm}_product.product_id='$product_id' ";
		$q .= "AND #__{vm}_vendor.vendor_id=#__{vm}_product.vendor_id";

		$db->query($q);
		$db->next_record();
		if ($db->f("vendor_name")) {
			return $db->f("vendor_name");
		}
		else {
			return "";
		}
	}

	/**
	 * Function to get the name of a vendor by its id
	 * @author pablo
	 * @param int $vendor_id
	 * @return string The name of the vendor
	 */
	function get_vend_idname($vendor_id) {
		$db = new ps_DB;

		$q = "SELECT vendor_name,vendor_id FROM #__{vm}_vendor ";
		$q .= "WHERE vendor_id='$vendor_id'";

		$db->query($q);
		$db->next_record();
		if ($db->f("vendor_name")) {
			return $db->f("vendor_name");
		}
		else {
			return "";
		}
	}

	/**
	 * Function to get the vendor_id of a product
	 * @author pablo
	 * @param int $product_id
	 * @return int The vendor id
	 */
	function get_vendor_id($product_id) {
		$db = new ps_DB;
		if( empty( $_SESSION['product_sess'][$product_id]['vendor_id'] )) {
			$q = "SELECT vendor_id FROM #__{vm}_product ";
			$q .= "WHERE product_id='$product_id' ";

			$db->query($q);
			$db->next_record();
			if ($db->f("vendor_id")) {
				$_SESSION['product_sess'][$product_id]['vendor_id'] = $db->f("vendor_id");
			}
			else {
				$_SESSION['product_sess'][$product_id]['vendor_id'] = "";
			}
		}
		return $_SESSION['product_sess'][$product_id]['vendor_id'];
	}

	/**
	 * Function to get the manufacturer id the product $product_id is assigned to
	 * @author soeren
	 * @param int $product_id
	 * @return int The manufacturer id
	 */
	function get_manufacturer_id($product_id) {
		$db = new ps_DB;

		$q = "SELECT manufacturer_id FROM #__{vm}_product_mf_xref ";
		$q .= "WHERE product_id='$product_id' ";

		$db->query($q);
		$db->next_record();
		if ($db->f("manufacturer_id")) {
			return $db->f("manufacturer_id");
		}
		else {
			return false;
		}
	}

	/**
	 * Functon to get the name of the manufacturer this product is assigned to
	 *
	 * @param int $product_id
	 * @return string the manufacturer name
	 */
	function get_mf_name($product_id) {
		$db = new ps_DB;

		$q = "SELECT mf_name,#__{vm}_manufacturer.manufacturer_id FROM #__{vm}_product_mf_xref,#__{vm}_manufacturer ";
		$q .= "WHERE product_id='$product_id' ";
		$q .= "AND #__{vm}_manufacturer.manufacturer_id=#__{vm}_product_mf_xref.manufacturer_id";

		$db->query($q);
		$db->next_record();
		if ($db->f("mf_name")) {
			return $db->f("mf_name");
		}
		else {
			return "";
		}
	}
	/**
	 * This function retrieves the "neighbor" products of a product specified by $product_id
	 * Neighbors are the previous and next product in the current list
	 *
	 * @param int $product_id
	 * @return array
	 */
	function get_neighbor_products( $product_id ) {
		global $perm, $orderby, $my, $auth, $keyword, $DescOrderBy, $limit, $limitstart, $search_limiter, $search_op, 
			$category_id, $manufacturer_id, $vm_mainframe, $vmInputFilter, $product_type_id, $keyword1, $keyword2;
		$limit = 2000;
        $limitstart = 0;
		if( !empty( $_SESSION['last_browse_parameters'])) {
			foreach( $_SESSION['last_browse_parameters'] as $paramName => $paramValue ) {
				$$paramName = $paramValue;
			}
		}
		$db = new ps_DB();
		$db_browse = new ps_DB();
		
		if (defined('VM_ALLOW_EXTENDED_CLASSES') && defined('VM_THEMEPATH') && VM_ALLOW_EXTENDED_CLASSES && file_exists(VM_THEMEPATH.'user_class/shop_browse_queries.php')) {
			// Load the user-defined shop_browse_queries file
			include_once(VM_THEMEPATH.'user_class/shop_browse_queries.php');
		} else {
			// Otherwise we have to use the original file
			include( PAGEPATH . 'shop_browse_queries.php' );
		}
		
		
		$db->query( $list );
		
		$neighbors = array('previous'=>'',
							'next'=>'');
		
		while( $db->next_record() ) {
			if( $db->f( 'product_id' ) == $product_id ) {
				$previous_row = $db->previousRow();
				$next_row = $db->nextRow();
				
				if( !empty( $previous_row->product_id )) {
					$neighbors['previous']['product_id'] = $previous_row->product_id;
					$neighbors['previous']['product_name'] = $previous_row->product_name;
				} 
				if( !empty( $next_row->product_id )) {
					$neighbors['next']['product_id'] = $next_row->product_id;
					$neighbors['next']['product_name'] = $next_row->product_name;
				}
			}
		}
		return $neighbors;
	}
	/**
	 * Prints the img tag for the given product image
	 *
	 * @param string $image The name of the imahe OR the full URL to the image
	 * @param string $args Additional attributes for the img tag
	 * @param int $resize 
	 * (1 = resize the image by using height and width attributes, 
	 * 0 = do not resize the image)
	 * @param string $path_appendix The path to be appended to IMAGEURL / IMAGEPATH
	 */
	function show_image($image, $args="", $resize=1, $path_appendix="product") {
		echo $this->image_tag($image, $args, $resize, $path_appendix);
	}

	/**
	 * Returns the img tag for the given product image
	 *
	 * @param string $image The name of the imahe OR the full URL to the image
	 * @param string $args Additional attributes for the img tag
	 * @param int $resize 
	 * (1 = resize the image by using height and width attributes, 
	 * 0 = do not resize the image)
	 * @param string $path_appendix The path to be appended to IMAGEURL / IMAGEPATH
	 * @return The HTML code of the img tag
	 */
	function image_tag($image, $args="", $resize=1, $path_appendix='product', $thumb_width=0, $thumb_height=0 ) {
		global $mosConfig_live_site, $mosConfig_absolute_path;
		require_once( CLASSPATH . 'imageTools.class.php');
		
		$border="";
		if( strpos( $args, "border=" )===false ) {
			$border = 'border="0"';
		}
		$height = $width = 0;
		
		if ($image != "") {
			// URL
			if( substr( $image, 0, 4) == "http" ) {
				$url = $image;
				}
			// local image file
			else {
				if(PSHOP_IMG_RESIZE_ENABLE == '1' && $resize==1) {
					$url = $mosConfig_live_site."/components/com_virtuemart/show_image_in_imgtag.php?filename=".urlencode($image)."&amp;newxsize=".PSHOP_IMG_WIDTH."&amp;newysize=".PSHOP_IMG_HEIGHT."&amp;fileout=";
					if( !strpos( $args, "height=" )) {
						$arr = @getimagesize( vmImageTools::getresizedfilename( $image, $path_appendix, '', $thumb_height, $thumb_width ) );
						$width = $arr[0]; $height = $arr[1];
					}
				}
				else {
					
					$url = IMAGEURL.$path_appendix.'/'.$image;
					$using_resized_image = false;	
					if( $resize ) {
						$image = vmImageTools::getresizedfilename( $image, $path_appendix, '', $thumb_height, $thumb_width );
						if( file_exists($image)) {
							$using_resized_image = true;							
						}
					}
					if( $resize && ! $using_resized_image) {
						if( $height < $width ) {
							$width = @round($width / ($height / PSHOP_IMG_HEIGHT));
							$height = PSHOP_IMG_HEIGHT;
						} else {
							$height = @round($height / ($width / PSHOP_IMG_WIDTH ));
							$width = PSHOP_IMG_WIDTH;
						}
					}
					if( file_exists($image)) {
						$url = str_replace( $mosConfig_absolute_path, $mosConfig_live_site, $image );
						
					} elseif( file_exists($mosConfig_absolute_path.'/'.$image)) {
						$url = $mosConfig_live_site.'/'.$image;
					}
					
					$url = str_replace('//', '/', $url );
					$url = str_replace(':/', '://', $url );
					if( !strpos( $args, "height=" ) ) {
						$f = str_replace( IMAGEURL, IMAGEPATH, $url );
						
						if ( file_exists($f) ) {
						    $arr = getimagesize( $f );
						    $width = $arr[0]; $height = $arr[1];
						} else {
						    $width = 100; $height = 100;
						}
						
					}

				}
				$url = str_replace( basename( $url ), $GLOBALS['VM_LANG']->convert(basename($url)), $url );
			}
		}
		else {
			$url = VM_THEMEURL.'images/'.NO_IMAGE;
		}	
		
		return vmCommonHTML::imageTag( $url, '', '', $height, $width, '', '', $args.' '.$border );

	}

	/**
	 * Get the tax rate...
	 * @author soeren
	 * @return int The tax rate found
	 */
	function get_taxrate( $ship_to_info_id = '' ) {
		global $page;
		$ps_vendor_id = $_SESSION["ps_vendor_id"];
		$auth = $_SESSION['auth'];

		if( !defined('_VM_IS_BACKEND' ) || $page == 'product.product_list' || $page == 'order.order_print') {

			$db = new ps_DB;

			if ($auth["show_price_including_tax"] == 1) {

				require_once( CLASSPATH . 'ps_checkout.php' );
				if (! ps_checkout::tax_based_on_vendor_address ($ship_to_info_id) ) {
					if( $auth["user_id"] > 0 ) {

						$ship_to_info_id = !empty($ship_to_info_id)? $ship_to_info_id : vmGet( $_REQUEST, 'ship_to_info_id');

							//**  Select country and state  */
							if( empty($ship_to_info_id)){									
								$q = "SELECT state, country FROM #__{vm}_user_info WHERE user_id='". $auth["user_id"] . "'";
								$db->query($q);
								$db->next_record();
								$state = $db->f("state");
								$country = $db->f("country");
							} 
							else {
								$q  = "SELECT country, state FROM #__{vm}_user_info WHERE user_info_id='" . $ship_to_info_id."'";
								$db->query($q);
								$db->next_record();
								$state = $db->f("state");
								$country = $db->f("country");
							}

						$q = "SELECT tax_rate FROM #__{vm}_tax_rate WHERE tax_country='$country'\n";
						if( !empty($state)) {
							$q .= "AND (tax_state='$state' OR tax_state=' $state ' OR tax_state='-')";
						}
						$db->query($q);
						if ($db->next_record()) {
							$_SESSION['taxrate'][$ps_vendor_id] = $db->f("tax_rate");
						}
						else {
							$_SESSION['taxrate'][$ps_vendor_id] = 0;
						}
					}
					else {
						$_SESSION['taxrate'][$ps_vendor_id] = 0;
					}

				}
				else {
					if( !isset( $_SESSION['taxrate'][$ps_vendor_id] )) {
						// let's get the store's tax rate
						$q = "SELECT `tax_rate` FROM #__{vm}_vendor, #__{vm}_tax_rate ";
						$q .= "WHERE tax_country=vendor_country AND #__{vm}_vendor.vendor_id=1 ";
						// !! Important !! take the highest available tax rate for the store's country
						$q .= "ORDER BY `tax_rate` DESC";
						$db->query($q);
						if ($db->next_record()) {
							$_SESSION['taxrate'][$ps_vendor_id] = $db->f("tax_rate");
						}
						else {
							$_SESSION['taxrate'][$ps_vendor_id] = 0;
						}
					}
					return $_SESSION['taxrate'][$ps_vendor_id];
				}

			}
			else {
				$_SESSION['taxrate'][$ps_vendor_id] = 0;
			}

			return $_SESSION['taxrate'][$ps_vendor_id];
		}
		else {
			return 0;
		}
	}

	/**
	 * Function to get the tax rate of product $product_id
	 * If not found, it uses get_taxrate()
	 *
	 * @param int $product_id
	 * @param int $weight_subtotal (tax virtual/zero-weight items?)
	 * @return int The tax rate for the product
	 */
	function get_product_taxrate( $product_id, $weight_subtotal=0, $ship_to_info_id = '' ) {
		global $vendor_country_3_code;
		$db = new ps_DB;
		// Product's weight_subtotal, if weight_subtotal is 0!
		$product_weight = ps_product::get_field($product_id, 'product_weight');
		
		if( $weight_subtotal == 0 && $product_weight > 0 ) {
			$weight_subtotal = $product_weight;
		}
		
		require_once( CLASSPATH . 'ps_checkout.php' );

		// EU TAX_MODE
		$auth = $_SESSION['auth'];
		$userid = $auth["user_id"];
		if ( $userid > 0 && TAX_MODE == '17749' && ps_checkout::country_in_eu_common_vat_zone($vendor_country_3_code)){
			$ship_country = '';
			$user_info_id = '';
			if ( vmGet( $_REQUEST, 'ship_to_info_id') || $ship_to_info_id ){
				if (!$ship_to_info_id){
					$ship_to_info_id = vmGet( $_REQUEST, 'ship_to_info_id');
				}
				$q  = "SELECT country FROM #__{vm}_user_info WHERE user_info_id='" . $ship_to_info_id ."'";
				$db->query($q);
				$db->next_record();
				$ship_country = $db->f("country");
			}

			if ( $ship_to_info_id == '') {
				$ship_country = $auth["country"];
				$q = "SELECT user_info_id FROM #__{vm}_user_info WHERE user_id = '" .(int)$userid . "' AND address_type='BT'";
				$db->query($q);
				$user_info_id = $db->f("user_info_id");
			}

			// Check if user country is outside EU.
			$eu_vat = '';
			if( ps_checkout::country_in_eu_common_vat_zone($ship_country)){
				$eu_vat = 'yes';
			}
			if( $eu_vat == 'yes' ) {
				$q="SELECT name FROM #__{vm}_userfield WHERE type='euvatid' AND published=1";
				$db->query($q);
				$vatid_fieldname = $db->f( 'name' );
			}
			//???
			if( !$eu_vat && ps_checkout::country_in_eu_common_vat_zone($vendor_country_3_code)) {
				$_SESSION['product_sess'][$product_id]['tax_rate'] = 0;
				return $_SESSION['product_sess'][$product_id]['tax_rate'];
			}

			// Handle TAX if EU VAT ID
			if( $eu_vat == 'yes' && $vendor_country_3_code != $ship_country){
				$q = "SELECT country_2_code FROM #__{vm}_country WHERE country_3_code='" . $ship_country ."'";
				$db->query($q);
				$db->next_record();
				$ship_country_2_code = $db->f("country_2_code");
				if( $vatid_fieldname  ) {
					$q = "SELECT `$vatid_fieldname` FROM #__{vm}_user_info WHERE user_info_id='" . $ship_to_info_id ."' OR user_info_id='" . $user_info_id."'";
					$db->query($q);
					while($db->next_record()) { 
						$vat_id = $db->f($vatid_fieldname);
						if( $ship_country_2_code == substr($vat_id, 0, 2) ) {
							$_SESSION['product_sess'][$product_id]['tax_rate'] = 0;
							return $_SESSION['product_sess'][$product_id]['tax_rate'];
						}
					}
				}
			}
		}

		if ( ( $weight_subtotal != 0 or TAX_VIRTUAL=='1' ) && !ps_checkout::tax_based_on_vendor_address( $ship_to_info_id ) ) {
			$tax_rate_id = ps_product::get_field($product_id, 'product_tax_id');
			if( $tax_rate_id == 0 ) {
				// if the tax rate was explicitely set to "0 (none)", the product should not be taxed, no matter what else
				$_SESSION['product_sess'][$product_id]['tax_rate'] = 0;
			} else {
				$_SESSION['product_sess'][$product_id]['tax_rate'] = $this->get_taxrate( $ship_to_info_id );
			}
			return $_SESSION['product_sess'][$product_id]['tax_rate'];
		}
		elseif( ( $weight_subtotal == 0 or TAX_VIRTUAL != '1' ) && !ps_checkout::tax_based_on_vendor_address( $ship_to_info_id ) ) {
			$_SESSION['product_sess'][$product_id]['tax_rate'] = 0;
			return $_SESSION['product_sess'][$product_id]['tax_rate'];
		}
		elseif( ps_checkout::tax_based_on_vendor_address ( $ship_to_info_id ) ) {

//			if( empty( $_SESSION['product_sess'][$product_id]['tax_rate'] ) ) {
				$db = new ps_DB;
				// Product's tax rate id has priority!
				$q = 'SELECT tax_rate FROM #__{vm}_product, #__{vm}_tax_rate ';
				$q .= 'WHERE product_tax_id=tax_rate_id AND product_id='.(int)$product_id;
				$db->query($q);
				if ( $db->next_record() ) {
					$rate = $db->f("tax_rate");
				}
				else {
					// if we didn't find a product tax rate id, let's get the store's tax rate
					$rate = $this->get_taxrate( $ship_to_info_id );
				}
				if ($weight_subtotal != 0 or TAX_VIRTUAL=='1') {
					$_SESSION['product_sess'][$product_id]['tax_rate'] = $rate;
					return $rate;
				}
				else {
					$_SESSION['product_sess'][$product_id]['tax_rate'] = 0;
					return 0;
				}
//			}
//			else {
//				return $_SESSION['product_sess'][$product_id]['tax_rate'];
//			}
		}
		return 0;
	}

	/**
	 * Function to get the "pure" undiscounted and untaxed price 
	 * of product $product_id. Used by the administration section.
	 *
	 * @param int $product_id
	 * @return array The product price information
	 */
	function get_retail_price($product_id) {

		$db = new ps_DB;
		// Get the vendor id for this product.
		$q = "SELECT vendor_id FROM #__{vm}_product WHERE product_id='$product_id'";
		$db->setQuery($q); $db->query();
		$db->next_record();
		$vendor_id = $db->f("vendor_id");

		// Get the default shopper group id for this product and user
		$q = "SELECT shopper_group_id FROM #__{vm}_shopper_group WHERE `vendor_id`='$vendor_id' AND `default`='1'";
		$db->setQuery($q); $db->query();
		$db->next_record();
		$default_shopper_group_id = $db->f("shopper_group_id");

		$q = "SELECT product_price,product_currency,price_quantity_start,price_quantity_end 
				FROM #__{vm}_product_price 
				WHERE product_id='$product_id' AND shopper_group_id='$default_shopper_group_id'
				ORDER BY price_quantity_start";
		$db->query($q);
		if ($db->next_record()) {
			$price_info["product_price"]= $db->f("product_price");
			$price_info["product_currency"]=$db->f("product_currency");
			$price_info["price_quantity_start"]=$db->f("price_quantity_start"); // added alatak
			$price_info["price_quantity_end"]=$db->f("price_quantity_end");// added alatak
		}
		else {
			$price_info["product_price"]= "";
			$price_info["product_currency"] = $_SESSION['vendor_currency'];
			$price_info["price_quantity_start"]=$db->f("price_quantity_start"); // added alatak
			$price_info["price_quantity_end"]=$db->f("price_quantity_end");// added alatak
		}
		return $price_info;
	}

	/**
	 * Get the price of product $product_id for the shopper group associated
	 * with $auth['user_id'] - including shopper group discounts
	 *
	 * @param int $product_id
	 * @param boolean $check_multiple_prices Check if the product has more than one price for that shopper group?
	 * @return array The product price information
	 */
	function get_price($product_id, $check_multiple_prices=false, $overrideShopperGroup='' ) {
		if( empty( $product_id)) return array();
		$auth = $_SESSION['auth'];
		$cart = $_SESSION['cart'];

		if( empty( $GLOBALS['product_info'][$product_id]['price'] )
		|| !empty($GLOBALS['product_info'][$product_id]['price']["product_has_multiple_prices"])
		|| $check_multiple_prices) {
			$db = new ps_DB;

			$vendor_id = $this->get_vendor_id($product_id);
			
			if( $overrideShopperGroup === '') {
				$shopper_group_id = $auth["shopper_group_id"];
				$shopper_group_discount = $auth["shopper_group_discount"];
			}
			else {
				$shopper_group_id = $overrideShopperGroup;
				$shopper_group_discount = 0;
			}
			
			ps_shopper_group::makeDefaultShopperGroupInfo();
			
			// Get the product_parent_id for this product/item
			$product_parent_id = $this->get_field($product_id, "product_parent_id");

			if( !$check_multiple_prices ) {
				/* Added for Volume based prices */
				// This is an important decision: we add up all product quantities with the same product_id,
				// regardless to attributes. This gives "real" volume based discount, because our simple attributes
				// depend on one and the same product_id
                $quantity = 0;
                $parent_id = "";
				if ($product_parent_id) {
                	$parent = true;
                }
                else {
                    $parent = false; 
                }
                for ($i=0;$i<$cart["idx"];$i++) {
                    if ($cart[$i]["product_id"] == $product_id) {
                        if ($parent) {
                        	$parent_id  = $cart[$i]["parent_id"];
                        }
                        else {
                        	$quantity += $cart[$i]["quantity"];
                        }
                    }
                }
                if ($parent) {
                    for ($i=0;$i<$cart["idx"];$i++) {
                        if (@$cart[$i]['parent_id'] == $parent_id) {
                            $quantity  += $cart[$i]["quantity"];
                        }
                    }
                }
                
				$volume_quantity_sql = " ORDER BY price_quantity_start";
				if( $quantity > 0 ) {
					$volume_quantity_sql = " AND (('$quantity' >= price_quantity_start AND '$quantity' <= price_quantity_end)
                                OR (price_quantity_end='0') OR ('$quantity' > price_quantity_end)) ORDER BY price_quantity_end DESC";
				}
			}
			else {
				$volume_quantity_sql = " ORDER BY price_quantity_start";
			}
			
			// Get the price array
			$price = $this->getPriceByShopperGroup( $product_id, $shopper_group_id, $check_multiple_prices, $volume_quantity_sql );
			if( !$price && $product_parent_id ) {
				// If this is a child product and it has not a price for the requested shopper group, get the price for the default shopper group
				$price = $this->getPriceByShopperGroup( $product_id, $GLOBALS['vendor_info'][$vendor_id]['default_shopper_group_id'], $check_multiple_prices, $volume_quantity_sql );
				if( !$price ) {
					// if the child product has no price at all, get the price of the parent product for that shopper group
					$price = $this->getPriceByShopperGroup( $product_parent_id, $shopper_group_id, $check_multiple_prices, $volume_quantity_sql );					
				}
				if( !$price ) {
					// if the parent product has no price for the requested shopper group, get the price of the default shopper group
					$price = $this->getPriceByShopperGroup( $product_parent_id, $GLOBALS['vendor_info'][$vendor_id]['default_shopper_group_id'], $check_multiple_prices, $volume_quantity_sql );	
				}
			}
			elseif( !$price ) {
				// if the product has no price for the requested shopper group, get the price of the default shopper group
				$price = $this->getPriceByShopperGroup( $product_id, $GLOBALS['vendor_info'][$vendor_id]['default_shopper_group_id'], $check_multiple_prices, $volume_quantity_sql );
			}
			return $price;
		}
		else {
			return $GLOBALS['product_info'][$product_id]['price'];
		}
	}
	/**
	 * Returns the price for a specific shopper group,
	 * Returns nothing, when the shopper group has no price
	 *
	 * @param int $product_id
	 * @param int $shopper_group_id
	 * @param boolean $check_multiple_prices
	 * @param string $additionalSQL
	 * @return mixed
	 */
	function getPriceByShopperGroup( $product_id, $shopper_group_id, $check_multiple_prices=false, $additionalSQL='' ) {
		global $auth;
		static $resultcache = array();
		$db = new ps_DB;
		$vendor_id = $_SESSION['ps_vendor_id'];
		if( empty( $shopper_group_id )) {
			ps_shopper_group::makeDefaultShopperGroupInfo();
			$shopper_group_id = $GLOBALS['vendor_info'][$vendor_id]['default_shopper_group_id'];
		}
	
		$whereClause='WHERE product_id=%s AND shopper_group_id=%s ';
		$whereClause = sprintf( $whereClause, intval($product_id), intval($shopper_group_id) );

		$q = "SELECT `product_price`, `product_price_id`, `product_currency` FROM `#__{vm}_product_price` $whereClause $additionalSQL";
		
		$sig = sprintf("%u\n", crc32($q));

		if( !isset($resultcache[$sig])) {
			$db->query($q);
			if( !$db->next_record() ) return false;
			$price_info["product_price"]= $db->f("product_price") * ((100 - $auth["shopper_group_discount"])/100);
			$price_info["product_currency"] = $db->f("product_currency");
			
			$price_info["product_base_price"]= $db->f("product_price") * ((100 - $auth["shopper_group_discount"])/100);
			$price_info["product_has_multiple_prices"] = $db->num_rows() > 1;
			
			$price_info["product_price_id"] = $db->f("product_price_id");					
			$price_info["item"]=true;
			$GLOBALS['product_info'][$product_id]['price'] = $price_info;
			// Store the result for later
			$resultcache[$sig] = $price_info;
			
			return $GLOBALS['product_info'][$product_id]['price'];
		} 
		else {
			return $resultcache[$sig];
		}
		
	}
	/**
	 * Adjusts the price from get_price for the selected attributes
	 * @author Nathan Hyde <nhyde@bigDrift.com>
	 * @author curlyroger from his post at <http://www.phpshop.org/phpbb/viewtopic.php?t=3052>
	 *
	 * @param int $product_id
	 * @param string $description
	 * @return array The adjusted price information
	 */
	function get_adjusted_attribute_price ($product_id, $description='') {

		global $mosConfig_secret;
		$auth = $_SESSION['auth'];
		$price = $this->get_price($product_id);

		$base_price = $price["product_price"];
		$setprice = 0;
		$set_price = false;
		$adjustment = 0;

		// We must care for custom attribute fields! Their value can be freely given
		// by the customer, so we mustn't include them into the price calculation
		// Thanks to AryGroup@ua.fm for the good advice
		if( empty( $_REQUEST["custom_attribute_fields"] )) {
			if( !empty( $_SESSION["custom_attribute_fields"] )) {
				$custom_attribute_fields = vmGet( $_SESSION, "custom_attribute_fields", Array() );
				$custom_attribute_fields_check = vmGet( $_SESSION, "custom_attribute_fields_check", Array() );
			}
			else
			$custom_attribute_fields = $custom_attribute_fields_check = Array();
		}
		else {
			$custom_attribute_fields = $_SESSION["custom_attribute_fields"] = vmGet( $_REQUEST, "custom_attribute_fields", Array() );
			$custom_attribute_fields_check = $_SESSION["custom_attribute_fields_check"]= vmGet( $_REQUEST, "custom_attribute_fields_check", Array() );
		}

		// if we've been given a description to deal with, get the adjusted price
		if ($description != '') { // description is safe to use at this point cause it's set to ''
			require_once(CLASSPATH.'ps_product_attribute.php');
			$product_attributes = ps_product_attribute::getAdvancedAttributes($product_id, true);
			
			$attribute_keys = explode( ";", $description );

			for($i=0; $i < sizeof($attribute_keys); $i++ ) {
				$temp_desc = $attribute_keys[$i];
				
				$temp_desc = trim( $temp_desc );
				// Get the key name (e.g. "Color" )
				$this_key = substr( $temp_desc, 0, strpos($temp_desc, ":") );
				$this_value = substr( $temp_desc, strpos($temp_desc, ":")+1 );
				
				if( in_array( $this_key, $custom_attribute_fields )) {
					if( @$custom_attribute_fields_check[$this_key] == md5( $mosConfig_secret.$this_key )) {
						// the passed value is valid, don't use it for calculating prices
						continue;
					}
				}
                
				if( isset( $product_attributes[$this_key]['values'][$this_value] )) {
					$modifier = $product_attributes[$this_key]['values'][$this_value]['adjustment'];
					$operand = $product_attributes[$this_key]['values'][$this_value]['operand'];

					// if we have a number, allow the adjustment
					if (true == is_numeric($modifier) ) {
                    
						// Now add or sub the modifier on
						if ($operand=="+") {
							$adjustment += $modifier;
						}
						else if ($operand=="-") {
						$adjustment -= $modifier;
						}
						else if ($operand=='=') {
							// NOTE: the +=, so if we have 2 sets they get added
							// this could be moded to say, if we have a set_price, then
							// calc the diff from the base price and start from there if we encounter
							// another set price... just a thought.
	
							$setprice += $modifier;
							$set_price = true;
						}
					}
				} else {
					continue;
				}
			}
		}

		// no set price was set from the attribs
		if ($set_price == false) {
			$price["product_price"] = $base_price + ($adjustment)*(1 - ($auth["shopper_group_discount"]/100));
		}
		else {
			// otherwise, set the price
			// add the base price to the price set in the attributes
			// then subtract the adjustment amount
			// we could also just add the set_price to the adjustment... not sure on that one.
			if (!empty($adjustment)) {
				$setprice += $adjustment;
			}
			$setprice *= 1 - ($auth["shopper_group_discount"]/100);
			$price["product_price"] = $setprice;
		}

		// don't let negative prices get by, set to 0
		if ($price["product_price"] < 0) {
			$price["product_price"] = 0;
		}
		// Get the DISCOUNT AMOUNT
		$discount_info = $this->get_discount( $product_id );

		$my_taxrate = $this->get_product_taxrate($product_id);
		
		// If discounts are applied after tax, but prices are shown without tax,
		// AND tax is EU mode and shopper is not in the EU,
		// then ps_product::get_product_taxrate() returns 0, so $my_taxrate = 0.
		// But, the discount still needs to be reduced by the shopper's tax rate, so we obtain it here:
		if( PAYMENT_DISCOUNT_BEFORE != '1'  && $auth["show_price_including_tax"] != 1 && !ps_checkout::tax_based_on_vendor_address() ) {
			$db = new ps_DB;
			$ps_vendor_id = $_SESSION["ps_vendor_id"];
			require_once( CLASSPATH . 'ps_checkout.php' );
			if (! ps_checkout::tax_based_on_vendor_address ()) {
				if( $auth["user_id"] > 0 ) {

					$q = "SELECT state, country FROM #__{vm}_user_info WHERE user_id='". $auth["user_id"] . "'";
					$db->query($q);

					$db->next_record();
					$state = $db->f("state");
					$country = $db->f("country");

					$q = "SELECT tax_rate FROM #__{vm}_tax_rate WHERE tax_country='$country' ";
					if( !empty($state)) {
						$q .= "AND (tax_state='$state' OR tax_state=' $state ' OR tax_state='-')";
					}
					$db->query($q);
					if ($db->next_record()) {
						$my_taxrate = $db->f("tax_rate");
					}
					else {
						$my_taxrate = 0;
					}
				}
				else {
					$my_taxrate = 0;
				}

			}
			else {
				if( empty( $_SESSION['taxrate'][$ps_vendor_id] )) {
					// let's get the store's tax rate
					$q = "SELECT `tax_rate` FROM #__{vm}_vendor, #__{vm}_tax_rate ";
					$q .= "WHERE tax_country=vendor_country AND #__{vm}_vendor.vendor_id=1 ";
					// !! Important !! take the highest available tax rate for the store's country
					$q .= "ORDER BY `tax_rate` DESC";
					$db->query($q);
					if ($db->next_record()) {
						$my_taxrate = $db->f("tax_rate");
					}
					else {
						$my_taxrate = 0;
					}
				}
			}
		}
		
		// Apply the discount
		if( !empty($discount_info["amount"])) {
			$undiscounted_price = $base_price;
			switch( $discount_info["is_percent"] ) {
				case 0:
					if( PAYMENT_DISCOUNT_BEFORE == '1' ) {
						// If we subtract discounts BEFORE tax
						// Subtract the whole discount
						$price["product_price"] -= $discount_info["amount"];
					}
					else {
						// But, if we subtract discounts AFTER tax
						// Subtract the untaxed portion of the discount
						$price["product_price"] -= $discount_info["amount"]/($my_taxrate + 1);
					}
					break;
				case 1:
					$price["product_price"] -=  $price["product_price"]*($discount_info["amount"]/100);
					break;
			}
		}

		return $price;
	}

	/**
	 * This function can parse an "advanced / custom attribute"
	 * description like
	 * Size:big[+2.99]; Color:red[+0.99]
	 * and return the same string with values, tax added
	 * Size: big (+3.47), Color: red (+1.15)
	 * 
	 * @param string $description
	 * @param int $product_id
	 * @return string The reformatted description
	 */
	function getDescriptionWithTax( $description, $product_id=0 ) {
		global $CURRENCY_DISPLAY, $mosConfig_secret;
		require_once(CLASSPATH.'ps_product_attribute.php');
		
		$auth = $_SESSION['auth'];
		$description = stripslashes($description);
        
		// if we've been given a description to deal with, get the adjusted price
		if ($description != '' && $auth["show_price_including_tax"] == 1 && $product_id != 0 ) {
			$my_taxrate = $this->get_product_taxrate($product_id);
			$price = $this->get_price( $product_id );
			$product_currency = $price['product_currency'];
		}
		else {
			$my_taxrate = 0.00;
			$product_currency = '';
		}
		// We must care for custom attribute fields! Their value can be freely given
		// by the customer, so we mustn't include them into the price calculation
		// Thanks to AryGroup@ua.fm for the good advice
		if( empty( $_REQUEST["custom_attribute_fields"] )) {
			if( !empty( $_SESSION["custom_attribute_fields"] )) {
				$custom_attribute_fields = vmGet( $_SESSION, "custom_attribute_fields", Array() );
				$custom_attribute_fields_check = vmGet( $_SESSION, "custom_attribute_fields_check", Array() );
			}
			else {
				$custom_attribute_fields = $custom_attribute_fields_check = Array();
			}
		}
		else {
			$custom_attribute_fields = $_SESSION["custom_attribute_fields"] = vmGet( $_REQUEST, "custom_attribute_fields", Array() );
			$custom_attribute_fields_check = $_SESSION["custom_attribute_fields_check"]= vmGet( $_REQUEST, "custom_attribute_fields_check", Array() );
		}

		$product_attributes = ps_product_attribute::getAdvancedAttributes($product_id);
		$attribute_keys = explode( ";", $description );

		foreach( $attribute_keys as $temp_desc ) {
			$finish = strpos($temp_desc,"]");
			$temp_desc = trim( $temp_desc );
			// Get the key name (e.g. "Color" )
			$this_key = substr( $temp_desc, 0, strpos($temp_desc, ":") );
			$this_value = substr( $temp_desc, strpos($temp_desc, ":")+1 );

			if( in_array( $this_key, $custom_attribute_fields )) {
				if( @$custom_attribute_fields_check[$this_key] == md5( $mosConfig_secret.$this_key )) {
					// the passed value is valid, don't use it for calculating prices
					continue;
				}
			}
            
			if( isset( $product_attributes[$this_key]['values'][$this_value] )) {
				$modifier = $product_attributes[$this_key]['values'][$this_value]['adjustment'];
				$operand = $product_attributes[$this_key]['values'][$this_value]['operand'];

				$value_notax = $GLOBALS['CURRENCY']->convert( $modifier, $product_currency );
				if( abs($value_notax) >0 ) {
					$value_taxed = $value_notax * ($my_taxrate+1);
					$temp_desc_new  = str_replace( $operand.$modifier, $operand.' '.$CURRENCY_DISPLAY->getFullValue( $value_taxed ), $temp_desc );
                    
					$description = str_replace( $this_key.':'.$this_value, 
												 $this_key.':'.$this_value.' ('.$operand.' '.$CURRENCY_DISPLAY->getFullValue( $value_taxed ).')',
													$description);

				}
				$temp_desc = substr($temp_desc, $finish+1);
			}
			
		}
        
		$description = str_replace( $CURRENCY_DISPLAY->symbol, '@saved@', $description );
		$description = str_replace( "[", " (", $description );
		$description = str_replace( "]", ")", $description );
		$description = str_replace( ":", ": ", $description );
		$description = str_replace( ";", "<br/>", $description );
		$description = str_replace( '@saved@', $CURRENCY_DISPLAY->symbol, $description );

		return $description;
	}

	function calcEndUserprice( $product_id, $overrideShoppergroup ) {
		global $VM_LANG, $CURRENCY_DISPLAY;
		$auth = $_SESSION['auth'];
		// Get the DISCOUNT AMOUNT
		$discount_info = $this->get_discount( $product_id );

		// Get the Price according to the quantity in the Cart
		$price_info = $this->get_price( $product_id, false, $overrideShoppergroup );

		if (isset($price_info["product_price_id"])) {

			$base_price = $price_info["product_price"];
			$price = $price_info["product_price"];

			if ($auth["show_price_including_tax"] == 1) {
				$my_taxrate = $this->get_product_taxrate($product_id);
				$base_price += ($my_taxrate * $price);
			}
			else {
				$my_taxrate = 0;
			}
			$tax = $my_taxrate * 100;
			$price_info['tax_rate'] = $VM_LANG->_('PHPSHOP_TAX_LIST_RATE').': '.$tax.'%';
			// Calculate discount
			if( !empty($discount_info["amount"])) {
				
				switch( $discount_info["is_percent"] ) {
					case 0:
						$base_price -= $discount_info["amount"];
						$price_info['discount_info'] = $VM_LANG->_('PHPSHOP_PRODUCT_DISCOUNT_LBL').': '.$CURRENCY_DISPLAY->getFullValue($discount_info['amount']);
						break;
					case 1:
						$base_price *= (100 - $discount_info["amount"])/100;
						$price_info['discount_info'] = $VM_LANG->_('PHPSHOP_PRODUCT_DISCOUNT_LBL').': '.$discount_info['amount'].'%';
						break;
				}
			}
			$price_info['product_price'] = $base_price;
		}
		return $price_info;
	}

	/**
         * Function to calculate the price, apply discounts from the discount table
         * and reformat the price
	 *
	 * @param int $product_id
	 * @param boolean $hide_tax Wether to show the text "(including X.X% tax)" or not
	 * @return string The formatted price
	 */
	function show_price( $product_id, $hide_tax = false ) {
		global $VM_LANG, $CURRENCY_DISPLAY,$vendor_mail;
		$auth = $_SESSION['auth'];
		
		$tpl = new $GLOBALS['VM_THEMECLASS']();
		
		$product_name = htmlentities( $this->get_field($product_id, 'product_name'), ENT_QUOTES );
		$tpl->set( 'product_id', $product_id );
		$tpl->set( 'product_name', $product_name );
		$tpl->set( 'vendor_mail', $vendor_mail );
		$discount_info = $base_price = array();
		$text_including_tax = '';
		
		if( $auth['show_prices'] ) {
			// Get the DISCOUNT AMOUNT
			$discount_info = $this->get_discount( $product_id );
			if( !$discount_info["is_percent"] && $discount_info["amount"] != 0 ) {
				$discount_info["amount"] = $GLOBALS['CURRENCY']->convert($discount_info["amount"]);
			}
			// Get the Price according to the quantity in the Cart
			$price_info = $this->get_price( $product_id );
			$tpl->set( 'price_info', $price_info );
			
			// Get the Base Price of the Product
			$base_price_info = $this->get_price($product_id, true );
			$tpl->set( 'base_price_info', $base_price_info );
			if( $price_info === false ) {
				$price_info = $base_price_info;
			}
			$html = "";
			$undiscounted_price = 0;
			if (isset($price_info["product_price_id"])) {
				if( $base_price_info["product_price"]== $price_info["product_price"] ) {
					$price = $base_price = $GLOBALS['CURRENCY']->convert( $base_price_info["product_price"], $price_info['product_currency'] );
				} else {
					$base_price = $GLOBALS['CURRENCY']->convert( $base_price_info["product_price"], $price_info['product_currency'] );
					$price = $GLOBALS['CURRENCY']->convert( $price_info["product_price"], $price_info['product_currency'] );	
				}

				if ($auth["show_price_including_tax"] == 1) {
					$my_taxrate = $this->get_product_taxrate($product_id);
					$base_price += ($my_taxrate * $base_price);
				}
				else {
					$my_taxrate = 0;
				}
				// Calculate discount
				if( !empty($discount_info["amount"])) {
					$undiscounted_price = $base_price;
					switch( $discount_info["is_percent"] ) {
						case 0:
							// If we subtract discounts BEFORE tax
							if( PAYMENT_DISCOUNT_BEFORE == '1' ) {
								// and if our prices are shown with tax
								if( $auth["show_price_including_tax"] == 1) {
									// then we add tax to the (untaxed) discount
									$discount_info['amount'] += ($my_taxrate*$discount_info['amount']);
								} 
								// but if our prices are shown without tax
									// we just leave the (untaxed) discount amount as it is
								 	
							}
							// But, if we subtract discounts AFTER tax
								// and if our prices are shown with tax
									// we just leave the (untaxed) discount amount as it is
								// but if  prices are shown without tax
									// we just leave the (untaxed) discount amount as it is
									// even though this is not really a good combination of settings

							$base_price -= $discount_info["amount"];
							break;
						case 1:
							$base_price *= (100 - $discount_info["amount"])/100;
							break;
					}
				}
				$text_including_tax = "";
				if (!empty($my_taxrate)) {
					$tax = $my_taxrate * 100;
					// only show "including x % tax" when it shall
					// not be hidden
					if( !$hide_tax && $auth["show_price_including_tax"] == 1 && VM_PRICE_SHOW_INCLUDINGTAX) {
						$text_including_tax = $VM_LANG->_('PHPSHOP_INCLUDING_TAX');
						eval ("\$text_including_tax = \"$text_including_tax\";");
					}
				}

				// Check if we need to display a Table with all Quantity <=> Price Relationships
				if( $base_price_info["product_has_multiple_prices"] && !$hide_tax ) {
					$db = new ps_DB;
					// Quantity Discount Table
					$q = "SELECT product_price, product_currency, price_quantity_start, price_quantity_end 
							FROM #__{vm}_product_price
				  			WHERE product_id='$product_id' 
				  			AND shopper_group_id='".$auth["shopper_group_id"]."' 
				  			ORDER BY price_quantity_start";
					$db->query( $q );

					//         $prices_table = "<table align=\"right\">
					$prices_table = "<table width=\"100%\">
					  <thead><tr class=\"sectiontableheader\">
					  <th>".$VM_LANG->_('PHPSHOP_CART_QUANTITY')."</th>
					  <th>".$VM_LANG->_('PHPSHOP_CART_PRICE')."</th>
					  </tr></thead>
					  <tbody>";
					$i = 1;
					if ($db->num_rows()==0) {
						// get the vendor ID
						$q = "SELECT vendor_id FROM #__{vm}_product WHERE product_id='$product_id'";
						$db->setQuery($q); $db->query();
						$db->next_record();
						$vendor_id = $db->f("vendor_id");
						// get the default shopper group ID
						$q = "SELECT shopper_group_id FROM #__{vm}_shopper_group WHERE `vendor_id`='$vendor_id' AND `default`='1'";
						$db->setQuery($q); $db->query();
						$db->next_record();						
						$default_shopper_group_id = $db->f("shopper_group_id");
						// get the current shopper group discount
						$q = "SELECT * FROM #__{vm}_shopper_group WHERE shopper_group_id=" . $auth["shopper_group_id"];
						$db->setQuery($q); $db->query();
						$db->next_record();
						$shopper_group_discount = $db->f("shopper_group_discount");
						// check for prices in default shopper group
						$q = "SELECT product_price, price_quantity_start, price_quantity_end, product_currency FROM #__{vm}_product_price
							WHERE product_id='$product_id' AND shopper_group_id='".$default_shopper_group_id."' ORDER BY price_quantity_start";
						$db->query( $q );
						while( $db->next_record() ) {
							$prices_table .= "<tr class=\"sectiontableentry$i\"><td>".$db->f("price_quantity_start")." - ".$db->f("price_quantity_end")."</td>";
							$prices_table .= "<td>";
							if (!empty($my_taxrate))
								$prices_table .= $CURRENCY_DISPLAY->getFullValue( ($my_taxrate+1)*$db->f("product_price")*((100-$shopper_group_discount)/100) );
							else
								$prices_table .= $CURRENCY_DISPLAY->getFullValue( $db->f("product_price")*((100-$shopper_group_discount)/100) );
							$prices_table .= "</td></tr>";
							$i == 1 ? $i++ : $i--;
						}
					} else {
						// get the current shopper group discount
						$dbsg = new ps_DB();
						$q = "SELECT * FROM #__{vm}_shopper_group WHERE shopper_group_id=" . $auth["shopper_group_id"];
						$dbsg->setQuery($q);
						 $dbsg->query();
						$dbsg->next_record();
						$shopper_group_discount = $dbsg->f("shopper_group_discount");
						while( $db->next_record() ) {
							$price = $GLOBALS['CURRENCY']->convert( $db->f("product_price"), $db->f("product_currency") );
							$prices_table .= "<tr class=\"sectiontableentry$i\"><td>".$db->f("price_quantity_start")." - ".$db->f("price_quantity_end")."</td>";
							$prices_table .= "<td>";
							if (!empty($my_taxrate)) {
								$prices_table .= $CURRENCY_DISPLAY->getFullValue( ($my_taxrate+1)*$price*((100-$shopper_group_discount)/100) );
							}
							else {
								$prices_table .= $CURRENCY_DISPLAY->getFullValue( $price*((100-$shopper_group_discount)/100) );
							}
							$prices_table .= "</td></tr>";
							$i == 1 ? $i++ : $i--;
						}
					}
					$prices_table .= "</tbody></table>";
					if( @$_REQUEST['page'] != "shop.product_details" ) {
						$html .= vmToolTip( $prices_table );
					}
					else
					$html .= $prices_table;
				}
			}
		}
		$tpl->set( 'discount_info', $discount_info );
		$tpl->set( 'text_including_tax', $text_including_tax );
		$tpl->set( 'undiscounted_price', @$undiscounted_price );
		$tpl->set( 'base_price', $base_price );
        $tpl->set( 'price_table', $html);
        $tpl->set( 'parent_has_children', $this->parent_has_children($product_id));
		return $tpl->fetch( 'common/price.tpl.php');

	}

	/**
	 * Get the information about the discount for a product
	 *
	 * @param int $product_id
	 * @return array The discount information
	 */
	function get_discount( $product_id ) {
		global $mosConfig_lifetime;

		// We use the Session now to store the discount info for
		// each product. But this info can change regularly,
		// so we check if the session time has expired
		if( empty( $_SESSION['product_sess'][$product_id]['discount_info'] )
		|| (time() - $_SESSION['product_sess'][$product_id]['discount_info']['create_time'] )>$mosConfig_lifetime) {
			$db = new ps_DB;
			$starttime = time();
			$year = date('Y');
			$month = date('n');
			$day = date('j');
			// get the beginning time of today
			$endofday = mktime(0, 0, 0, $month, $day, $year) - 1440;

			// Get the DISCOUNT AMOUNT
			$q = "SELECT amount,is_percent FROM #__{vm}_product,#__{vm}_product_discount ";
			$q .= "WHERE product_id='$product_id' AND (start_date<='$starttime' OR start_date=0) AND (end_date>='$endofday' OR end_date=0) ";
			$q .= "AND product_discount_id=discount_id";
			$db->query( $q );
			if( $db->next_record() ) {
				$discount_info["amount"] = $db->f("amount");
				$discount_info["is_percent"] = $db->f("is_percent");
                $no_discount = false;
			}
			else {
				$discount_info["amount"] = 0;
				$discount_info["is_percent"] = 0;
                $no_discount = true;
			}
            if ($no_discount) {
                $q = "SELECT product_parent_id FROM #__{vm}_product WHERE product_id=$product_id";
                $db->query($q);
                if($db->next_record()) {
                    $q = "SELECT amount,is_percent FROM #__{vm}_product,#__{vm}_product_discount ";
			        $q .= "WHERE product_id='".$db->f("product_parent_id")."' AND (start_date<='$starttime' OR start_date=0) AND (end_date>='$endofday' OR end_date=0) ";
			        $q .= "AND product_discount_id=discount_id";
			        $db->query( $q );
			        if( $db->next_record() ) {
				        $discount_info["amount"] = $db->f("amount");
				        $discount_info["is_percent"] = $db->f("is_percent");
			        }
                }
            }                
			$discount_info['create_time'] = time();
			$_SESSION['product_sess'][$product_id]['discount_info'] = $discount_info;
			return $discount_info;
		}
		else
		    return $_SESSION['product_sess'][$product_id]['discount_info'];
	}

	/**
	 * display a snapshot of a product based on the product sku.
	 * This was written to provide a quick way to display a product inside of modules
	 *
	 * @param string $product_sku The SKU identifying the product
	 * @param boolean $show_price Show the product price?
	 * @param boolean $show_addtocart Show the add-to-cart link?
	 * @param boolean $show_product_name Show the product name?
	 */
	function show_snapshot($product_sku, $show_price=true, $show_addtocart=true, $show_product_name = true ) {
		echo $this->product_snapshot( $product_sku, $show_price, $show_addtocart, $show_product_name );
	}

	/**
	 * Returns HTML code for a snapshot of a product based on the product sku.
	 * This was written to provide a quick way to display a product inside of modules
	 *
	 * @param string $product_sku The SKU identifying the product
	 * @param boolean $show_price Show the product price?
	 * @param boolean $show_addtocart Show the add-to-cart link?
	 * @param boolean $show_product_name Show the product name?
	 */
	function product_snapshot( $product_sku, $show_price=true, $show_addtocart=true, $show_product_name = true ) {

		global $sess, $mm_action_url;

		$db = new ps_DB;

		require_once(CLASSPATH.'ps_product_category.php');
		$ps_product_category = new ps_product_category;

		$q = "SELECT product_id, product_name, product_parent_id, product_thumb_image FROM #__{vm}_product WHERE product_sku='$product_sku'";
		$db->query( $q );

		if ($db->next_record()) {
			$product_id = $db->f("product_id" );
			$tpl = new $GLOBALS['VM_THEMECLASS']();
			
			$cid = $ps_product_category->get_cid( $product_id );

			$tpl->set( 'product_id', $product_id);
			$tpl->set( 'product_name', $db->f("product_name") );
			$tpl->set( 'show_product_name', $show_product_name );
			
			if ($db->f("product_parent_id")) {
				$url = "?page=shop.product_details&category_id=$cid&flypage=".$this->get_flypage($db->f("product_parent_id"));
				$url .= "&product_id=" . $db->f("product_parent_id");
			} else {
				$url = "?page=shop.product_details&category_id=$cid&flypage=".$this->get_flypage($db->f("product_id"));
				$url .= "&product_id=" . $db->f("product_id");
			}
			$product_link = $sess->url($mm_action_url. "index.php" . $url);
			$tpl->set( 'product_link', $product_link );
			$tpl->set( 'product_thumb_image', $db->f("product_thumb_image"), "alt=\"".$db->f("product_name")."\"");

			if (_SHOW_PRICES == '1' && $show_price) {
				// Show price, but without "including X% tax"
				$price = $this->show_price( $db->f("product_id"), true );
				$tpl->set( 'price', $price );
			}
			if (USE_AS_CATALOGUE != 1 && $show_addtocart 
					&& isset( $GLOBALS['product_info'][$product_id]['price']['product_price_id'] ) // Product must have a price to add it to cart
					&& !$this->product_has_attributes($product_id, true)  // Parent Products and Products with attributes can't be added to cart this way
					) {
				$url = "?page=shop.cart&func=cartAdd&product_id=" .  $db->f("product_id");
				$addtocart_link = $sess->url($mm_action_url. "index.php" . $url);
				$tpl->set( 'addtocart_link', $addtocart_link );
			}
			return $tpl->fetch( 'common/productsnapshot.tpl.php');
		}
		
		return '';
		
	}

	/**
	 * Use this function if you need the weight of a product
	 *
	 * @param int $prod_id
	 * @return int The weight of the product
	 */
	function get_weight($prod_id) {
		return (float)$this->get_field( $prod_id, "product_weight");
	}
	/**
	 * Print the availability HTML code for product $prod_id
	 *
	 * @param int $prod_id
	 */
	function show_availability($prod_id) {
		echo $this->get_availability($prod_id);
	}

	/**
	 * Returns the availability information as HTML code
	 * @author soeren
	 * @param int $prod_id
	 * @return string
	 */
	function get_availability($prod_id) {

		$html = '';
		$availArr = $this->get_availability_data( $prod_id );
		if( !empty( $availArr )) {
			$tpl = vmTemplate::getInstance();
			$tpl->set( 'product_id', $prod_id );
			$tpl->set( 'product_available_date', $availArr['product_available_date'] );
			$tpl->set( 'product_availability', $availArr['product_availability'] );
			$tpl->set( 'product_in_stock', $availArr['product_in_stock'] );
			$html = $tpl->fetch( 'common/availability.tpl.php');
		}
		return $html;
	}
	/**
	 * Retrieves the data related to availability information
	 *
	 * @param int $prod_id
	 * @return array
	 */
	function get_availability_data( $prod_id) {
		$is_parent = $this->parent_has_children( $prod_id );
		$availArr = array();
		if( !$is_parent ) {			
			$availArr['product_id'] = $prod_id;
			$availArr['product_available_date'] = $this->get_field( $prod_id, 'product_available_date');
			$availArr['product_availability'] = $this->get_field( $prod_id, 'product_availability');
			$availArr['product_in_stock'] = $this->get_field( $prod_id, 'product_in_stock');
		}
		return $availArr;
	}
	/**
	 * Modifies the product_publish field and toggles it from Y to N or N to Y
	 * for product $d['product_id']
	 * @deprecated 
	 * @param int $d $d['task'] must be "publish" or "unpublish"
	 * @return unknown
	 */
	function product_publish( &$d ) {
		$this->handlePublishState( $d );
		return;
	}
	/**
	 * Function to check if a product exists (and is published)
	 * @static 
	 * @param int $product_id
	 * @param boolean $check_publishstate
	 * @return boolean
	 */
    function product_exists( $product_id, $check_publishstate=true ) {
    	$db = new ps_DB();
    	$q = "SELECT product_id FROM #__{vm}_product WHERE ";
		$q .= "product_id = ".(int)$product_id;
		if( $check_publishstate ) {
			$q.= ' AND product_publish=\'Y\'';
		}
		$db->query ( $q );
		return $db->num_rows() > 0;
    }
    /**
     * Assembles the $child_options variable for storage in the product table
     *
     * @param unknown_type $d
     * @return unknown
     */
    function set_child_options( $d ) {
		if($d["product_parent_id"] !=0) {
			$child_options = null;
        } else {
			$child_options = vmrequest::getYesOrNo('display_use_parent').","
										.vmGet( $d, 'product_list', 'N' ).","
										.vmrequest::getYesOrNo('display_headers').","
										.vmrequest::getYesOrNo('product_list_child').","
										.vmrequest::getYesOrNo('product_list_type').","
										.vmrequest::getYesOrNo('display_desc').","
										.vmrequest::getVar('desc_width').","
										.vmrequest::getVar('attrib_width').","
										.vmrequest::getVar('child_class_sfx');
        }
        return $child_options;
    }
    
    function &get_child_options( $product_id ) {
    	$child_options= array();
    	$child_options_string = ps_product::get_field( $product_id, 'child_options', true ); 
		$fields=explode(',',$child_options_string);
		if( !empty( $fields)) {
			$child_options['display_use_parent'] =array_shift($fields);
			$child_options['product_list'] =array_shift($fields);
			$child_options['display_header'] =array_shift($fields);
			$child_options['product_list_child'] =array_shift($fields);
			$child_options['product_list_type'] =array_shift($fields);
			$child_options['ddesc'] =array_shift($fields);
			$child_options['display_desc'] =& $child_options['ddesc']; 
			$child_options['dw'] =array_shift($fields);
			$child_options['desc_width'] =& $child_options['dw']; 
			$child_options['aw'] =array_shift($fields);
			$child_options['attrib_width'] =& $child_options['aw']; 
			$child_options['class_suffix'] =array_shift($fields);
			$child_options['child_class_sfx'] =& $child_options['class_suffix'];
		}
		return $child_options;
    }
    /**
     * Assembles the string "quantity_options" for storage in the product table
     *
     * @param array $d
     * @return string
     */
    function set_quantity_options( &$d ) {
    	return vmGet($d,'quantity_box').","
        			.vmRequest::getInt('quantity_start').","
        			.vmRequest::getInt('quantity_end').","
        			.vmRequest::getInt('quantity_step');
    }
    /**
     * Disassembles the comma-separated "quantity_options" string
     * and creates an array with associative indices
     *
     * @param int $product_id
     * @return array
     */
    function &get_quantity_options( $product_id ) {
    	$quantity_options = array('quantity_start' => 0, 'quantity_end' => 0, 'quantity_step' => 1 );
    	$quantity_options_string = ps_product::get_field($product_id, 'quantity_options');
    	
    	$fields = explode(',', $quantity_options_string );
    	if( !empty( $fields )&& sizeof($fields) > 1 ) {
    		$quantity_options['quantity_box'] = $fields[0];
    		$quantity_options['display_type'] = $fields[0];
    		$quantity_options['quantity_start'] = $fields[1];
    		$quantity_options['quantity_end'] = $fields[2];
    		$quantity_options['quantity_step'] = $fields[3];
    	}
    	return $quantity_options;
    }
    
    /**
     * Retrieves the maximum and minimum quantity for the product specified by $product_id
     *
     * @param int $product_id
     * @return array
     */
    function product_order_levels($product_id) {
        
        $min_order=0;
        $max_order=0;
        $product_order_levels = ps_product::get_field( $product_id, 'product_order_levels');
        $product_parent_id = ps_product::get_field( $product_id, 'product_parent_id');
        
        
		if($product_order_levels != ',') {
			$order_levels = $product_order_levels;
			$levels = explode(",",$order_levels);
			$min_order = array_shift($levels);
			$max_order = array_shift($levels);
		}
		else if($product_parent_id > 0) {
            //check parent if product_parent_id != 0
        	$product_order_levels = ps_product::get_field( $product_parent_id, 'product_order_levels');
        	$product_parent_id = ps_product::get_field( $product_parent_id, 'product_parent_id');
			
			if($product_order_levels != ",") {
				$order_levels = $product_order_levels;
				$levels = explode(",",$order_levels);
				$min_order = array_shift($levels);
				$max_order = array_shift($levels);
			}
		}
        
        return array($min_order,$max_order);
    }
    
    
    function featuredProducts($random, $products, $categories) {
	    global $VM_LANG;
	    require_once( CLASSPATH . 'ps_product_attribute.php');
	    $ps_product_attribute = new ps_product_attribute();
	    $db = new ps_DB;
	    $tpl = new $GLOBALS['VM_THEMECLASS']();
	    $category_id = null;
	    if($categories) {
	        $category_id = vmRequest::getInt('category_id');
	    }
        if ( $category_id ) {
	        $q  = "SELECT DISTINCT product_sku,#__{vm}_product.product_id,product_name,product_s_desc,product_thumb_image, product_full_image, product_in_stock, product_url FROM #__{vm}_product, #__{vm}_product_category_xref, #__{vm}_category WHERE \n";
	        $q .= "(#__{vm}_product.product_parent_id='' OR #__{vm}_product.product_parent_id='0') \n";
	        $q .= "AND #__{vm}_product.product_id=#__{vm}_product_category_xref.product_id \n";
	        $q .= "AND #__{vm}_category.category_id=#__{vm}_product_category_xref.category_id \n";
            $q .= "AND #__{vm}_category.category_id='$category_id' \n";
	        $q .= "AND #__{vm}_product.product_publish='Y' \n";
	        $q .= "AND #__{vm}_product.product_special='Y' \n";
	        if( CHECK_STOCK && PSHOP_SHOW_OUT_OF_STOCK_PRODUCTS != "1") {
		        $q .= " AND product_in_stock > 0 \n";
	        }
	        $q .= 'ORDER BY RAND() LIMIT 0, '.(int)$products;
        }
        else {
	        $q  = "SELECT DISTINCT product_sku,product_id,product_name,product_s_desc,product_thumb_image, product_full_image, product_in_stock, product_url FROM #__{vm}_product WHERE ";
	        $q .= "(#__{vm}_product.product_parent_id='' OR #__{vm}_product.product_parent_id='0') AND vendor_id='".$_SESSION['ps_vendor_id']."' ";
	        $q .= "AND #__{vm}_product.product_publish='Y' ";
	        $q .= "AND #__{vm}_product.product_special='Y' ";
	        if( CHECK_STOCK && PSHOP_SHOW_OUT_OF_STOCK_PRODUCTS != "1") {
		        $q .= " AND product_in_stock > 0 ";
	        }
	        $q .= 'ORDER BY RAND() LIMIT 0, '.(int)$products;
        }
        $db->query($q);
        // Output using template
        if( $db->num_rows() > 0 ) {
	        $i = 0;
            $featured_products = array();
	        while($db->next_record()) {
                $flypage = $this->get_flypage($db->f("product_id"));
                $featured_products[$i]['product_sku'] = $db->f("product_sku");
                $featured_products[$i]['product_name'] = $db->f("product_name");
                $price = "";
                if (_SHOW_PRICES == '1') {
				    // Show price, but without "including X% tax"
				    $price = $this->show_price( $db->f("product_id"), false );
			    }
                $featured_products[$i]['product_price'] = $price;
                $featured_products[$i]['product_s_desc'] = $db->f("product_s_desc");
                $featured_products[$i]['product_url'] = $db->f("product_url");
                $featured_products[$i]['product_thumb'] = $db->f("product_thumb_image");
                $featured_products[$i]['product_full_image'] = $db->f("product_full_image");
                $featured_products[$i]['product_id'] = $db->f("product_id");
                $featured_products[$i]['flypage'] = $flypage;
                $featured_products[$i]['form_addtocart'] = "";
                if (USE_AS_CATALOGUE != '1' && $price != "" 
	                && !stristr( $price, $VM_LANG->_('PHPSHOP_PRODUCT_CALL') )
	                && !$this->product_has_attributes( $db->f('product_id'), true )
	                && $tpl->get_cfg( 'showAddtocartButtonOnProductList' ) ) {
			        $tpl->set( 'i', $i );
			        $tpl->set( 'product_id', $db->f('product_id') );
			        $tpl->set( 'ps_product_attribute', $ps_product_attribute );
			        $tpl->set( 'product_in_stock', $db->f('product_in_stock'));
			        $featured_products[$i]['form_addtocart'] = $tpl->fetch( 'browse/includes/addtocart_form.tpl.php' );
			        $featured_products[$i]['has_addtocart'] = true;
		        }
		        $i++;
	        }
            $tpl->set( 'featured_products', $featured_products );
            return $tpl->fetch( 'common/featuredProducts.tpl.php'); 
        }
    }
    
    function latestProducts($random, $products) {
    	return "";
    }

    function addRecentProduct($product_id,$category_id,$maxviewed) {
    	global $recentproducts;
    	//Check to see if we alread have recent
    	if($_SESSION['recent']['idx'] !=0) {
    		for($i=0;$i<$_SESSION['recent']['idx'];$i++){
    			//Check if it already exists and remove and reorder array
    			if($_SESSION['recent'][$i]['product_id']==$product_id) {
    				for($k=$i;$k<$_SESSION['recent']['idx']-1;$k++){
    					$_SESSION['recent'][$k]=$_SESSION['recent'][$k+1];	
    				}
    				array_pop($_SESSION['recent']);
    				$_SESSION['recent']['idx']--;
    			}
    		}
    	}
    	// add product to recently viewed
    	$_SESSION['recent'][$_SESSION['recent']['idx']]['product_id'] = $product_id;
    	$_SESSION['recent'][$_SESSION['recent']['idx']]['category_id'] = $category_id;
    	$_SESSION['recent']['idx']++;
    	//Check to see if we have reached are limit and remove first item
    	if($_SESSION['recent']['idx'] > $maxviewed+1) {
    		for($k=0;$k<$_SESSION['recent']['idx']-1;$k++){
    			$_SESSION['recent'][$k]=$_SESSION['recent'][$k+1];	
    		}
    		array_pop($_SESSION['recent']);
    		$_SESSION['recent']['idx']--;
    	}
    }
    
    function recentProducts($product_id,$maxitems) {
    	global $db, $VM_LANG, $sess;
    	if( $maxitems == 0 ) return;
    	
    	$recentproducts = $_SESSION['recent'];
    	//No recent products so return empty
		if($recentproducts['idx'] == 0) {
			//return "";
		}
    	$tpl = new $GLOBALS['VM_THEMECLASS']();
		$db = new ps_DB;
        $dbp = new ps_DB;
		$k=0;
		$recent = array();
		// Iterate through loop backwards (newest to oldest)
		for($i=$recentproducts['idx']-1;$i >= 0;$i--) {
			//Check if on current product and don't display
			if($recentproducts[$i]['product_id']== $product_id){
				continue;
			}
			// If we have not reached max products add the next product
			if($k < $maxitems) {
				$prod_id = $recentproducts[$i]['product_id'];
				$category_id = $recentproducts[$i]['category_id'];
				$q = "SELECT product_name, category_name, c.category_flypage,product_s_desc,product_thumb_image ";
				$q .= "FROM #__{vm}_product as p,#__{vm}_category as c,#__{vm}_product_category_xref as cx ";
				$q .= "WHERE p.product_id = '$prod_id' ";
				$q .= "AND c.category_id = '$category_id' ";
				$q .= "AND p.product_id = cx.product_id ";
				$q .= "AND c.category_id=cx.category_id ";
				$q .= "AND p.product_publish='Y' ";
				$q .= "AND c.category_publish='Y' ";
				$q .= "LIMIT 0,1";
				$db->query( $q );
				if( !$db->next_record() ) {
					continue;
				}
                if(!$this->is_product($prod_id )) {
                    $prod_id_p = $this->get_field($prod_id,"product_parent_id");
                    $q = "SELECT product_name,category_name, c.category_flypage,product_s_desc,product_thumb_image ";
				    $q .= "FROM #__{vm}_product as p,#__{vm}_category as c,#__{vm}_product_category_xref as cx ";
				    $q .= "WHERE p.product_id = '$prod_id_p' ";
				    $q .= "AND c.category_id = '$category_id' ";
				    $q .= "AND p.product_id = cx.product_id ";
				    $q .= "AND c.category_id=cx.category_id LIMIT 0,1";
                    $dbp->query( $q );
                }
				$recent[$k]['product_s_desc'] = $db->f("product_s_desc");
                if($recent[$k]['product_s_desc']=="" && !empty($prod_id_p)) {
                    $recent[$k]['product_s_desc'] = $dbp->f("product_s_desc");
                }
				$flypage = $db->f("category_flypage");  
                if(empty($flypage) && !empty($prod_id_p))
                    $flypage = $dbp->sf("category_flypage");
				if( empty( $flypage )) {
					$flypage = FLYPAGE;
				}
				$flypage = str_replace( 'shop.', '', $flypage);
				$flypage = stristr( $flypage, '.tpl') ? $flypage : $flypage . '.tpl';
				$recent[$k]['product_url'] = $sess->url("page=shop.product_details&amp;product_id=$prod_id&amp;category_id=$category_id&amp;flypage=$flypage");
				$recent[$k]['category_url'] = $sess->url("page=shop.browse&amp;category_id=$category_id");
				$recent[$k]['product_name'] = $db->f("product_name"); 
                if($recent[$k]['product_name']=="" && !empty($prod_id_p)) {
                    $recent[$k]['product_name'] = $dbp->f("product_name");
                }
                $recent[$k]['product_name'] = shopMakeHtmlSafe($recent[$k]['product_name']);
				$recent[$k]['category_name'] = $db->f("category_name");
                if($recent[$k]['category_name']=="" && !empty($prod_id_p)) {
                    $recent[$k]['category_name'] = $dbp->f("category_name");
                }
				$recent[$k]['product_thumb_image'] = $db->f("product_thumb_image");
				if($recent[$k]['product_thumb_image']=="" && !empty($prod_id_p)) {
                    $recent[$k]['product_thumb_image'] = $dbp->f("product_thumb_image");
                }
				$k++;
			}
		}
		if($k == 0) {
			return "";
		}
		$tpl->set("recent_products",$recent);
		return $tpl->fetch( 'common/recent.tpl.php' );
    }
    
}  // ENd of CLASS ps_product

// Check if there is an extended class in the Themes and if it is allowed to use them
// If the class is called outside Virtuemart, we have to make sure to load the settings
// Thomas Kahl - Feb. 2009
if (!defined('VM_ALLOW_EXTENDED_CLASSES') && file_exists(dirname(__FILE__).'/../virtuemart.cfg.php')) {
	include_once(dirname(__FILE__).'/../virtuemart.cfg.php');
}
// If settings are loaded, extended Classes are allowed and the class exisits...
if (defined('VM_ALLOW_EXTENDED_CLASSES') && defined('VM_THEMEPATH') && VM_ALLOW_EXTENDED_CLASSES && file_exists(VM_THEMEPATH.'user_class/'.basename(__FILE__))) {
	// Load the theme-user_class as extended
	include_once(VM_THEMEPATH.'user_class/'.basename(__FILE__));
} else {
	// Otherwise we have to use the original classname to extend the core-class
	class ps_product extends vm_ps_product {}
}

?>
