<?php
class Products{
	var $aProducts = null;
	var $aProductsPages = null;
	var $mData = null;
	var $aPages = null;
	var $aNewProducts = Array( );
	var $aBestsellers = Array( );
	function __construct( ){
	}
	function generateCache( ){
	}
	public function generateListSQL( $iContent = null, $aProducts = null, $aSearch = null, $aAdditionalFields = null, $iMin = null, $iMax = null, $bShowOnlyAvailable = false ){
		global $sUrlExt;
		$oDB = & DataBase::getInstance( );
		$iStatus = throwStatus( );
		if( isset( $_SESSION['iUserClient'] ) && is_numeric( $_SESSION['iUserClient'] ) && ( $_SESSION['iUserClient'] > 0 ) && defined( 'CUSTOMER_PAGE' ) ){
			$sUserId = intval( $_SESSION['iUserClient'] );
		} else{
			$sUserId = 'NULL';
		}
		$sqlHaving = null;
		$sql = 'SELECT SQL_CALC_FOUND_ROWS `products`.*, `promoted`.`iPromotedMin`, `producers`.`sName` AS `prodsName`, `producers`.`sLogoUrl` AS `prodsLogoUrl`, `products`.`TiAvailable` AS `iAvailable`, ';
		if( !empty( $aAdditionalFields ) ){
			$sql.= '`products_ext`.`' . implode( '`, `products_ext`.`', $aAdditionalFields ) . '`, ';
		}
		$sql.= ( ( $_GET['iNewComments'] ) ? '`new_comments`.`iNewCommentsCount`, ' : null ) . '`products`.`fPrice` AS `fCatalogPrice`, `vat`.`fVatValue`, ' . $oDB->throwRoutineName( '%%_calculateProductPriceForUser' ) . '( ' . $sUserId . ', `products`.`iProduct`, ' . JOIN_PROMOTIONS . ' ) AS `fPrice`, LEAST( ' . ( STOCK_GLOBAL_UNLIMITED ? '1' : '`products`.`TiCanBuy`' ) . ', ' . $oDB->throwRoutineName( '%%_getProductCanBuyForUser' ) . ' ( ' . $sUserId . ', `products`.`iProduct` ) ) AS `iCanBuy`, `products`.`TiVariantsCount` AS `iVariantCount`, ' . $oDB->throwRoutineName( '%%_getProductVisiblePriceForUser' ) . ' ( ' . $sUserId . ', `products`.`iProduct` ) AS `iVisiblePrice`
							FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `products` ';
		if( is_numeric( $iContent ) && !isset( $aProducts ) ){
			$GLOBALS['oPage']->mData = null;
			$aPages = $GLOBALS['oPage']->throwAllChildren( $iContent );
			$aPages[] = $iContent;
			$sql.= 'INNER JOIN ( SELECT DISTINCT `iProduct` FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` WHERE `iPage` IN ( ' . implode( ', ', $aPages ) . ' ) ) AS `pp` USING( `iProduct` ) ';
		}
		$sql.= 'LEFT JOIN ( SELECT MIN( `iPromoted` ) `iPromotedMin`, `iProduct` FROM `' . $oDB->throwTableName( DB_PRODUCTS_PROMOTED ) . '` WHERE `iTimeValid` IS NULL OR `iTimeValid` >= UNIX_TIMESTAMP() GROUP BY `iProduct` ) `promoted` USING( `iProduct` ) ';
		if( !empty( $aAdditionalFields ) || isset( $_GET['iDescription'] ) ){
			$sql.= 'INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_EXT ) . '` AS `products_ext` USING( `iProduct` ) ';
		}
		$sql.= 'INNER JOIN `' . $oDB->throwTableName( DB_VAT ) . '` AS `vat` USING( `iVat` ) ';
		$sql.= 'LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCERS ) . '` AS `producers` USING( `iProducer` ) ';
//		$sql.= 'INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` `pv` USING( `iProduct` ) ';

		if( $_GET['iNewComments'] ){
			$sql.= 'LEFT JOIN ( SELECT `iLink` AS `iProduct`, COUNT( `iComment` ) AS `iNewCommentsCount` FROM `' . $oDB->throwTableName( DB_PRODUCTS_COMMENTS ) . '` WHERE `iAccepted` = 0 GROUP BY `iProduct` ) AS `new_comments` USING( `iProduct` )';
			$sqlHaving.= ( empty( $sqlHaving ) ? ' HAVING' : ' AND' ) . ' `iNewCommentsCount` > 0';
			$sUrlExt.= '&amp;iNewComments=1';
		}

		$sqlPrefix = $sql;
		$sql = ' WHERE 1';
		
		if( isset( $iMin ) ){
			$sql.= ' AND `iPosition` >= ' . $iMin;
		}
		if( $bShowOnlyAvailable ){
			$sql .= ' AND `TiCanBuy` >= 1';
		}
		
		if( isset( $iMax ) ){
			$sql.= ' AND `iPosition` <= ' . $iMax;
		}

		if( isset( $_GET['iStatus'] ) && in_array( $_GET['iStatus'], Array( 0, 1 ) ) ){
			$sql.= ' AND `iStatus` = ' . $_GET['iStatus'];
			$sUrlExt.= '&amp;iStatus='.$_GET['iStatus'];
		}
		if( isset( $_GET['iCanBuy'] ) && in_array( $_GET['iCanBuy'], Array( 0, 1 ) ) ){
			$sql.= ' AND `TiCanBuy` = ' . $_GET['iCanBuy'];
			$sUrlExt.= '&amp;iCanBuy='.$_GET['iCanBuy'];
		}
		if( isset( $_GET['iCompare'] ) && in_array( $_GET['iCompare'], Array( 0, 1 ) ) ){
			$sql.= ' AND `iCompare` = ' . $_GET['iCompare'];
			$sUrlExt.= '&amp;iCompare='.$_GET['iCompare'];
		}
		if( isset( $_GET['iCurrencyDependent'] ) && in_array( $_GET['iCurrencyDependent'], Array( 0, 1 ) ) ){
			$sql.= ' AND `iCurrencyDependent` = ' . $_GET['iCurrencyDependent'];
			$sUrlExt.= '&amp;iCurrencyDependent='.$_GET['iCurrencyDependent'];
		}
		if( isset( $_GET['iBuyPrice'] ) && in_array( $_GET['iBuyPrice'], Array( 0, 1 ) ) ){
			if( $_GET['iBuyPrice'] ){
				$sql.= ' AND ( `fBuyPrice` IS NOT NULL )';
			} else{
				$sql.= ' AND ( `fBuyPrice` IS NULL )';
			}
			$sUrlExt.= '&amp;iBuyPrice='.$_GET['iBuyPrice'];
		}
		if( isset( $_GET['iDescription'] ) && in_array( $_GET['iDescription'], Array( 0, 1 ) ) ){
			if( $_GET['iDescription'] ){
				$sql.= ' AND ( ( `sDescriptionShort` IS NOT NULL AND `sDescriptionShort` != \'\' ) OR ( `sDescriptionFull` IS NOT NULL AND `sDescriptionFull` != \'\' ) )';
			} else{
				$sql.= ' AND ( ( `sDescriptionShort` IS NULL OR `sDescriptionShort` = \'\' ) AND ( `sDescriptionFull` IS NULL OR `sDescriptionFull` = \'\' ) )';
			}
			$sUrlExt.= '&amp;iDescription='.$_GET['iDescription'];
		}
		if( $_GET['iCarriersPrices'] ){
			$sql.= ' AND EXISTS( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_CARRIERS_PRICES ) . '` WHERE `iProduct` = `products`.`iProduct` )';
			$sUrlExt.= '&amp;iCarriersPrices=1';
		}
		if( $_GET['iVariants'] == 2 ){
			$sql.= ' AND ( SELECT COUNT( `iProductVariant` ) FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` WHERE `iProduct` = `products`.`iProduct` ) > 1';
			$sUrlExt.= '&amp;iVariants=2';
		} elseif( $_GET['iVariants'] == 1 ){
			$sql.= ' AND ( SELECT COUNT( `iProductVariant` ) FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` WHERE `iProduct` = `products`.`iProduct` ) = 1';
			$sUrlExt.= '&amp;iVariants=1';
		}
		if( $_GET['iGroupsDiscounts'] ){
			$sql.= ' AND EXISTS( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_DISCOUNTS ) . '` WHERE `iProduct` = `products`.`iProduct` )';
			$sUrlExt.= '&amp;iGroupsDiscounts=1';
		}
		if( $_GET['iDiscounted'] ){
			$sqlHaving.= ( empty( $sqlHaving ) ? ' HAVING' : ' AND' ) . ' `fPriceOld` > `fPrice`';
			$sUrlExt.= '&amp;iDiscounted=1';
		}
		if( $_GET['iTimePrice'] && $_GET['iTimePrice'] == 1 ){
			$sql.= ' AND `fPriceTimeValue` < `fPrice`';
			$sUrlExt.= '&amp;iTimePrice=1';
		}
		if( $_GET['iTimePrice'] && $_GET['iTimePrice'] == 2 ){
			$sql.= ' AND `fPriceTimeValue` < `fPrice` AND `iPriceTimeFrom` < UNIX_TIMESTAMP() AND `iPriceTimeTo` > UNIX_TIMESTAMP()';
			$sUrlExt.= '&amp;iTimePrice=2';
		}
		if( $_GET['iTimePrice'] && $_GET['iTimePrice'] == 3 ){
			$sql.= ' AND `fPriceTimeValue` < `fPrice` AND `iPriceTimeTo` <= UNIX_TIMESTAMP()';
			$sUrlExt.= '&amp;iTimePrice=2';
		}
		if( is_array( $_GET['aPromoted'] ) ) foreach( $_GET['aPromoted'] as $iPromoted => $null ){
			$sql.= ' AND EXISTS( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PROMOTED ) . '` WHERE `iPromoted` = ' . intval( $iPromoted ) . ' AND `iProduct` = `products`.`iProduct` AND ( `iTimeValid` IS NULL OR `iTimeValid` >= UNIX_TIMESTAMP() ) )';
			$sUrlExt.= '&amp;aPromoted[' . intval( $iPromoted ) . ']=1';
		}
		if( $_GET['iWaitingAvailNewsletter'] ){
			$sql.= ' AND EXISTS( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_AVAIL_NEWSLETTER ) . '` WHERE `iTimeSent` IS NULL AND `iProduct` = `products`.`iProduct` )';
			$sUrlExt.= '&amp;iWaitingAvailNewsletter=1';
		}
		if( isset( $aProducts ) && is_array( $aProducts ) ){
			/********************************************/
			/*         PREDEFINED PRODUCTS LIST         */
			/********************************************/
			$sql.= ' AND `products`.`iProduct` IN ( \'' . implode( '\', \'', $aProducts ) . '\' )';
			if( is_array( $GLOBALS['oPage']->aPages ) ){
				$aPages = array_keys( $GLOBALS['oPage']->aPages );
				$sql.= ' AND EXISTS ( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `products_pages` WHERE `products`.`iProduct` = `products_pages`.`iProduct` AND `products_pages`.`iPage` IN ( \'' . implode( '\', \'', $aPages ) . '\' ) LIMIT 1 )';
			} else{
				$sql.= ' AND FALSE';
			}
		}
		if( isset( $aSearch ) && is_array( $aSearch ) ){
			/********************************************/
			/*             PRODUCTS  SEARCH             */
			/********************************************/
			if( $config['search_products_description'] ){
				$sqlPrefix.= ' LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_EXT ) . '` AS `products_ext` USING( `iProduct` )';
			}
			if( isset( $aSearch['sPhrase'] ) && !empty( $aSearch['sPhrase'] ) ){
				preg_match_all( '/(-?)((\\"([^\\"]+)\\")|([^\\"\\s][^\\s]*))/', htmlspecialchars_decode( $aSearch['sPhrase'] ), $aWords, PREG_SET_ORDER );
				$aSearchP = Array( );
				foreach( $aWords as $aWord ){
					$sWord = $aWord[4];
					if( empty( $sWord ) ) $sWord = $aWord[5];
					$sWord = $oDB->escape( $sWord );
					if( $aWord[1] == '-' ){
						$aSearchP[] = '( `products`.`sName` NOT LIKE \'%' . $sWord . '%\' AND `products`.`sSearchWords` NOT LIKE \'%' . $sWord . '%\' AND `products`.`sCatalogNumber` NOT LIKE \'%' . $sWord . '%\' AND `sProductIndex` NOT LIKE \'%' . $sWord . '%\' '  . ( $config['search_products_description'] ? 'AND `products`.`sDescriptionShort` NOT LIKE \'%' . $sWord . '%\' AND `products_ext`.`sDescriptionFull` NOT LIKE \'%' . $sWord . '%\' ' : null ) . ( defined( 'CUSTOMER_PAGE' ) ? null : ' AND NOT EXISTS( SELECT 1 FROM `'.$oDB->throwTableName( DB_PRODUCTS_VARIANTS ).'` `pv` WHERE `pv`.`iProduct` = `products`.`iProduct` AND `pv`.`sOuterId` LIKE \'%' . $sWord . '%\' ) ' ) . ' AND NOT EXISTS( SELECT 1 FROM `' . $oDB->throwTableName( DB_ADVSEARCHER_PRODUCTS ) . '` `asp` INNER JOIN `' . $oDB->throwTableName( DB_ADVSEARCHER_VALUES ) . '` `asv` USING( `iValue` ) WHERE `asp`.`iProduct` = `products`.`iProduct` AND CONCAT( `asv`.`sParentValues`, \' &raquo; \', `asv`.`sValue` ) LIKE \'%' . $sWord . '%\' ) )';
					} else{
						$aSearchP[] = '( `products`.`sName` LIKE \'%' . $sWord . '%\' OR `products`.`sSearchWords` LIKE \'%' . $sWord . '%\' OR `products`.`sCatalogNumber` LIKE \'%' . $sWord . '%\' OR `sProductIndex` LIKE \'%' . $sWord . '%\' '  . ( $config['search_products_description'] ? 'OR `products`.`sDescriptionShort` LIKE \'%' . $sWord . '%\' OR `products_ext`.`sDescriptionFull` LIKE \'%' . $sWord . '%\' ' : null ) . ( defined( 'CUSTOMER_PAGE' ) ? null : ' OR EXISTS( SELECT 1 FROM `'.$oDB->throwTableName( DB_PRODUCTS_VARIANTS ).'` `pv` WHERE `pv`.`iProduct` = `products`.`iProduct` AND `pv`.`sOuterId` LIKE \'%' . $sWord . '%\' ) ' ) . '  OR EXISTS( SELECT 1 FROM `' . $oDB->throwTableName( DB_ADVSEARCHER_PRODUCTS ) . '` `asp` INNER JOIN `' . $oDB->throwTableName( DB_ADVSEARCHER_VALUES ) . '` `asv` USING( `iValue` ) WHERE `asp`.`iProduct` = `products`.`iProduct` AND CONCAT( `asv`.`sParentValues`, \' &raquo; \', `asv`.`sValue` ) LIKE \'%' . $sWord . '%\' ) )';
					}
				}
				$sql.= ' AND (' . implode( ' AND ', $aSearchP ) . ')';
			} elseif( isset( $_GET['sTag'] ) && !empty( $_GET['sTag'] ) ){
				$sql.= ' AND EXISTS( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_TAGS ) . '` WHERE `iProduct` = `products`.`iProduct` AND REPLACE( `sTag`, \' \', \'_\' ) LIKE \'' . $oDB->escape( $_GET['sTag'] ) . '\' )';
			} else{
				$sql.= ' AND 1';
			}
			
			if( isset( $_GET['iPhoto'] ) ){
				if( $_GET['iPhoto'] ){
					$sql .= ' AND EXISTS( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_FILES ) . '` `pf` WHERE `pf`.`iProduct` = `products`.`iProduct` AND `iPhoto` = 1 )';
				} else{
					$sql .= ' AND NOT EXISTS( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_FILES ) . '` `pf` WHERE `pf`.`iProduct` = `products`.`iProduct` AND `iPhoto` = 1 )';
				}
			}

			if( isset( $aSearch['iProduct'] ) && !empty( $aSearch['iProduct'] ) ){
				if( is_numeric( $aSearch['iProduct'] ) ) $sql.= ' AND `products`.`iProduct` = \'' . intval( $aSearch['iProduct'] ) . '\'';
				else{
					$aProductsSearch = preg_split( '/[^0-9]+/', $aSearch['iProduct'] );
					if( is_array( $aProductsSearch ) ) $sql.= ' AND `products`.`iProduct` IN ( \'' . implode( '\', \'', $aProductsSearch ) . '\' )';
				}
			}
			if( isset( $aSearch['iProducer'] ) && !empty( $aSearch['iProducer'] ) ){
				if( is_array( $aSearch['iProducer'] ) ){
					foreach( $aSearch['iProducer'] as &$iProducer ) $iProducer = intval( $iProducer );
					$sql.= ' AND `products`.`iProducer` IN( \'' . implode( '\', \'', $aSearch['iProducer'] ) . '\' )';
				} else{
					$sql.= ' AND `products`.`iProducer` = \'' . intval( $aSearch['iProducer'] ) . '\'';
				}
			}
			if( isset( $aSearch['sPriceFrom'] ) && !empty( $aSearch['sPriceFrom'] ) ) $sqlHaving.= ( empty( $sqlHaving ) ? ' HAVING' : ' AND' ) . ' `fPrice` >= \'' . $oDB->escape( $aSearch['sPriceFrom'] ) . '\'';
			if( isset( $aSearch['sPriceTo'] ) && !empty( $aSearch['sPriceTo'] ) ) $sqlHaving.= ( empty( $sqlHaving ) ? ' HAVING' : ' AND' ) . ' `fPrice` <= \'' . $oDB->escape( $aSearch['sPriceTo'] ) . '\'';
			if( isset( $aSearch['sCatalogNumber'] ) && !empty( $aSearch['sCatalogNumber'] ) ) $sql.= ' AND `products`.`sCatalogNumber` = \'' . $oDB->escape( $aSearch['sCatalogNumber'] ) . '\'';
			if( isset( $aSearch['sProductIndex'] ) && !empty( $aSearch['sProductIndex'] ) ) $sql.= ' AND `products`.`sProductIndex` = \'' . $oDB->escape( $aSearch['sProductIndex'] ) . '\'';
			#if( isset( $aSearch['aAttributeValue'] ) && is_array( $aSearch['aAttributeValue'] ) ) foreach( $aSearch['aAttributeValue'] as $iAttribute => $iAttributeValue ) if( !empty( $iAttributeValue ) ) $sql.= ' AND EXISTS ( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` USING ( `iProductVariant` ) WHERE `iProduct` = `products`.`iProduct` AND `iStatus` >= ' . intval( $iStatus ) . ' AND `iAttributeValue` = \'' . intval( $iAttributeValue ) . '\' LIMIT 1 )';
			if( isset( $aSearch['aAttributeValue'] ) && is_array( $aSearch['aAttributeValue'] ) ){
				foreach( $aSearch['aAttributeValue'] as $iAttribute => $iAttributeValue ){
					if( !empty( $iAttributeValue ) ){
						$sql.= ' AND ( EXISTS ( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` USING ( `iProductVariant` ) WHERE `iProduct` = `products`.`iProduct` AND `iStatus` >= ' . intval( $iStatus ) . ' AND `iAttributeValue` = \'' . intval( $iAttributeValue ) . '\' LIMIT 1 ) OR EXISTS ( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES_CONST ) . '` WHERE `iProduct` = `products`.`iProduct` AND `iAttributeValue` = \'' . intval( $iAttributeValue ) . '\' LIMIT 1 ) ) ';
						$sUrlExt.= '&amp;aAttributeValue['.$iAttribute.']='.$iAttributeValue;
					}
				}
			}
			if( isset( $aSearch['aPages'] ) ) $sql.= ' AND EXISTS ( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `products_pages` WHERE `products`.`iProduct` = `products_pages`.`iProduct` AND `products_pages`.`iPage` IN ( \'' . implode( '\', \'', $aSearch['aPages'] ) . '\' ) LIMIT 1 )';
		} elseif( is_numeric( $iContent ) && !isset( $aProducts ) ){
			/********************************************/
			/*               PAGE CONTENT               */
			/********************************************/
			if( isset( $GLOBALS['aAdvsearcherColumns'] ) && isset( $_SESSION['iAdvsearcherValue_' . array_pop( array_keys( $GLOBALS['aAdvsearcherColumns'] ) )] ) ){
				$sql.= ' AND EXISTS (
									SELECT 1
										FROM `' . $oDB->throwTableName( DB_ADVSEARCHER_PRODUCTS ) . '` AS `ap`
										WHERE `products`.`iProduct` = `ap`.`iProduct`
											AND `ap`.`iValue` = ' . intval( $_SESSION['iAdvsearcherValue_' . array_pop( array_keys( $GLOBALS['aAdvsearcherColumns'] ) )] ) . '
										LIMIT 1
									)';
			}
			if( isset( $_GET['aFilterAttributes'] ) ){
				foreach( $_GET['aFilterAttributes'] as $iAttribute => $aValues ){
					$aValuesKeys = array_keys( $aValues );
					$aValues = Array();
					$aValuesGroups = Array();
					foreach( $aValuesKeys as $sValue ){
						$sUrlExt.= '&amp;aFilterAttributes['.$iAttribute.']['.$sValue.']=1';
						if( preg_match( '/^g[0-9]+$/', $sValue ) ){
							$aValuesGroups[] = str_replace( 'g', '', $sValue );
						} elseif( preg_match( '/^[0-9]+$/', $sValue ) ){
							$aValues[] = $sValue;
						}
					}
					if( $iAttribute > 0 ){
						$sql.= ' AND EXISTS (
											SELECT 1
												FROM (
													SELECT
															`iProduct`, `iAttributeValue`
														FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '`
															INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` USING( `iProductVariant` )
															WHERE `iStatus` >= ' . $iStatus . '
													UNION ALL
													SELECT `iProduct`, `iAttributeValue`
														FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES_CONST ) . '`
													) AS `pas`
													INNER JOIN `' . $oDB->throwTableName( DB_ATTRIBUTES_VALUES ) . '` AS `avs` USING( `iAttributeValue` )
												WHERE `products`.`iProduct` = `pas`.`iProduct`
													AND `avs`.`iAttribute` = ' . intval( $iAttribute ) . '
													AND ( 
															' . ( empty( $aValues ) ? '0' : ( '`avs`.`iAttributeValue` IN ( ' . implode( ', ', $aValues ) . ' )' ) ) . '
														OR
															' . ( empty( $aValuesGroups ) ? '0' : ( 'EXISTS( SELECT 1 FROM `' . $oDB->throwTableName( DB_ATTRIBUTES_FILTER_GROUPS ) . '` `afg` WHERE `avs`.`iAttributeValue` = `afg`.`iAttributeValue` AND `afg`.`iFilterGroup` IN ( ' . implode( ', ', $aValuesGroups ) . ' ) )' ) ) . '
														)
												LIMIT 1
											)';
					} elseif( $iAttribute == 0 ){
						$sql.= ' AND EXISTS (
											SELECT 1
												FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `ps`
												WHERE `products`.`iProduct` = `ps`.`iProduct`
													AND `ps`.`iProducer` IN ( ' . implode( ', ', $aValues ) . ' )
													AND `ps`.`iStatus` >= ' . $iStatus . '
												LIMIT 1
											)';
					} elseif( $iAttribute == -1 ){
						$sql .= ' AND EXISTS (
													SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PROMOTED ) . '` `ppr` WHERE `ppr`.`iPromoted` IN( ' . implode( ', ', $aValues ) . ' ) AND `ppr`.`iProduct` = `products`.`iProduct` AND ( `ppr`.`iTimeValid` IS NULL OR `ppr`.`iTimeValid` >= UNIX_TIMESTAMP() )
												)';
					}
				}
			}
			if( isset( $_GET['fFilterPriceFrom'] ) && !empty( $_GET['fFilterPriceFrom'] ) && is_numeric( $_GET['fFilterPriceFrom'] ) ){
				$sqlHaving.= ( empty( $sqlHaving ) ? ' HAVING' : ' AND' ) . ' `fPrice` >= ' . floatval( $_GET['fFilterPriceFrom'] );
			}
			if( isset( $_GET['fFilterPriceTo'] ) && !empty( $_GET['fFilterPriceTo'] ) && is_numeric( $_GET['fFilterPriceTo'] ) ){
				$sqlHaving.= ( empty( $sqlHaving ) ? ' HAVING' : ' AND' ) . ' `fPrice` <= ' . floatval( $_GET['fFilterPriceTo'] );
			}
		}
		$sql.= ' AND ' . $oDB->throwRoutineName( '%%_getProductVisibleForUser' ) . ' ( ' . $sUserId . ', `products`.`iProduct` ) >= ' . $iStatus . ' AND `products`.`iStatus` >= ' . $iStatus;
		if( $GLOBALS['config']['hide_unavailable_products'] ){
			$sql.= ' AND `TiCanBuy` >= ' . $iStatus;
		}
		if( strpos( $sUrlExt, '?' ) !== 0 ) $sUrlExt = preg_replace( '/\\&(amp\\;)?/', '?', $sUrlExt, 1 );
		return $sqlPrefix . $sql . $sqlHaving;
	}
	function throwProductsBlock( $iContent, $iBlock, $iList, $sSortOrder, $aProducts = null, $iMin = null, $iMax = null, $bShowOnlyAvailable = false ){
		$iPageNumber = ( isset( $GLOBALS['aActions']['o3'] ) && $GLOBALS['aActions']['o2'] == $iBlock ) ? $GLOBALS['aActions']['o3'] : 1;
		return $this->throwProducts( $iContent, $iList, $aProducts, $sSortOrder, $iPageNumber, $iMin, $iMax, $bShowOnlyAvailable );
	}
	function throwProducts( $iContent, $iList = null, $aProducts = null, $sSortOrder = null, $iPage = null, $iMin = null, $iMax = null, $bShowOnlyAvailable = false ){
		global $sUrlExt;
		$oFile = & Files::getInstance( );
		$oDB = & DataBase::getInstance( );
		// Main SQL
		if( isset( $aProducts ) && is_array( $aProducts ) ){
			// Predefined products list (compare etc.)
			$sql = $this->generateListSQL( null, $aProducts, null, null, null, null, $bShowOnlyAvailable );
		} elseif( isset( $GLOBALS['sPhrase'] ) || ( !empty( $GLOBALS['config']['search_page'] ) && ( $GLOBALS['config']['search_page'] == $iContent ) ) ){
			// Search products
			$aSearch = Array( 'sPhrase' => $GLOBALS['sPhrase'] );
			$sUrlExt = '?sPhrase=' . $GLOBALS['sPhrase'];
			if( isset( $GLOBALS['iFullSearch'] ) && ( $GLOBALS['iFullSearch'] ) ) $sUrlExt.= '&amp;iFullSearch=1';
			if( isset( $GLOBALS['iProduct'] ) && !empty( $GLOBALS['iProduct'] ) ){
				$sUrlExt.= '&amp;iProduct=' . intval( $GLOBALS['iProduct'] );
				$aSearch['iProduct'] = $GLOBALS['iProduct'];
			}
			if( isset( $GLOBALS['iProducer'] ) && !empty( $GLOBALS['iProducer'] ) ){
				$sUrlExt.= '&amp;iProducer=' . intval( $GLOBALS['iProducer'] );
				$aSearch['iProducer'] = $GLOBALS['iProducer'];
			}
			if( isset( $GLOBALS['sPriceFrom'] ) ){
				$sUrlExt.= '&amp;sPriceFrom=' . $GLOBALS['sPriceFrom'];
				$aSearch['sPriceFrom'] = $GLOBALS['sPriceFrom'];
			}
			if( isset( $GLOBALS['sPriceTo'] ) ){
				$sUrlExt.= '&amp;sPriceTo=' . $GLOBALS['sPriceTo'];
				$aSearch['sPriceTo'] = $GLOBALS['sPriceTo'];
			}
			if( isset( $GLOBALS['sCatalogNumber'] ) && !empty( $GLOBALS['sCatalogNumber'] ) ){
				$sUrlExt.= '&amp;sCatalogNumber=' . $GLOBALS['sCatalogNumber'];
				$aSearch['sCatalogNumber'] = $GLOBALS['sCatalogNumber'];
			}
			if( isset( $GLOBALS['sProductIndex'] ) && !empty( $GLOBALS['sProductIndex'] ) ){
				$sUrlExt.= '&amp;sProductIndex=' . $GLOBALS['sProductIndex'];
				$aSearch['sProductIndex'] = $GLOBALS['sProductIndex'];
			}
			if( isset( $GLOBALS['aAttributeValue'] ) && is_array( $GLOBALS['aAttributeValue'] ) ){
				foreach( $GLOBALS['aAttributeValue'] as $iAttribute => $iAttributeValue ) if( !empty( $iAttributeValue ) ){
					$sUrlExt.= '&amp;aAttributeValue[' . intval( $iAttribute ) . ']=' . intval( $iAttributeValue );
				}
				$aSearch['aAttributeValue'] = $GLOBALS['aAttributeValue'];
			}
			if( isset( $GLOBALS['iCategory'] ) && !empty( $GLOBALS['iCategory'] ) ){
				$sUrlExt.= '&amp;iCategory=' . $GLOBALS['iCategory'];
				$aSearch['aPages'] = $GLOBALS['oPage']->throwAllChildren( intval( $GLOBALS['iCategory'] ) );
				$aSearch['aPages'][] = intval( $GLOBALS['iCategory'] );
			} else{
				$aSearch['aPages'] = array_keys( $GLOBALS['oPage']->aPages );
			}
			$sql = $this->generateListSQL( null, null, $aSearch, null, $iMin, $iMax, $bShowOnlyAvailable );
		} else{
			// Page contents
			$sql = $this->generateListSQL( $iContent, null, null, null, $iMin, $iMax, $bShowOnlyAvailable );
		}
		/* Uzupełnienie SQL - ORDER i LIMIT */{
			//Products order
			if( $sSortOrder ) $sql.= ' ORDER BY ' . $sSortOrder;
			//Products limits / paging
			if( !isset( $iList ) || empty( $iList ) ){
				$iList = $GLOBALS['config']['products_list'];
			}
			$iPageNumber = isset( $iPage ) ? $iPage : 1;
			if( !isset( $iPageNumber ) || !is_numeric( $iPageNumber ) || $iPageNumber < 1 ) $iPageNumber = 1;
			if( isset( $_POST['sort'] ) || isset( $_POST['productsPerPage'] ) ){
				$iPageNumber = 1;
			}
			$iEnd = $iPageNumber * $iList;
			$iStart = $iEnd - $iList;
			$sql.= ' LIMIT ' . $iStart . ',' . $iList;
			/* -- Koniec uzupełnienia SQL */
		}
		//	WYWOŁANIE SQL
		$aProductsData = $oDB->executeSQL( $sql, true );
		$sql = 'SELECT FOUND_ROWS() AS `count`;';
		$iCount = $oDB->executeSQL( $sql, true, DataBase::SINGLE_VALUE );
		if( isset( $GLOBALS['config']['storage_page'] ) && isset( $GLOBALS['oPage']->aPages[$GLOBALS['config']['storage_page']] ) ) $aStorageProducts = $this->throwProductsStorage( );
		$aCompareProducts = $this->throwProductsCompare( );
		if( $iCount > 0 ){
			if( $iEnd > $iCount ) $iEnd = $iCount;
			$aProductsInfo['aPagesCountData'] = Array( 'iCount' => ceil( $iCount / $iList ), 'iPageNumber' => $iPageNumber );
			$this->mData = null;
			if( isset( $GLOBALS['config']['basket_page'] ) ){
				$sBasketLink = $GLOBALS['oPage']->getLinkName( $GLOBALS['config']['basket_page'] );
				$aProductsInfo['sBasketPage'] = !empty( $sBasketLink ) ? ( $sBasketLink . '?' ) : null;
			} else{
				$aProductsInfo['sBasketPage'] = null;
			}
			foreach( $aProductsData as &$aData ){
//				$aData['aTags'] = $oDB->executeSQL( 'SELECT `sTag` FROM `' . $oDB->throwTableName( DB_PRODUCTS_TAGS ) . '` WHERE `iProduct` = ' . intval( $aData['iProduct'] ), true, DataBase::SINGLE_COLUMN );
//				$aData['aPromoted'] = $oDB->executeSQL( 'SELECT `iPromoted`, 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PROMOTED ) . '` WHERE `iProduct` = ' . intval( $aData['iProduct'] ), true, DataBase::ASSOCIATIVE );
				if( !empty( $aData['iPromotedMin'] ) ){
					$aData['aPromoted'] = Array( $aData['iPromotedMin'] => 1 );
				} else{
					$aData['aPromoted'] = Array();
				}
				if( !empty( $aData['iPriceTimeFrom'] ) && ( $aData['iPriceTimeFrom'] <= time( ) ) && !empty( $aData['iPriceTimeTo'] ) && ( $aData['iPriceTimeTo'] > time( ) ) && ( $aData['fPriceTimeValue'] > 0 ) && ( $aData['fPriceTimeValue'] < $aData['fPrice'] ) ){
					if( defined( 'CUSTOMER_PAGE' ) ) $aData['fPrice'] = normalizePrice( $aData['fPriceTimeValue'] );
					$aData['iPriceTimeRemain'] = $aData['iPriceTimeTo'] - time( );
					$aData['sPriceTimeRemain'] = '<b>' . intval( $aData['iPriceTimeRemain'] / ( 3600 * 24 ) ) . '</b> <b>' . intval( ( $aData['iPriceTimeRemain'] % ( 3600 * 24 ) ) / 3600 ) . '</b> <b>' . intval( ( $aData['iPriceTimeRemain'] % 3600 ) / 60 ) . '</b> <b>' . intval( $aData['iPriceTimeRemain'] % 60 ) . '</b>';
					$aData['sDiscountPercent'] = round( ( ( 1 - ( $aData['fPriceTimeValue'] / $aData['fCatalogPrice'] ) ) * 100 ) );
					$aData['sDiscountValue'] = normalizePrice( $aData['fCatalogPrice'] - $aData['fPriceTimeValue'] );
					$aData['iPriceTimeCount'] = intval( $aData['iPriceTimeCount'] );
				} else{
					$aData['fPrice'] = normalizePrice( $aData['fPrice'] );
				}
				$aData['sLinkName'] = throwPageUrl( $aData['iProduct'] . ',' . change2Url( $aData['sName'] ) );
				$aData['sName'] = htmlspecialchars( $aData['sName'] );
				if( !empty( $aData['iPriceTimeFrom'] ) && ( $aData['iPriceTimeFrom'] <= time( ) ) && !empty( $aData['iPriceTimeTo'] ) && ( $aData['iPriceTimeTo'] > time( ) ) && ( $aData['fPriceTimeValue'] > 0 ) && ( $aData['fPriceTimeValue'] < $aData['fPrice'] ) ){
					$aData['fPrice'] = normalizePrice( $aData['fPriceTimeValue'] );
				} else{
					$aData['fPrice'] = normalizePrice( $aData['fPrice'] );
				}
				$aData['fCatalogPrice'] = normalizePrice( $aData['fCatalogPrice'] );
				if( $aData['fPriceOld'] ) $aData['fPriceOld'] = normalizePrice( $aData['fPriceOld'] );
				else $aData['fPriceOld'] = '';
				$aData['sPrice'] = displayPrice( $aData['fPrice'], true );
				if( !empty( $aData['fPriceOld'] ) && ( $aData['fPriceOld'] > $aData['fCatalogPrice'] ) ){
					$aData['sPriceOld'] = displayPrice( $aData['fPriceOld'] );
				} else{
					$aData['sPriceOld'] = null;
				}
//				$aData['aProducer'] = throwProducer( $aData['iProducer'] );
				$aData['aProducer'] = Array( 'iProducer' => $aData['iProducer'], 'sName' => $aData['prodsName'], 'sLogoUrl' => $aData['prodsLogoUrl'] );
				if( !empty( $aData['sDescriptionShort'] ) ){
					$aData['sDescriptionShort'] = changeTxt( $aData['sDescriptionShort'], 'nlNds' );
				}
				if( isset( $oFile->aImagesDefault[2][$aData['iProduct']] ) ){
					$aData['aDataImage'] = $oFile->aImagesDefault[2][$aData['iProduct']];
				}
				if( isset( $sBasketPage ) && ( $aData['iNotForSale'] == 0 ) && ( $aData['iCanBuy'] > 0 ) ){
					if( $aData['iVariantCount'] == 1 ) $aData['sBasketPage'] = $sBasketPage;
					else $aData['sBasketPage'] = $aData['sLinkName'] . '&amp;';
				}
				if( isset( $GLOBALS['config']['storage_page'] ) && isset( $GLOBALS['oPage']->aPages[$GLOBALS['config']['storage_page']] ) ){
					if( is_array( $aStorageProducts ) && in_array( $aData['iProduct'], $aStorageProducts ) ){
						$aData['bInStorage'] = true;
					} else{
						$aData['bInStorage'] = false;
					}
				}
				if( is_array( $aCompareProducts ) && in_array( $aData['iProduct'], $aCompareProducts ) ){
					$aData['bInCompare'] = true;
				} else{
					$aData['bInCompare'] = false;
				}
				if( isset( $aData['sDiscountPercent'] ) ){
					$aData['sCatalogPrice'] = displayPrice( $aData['fCatalogPrice'] );
					$aData['sPrice'] = displayPrice( $aData['fPrice'] );
				} elseif( $aData['fPrice'] < $aData['fCatalogPrice'] ){
					$aData['sCatalogPrice'] = displayPrice( $aData['fCatalogPrice'] );
				}
			}
		}
		$aProductsInfo['aList'] = $aProductsData;
		$aProductsInfo['sLinkNamePages'] = throwPageUrl( $GLOBALS['oPage']->getLinkName( $iContent ), true );
		$aProductsInfo['sLinkName'] = $GLOBALS['oPage']->getLinkName( $iContent ) . '?';
		$aProductsInfo['iList'] = $iList;
		return $aProductsInfo;
	}
	function throwProduct( $iProduct, $iStatus = true ){
		$oDB = & DataBase::getInstance( );
		if( $iStatus ){
			$iStatus = throwStatus( );
			if( is_numeric( $_SESSION['iUserClient'] ) && ( $_SESSION['iUserClient'] > 0 ) && defined( 'CUSTOMER_PAGE' ) ){
				$sUserId = intval( $_SESSION['iUserClient'] );
			} else{
				$sUserId = 'NULL';
			}
		} else{
			$iStatus = '0';
			$sUserId = 'NULL';
		}
		$sql = 'SELECT `products`.*, `products_ext`.*, `vat`.`fVatValue`, `products`.`fPrice` AS `fCatalogPrice`, ' . $oDB->throwRoutineName( '%%_calculateProductPriceForUser' ) . '( ' . $sUserId . ', `iProduct`, ' . JOIN_PROMOTIONS . ' ) AS `fPrice`, LEAST( ' . ( STOCK_GLOBAL_UNLIMITED ? '1' : '`products`.`TiCanBuy`' ) . ', ' . $oDB->throwRoutineName( '%%_getProductCanBuyForUser' ) . ' ( ' . $sUserId . ', `iProduct` ) ) AS `iCanBuy`, ' . $oDB->throwRoutineName( '%%_getProductVisiblePriceForUser' ) . ' ( ' . $sUserId . ', `iProduct` ) AS `iVisiblePrice` 
							FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `products`
								LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_EXT ) . '` AS `products_ext` USING ( `iProduct` )
								LEFT JOIN `' . $oDB->throwTableName( DB_VAT ) . '` AS `vat` USING( `iVat` )
							WHERE `products`.`iProduct` = \'' . intval( $iProduct ) . '\'
							AND ' . $oDB->throwRoutineName( '%%_getProductVisibleForUser' ) . ' ( ' . $sUserId . ', `iProduct` ) >= ' . $iStatus . ' AND `products`.`iStatus` >= ' . $iStatus;
		if( $GLOBALS['config']['hide_unavailable_products'] ){
			$sql.= ' AND `TiCanBuy` >= ' . $iStatus;
		}
		$aData = $oDB->executeSQL( $sql, true, DataBase::SINGLE_RECORD );
		if( !empty( $aData ) ){
			$aData['aTags'] = $oDB->executeSQL( 'SELECT `sTag` FROM `' . $oDB->throwTableName( DB_PRODUCTS_TAGS ) . '` WHERE `iProduct` = ' . intval( $aData['iProduct'] ), true, DataBase::SINGLE_COLUMN );
			$aData['aPromoted'] = $oDB->executeSQL( 'SELECT `iPromoted`, 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PROMOTED ) . '` WHERE `iProduct` = ' . intval( $aData['iProduct'] ) . ' AND ( `iTimeValid` IS NULL OR `iTimeValid` >= UNIX_TIMESTAMP() )', true, DataBase::ASSOCIATIVE );
			$aData['iStock'] = $aData['TiStock'];
			$sql = 'SELECT `pa`.`sText`
								FROM `' . $oDB->throwTableName( DB_PRODUCTS_AVAILABLE ) . '` AS `pa`
								WHERE `iAvailable` = ' . $aData['TiAvailable'];
			$aData['sAvailable'] = $oDB->executeSQL( $sql, true, DataBase::SINGLE_VALUE );
			$sql = 'SELECT `iTab`, `sContent` FROM `' . $oDB->throwTableName( DB_PRODUCTS_TABS ) . '` WHERE `iProduct` = \'' . intval( $iProduct ) . '\' ORDER BY `iTab`';
			$aData['aTabs'] = $oDB->executeSQL( $sql, true, DataBase::ASSOCIATIVE );
			foreach( $aData['aTabs'] as &$sTab ){
				$sTab = str_replace( '|n|', '', $sTab );
			}
			$aData['aCategories'] = Array( );
			$sql = 'SELECT `iPage`, `iMainCategory` FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` WHERE `iProduct` = \'' . intval( $iProduct ) . '\'';
			$aCategories = $oDB->executeSQL( $sql, true );
			foreach( $aCategories as $aCategory ){
				$aData['aCategories'][$aCategory['iPage']] = $aCategory['iPage'];
				if( $aCategory['iMainCategory'] ) $aData['iMainCategory'] = $aCategory['iPage'];
			}
			if( !isset( $aData['iMainCategory'] ) ){
				$aData['iMainCategory'] = $aCategory['iPage'];
			}
			$aData['aMainCategory'] = $GLOBALS['oPage']->throwPage( $aData['iMainCategory'] );
			if( $aData['bTimePromotionActive'] = ( !empty( $aData['iPriceTimeFrom'] ) && ( $aData['iPriceTimeFrom'] <= time( ) ) && !empty( $aData['iPriceTimeTo'] ) && ( $aData['iPriceTimeTo'] > time( ) ) ) && ( $aData['fPriceTimeValue'] > 0 ) && ( $aData['fPriceTimeValue'] < $aData['fPrice'] ) ){
				if( defined( 'CUSTOMER_PAGE' ) ) $aData['fPrice'] = normalizePrice( $aData['fPriceTimeValue'] );
				$aData['iPriceTimeRemain'] = $aData['iPriceTimeTo'] - time( );
				$aData['sPriceTimeRemain'] = '<b>' . intval( $aData['iPriceTimeRemain'] / ( 3600 * 24 ) ) . '</b> <b>' . intval( ( $aData['iPriceTimeRemain'] % ( 3600 * 24 ) ) / 3600 ) . '</b> <b>' . intval( ( $aData['iPriceTimeRemain'] % 3600 ) / 60 ) . '</b> <b>' . intval( $aData['iPriceTimeRemain'] % 60 ) . '</b>';
				$aData['sDiscountPercent'] = round( ( ( 1 - ( $aData['fPriceTimeValue'] / $aData['fCatalogPrice'] ) ) * 100 ) );
				$aData['sDiscountValue'] = normalizePrice( $aData['fCatalogPrice'] - $aData['fPriceTimeValue'] );
				$aData['iPriceTimeCount'] = intval( $aData['iPriceTimeCount'] );
			} else{
				$aData['fPrice'] = normalizePrice( $aData['fPrice'] );
			}
			if( $aData['fPriceOld'] ) $aData['fPriceOld'] = normalizePrice( $aData['fPriceOld'] );
			else $aData['fPriceOld'] = '';
			$aData['sDate'] = is_numeric( $aData['iTime'] ) ? date( 'Y-m-d H:i:s', $aData['iTime'] ) : null;
			$aData['sLinkName'] = throwPageUrl( $aData['iProduct'] . ',' . change2Url( $aData['sName'] ) );
			$aData['sName'] = htmlspecialchars( $aData['sName'] );
			$aData['sPrice'] = displayPrice( $aData['fPrice'] );
			$aData['sPriceOld'] = !empty( $aData['fPriceOld'] ) ? displayPrice( $aData['fPriceOld'] ) : null;
			$sql = 'SELECT `iCarrier`, `fPrice` FROM `' . $oDB->throwTableName( DB_PRODUCTS_CARRIERS_PRICES ) . '` WHERE `iProduct` = ' . intval( $iProduct );
			$aData['aCarriersPrices'] = $oDB->executeSQL( $sql, true, DataBase::ASSOCIATIVE );
			$aData['aAttributes'] = $this->throwAttributesByGroups( $iProduct );
			if( $GLOBALS['config']['product_variants_list'] || $GLOBALS['config']['product_variants_table'] ){
				$sql = 'SELECT
										`pv`.`iProductVariant`,
										`pv`.`fPrice` AS `fCatalogPrice`,
										' . $oDB->throwRoutineName( '%%_calculateProductVariantPriceForUser' ) . '( ' . $sUserId . ', `pv`.`iProductVariant`, ' . JOIN_PROMOTIONS . ' ) AS `fPrice`,
										`pv`.`iVat`,
										`pv`.`TiStock` AS `iStock`,
										`pv`.`sShortDescription`,
										IFNULL( `pv`.`fPriceOld`, `p`.`fPriceOld` ) `fPriceOld`,
										IFNULL( `pv`.`fPriceTimeValue`, `p`.`fPriceTimeValue` ) `fPriceTimeValue`,
										`a`.`sText` AS `sAvailable`,
										`a`.`iCanBuy`,
										`pav`.`sAttribs`,
										`pf`.`iFile`,
										`pf`.`sFileName`
									FROM ( SELECT * FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` WHERE `iProduct` = ' . intval( $iProduct ) . ' AND `iStatus` >= ' . intval( $iStatus ) . ' ) `pv`
										INNER JOIN ( SELECT * FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` WHERE `iProduct` = ' . intval( $iProduct ) . ' ) `p` USING( `iProduct` )
										INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_AVAILABLE ) . '` `a` ON( `a`.`iAvailable` = `pv`.`TiAvailable` )
										LEFT JOIN (
											SELECT
													GROUP_CONCAT( CONCAT( `av`.`iAttribute`, \':\', `av`.`iAttributeValue` ) SEPARATOR \';\' ) AS `sAttribs`,
													`pa`.`iProductVariant`
												FROM (
														SELECT `iProductVariant`, `iAttributeValue` FROM ( SELECT `iProductVariant` FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` WHERE `iProduct` = ' . intval( $iProduct ) . ' AND `iStatus` >= ' . intval( $iStatus ) . ' ) `pvi1` INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` USING( `iProductVariant` )
													UNION ALL
														SELECT `iProductVariant`, `iAttributeValue` FROM ( SELECT `iProduct`, `iProductVariant` FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` WHERE `iProduct` = ' . intval( $iProduct ) . ' AND `iStatus` >= ' . intval( $iStatus ) . ' ) `pvi2` INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES_CONST ) . '` USING( `iProduct` )
													) AS `pa`
													INNER JOIN `' . $oDB->throwTableName( DB_ATTRIBUTES_VALUES ) . '` `av` USING( `iAttributeValue` )
												GROUP BY `pa`.`iProductVariant`
										) `pav` USING( `iProductVariant` )
										LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_FILES ) . '` `pf` USING( `iFile` )';
				$aData['aVariants'] = $oDB->executeSQL( $sql, true );
				foreach( $aData['aVariants'] as &$aVariant ){
					$aAttribs = explode( ';', $aVariant['sAttribs'] );
					if( is_array( $aAttribs ) ) foreach( $aAttribs as &$mAttrib ){
						$mAttrib = explode( ':', $mAttrib );
						$aVariant['aAttributes'][$mAttrib[0]] = $mAttrib[1];
					}
					if($aData['bTimePromotionActive']){
						if( isset( $aVariant['fPriceTimeValue'] ) && !empty( $aVariant['fPriceTimeValue'] ) ){
							if( !(isset( $aVariant['fPriceOld'] ) ) ){
								$aVariant['fPriceOld'] = $aVariant['fPrice'];
							}
							$aVariant['fPrice'] = $aVariant['fPriceTimeValue'];
						}
						if( isset( $aData['fPriceTimeValue']) && $aData['fCatalogPrice'] == $aVariant['fPrice'] && !( isset($aVariant['fPriceTimeValue'] ) ) ){
							if( !(isset( $aVariant['fPriceOld'] ) ) ){
								$aVariant['fPriceOld'] = $aData['fCatalogPrice'];
							}
							$aVariant['fPrice'] = $aData['fPriceTimeValue'];
						}
					}
					$aVariant['sPrice'] = displayPrice( normalizePrice( $aVariant['fPrice'] ) );
					$aVariant['sPriceOld'] = displayPrice( normalizePrice( $aVariant['fPriceOld'] ) );
					$aVariant['sPriceOld'] = ( !empty( $aVariant['sPriceOld'] ) && ( $aVariant['fPriceOld'] > $aVariant['fCatalogPrice'] ) ) ? $aVariant['sPriceOld'] : null;
					$aVariant['sCatalogPrice'] = ( $aVariant['fPrice'] < $aVariant['fCatalogPrice'] ) ? displayPrice( normalizePrice( $aVariant['fCatalogPrice'] ) ) : null;
				}
			} else{
				$aData['aVariants'] = Array( true ); //Możliwość pokazania listy atrybutów
			}
			return $aData;
		} else{
			return null;
		}
	}
	function throwSubMenu( $sFile, $iPageParent, $iDepth = 1 ){
		$oDB = DataBase::getInstance( );
		$sql = 'SELECT `products`.*
							FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `products`
						WHERE EXISTS (
							SELECT 1
								FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `products_pages`
								WHERE `products`.`iProduct` = `products_pages`.`iProduct`
									AND `products_pages`.`iPage` = \'' . $iPageParent . '\'
								LIMIT 1
							)
						ORDER BY `iPosition` ASC, `sName` ASC, `iProduct` DESC';
		$aProducts = $oDB->executeSQL( $sql, true );
		$aMenu = Array( );
		if( isset( $aProducts ) ){
			foreach( $aProducts as $aData ){
				$bShowSub = false;
				$aData['sub'] = null;
				$aData['bSelected'] = false;
				$aData['bProductItem'] = true;
				$aData['sLinkName'] = throwPageUrl( $aData['iProduct'] . ',' . change2Url( $aData['sName'] ) );
				$aMenu[$aData['iProduct']] = $aData;
			}
		}
		return $aMenu;
	}
	function getPagesTree( $iProduct, $sProductName = null ){
		global $oPage;
		$oDB = DataBase::getInstance( );
		$oTpl = TplParser::getInstance( );
		$oPage->mData = null;
		$aResult = Array( );
		$sql = 'SELECT `products_pages`.`iPage`
							FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `products_pages`
								LEFT JOIN `' . $oDB->throwTableName( DB_PAGES ) . '` AS `pages` ON ( `pages`.`iPage` = `products_pages`.`iPage` )
							WHERE `products_pages`.`iProduct` = \'' . intval( $iProduct ) . '\'
							ORDER BY `iMainCategory` DESC, `iPage` LIMIT 1';
		if( $iPage = $oDB->executeSQL( $sql, true, DataBase::SINGLE_VALUE ) ){
			$aResult = $oPage->getPagesTree( $iPage );
			$aResult['aPath'][] = $aResult['aCurrent'];
			if( !isset( $sProductName ) ){
				$aProduct = $this->throwProduct( $iProduct );
			} else{
				$aProduct['sName'] = $sProductName;
			}
			$aResult['aCurrent'] = Array( 'sName' => $aProduct['sName'], 'sLinkName' => throwPageUrl( $iProduct . ',' . change2Url( $aProduct['sName'] ) ) );
		}
		return $aResult;
	}
	function throwAttributesByGroups( $iProduct ){
		$iStatus = throwStatus( );
		$oDB = & DataBase::getInstance( );
		$sql = 'SELECT DISTINCTROW `attributes`.`iAttribute`, `attributes`.`sName`, `attributes`.`iPosition`, `attributes_values`.`sValue`, `attributes_values`.`iAttributeValue`
							FROM ( SELECT `iAttributeValue` FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` AS `products_variants`
											INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` USING( `iProductVariant` )
										WHERE `products_variants`.`iProduct` = ' . intval( $iProduct ) . '
								AND `products_variants`.`iStatus` >= ' . $iStatus . '
										UNION ALL
											SELECT `iAttributeValue` FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES_CONST ) . '` WHERE `iProduct` = ' . intval( $iProduct ) . '
								) AS `products_attributes`
								INNER JOIN `' . $oDB->throwTableName( DB_ATTRIBUTES_VALUES ) . '` AS `attributes_values` USING( `iAttributeValue` )
								INNER JOIN `' . $oDB->throwTableName( DB_ATTRIBUTES ) . '` AS `attributes` USING( `iAttribute` )
							ORDER BY `attributes`.`iPosition`, `attributes`.`sName`, `attributes`.`iAttribute`, `attributes_values`.`iPosition`, `attributes_values`.`sValue`, `attributes_values`.`iAttributeValue`';
		$r = $oDB->executeSQL( $sql, true );
		$aAttributes = Array( );
		foreach( $r as $row ){
			if( !isset( $aAttributes[$row['iAttribute']] ) ){
				$aAttributes[$row['iAttribute']] = Array( 'sName' => $row['sName'], 'iPosition' => $row['iPosition'], 'aValues' => Array( ) );
			}
			$row['sValue'] = preg_replace( '/(#[0-9a-f]{6})/', '<div style="background-color: $1;" class="attrib-color-box"></div>', $row['sValue'] );
			$aAttributes[$row['iAttribute']]['aValues'][$row['iAttributeValue']] = $row['sValue'];
		}
		return $aAttributes;
	}
	function throwProductAttributesInfo( $iProduct, $iAttribute, $aAttributesValues ){
		if( is_numeric( $_SESSION['iUserClient'] ) && ( $_SESSION['iUserClient'] > 0 ) && defined( 'CUSTOMER_PAGE' ) ){
			$sUserId = intval( $_SESSION['iUserClient'] );
		} else{
			$sUserId = 'NULL';
		}
		$oDB = DataBase::getInstance( );
		$iStatus = throwStatus( );
		$sql = 'SELECT `iAttributeValue`, `sValue`, COUNT( `iProductVariant` ) AS `iOptions`, MIN( `iProductVariant` ) AS `iProductVariant`
							FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` AS `pv`
								INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` AS `pa` USING( `iProductVariant` )
								INNER JOIN `' . $oDB->throwTableName( DB_ATTRIBUTES_VALUES ) . '` AS `av` USING( `iAttributeValue` )
							WHERE `pv`.`iProduct` = ' . intval( $iProduct ) . '
								AND `av`.`iAttribute` = ' . intval( $iAttribute ) . '
								AND `pv`.`iStatus` >= ' . $iStatus;
		if( is_array( $aAttributesValues ) ) foreach( $aAttributesValues as $iA => $iV ){
			if( is_numeric( $iA ) && is_numeric( $iV ) ){
				$sql.= ' AND EXISTS ( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` AS `pas` WHERE `pas`.`iProductVariant` = `pv`.`iProductVariant` AND `pas`.`iAttributeValue` = ' . intval( $iV ) . ' )';
			}
		}
		$sql.= '	GROUP BY `iAttributeValue`, `sValue` ORDER BY `av`.`iPosition`, `av`.`sValue`';
		$aAttributes = $oDB->executeSQL( $sql, true );
		$aReturn = Array( );
		foreach( $aAttributes as $aAttribute ){
			$aAttribute['sValue'] = preg_replace( '/(#[0-9a-f]{6})/', '<div style="background-color: $1;" class="attrib-color-box"></div>', $aAttribute['sValue'] );
			if( $aAttribute['iOptions'] == 1 ){
				$sql = 'SELECT `pv`.`TiStock` AS `iStock`, ' . $oDB->throwRoutineName( '%%_calculateProductVariantPriceForUser' ) . '( ' . $sUserId . ', `iProductVariant`, ' . JOIN_PROMOTIONS . ' ) AS `fPrice`, IFNULL( `pv`.`fPrice`, `p`.`fPrice` ) AS `fCatalogPrice`, `pa`.`sText` AS `sAvailability`, IFNULL( `pv`.`fPriceOld`, `p`.`fPriceOld` ) AS `fPriceOld`, IFNULL( `pv`.`fPriceTimeValue`, `p`.`fPriceTimeValue` ) AS `fPriceTimeValue`, `p`.`iPriceTimeFrom`, `p`.`iPriceTimeTo`, `p`.`iPriceTimeMaxCount`, `p`.`iPriceTimeCount`, `f`.`sFileName` AS `sImage`, `v`.`fVatValue` AS `fVat`
									FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` AS `pv`
										LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_AVAILABLE ) . '` AS `pa` ON( `pa`.`iAvailable` = `pv`.`TiAvailable` )
										LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `p` ON( `p`.`iProduct` = `pv`.`iProduct` )
										LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_FILES ) . '` AS `f` ON( `pv`.`iFile` = `f`.`iFile` )
										LEFT JOIN `' . $oDB->throwTableName( DB_VAT ) . '` AS `v` ON( `v`.`iVat` = IFNULL( `pv`.`iVat`, `p`.`iVat` ) )
									WHERE `iProductVariant` = ' . $aAttribute['iProductVariant'] . '
										AND `pa`.`iCanBuy` > 0
										AND `pv`.`iStatus` >= ' . intval( $iStatus );
				$aVariant = $oDB->executeSQL( $sql, true, DataBase::SINGLE_RECORD );
				if( !empty( $aVariant ) ){
					if( !empty( $aVariant['iPriceTimeFrom'] ) && ( $aVariant['iPriceTimeFrom'] <= time( ) ) && !empty( $aVariant['iPriceTimeTo'] ) && ( $aVariant['iPriceTimeTo'] > time( ) ) && ( $aVariant['fPriceTimeValue'] > 0 ) && ( $aVariant['fPriceTimeValue'] < $aVariant['fPrice'] ) ){
						if( defined( 'CUSTOMER_PAGE' ) ) $aVariant['fPrice'] = normalizePrice( $aVariant['fPriceTimeValue'] );
					} else{
						$aVariant['fPrice'] = normalizePrice( $aVariant['fPrice'] );
					}
					unset( $aVariant['iPriceTimeFrom'] );
					unset( $aVariant['iPriceTimeTo'] );
					unset( $aVariant['iPriceTimeValue'] );
					$aReturn[] = Array( 'iAttributeValue' => $aAttribute['iAttributeValue'], 'sValue' => $aAttribute['sValue'], 'aVariant' => $aVariant );
				}
			} elseif( $aAttribute['iOptions'] > 1 ){
				$aReturn[] = Array( 'iAttributeValue' => $aAttribute['iAttributeValue'], 'sValue' => $aAttribute['sValue'], 'aVariant' => null );
			}
		}
		return( $aReturn );
	}
	function __call( $methodName, $methodArgs ){
		if( preg_match( '/^listProductsPromoted([0-9]+)_/', $methodName ) || in_array( $methodName, Array( 'listProductsDiscounted', 'listProductsTime', 'listRandomProducts' ) ) ){
			$iContent = $methodArgs[0];
			$iMaxCount = $methodArgs[1];
			$iBlock = $methodArgs[2];
			$bShowOnlyAvailable = $methodArgs[3];
			if( !isset( $iContent ) ) $iContent = -1;
			if( !isset( $iMaxCount ) ) $iMaxCount = false;
			$oDB = & DataBase::getInstance( );
			$iStatus = throwStatus( );

			if( preg_match( '/^listProductsPromoted([0-9]+)_/', $methodName, $aMatch ) ){
				$sql = 'SELECT `iProduct` FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `products` INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_PROMOTED ) . '` `products_promoted` USING( `iProduct` ) WHERE `iPromoted` = ' . intval( $aMatch[1] ) . ' AND `iStatus` >= ' . $iStatus . ' AND ( `iTimeValid` IS NULL OR `iTimeValid` >= UNIX_TIMESTAMP() )';
			} elseif( $methodName == 'listProductsDiscounted' ){
				$sql = 'SELECT `iProduct` FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `products` WHERE `fPriceOld` > `fPrice` AND `iStatus` >= ' . $iStatus . '';
			} elseif( $methodName == 'listProductsTime' ){
				$sql = 'SELECT `iProduct` FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `products` WHERE `fPriceTimeValue` < `fPrice` AND `iPriceTimeFrom` IS NOT NULL AND `iPriceTimeFrom` <= ' . time( ) . ' AND `iPriceTimeTo` IS NOT NULL AND `iPriceTimeTo` > ' . time( ) . ' AND `iStatus` >= ' . $iStatus . '';
			} elseif( $methodName == 'listRandomProducts' ){
				$sql = 'SELECT `iProduct` FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `products` WHERE `iStatus` >= ' . $iStatus;
			}

			if( is_array( $GLOBALS['oPage']->aPages ) ){
				$aPages = array_keys( $GLOBALS['oPage']->aPages );
				$sql.= ' AND EXISTS ( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `products_pages` WHERE `products`.`iProduct` = `products_pages`.`iProduct` AND `products_pages`.`iPage` IN ( \'' . implode( '\', \'', $aPages ) . '\' ) LIMIT 1 )';
			} else{
				$sql.= ' AND FALSE';
			}
			if( $bShowOnlyAvailable ) $sql .= ' AND `TiCanBuy` >= 1';
			$sql .= ' ORDER BY RAND() LIMIT ' . ( ( isset( $iMaxCount ) && is_numeric( $iMaxCount ) && ( $iMaxCount > 0 ) ) ? intval( $iMaxCount ) : intval( $GLOBALS['config']['iRandomProducts'] ) );
			
			$aProducts = $oDB->executeSQL( $sql, true, DataBase::SINGLE_COLUMN );
			return $this->throwProductsBlock( $iContent, $iBlock, $iMaxCount, 'RAND()', $aProducts, null, null, $bShowOnlyAvailable );
		} else{
			die( 'Undefined method ' . $methodName );
		}
	}
	function listBestsellersProducts( $iContent, $iMaxCount = false, $iBlock = null, $bShowOnlyAvailable = false, $iUser = null ){
		$oDB = & DataBase::getInstance( );
		$iStatus = throwStatus( );
		$sLang = strtolower( LANGUAGE );
		$subSql = 'SELECT SUM( `sub_orders_products`.`iQuantity` ) AS `iQuantity`, `sub_products_variants`.`iProduct`
				FROM `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS ) . '` AS `sub_orders_products`
					LEFT JOIN `' . $oDB->throwTableName( DB_ORDERS ) . '` AS `sub_orders` ON ( `sub_orders`.`iOrder` = `sub_orders_products`.`iOrder` )
					LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` AS `sub_products_variants` USING( `iProductVariant` )
					LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `sub_products` USING( `iProduct` )
				WHERE `sub_orders`.`sLanguage` = \'' . $sLang . '\'
					AND `sub_products`.`iStatus` >= ' . $iStatus;
		if( !empty( $iUser ) && is_numeric( $iUser ) ) $subSql.= ' AND `sub_orders`.`iUser` = ' . intval( $iUser );
		if( is_array( $GLOBALS['oPage']->aPages ) ){
			$aPages = array_keys( $GLOBALS['oPage']->aPages );
			$subSql.= ' AND EXISTS ( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `products_pages` WHERE `sub_products`.`iProduct` = `products_pages`.`iProduct` AND `products_pages`.`iPage` IN ( \'' . implode( '\', \'', $aPages ) . '\' ) LIMIT 1 )';
		} else{
			$subSql.= ' AND FALSE';
		}
		if( $bShowOnlyAvailable ) $subSql .= ' AND `sub_products`.`TiCanBuy` >= 1';
		$subSql.= '
				GROUP BY `sub_products_variants`.`iProduct`';
		$sql = 'SELECT `iProduct` FROM ( ' . $subSql . ' ) AS `sub` ORDER BY `iQuantity` DESC ' . ( ( isset( $iMaxCount ) && is_numeric( $iMaxCount ) && ( $iMaxCount > 0 ) ) ? 'LIMIT ' . intval( $iMaxCount ) : null );
		$aProducts = $oDB->executeSQL( $sql, true, DataBase::SINGLE_COLUMN );
		if( isset( $_POST['productsPerPage'] ) && is_numeric( $_POST['productsPerPage'] ) && $_POST['productsPerPage'] > 0 ) $_SESSION['products_list'] = $_POST['productsPerPage'];
		if( isset( $_SESSION['products_list'] ) && !empty( $_SESSION['products_list'] ) ) $iList = $_SESSION['products_list'];
		// **
		if( !isset( $iList ) ){
			$iList = $GLOBALS['config']['products_list'];
		}
		return $this->throwProductsBlock( $iContent, $iBlock, isset( $iMaxCount ) ? $iMaxCount : $iList, 'RAND()', $aProducts, null, null, $bShowOnlyAvailable );
	}
	function listNewProducts( $iContent, $iMaxCount = false, $iBlock = null, $bShowOnlyAvailable = false ){
		$oDB = & DataBase::getInstance( );
		$iStatus = throwStatus( );
		$sql = 'SELECT `iProduct` FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `products` WHERE `iStatus` >= ' . $iStatus;
		if( is_array( $GLOBALS['oPage']->aPages ) ){
			$aPages = array_keys( $GLOBALS['oPage']->aPages );
			$sql.= ' AND EXISTS ( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `products_pages` WHERE `products`.`iProduct` = `products_pages`.`iProduct` AND `products_pages`.`iPage` IN ( \'' . implode( '\', \'', $aPages ) . '\' ) LIMIT 1 )';
		} else{
			$sql.= ' AND FALSE';
		}
		if( $bShowOnlyAvailable ) $sql .= ' AND `TiCanBuy` >= 1';
		$sql.= ' ORDER BY `iTime` DESC LIMIT ' . ( ( isset( $iMaxCount ) && is_numeric( $iMaxCount ) && ( $iMaxCount > 0 ) ) ? intval( $iMaxCount ) : intval( $GLOBALS['config']['iNewProducts'] ) );
		$aProducts = $oDB->executeSQL( $sql, true, DataBase::SINGLE_COLUMN );
		return $this->throwProductsBlock( $iContent, $iBlock, $iMaxCount, '`iTime` DESC', $aProducts, null, null, $bShowOnlyAvailable );
	}
	function listLastVisitedProducts( $iContent, $iMaxCount = false, $iBlock = null, $bShowOnlyAvailable = false ){
		$oDB = & DataBase::getInstance( );
		$iStatus = throwStatus( );
		$sql = 'SELECT `iProduct` FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `products` WHERE `iStatus` >= ' . $iStatus;
		if( is_array( $GLOBALS['oPage']->aPages ) ){
			$aPages = array_keys( $GLOBALS['oPage']->aPages );
			$sql.= ' AND EXISTS ( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `products_pages` WHERE `products`.`iProduct` = `products_pages`.`iProduct` AND `products_pages`.`iPage` IN ( \'' . implode( '\', \'', $aPages ) . '\' ) LIMIT 1 )';
		} else{
			$sql.= ' AND FALSE';
		}
		if( $bShowOnlyAvailable ) $sql .= ' AND `TiCanBuy` >= 1';
		$sql.= ' ORDER BY `iLastVisited` DESC LIMIT ' . ( ( isset( $iMaxCount ) && is_numeric( $iMaxCount ) && ( $iMaxCount > 0 ) ) ? intval( $iMaxCount ) : intval( $GLOBALS['config']['iLastVisitedProducts'] ) );
		$aProducts = $oDB->executeSQL( $sql, true, DataBase::SINGLE_COLUMN );
		return $this->throwProductsBlock( $iContent, $iBlock, $iMaxCount, '`iLastVisited` DESC', $aProducts, null, null, $bShowOnlyAvailable );
	}
	function listPopularProducts( $iContent, $iMaxCount = false, $iBlock = null, $bShowOnlyAvailable = false ){
		$oDB = & DataBase::getInstance( );
		$iStatus = throwStatus( );
		$sql = 'SELECT `iProduct` FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `products` LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_STATS ) . '` USING( `iProduct` ) WHERE `iStatus` >= ' . $iStatus;
		if( is_array( $GLOBALS['oPage']->aPages ) ){
			$aPages = array_keys( $GLOBALS['oPage']->aPages );
			$sql.= ' AND EXISTS ( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `products_pages` WHERE `products`.`iProduct` = `products_pages`.`iProduct` AND `products_pages`.`iPage` IN ( \'' . implode( '\', \'', $aPages ) . '\' ) LIMIT 1 )';
		} else{
			$sql.= ' AND FALSE';
		}
		if( $bShowOnlyAvailable ) $sql .= ' AND `TiCanBuy` >= 1';
		$sql.= ' GROUP BY `iProduct` ORDER BY SUM(`iElement`) DESC LIMIT ' . ( ( isset( $iMaxCount ) && is_numeric( $iMaxCount ) && ( $iMaxCount > 0 ) ) ? intval( $iMaxCount ) : intval( $GLOBALS['config']['iLastVisitedProducts'] ) );
		$aProducts = $oDB->executeSQL( $sql, true, DataBase::SINGLE_COLUMN );
		return $this->throwProductsBlock( $iContent, $iBlock, $iMaxCount, 'RAND()', $aProducts, null, null, $bShowOnlyAvailable );
	}
	function listSiteMap( $iPage ){
		$oDB = & DataBase::getInstance( );
		$sql = $this->generateListSQL( $iPage );
		$aProducts = $oDB->executeSQL( $sql, true );
		foreach( $aProducts as &$aData ){
			$aData['sPrice'] = displayPrice( $aData['fPrice'] );
			$aData['sLinkName'] = throwPageUrl( $aData['iProduct'] . ',' . change2Url( $aData['sName'] ) );
		}
		return $aProducts;
	}
	function listProductsCrossSell( $iProduct, $iMaxCount = null, $bShowOnlyAvailable = false ){
		global $config;
		$oDB = & DataBase::getInstance( );
		$sLang = strtolower( LANGUAGE );
		if( !isset( $iMaxCount ) ) $iMaxCount = $config['iCrossSellProducts'];
		$sql = 'SELECT `pvs`.`iProduct` AS `iProduct`
							FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` AS `pvs`
								LEFT JOIN `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS ) . '` AS `ops` ON( `pvs`.`iProductVariant` = `ops`.`iProductVariant` )
								LEFT JOIN `' . $oDB->throwTableName( DB_ORDERS ) . '` AS `orders` ON ( `ops`.`iOrder` = `orders`.`iOrder` )
								LEFT JOIN `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS ) . '` AS `opc` ON( `ops`.`iOrder` = `opc`.`iOrder` )
								LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` AS `pvc` ON( `pvc`.`iProductVariant` = `opc`.`iProductVariant` )
							WHERE `orders`.`sLanguage` = \'' . $sLang . '\'
								AND `pvc`.`iProduct` = ' . intval( $iProduct ) . '
								AND `pvs`.`iProduct` != ' . intval( $iProduct );
		if( is_array( $GLOBALS['oPage']->aPages ) ){
			$aPages = array_keys( $GLOBALS['oPage']->aPages );
			$sql.= ' AND EXISTS ( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `products_pages` WHERE `pvs`.`iProduct` = `products_pages`.`iProduct` AND `products_pages`.`iPage` IN ( \'' . implode( '\', \'', $aPages ) . '\' ) LIMIT 1 )';
		} else{
			$sql.= ' AND FALSE';
		}
		$aProducts = $oDB->executeSQL( $sql, true, DataBase::SINGLE_COLUMN );
		return $this->throwProducts(-1, $iMaxCount, $aProducts, 'RAND()', null, null, null, $bShowOnlyAvailable );
	}
	function listProductsSimilar( $iProduct = 0, $fPrice = 0, $aAttribs = Array(), $iMaxCount = null, $bShowOnlyAvailable = false ){
		$oDB = & DataBase::getInstance( );
		$sql = 'SELECT DISTINCT `p`.`iProduct`
							FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` `pv` INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS ) . '` `p` USING( `iProduct` )
							WHERE `p`.`iProduct` != ' . intval( $iProduct );
		foreach( $aAttribs as $aValues ){
			foreach( $aValues as $iValue ){
				if( !is_int( $iValue ) ) die( 'Invalid attribute value!' ); //Zabezpieczenie przed SQL Injection
			}
			$sql .= ' AND EXISTS( SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` WHERE `iProductVariant` = `pv`.`iProductVariant` AND `iAttributeValue` IN( ' . implode( $aValues ) . ' ) )';
		}
		$sql .= ' ORDER BY ABS( `p`.`fPrice` - ' . floatval( $fPrice ) . ' )';
		if( $iMaxCount ) $sql .= ' LIMIT ' . intval( $iMaxCount );
		return $this->throwProducts( -1, $iMaxCount, $oDB->executeSQL( $sql, true, DataBase::SINGLE_COLUMN ), 'RAND()', null, null, null, $bShowOnlyAvailable );
	}
	function listProductsRelatedByBox( $iProduct, $iMaxCount = null, $iBox = null, $bShowOnlyAvailable = false ){
		global $config;
		$oDB = & DataBase::getInstance( );
		$sql = 'SELECT `iRelated` FROM `' . $oDB->throwTableName( DB_PRODUCTS_RELATED ) . '` WHERE `iProduct` = ' . intval( $iProduct ) . ' AND `iBox` = ' . intval( $iBox );
		$aProducts = $oDB->executeSQL( $sql, true, DataBase::SINGLE_COLUMN );
		return $this->throwProducts(-1, $iMaxCount, $aProducts, 'RAND()', null, null, null, $bShowOnlyAvailable );
	}
	function throwFilterForm( $iPage ){
		$oDB = & DataBase::getInstance( );
		$iStatus = throwStatus( );
		$sReturn = null;
		$GLOBALS['oPage']->mData = null;
		$aPages = $GLOBALS['oPage']->throwAllChildren( $iPage );
		$aPages[] = $iPage;
		if( isset( $_SESSION['iUserClient'] ) && is_numeric( $_SESSION['iUserClient'] ) && ( $_SESSION['iUserClient'] > 0 ) && defined( 'CUSTOMER_PAGE' ) ){
			$sUserId = intval( $_SESSION['iUserClient'] );
		} else{
			$sUserId = 'NULL';
		}
		$sql = 'SELECT COUNT( `iProduct` ) AS `iProductsCount`, \'' . $oDB->escape( $GLOBALS['lang']['Promoted'] ) . '\' AS `sName`, -1 AS `iAttribute`, `iPromoted` AS `iAttributeValue`, \'x\' AS `sValue`, 1 AS `iAttributeFilterVisible`, 1 AS `iValueFilterVisible`
							FROM ( SELECT DISTINCTROW `p`.`iProduct`, `ppr`.`iPromoted`
											FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `p`
												INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `pp` ON( `pp`.`iProduct` = `p`.`iProduct` AND `pp`.`iPage` IN ( \'' . implode( '\', \'', $aPages ) . '\' ) )
												INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_PROMOTED ) . '` AS `ppr` ON( `ppr`.`iProduct` = `p`.`iProduct` AND ( `iTimeValid` IS NULL OR `iTimeValid` >= UNIX_TIMESTAMP() ) )';
		$sql.= ' WHERE `p`.`iStatus` >= ' . $iStatus;
		if( isset( $_GET['aFilterAttributes'] ) ){
			foreach( $_GET['aFilterAttributes'] as $iAttribute => $aValues ){
				if( !empty( $aValues ) && !preg_match( '/^[\\s,]*$/', implode( ', ', array_keys( $aValues ) ) ) ){
					if( $iAttribute > 0 ){
						$sql.= ' AND EXISTS (
											SELECT 1
												FROM (
													SELECT
															`iProduct`, `iAttributeValue`
														FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '`
															INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` USING( `iProductVariant` )
															WHERE `iStatus` >= ' . $iStatus . '
													UNION ALL
													SELECT `iProduct`, `iAttributeValue`
														FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES_CONST ) . '`
													) AS `pas`
													INNER JOIN `' . $oDB->throwTableName( DB_ATTRIBUTES_VALUES ) . '` AS `avs` USING( `iAttributeValue` )
												WHERE `p`.`iProduct` = `pas`.`iProduct`
													AND `avs`.`iAttribute` = ' . intval( $iAttribute ) . '
													AND `avs`.`iAttributeValue` IN ( ' . preg_replace( '/[^0-9 ,]/', '', implode( ', ', array_keys( $aValues ) ) ) . ' )
												LIMIT 1
											)';
					} elseif( $iAttribute == 0 ){
						$sql.= ' AND EXISTS (
											SELECT 1
												FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `ps`
												WHERE `p`.`iProduct` = `ps`.`iProduct`
													AND `ps`.`iProducer` IN ( ' . preg_replace( '/[^0-9 ,]/', '', implode( ', ', array_keys( $aValues ) ) ) . ' )
													AND `ps`.`iStatus` >= ' . $iStatus . '
												LIMIT 1
											)';
					} elseif( $iAttribute == -1 ){
							$sql .= ' AND EXISTS (
													SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PROMOTED ) . '` `ppr` WHERE `ppr`.`iPromoted` IN( ' . implode( ', ', $aValues ) . ' ) AND `ppr`.`iProduct` = `p`.`iProduct` AND ( `iTimeValid` IS NULL OR `iTimeValid` >= UNIX_TIMESTAMP() )
												)';
					}					
				}
			}
		}
		if( isset( $_GET['fFilterPriceFrom'] ) && !empty( $_GET['fFilterPriceFrom'] ) ){
			$sql.= ' AND ' . $oDB->throwRoutineName( '%%_calculateProductPriceForUser' ) . '( ' . $sUserId . ', `p`.`iProduct`, ' . JOIN_PROMOTIONS . ' ) >= ' . floatval( $_GET['fFilterPriceFrom'] );
		}
		if( isset( $_GET['fFilterPriceTo'] ) && !empty( $_GET['fFilterPriceTo'] ) ){
			$sql.= ' AND ' . $oDB->throwRoutineName( '%%_calculateProductPriceForUser' ) . '( ' . $sUserId . ', `p`.`iProduct`, ' . JOIN_PROMOTIONS . ' ) <= ' . floatval( $_GET['fFilterPriceTo'] );
		}
		$sql.= ') AS `sub`';
		$sql.= ' GROUP BY `sName`, `iAttribute`, `iAttributeValue`, `sValue`';
		$sql.= ' ORDER BY `sName`, `iAttribute`, `iAttributeValue`';
		$aInfo = $oDB->executeSQL( $sql, true );
		$aAttributes = Array( );
		foreach( $aInfo as $aData ){
			if( !isset( $aAttributes[$aData['iAttribute']] ) ){
				$aAttributes[$aData['iAttribute']] = Array( 'sName' => $aData['sName'], 'iType' => $GLOBALS['config']['filter_products_producer_type'], 'iFilterVisible' => $GLOBALS['config']['filter_products_producer_visible'], 'aValues' => Array( ) );
			}
			$aAttributes[$aData['iAttribute']]['aValues'][$aData['iAttributeValue']] = Array( 'iValue' => $aData['iAttributeValue'], 'sValue' => $GLOBALS['config']['promoted'.$aData['iAttributeValue'].'_name'], 'iFilterVisible' => 1, 'iProductsCount' => $aData['iProductsCount'], 'bChecked' => isset( $_GET['aFilterAttributes'][$aData['iAttribute']][$aData['iAttributeValue']] ) );
		}

		$sql = 'SELECT COUNT( `iProduct` ) AS `iProductsCount`, \'' . $oDB->escape( $GLOBALS['lang']['Producer'] ) . '\' AS `sName`, 0 AS `iAttribute`, `iProducer` AS `iAttributeValue`, `sName` AS `sValue`, 1 AS `iAttributeFilterVisible`, 1 AS `iValueFilterVisible`
							FROM ( SELECT DISTINCTROW `p`.`iProduct`, `pr`.`iProducer`, `pr`.`sName`
											FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `p`
												INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `pp` ON( `pp`.`iProduct` = `p`.`iProduct` AND `pp`.`iPage` IN ( \'' . implode( '\', \'', $aPages ) . '\' ) )
												INNER JOIN `' . $oDB->throwTableName( DB_PRODUCERS ) . '` AS `pr` USING( `iProducer` )';
		$sql.= ' WHERE `p`.`iStatus` >= ' . $iStatus;
		if( isset( $_GET['aFilterAttributes'] ) ){
			foreach( $_GET['aFilterAttributes'] as $iAttribute => $aValues ){
				if( !empty( $aValues ) && !preg_match( '/^[\\s,]*$/', implode( ', ', array_keys( $aValues ) ) ) ){
					if( $iAttribute > 0 ){
						$sql.= ' AND EXISTS (
											SELECT 1
												FROM (
													SELECT
															`iProduct`, `iAttributeValue`
														FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '`
															INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` USING( `iProductVariant` )
															WHERE `iStatus` >= ' . $iStatus . '
													UNION ALL
													SELECT `iProduct`, `iAttributeValue`
														FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES_CONST ) . '`
													) AS `pas`
													INNER JOIN `' . $oDB->throwTableName( DB_ATTRIBUTES_VALUES ) . '` AS `avs` USING( `iAttributeValue` )
												WHERE `p`.`iProduct` = `pas`.`iProduct`
													AND `avs`.`iAttribute` = ' . intval( $iAttribute ) . '
													AND `avs`.`iAttributeValue` IN ( ' . preg_replace( '/[^0-9 ,]/', '', implode( ', ', array_keys( $aValues ) ) ) . ' )
												LIMIT 1
											)';
					} elseif( $iAttribute == 0 ){
						$sql.= ' AND EXISTS (
											SELECT 1
												FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `ps`
												WHERE `p`.`iProduct` = `ps`.`iProduct`
													AND `ps`.`iProducer` IN ( ' . preg_replace( '/[^0-9 ,]/', '', implode( ', ', array_keys( $aValues ) ) ) . ' )
													AND `ps`.`iStatus` >= ' . $iStatus . '
												LIMIT 1
											)';
					} elseif( $iAttribute == -1 ){
							$sql .= ' AND EXISTS (
													SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PROMOTED ) . '` `ppr` WHERE `ppr`.`iPromoted` IN( ' . implode( ', ', $aValues ) . ' ) AND `ppr`.`iProduct` = `p`.`iProduct` AND ( `iTimeValid` IS NULL OR `iTimeValid` >= UNIX_TIMESTAMP() )
												)';
					}					
				}
			}
		}
		if( isset( $_GET['fFilterPriceFrom'] ) && !empty( $_GET['fFilterPriceFrom'] ) ){
			$sql.= ' AND ' . $oDB->throwRoutineName( '%%_calculateProductPriceForUser' ) . '( ' . $sUserId . ', `p`.`iProduct`, ' . JOIN_PROMOTIONS . ' ) >= ' . floatval( $_GET['fFilterPriceFrom'] );
		}
		if( isset( $_GET['fFilterPriceTo'] ) && !empty( $_GET['fFilterPriceTo'] ) ){
			$sql.= ' AND ' . $oDB->throwRoutineName( '%%_calculateProductPriceForUser' ) . '( ' . $sUserId . ', `p`.`iProduct`, ' . JOIN_PROMOTIONS . ' ) <= ' . floatval( $_GET['fFilterPriceTo'] );
		}
		$sql.= ') AS `sub`';
		$sql.= ' GROUP BY `sName`, `iAttribute`, `iAttributeValue`, `sValue`';
		$sql.= ' ORDER BY `sName`, `iAttribute`, `sValue`';
		$aInfo = $oDB->executeSQL( $sql, true );
		foreach( $aInfo as $aData ){
			if( !isset( $aAttributes[$aData['iAttribute']] ) ){
				$aAttributes[$aData['iAttribute']] = Array( 'sName' => $aData['sName'], 'iType' => $GLOBALS['config']['filter_products_producer_type'], 'iFilterVisible' => $GLOBALS['config']['filter_products_producer_visible'], 'aValues' => Array( ) );
			}
			$aData['sValue'] = preg_replace( '/(#[0-9a-f]{6})/', '<div style="background-color: $1;" class="attrib-color-box"></div>', $aData['sValue'] );
			$aAttributes[$aData['iAttribute']]['aValues'][$aData['iAttributeValue']] = Array( 'iValue' => $aData['iAttributeValue'], 'sValue' => $aData['sValue'], 'iFilterVisible' => 1, 'iProductsCount' => $aData['iProductsCount'], 'bChecked' => isset( $_GET['aFilterAttributes'][$aData['iAttribute']][$aData['iAttributeValue']] ) );
		}

		$sqlfilter = '';
		if( isset( $_GET['aFilterAttributes'] ) ){
			foreach( $_GET['aFilterAttributes'] as $iAttribute => $aValues ){
				$aValuesKeys = array_keys( $aValues );
				$aValues = Array();
				$aValuesGroups = Array();
				foreach( $aValuesKeys as $sValue ){
					if( preg_match( '/^g[0-9]+$/', $sValue ) ){
						$aValuesGroups[] = str_replace( 'g', '', $sValue );
					} elseif( preg_match( '/^[0-9]+$/', $sValue ) ){
						$aValues[] = $sValue;
					}
				}
				if( $iAttribute > 0 ){
					$sqlfilter.= ' AND EXISTS (
										SELECT 1
											FROM (
												SELECT
														`iProduct`, `iAttributeValue`
													FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '`
														INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` USING( `iProductVariant` )
														WHERE `iStatus` >= ' . $iStatus . '
												UNION ALL
												SELECT `iProduct`, `iAttributeValue`
													FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES_CONST ) . '`
												) AS `pas`
												INNER JOIN `' . $oDB->throwTableName( DB_ATTRIBUTES_VALUES ) . '` AS `avs` USING( `iAttributeValue` )
											WHERE `p`.`iProduct` = `pas`.`iProduct`
												AND `avs`.`iAttribute` = ' . intval( $iAttribute ) . '
												AND ( 
														' . ( empty( $aValues ) ? '0' : ( '`avs`.`iAttributeValue` IN ( ' . implode( ', ', $aValues ) . ' )' ) ) . '
													OR
														' . ( empty( $aValuesGroups ) ? '0' : ( 'EXISTS( SELECT 1 FROM `' . $oDB->throwTableName( DB_ATTRIBUTES_FILTER_GROUPS ) . '` `afg` WHERE `avs`.`iAttributeValue` = `afg`.`iAttributeValue` AND `afg`.`iFilterGroup` IN ( ' . implode( ', ', $aValuesGroups ) . ' ) )' ) ) . '
													)
											LIMIT 1
										)';
				} elseif( $iAttribute == 0 ){
					$sqlfilter.= ' AND EXISTS (
										SELECT 1
											FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `ps`
											WHERE `p`.`iProduct` = `ps`.`iProduct`
												AND `ps`.`iProducer` IN ( ' . preg_replace( '/[^0-9 ,]/', '', implode( ', ', $aValues ) ) . ' )
												AND `ps`.`iStatus` >= ' . $iStatus . '
											LIMIT 1
										)';
				} elseif( $iAttribute == -1 ){
						$sql .= ' AND EXISTS (
													SELECT 1 FROM `' . $oDB->throwTableName( DB_PRODUCTS_PROMOTED ) . '` `ppr` WHERE `ppr`.`iPromoted` IN( ' . implode( ', ', $aValues ) . ' ) AND `ppr`.`iProduct` = `p`.`iProduct` AND ( `iTimeValid` IS NULL OR `iTimeValid` >= UNIX_TIMESTAMP() )
											)';
				}					
			}
		}
		if( isset( $_GET['fFilterPriceFrom'] ) && !empty( $_GET['fFilterPriceFrom'] ) && is_numeric( $_GET['fFilterPriceFrom'] ) ){
			$sqlfilter.= ' AND ' . $oDB->throwRoutineName( '%%_calculateProductPriceForUser' ) . '( ' . $sUserId . ', `p`.`iProduct`, ' . JOIN_PROMOTIONS . ' ) >= ' . floatval( $_GET['fFilterPriceFrom'] );
		}
		if( isset( $_GET['fFilterPriceTo'] ) && !empty( $_GET['fFilterPriceTo'] ) && is_numeric( $_GET['fFilterPriceTo'] ) ){
			$sqlfilter.= ' AND ' . $oDB->throwRoutineName( '%%_calculateProductPriceForUser' ) . '( ' . $sUserId . ', `p`.`iProduct`, ' . JOIN_PROMOTIONS . ' ) <= ' . floatval( $_GET['fFilterPriceTo'] );
		}

		$sql = 'SELECT COUNT( `iProduct` ) AS `iProductsCount`,
								`iType`,
								`sName`,
								`iAttribute`,
								`iAttributeValue`,
								`sValue`,
								`iAttributeFilterVisible`,
								`iValueFilterVisible`
							FROM (
								SELECT DISTINCTROW
										`iProduct`,
										`a`.`iType`,
										`a`.`iPosition`,
										`av`.`iPosition` AS `iValuePosition`,
										`a`.`sName`,
										`a`.`iAttribute`,
										`av`.`iAttributeValue`,
										`av`.`sValue`,
										`a`.`iFilterVisible` AS `iAttributeFilterVisible`,
										`av`.`iFilterVisible` AS `iValueFilterVisible`
									FROM ( SELECT `iProduct` FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` USING( `iProduct` ) WHERE `iStatus` >= ' . $iStatus . ' AND `iPage` IN ( \'' . implode( '\', \'', $aPages ) . '\' ) ) AS `p`
										INNER JOIN (
											SELECT
													`iProduct`, `iAttributeValue`
												FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '`
													INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` USING( `iProductVariant` )
													WHERE `iStatus` >= ' . $iStatus . '
											UNION ALL
											SELECT `iProduct`, `iAttributeValue`
												FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES_CONST ) . '`
										) AS `pa` USING( `iProduct` )
										INNER JOIN `' . $oDB->throwTableName( DB_ATTRIBUTES_VALUES ) . '` AS `av` USING( `iAttributeValue` )
										INNER JOIN `' . $oDB->throwTableName( DB_ATTRIBUTES ) . '` AS `a` USING( `iAttribute` )
									WHERE `a`.`iFilterVisible` >= 0
										AND NOT EXISTS (
											SELECT 1
												FROM `' . $oDB->throwTableName( DB_ATTRIBUTES_FILTER_GROUPS ) . '` AS `afg`
												WHERE `afg`.`iAttributeValue` = `av`.`iAttributeValue`
											)';
		$sql.= $sqlfilter;
		$sql.= '
								UNION ALL
								SELECT DISTINCTROW
										`iProduct`,
										`a`.`iType`,
										`a`.`iPosition`,
										`fg`.`iPosition` AS `iValuePosition`,
										`a`.`sName`,
										`a`.`iAttribute`,
										CONCAT( \'g\', `fg`.`iFilterGroup` ) AS `iAttributeValue`,
										`fg`.`sValue`,
										`a`.`iFilterVisible` AS `iAttributeFilterVisible`,
										1 AS `iValueFilterVisible`
									FROM ( SELECT `iProduct` FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` USING( `iProduct` ) WHERE `iStatus` >= ' . $iStatus . ' AND `iPage` IN ( \'' . implode( '\', \'', $aPages ) . '\' ) )  AS `p`
										INNER JOIN (
											SELECT
													`iProduct`, `iAttributeValue`
												FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '`
													INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` USING( `iProductVariant` )
													WHERE `iStatus` >= ' . $iStatus . '
											UNION ALL
											SELECT `iProduct`, `iAttributeValue`
												FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES_CONST ) . '`
										) AS `pa` USING( `iProduct` )
										INNER JOIN `' . $oDB->throwTableName( DB_ATTRIBUTES_FILTER_GROUPS ) . '` AS `afg` USING( `iAttributeValue` )
										INNER JOIN `' . $oDB->throwTableName( DB_FILTER_GROUPS ) . '` AS `fg` USING( `iFilterGroup` )
										INNER JOIN `' . $oDB->throwTableName( DB_ATTRIBUTES ) . '` AS `a` USING( `iAttribute` )
									WHERE `a`.`iFilterVisible` >= 0';
		$sql.= $sqlfilter;
		$sql.= '
								) AS `sub`
							GROUP BY `sName`, `iAttribute`, `iType`, `iAttributeValue`, `sValue`
							ORDER BY `iPosition`, `sName`, `iAttribute`, `iValuePosition`, `sValue`';
		$aInfo = $oDB->executeSQL( $sql, true );
		foreach( $aInfo as $aData ){
			if( !isset( $aAttributes[$aData['iAttribute']] ) ){
				$aAttributes[$aData['iAttribute']] = Array( 'sName' => $aData['sName'], 'iType' => $aData['iType'], 'iFilterVisible' => $aData['iAttributeFilterVisible'], 'aValues' => Array( ) );
			}
			$aData['sValue'] = preg_replace( '/(#[0-9a-f]{6})/', '<div style="background-color: $1;" class="attrib-color-box"></div>', $aData['sValue'] );
			$aAttributes[$aData['iAttribute']]['aValues'][$aData['iAttributeValue']] = Array( 'iValue' => $aData['iAttributeValue'], 'sValue' => $aData['sValue'], 'iFilterVisible' => $aData['iValueFilterVisible'], 'iProductsCount' => $aData['iProductsCount'], 'bChecked' => isset( $_GET['aFilterAttributes'][$aData['iAttribute']][$aData['iAttributeValue']] ) );
		}
		$GLOBALS['aData']['sLinkName2'] = null;
		return $aAttributes;
	}
	function throwFilterProducers( $iPage ){
		$oDB = & DataBase::getInstance( );
		$iStatus = throwStatus( );
		$sReturn = null;
		$GLOBALS['oPage']->mData = null;
		$aPages = $GLOBALS['oPage']->throwAllChildren( $iPage );
		$aPages[] = $iPage;
		if( isset( $_SESSION['iUserClient'] ) && is_numeric( $_SESSION['iUserClient'] ) && ( $_SESSION['iUserClient'] > 0 ) && defined( 'CUSTOMER_PAGE' ) ){
			$sUserId = intval( $_SESSION['iUserClient'] );
		} else{
			$sUserId = 'NULL';
		}
		$sql = 'SELECT COUNT( `iProduct` ) AS `iProductsCount`, \'' . $GLOBALS['lang']['Producer'] . '\' AS `sName`, 0 AS `iAttribute`, `iProducer` AS `iAttributeValue`, `sName` AS `sValue`, `sLogoUrl`
							FROM ( SELECT DISTINCTROW `p`.`iProduct`, `pr`.`iProducer`, `pr`.`sName`, `pr`.`sLogoUrl`, `pr`.`iPosition`
											FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `p`
												INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_PAGES ) . '` AS `pp` ON( `p`.`iProduct` = `pp`.`iProduct` AND `pp`.`iPage` IN ( \'' . implode( '\', \'', $aPages ) . '\' ) )
												INNER JOIN `' . $oDB->throwTableName( DB_PRODUCERS ) . '` AS `pr` USING( `iProducer` )
											WHERE `p`.`iStatus` >= ' . $iStatus . '
										) AS `sub`
							GROUP BY `sName`, `iAttribute`, `iAttributeValue`, `sValue`
							ORDER BY `iPosition`, `sName`, `iAttribute`, `sValue`';
		$aInfo = $oDB->executeSQL( $sql, true );
		$aAttributes = Array( );
		foreach( $aInfo as $aData ){
			if( !isset( $aAttributes[$aData['iAttribute']] ) ){
				$aAttributes[$aData['iAttribute']] = Array( 'sName' => $aData['sName'], 'aValues' => Array( ) );
			}
			$aAttributes[$aData['iAttribute']]['aValues'][$aData['iAttributeValue']] = Array( 'iValue' => $aData['iAttributeValue'], 'sValue' => $aData['sValue'], 'iProductsCount' => $aData['iProductsCount'], 'sLogoUrl' => $aData['sLogoUrl'] );
		}
		$GLOBALS['aData']['sLinkName2'] = null;
		return $aAttributes;
	}
	/**
	 ************** PRODUCTS COMPARE ******************
	 *
	 */
	function listProductsCompare( $sFile, $aProducts ){
		$oDB = & DataBase::getInstance( );
		$oFile = & Files::getInstance( );
		$oTpl = & TplParser::getInstance( );
		if( is_array( $aProducts ) ){
			$sql = $this->generateListSQL( null, $aProducts, null, Array( 'sWeightDisplayed', 'sSizeXYZ', 'sProducerURL' ) );
			$sort_orders = Array( 'default' => '`products`.`iPosition` ASC, `products`.`sName` ASC, `products`.`iTime` ASC', 'price_inc' => '`products`.`fPrice` ASC', 'price_desc' => '`products`.`fPrice` DESC', 'name_inc' => '`products`.`sName` ASC', 'name_desc' => '`products`.`sName` DESC', 'date_inc' => '`products`.`iTime` ASC', 'date_desc' => '`products`.`iTime` DESC' );
			//Backward compatibility
			if( $sSortOrder === true ) $sSortOrder = 'RAND()';
			if( isset( $_POST['sort'] ) && array_key_exists( $_POST['sort'], $sort_orders ) ) $_SESSION['sort_order'] = $_POST['sort'];
			if( !isset( $_SESSION['sort_order'] ) || !array_key_exists( $_SESSION['sort_order'], $sort_orders ) ) $_SESSION['sort_order'] = 'default';
			if( !empty( $_SESSION['sort_order'] ) && !isset( $sSortOrder ) ) $sql.= ' ORDER BY ' . $sort_orders[$_SESSION['sort_order']];
			if( $sSortOrder ) $sql.= ' ORDER BY ' . $sSortOrder;
			$aProductsData = $oDB->executeSQL( $sql, true );
			$aAttributes = Array( );
			foreach( $aProductsData as $iIndex => &$aData ){
				$aData['sLinkName'] = throwPageUrl( $aData['iProduct'] . ',' . change2Url( $aData['sName'] ) );
				$aData['sName'] = htmlspecialchars( $aData['sName'] );
				$aData['fPrice'] = normalizePrice( $aData['fPrice'] );
				$aData['fCatalogPrice'] = normalizePrice( $aData['fCatalogPrice'] );
				if( $aData['fPriceOld'] ) $aData['fPriceOld'] = normalizePrice( $aData['fPriceOld'] );
				else $aData['fPriceOld'] = '';
				$aAttributeInfo = $this->throwAttributesByGroups( $aData['iProduct'] );
				$aData['aAttributes'] = $aAttributeInfo;
				$aAttributes = $aAttributes + $aAttributeInfo;
				$aData['sPrice'] = displayPrice( $aData['fPrice'], true );
				$aData['aProducer'] = throwProducer( $aData['iProducer'] );
				if( !empty( $aData['aProducer'] ) ){
					$aData['sProducer'] = $aData['aProducer']['sName'];
				} else{
					$aData['sProducer'] = null;
				}
				$aData['aDataImage'] = ( isset( $oFile->aImagesDefault[2][$aData['iProduct']] ) ) ? $oFile->aImagesDefault[2][$aData['iProduct']] : null;
				$aData['sPriceOld'] = ( !empty( $aData['fPriceOld'] ) && ( $aData['fPriceOld'] > $aData['fCatalogPrice'] ) ) ? displayPrice( $aData['fPriceOld'] ) : null;
				$aData['sCatalogPrice'] = ( $aData['fPrice'] < $aData['fCatalogPrice'] ) ? displayPrice( $aData['fCatalogPrice'] ) : null;
			}
			$oTpl->assign( 'aProducts', $aProductsData );
			$oTpl->assign( 'aAttributes', $aAttributes );
		} else{
			$oTpl->assign( 'aProducts', null );
		}
		return $oTpl->parse( $sFile );
	}
	function throwProductsCompare( ){
		if( isset( $_COOKIE[SESSION_NAME_SELLSMART . '_compare'] ) ) return json_decode( $_COOKIE[SESSION_NAME_SELLSMART . '_compare'] );
		else return Array( );
	}
	/**
	 ************** PRODUCTS STORAGE ******************
	 *
	 */
	function addToStorage( $iProduct ){
		if( isset( $_COOKIE['storageProductsJSON'] ) ) $aStorageProducts = json_decode( $_COOKIE['storageProductsJSON'] );
		else $aStorageProducts = Array( );
		if( !in_array( $iProduct, $aStorageProducts ) ) $aStorageProducts[] = $iProduct;
		sort( $aStorageProducts );
		setcookie( 'storageProductsJSON', json_encode( $aStorageProducts ), time( ) + 60 * 60 * 24 * 365 );
		$_COOKIE['storageProductsJSON'] = json_encode( $aStorageProducts );
	}
	function removeFromStorage( $iProduct ){
		if( isset( $_COOKIE['storageProductsJSON'] ) ) $aStorageProducts = json_decode( $_COOKIE['storageProductsJSON'] );
		else $aStorageProducts = Array( );
		if( in_array( $iProduct, $aStorageProducts ) ){
			foreach( $aStorageProducts as $iKey => $iValue ) if( $iValue == $iProduct ) unset( $aStorageProducts[$iKey] );
		}
		sort( $aStorageProducts );
		setcookie( 'storageProductsJSON', json_encode( $aStorageProducts ), time( ) + 60 * 60 * 24 * 365 );
		$_COOKIE['storageProductsJSON'] = json_encode( $aStorageProducts );
	}
	function listProductsStorage( $iContent = - 1, $iMaxCount = null ){
		return $this->throwProducts( $iContent, $iMaxCount, $this->throwProductsStorage( ) );
	}
	function throwProductsStorage( ){
		if( isset( $_COOKIE['storageProductsJSON'] ) ) return json_decode( $_COOKIE['storageProductsJSON'] );
		else return Array( );
	}
} ?>