Example #1
0
	private function _ImportImage($productId, $record, $index = '')
	{
		$existingImage = false;
		$imageId = 0;

		if (!empty($record['prodimageid' . $index]) && IsId($record['prodimageid' . $index])) {
			$imageId = $record['prodimageid' . $index];
			try {
				$existingImage = new ISC_PRODUCT_IMAGE((int)$imageId);
				if ($existingImage->getProductId() != $productId) {
					// the existing image doesn't belong to this product
					$existingImage = false;
				}
			}
			catch (Exception $ex) {
			}
		}
		$imageFile = $record['prodimagefile' . $index];
		$imageDescription = '';
		if (!empty($record['prodimagedescription' . $index])) {
			$imageDescription = $record['prodimagedescription' . $index];
		}
		$imageIsThumb = false;
		if (!empty($record['prodimageisthumb' . $index])) {
			$imageIsThumb = $record['prodimageisthumb' . $index];
		}
		$imageSort = -1;
		if (isset($record['prodimagesort' . $index]) && $record['prodimagesort' . $index] != '') {
			$imageSort = (int)$record['prodimagesort' . $index];
		}

		$importedImage = false;

		if (!$existingImage || $existingImage->getSourceFilePath() != $imageFile) {
			if (preg_match('#^(?P<scheme>[a-zA-Z0-9\.]+)://#i', $imageFile, $matches)) {
				// code exists in the new product image management classes to handle these imports
				$imageAdmin = new ISC_ADMIN_PRODUCT_IMAGE();

				// the filename is an external URL, import it against the calcualted product hash
				$imageAdmin->importImagesFromUrls(false, array($imageFile), $importImages, $importImageErrors, false, true);

				if (!empty($importImages)) {
					$importedImage = $importImages[0];
				}

				if (!empty($importImageErrors)) {
					// as this import works on one file only and importImagesFromWebUrls creates one error per file, can simply tack on the new error
					$importImageError = $importImageErrors[0];
					if (is_array($importImageError)) {
						$this->ImportSession['Results']['Warnings'][] = $importImageError[1];
					} else {
						$this->ImportSession['Results']['Warnings'][] = $importImageError;
					}
				}
			} else {
				// the filename is a local file
				$importImageFilePath = ISC_BASE_PATH . "/" . GetConfig('ImageDirectory') . "/import/" . $imageFile;
				if (file_exists($importImageFilePath)) {
					try {
						$importedImage = ISC_PRODUCT_IMAGE::importImage($importImageFilePath, basename($importImageFilePath), false, false, false, false);
						$productImages[] = $importedImage;
					} catch (Exception $exception) {
						$this->ImportSession['Results']['Warnings'][] = $exception->getMessage();
					}
				}
				else {
					$this->ImportSession['Results']['Warnings'][] = $record['prodname'].GetLang('ImportProductImageDoesntExist');
				}
			}
		}

		// do we have an existing image?
		if ($existingImage) {
			// assign the imported image file to our existing image
			if ($importedImage) {
				$existingImage->setSourceFilePath($importedImage->getSourceFilePath());
				$existingImage->saveToDatabase(false);
			}

			// use the existing image to set the description, thumb, sort
			$importedImage = $existingImage;
		}

		if ($importedImage) {
			$importedImage->setDescription($imageDescription);
			$importedImage->setIsThumbnail($imageIsThumb);
			if ($imageSort >= 0) {
				$importedImage->setSort($imageSort);
			}
		}

		return $importedImage;
	}
Example #2
0
		/**
		* _GetVariationData
		* Load the variation data for a product either from the form or database
		*
		* @param Int $ProductId The ID of the product to load variations for. 0 if it's a new product
		* @param String $RefArray The array to store the variation details in
		* @return Void
		*/
		public function _GetVariationData($ProductId = 0, &$RefArray = array())
		{
			if($ProductId == 0) {
				// First, do we even have a variation selected?
				if(isset($_POST['variationId']) && is_numeric($_POST['variationId']) && isset($_POST['options'])) {
					foreach($_POST['options'] as $option_counter => $option) {
						$tmp = array();

						// The combination ID hasn't been assigned yet
						if(isset($option['id'])) {
							$tmp['combinationid'] = $option['id'];
						}
						else {
							$tmp['combinationid'] = 0;
						}

						// The product ID hasn't been assigned yet
						$tmp['vcproductid'] = 0;

						// The variation id
						$tmp['vcvariationid'] = (int)$_POST['variationId'];

						// Is the combination enabled?
						$tmp['vcenabled'] = 0;
						if(isset($option['enabled'])) {
							$tmp['vcenabled'] = 1;
						}

						// The variation option combination
						$ids = preg_replace("/^#/", "", $option['variationcombination']);
						$ids = str_replace("#", ",", $ids);
						$tmp['vcoptionids'] = $ids;

						// The product option's SKU
						$tmp['vcsku'] = $option['sku'];

						// The price difference type
						$tmp['vcpricediff'] = $option['pricediff'];

						// The price difference or fixed price
						$tmp['vcprice'] = DefaultPriceFormat($option['price']);

						// The weight difference type
						$tmp['vcweightdiff'] = $option['weightdiff'];

						// The weight difference or fixed weight
						$tmp['vcweight'] = DefaultDimensionFormat($option['weight']);

						$tmp['vcimage'] = '';
						$tmp['vcimagezoom'] = '';
						$tmp['vcimagestd'] = '';
						$tmp['vcimagethumb'] = '';


						if (isset($_FILES['options']['name'][$option_counter]['image']) && $_FILES['options']['name'][$option_counter]['image'] != '') {
							try {
								$image = ISC_PRODUCT_IMAGE::importImage(
									$_FILES['options']['tmp_name'][$option_counter]['image'],
									$_FILES['options']['name'][$option_counter]['image'],
									false,
									false,
									true,
									false
								);

								$tmp['vcimage'] = $image->getSourceFilePath();
								$tmp['vcimagezoom'] = $image->getResizedFilePath(ISC_PRODUCT_IMAGE_SIZE_ZOOM, true, false);
								$tmp['vcimagestd'] = $image->getResizedFilePath(ISC_PRODUCT_IMAGE_SIZE_STANDARD, true, false);
								$tmp['vcimagethumb'] = $image->getResizedFilePath(ISC_PRODUCT_IMAGE_SIZE_THUMBNAIL, true, false);
							}
							catch (Exception $ex) {
							}
						}
						elseif (isset($option['delimage'])) {
							$tmp['vcimage'] = "REMOVE";
						}

						// The current stock level
						if(isset($option['currentstock'])) {
							$tmp['vcstock'] = (int)$option['currentstock'];
						}
						else {
							$tmp['vcstock'] = 0;
						}

						// The low stock level
						if(isset($option['lowstock'])) {
							$tmp['vclowstock'] = (int)$option['lowstock'];
						}
						else {
							$tmp['vclowstock'] = 0;
						}

						// Push the option to the stack
						array_push($RefArray, $tmp);
					}
				}
			}
		}
Example #3
0
	/**
	* Processor for importing an image via external URL
	*
	* @param int|string $productId Id of a product to import images to, or hash of a product being created if $hash is true
	* @param array $urls A list of image urls to import
	* @param array $images By reference blank array to be populated with successful images
	* @param array $errors By reference blank array to be populated with errors - each element may be a string or an array(url, error)
	* @param bool $hash If true, $productId will be treated as a product hash
	* @param bool $generateImages If true, when importing, will attempt to generate thumbnail images -- may not be desirable if importing many images at once
	*/
	public function importImagesFromUrls($productId, $urls, &$images, &$errors, $hash = false, $generateImages = true)
	{
		foreach ($urls as $originalUrl) {
			$url = $originalUrl;
			if (!preg_match('#^[a-zA-Z0-9\.]+://#i', $url)) {
				// no scheme provided in the url, assume http
				$url = 'http://' . $url;
			}

			$urlInfo = @parse_url($url);

			if (!$urlInfo) {
				$errors[] = array($originalUrl, sprintf(GetLang('ProductImageInvalidUrl'), $originalUrl));
				continue;
			}

			if ($urlInfo['scheme'] != 'http' && $urlInfo['scheme'] != 'https') {
				$errors[] = array($originalUrl, sprintf(GetLang('ProductImageHttpOnly'), $originalUrl));
				continue;
			}

			$response = PostToRemoteFileAndGetResponse($url, '', 90, $ptrfError);

			if (!$response) {
				// show error with details from PostToRemoteFileAndGetResponse

				switch ($ptrfError) {
					case ISC_REMOTEFILE_ERROR_TIMEOUT:
						$error = GetLang('ProductImageCouldNotBeDownloadedTimeout');
						break;

					case ISC_REMOTEFILE_ERROR_HTTPERROR:
						$error = GetLang('ProductImageCouldNotBeDownloadedHttpError');
						break;

					case ISC_REMOTEFILE_ERROR_EMPTY:
						$error = GetLang('ProductImageCouldNotBeDownloadedEmpty');
						break;

					case ISC_REMOTEFILE_ERROR_DNSFAIL:
						$error = GetLang('ProductImageCouldNotBeDownloadedDns');
						break;

					default:
						$error = sprintf(GetLang('ProductImageCouldNotBeDownloadedOther'), $ptrfError);
						break;
				}
				$errors[] = array($originalUrl, $error);
				continue;
			}

			// to work correctly with the product image 'import' process the image must be a file, so save it to the cache directory temporarily
			while (true) {
				// we can name it .tmp because the extension will be corrected after the image type is detected
				$temporaryPath = ISC_CACHE_DIRECTORY . 'productimage_' . ISC_PRODUCT_IMAGE::randomString(16) . '.tmp';

				if (!file_exists($temporaryPath)) {
					break;
				}
			}

			$fh = @fopen($temporaryPath, 'wb');
			if (!$fh) {
				$errors[] = array($originalUrl, GetLang('ProductImageCacheWriteError'));
				continue;
			}

			if (!@fwrite($fh, $response)) {
				$errors[] = array($originalUrl, GetLang('ProductImageCacheWriteError'));
				@fclose($fh);
				continue;
			}

			@fclose($fh);

			// determine original filename based on request path
			$originalFilename = @$urlInfo['path'];
			if ($originalFilename) {
				$pathInfo = pathinfo($urlInfo['path']);
			}

			if ($originalFilename && $pathInfo['basename']) {
				$originalFilename = $pathInfo['basename'];
			} else {
				// if no original filename was specified (e.g.: the image was the result of a script like http://www.example.com/) then generate a random name for it
				// don't need an extension because importImage will add the correct one for the type of image it is
				$originalFilename = 'image' . ISC_PRODUCT_IMAGE::randomString(3);
			}

			try {
				$image = ISC_PRODUCT_IMAGE::importImage($temporaryPath, $originalFilename, $productId, $hash, true, $generateImages);
			} catch (ISC_PRODUCT_IMAGE_IMPORT_INVALIDIMAGEFILE_EXCEPTION $exception) {
				$errors[] = array($originalUrl, $exception->getMessage() . ' ' . GetLang('ProductImageInvalidFileFromWeb'));
				@unlink($temporaryPath);
				continue;
			} catch (ISC_PRODUCT_IMAGE_IMPORT_EXCEPTION $exception) {
				// these exceptions should have language-powered messages so are safe to return to the user
				$errors[] = array($originalUrl, $exception->getMessage());
				@unlink($temporaryPath);
				continue;
			} catch (Exception $exception) {
				// other unknown error
				$errors[] = array($originalUrl, GetLang('ProductImageProcessUnknownError'));
				@unlink($temporaryPath);
				continue;
			}

			// all done, add to list of successful images
			$images[] = $image;
		}
	}
Example #4
0
		private function BulkUpdateVariations()
		{
			$productId = 0;
			$vid = 0;
			$inv = 0;
			$useHash = false;

			if(isset($_GET['v']) && is_numeric($_GET['v']) && isset($_GET['inv']) && is_numeric($_GET['inv'])) {
				$vid = (int)$_GET['v'];
				$inv = (bool)$_GET['inv'];
			}

			if (isset($_GET['productId'])) {
				$productId = (int)$_GET['productId'];
			}

			if (isId($productId)) {
				$query = 'SELECT prodvariationid FROM [|PREFIX|]products WHERE productid = ' . $productId;
				$res = $GLOBALS['ISC_CLASS_DB']->Query($query);
				if ($row = $GLOBALS['ISC_CLASS_DB']->Fetch($res)) {
					if ($row['prodvariationid'] != $vid) {
						$useHash = true;
					}
				}
			}

			if (!empty($_GET['productHash'])) {
				$useHash = true;
				$productId = $GLOBALS['ISC_CLASS_DB']->Quote($_GET['productHash']);
			}

			if ($useHash) {
				$whereSQL = "vcproductid = 0 AND vcproducthash = '" . $productId . "' ";
			}
			else {
				$whereSQL = 'vcproductid = ' . $productId . ' ';
			}

			$filterOptions = array();
			if (isset($_GET['filterOptions'])) {
				parse_str($_GET['filterOptions'], $filterOptions);
			}

			// create the sql to update the filtered options
			$optionSQL = '';
			if (!empty($filterOptions)) {
				foreach ($filterOptions as $optionName => $optionValues) {
					$thisOptionSQL = '';
					foreach ($optionValues as $value) {
						if ($value == 'all') {
							continue;
						}

						if ($thisOptionSQL) {
							$thisOptionSQL .= ' OR ';
						}
						$thisOptionSQL .= "CONCAT(',', vcoptionids, ',') LIKE '%," . $value . ",%'";
					}

					if ($thisOptionSQL) {
						if ($optionSQL) {
							$optionSQL .= " AND ";
						}

						$optionSQL .= "(" . $thisOptionSQL . ")";
					}
				}
			}

			if ($optionSQL != '') {
				$optionSQL = ' AND ' . $optionSQL;
			}

			$updates = array();
			switch ($_GET['updatePurchaseable']) {
				case "reset":
				case "yes":
					$updates[] = "vcenabled = '1'";
					break;
				case "no":
					$updates[] = "vcenabled = '0'";
					break;
			}

			switch ($_GET['updatePriceDiff']) {
				case "reset":
					$updates[] = "vcpricediff = ''";
					$updates[] = "vcprice = 0";
					break;
				case "add":
				case "subtract":
				case "fixed":
					$updates[] = "vcpricediff = '" . $_GET['updatePriceDiff'] . "'";
					$updates[] = "vcprice = " . (float)$_GET['updatePrice'];
					break;
			}

			switch ($_GET['updateWeightDiff']) {
				case "reset":
					$updates[] = "vcweightdiff = ''";
					$updates[] = "vcweight = 0";
					break;
				case "add":
				case "subtract":
				case "fixed":
					$updates[] = "vcweightdiff = '" . $_GET['updateWeightDiff'] . "'";
					$updates[] = "vcweight = " . (float)$_GET['updateWeight'];
					break;
			}

			if ($inv) {
				if ($_GET['updateStockLevel'] != '') {
					$updates[] = 'vcstock = ' . (int)$_GET['updateStockLevel'];
				}

				if ($_GET['updateLowStockLevel'] != '') {
					$updates[] = 'vclowstock = ' . (int)$_GET['updateLowStockLevel'];
				}
			}

			// delete existing images?
			if (isset($_GET['updateDelImages'])) {
				// get distinct images not associated with variations that aren't in the current filter
				$query = '
					SELECT
						vcimagezoom,
						vcimagestd,
						vcimagethumb
					FROM
						[|PREFIX|]product_variation_combinations pvc
					WHERE
						' . $whereSQL .
						$optionSQL . '
					GROUP BY
						vcimagezoom
					HAVING
						COUNT(*) = (
									SELECT
										COUNT(*)
									FROM
										[|PREFIX|]product_variation_combinations pvc2
									WHERE
										pvc2.vcproductid = pvc.vcproductid AND
										pvc2.vcimagezoom = pvc.vcimagezoom
									)
				';

				$res = $GLOBALS['ISC_CLASS_DB']->Query($query);
				while ($row = $GLOBALS['ISC_CLASS_DB']->Fetch($res)) {
					GetClass('ISC_ADMIN_PRODUCT')->DeleteVariationImagesForRow($row);
				}

				$updates[] = "vcimage = ''";
				$updates[] = "vcimagezoom = ''";
				$updates[] = "vcimagestd = ''";
				$updates[] = "vcimagethumb = ''";
			}
			// import image
			elseif (isset($_FILES['updateImage'])) {
				try {
					$image = ISC_PRODUCT_IMAGE::importImage($_FILES['updateImage']['tmp_name'], $_FILES['updateImage']['name'], false, false, true, false);

					$zoom = $image->getResizedFilePath(ISC_PRODUCT_IMAGE_SIZE_ZOOM, true, false);
					$standard = $image->getResizedFilePath(ISC_PRODUCT_IMAGE_SIZE_STANDARD, true, false);
					$thumb = $image->getResizedFilePath(ISC_PRODUCT_IMAGE_SIZE_THUMBNAIL, true, false);

					$updates[] = "vcimage = '" . $image->getSourceFilePath() . "'";
					$updates[] = "vcimagezoom = '" . $zoom . "'";
					$updates[] = "vcimagestd = '" . $standard . "'";
					$updates[] = "vcimagethumb = '" . $thumb . "'";
				}
				catch (Exception $ex) {

				}
			}

			if (!empty($updates)) {
				$updates[] = "vclastmodified = " . time();

				$updateSQL = implode(', ', $updates);

				// update the combinations
				$query = 'UPDATE [|PREFIX|]product_variation_combinations SET ' . $updateSQL . ' WHERE ' . $whereSQL . $optionSQL;
				$GLOBALS['ISC_CLASS_DB']->Query($query);
			}

			// regenerate the combinations table to get fresh data
			$html = $this->GetVariationCombinationsTable($filterOptions, true);
			$response['tableData'] = $html;
			echo '<textarea>'.isc_json_encode($response).'</textarea>';
			exit;
		}
Example #5
0
	protected function _ImportRecord($record)
	{
		// the field we have chosen to identify the product
		$prodIdentField = $this->ImportSession['IdentField'];
		$identFieldName = $this->_ImportFields[$prodIdentField];

		// chosen ident field is empty, can't continue
		if (empty($prodIdentField)) {
			$this->addImportResult('Failures', GetLang('NoIdentField', array('identField' => $identFieldName)));
			return;
		}

		// get the product for this row
		$query = "SELECT * FROM [|PREFIX|]products WHERE " . $prodIdentField . " = '" . $GLOBALS['ISC_CLASS_DB']->Quote(trim($record[$prodIdentField])) . "'";
		$result = $GLOBALS['ISC_CLASS_DB']->Query($query);
		if (($prod = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) === false) {
			// no prod found? failrow
			$this->addImportResult('Failures', GetLang('AssociatedProductNotFound', array('identField' => $identFieldName, 'identValue' => $record[$prodIdentField])));
			return;
		}
		$productID = $prod['productid'];
		$variationID = $prod['prodvariationid'];

		//---- the fields we'll be updating the product with ----

		// variation code
		if (isset($record['prodvarsku'])) {
			$prodCode = $record['prodvarsku'];

			if (!empty($prodCode) || (empty($prodCode) && $this->ImportSession['DefaultForEmpty'])) {
				$updateFields['vcsku'] = $prodCode;
			}
		}
		elseif (isset($record['prodcode'])) {
			$prodCode = $record['prodcode'];

			if (!empty($prodCode) || (empty($prodCode) && $this->ImportSession['DefaultForEmpty'])) {
				$updateFields['vcsku'] = $prodCode;
			}
		}

		// variation price
		if (isset($record['prodvarprice'])) {
			$varPrice = $record['prodvarprice'];

			if (empty($varPrice) && $this->ImportSession['DefaultForEmpty']) {
				$updateFields['vcprice'] = 0;
				$updateFields['vcpricediff'] = '';
			}
			else {
				// prefixed by a + then it's a price addition
				if (isc_substr($varPrice, 0, 1) == "+") {
					$priceDiff = "add";
				} // price subtraction
				elseif ($varPrice < 0) {
					$priceDiff = "subtract";
				} // fixed price
				else {
					$priceDiff = "fixed";
				}

				$varPrice = abs((float)DefaultPriceFormat($varPrice));
				$updateFields['vcprice'] = $varPrice;
				$updateFields['vcpricediff'] = $priceDiff;
			}
		}

		// variation weight
		if (isset($record['prodvarweight'])) {
			$varWeight = $record['prodvarweight'];

			if (empty($varWeight) && $this->ImportSession['DefaultForEmpty']) {
				$updateFields['vcweight'] = 0;
				$updateFields['vcweightdiff'] = '';
			}
			elseif (!empty($record['prodvarweight'])) {
				// prefixed by a + then it's a weight addition
				if (isc_substr($varWeight, 0, 1) == "+") {
					$weightDiff = "add";
				} // weight subtraction
				elseif ($varWeight < 0) {
					$weightDiff = "subtract";
				} // fixed weight
				else {
					$weightDiff = "fixed";
				}

				$updateFields['vcweight'] = abs((float)$varWeight);
				$updateFields['vcweightdiff'] = $weightDiff;
			}
		}

		// stock level
		if (isset($record['prodvarstock'])) {
			if (empty($record['prodvarstock']) && $this->ImportSession['DefaultForEmpty']) {
				$updateFields['vcstock'] = 0;
			}
			else {
				$updateFields['vcstock'] = $record['prodvarstock'];
			}
		}

		// low stock level
		if (isset($record['prodvarlowstock'])) {
			if (empty($record['prodvarlowstock']) && $this->ImportSession['DefaultForEmpty']) {
				$updateFields['vclowstock'] = 0;
			}
			else {
				$updateFields['vclowstock'] = $record['prodvarlowstock'];
			}
		}

		// enable the option?
		if (isset($record['prodvarenabled'])) {
			if (empty($record['prodvarenabled']) && $this->ImportSession['DefaultForEmpty']) {
				$updateFields['vcenabled'] = 1;
			}
			else {
				$updateFields['vcenabled'] = $this->StringToYesNoInt($record['prodvarenabled']);
			}
		}
		else {
			// enable by default
			$updateFields['vcenabled'] = 1;
		}

		// variation image
		if(!empty($record['prodvarimage'])) {
			// code exists in the new product image management classes to handle these imports
			$imageFile = $record['prodvarimage'];
			$imageAdmin = new ISC_ADMIN_PRODUCT_IMAGE;
			$variationImage = false;

			// check if this image file (either remote or local) has already been processed on a previous variation, if
			// so, simply re-use those values instead of re-downloading / re-processing
			if (isset($this->ImportSession['ImportedImages'][$imageFile])) {
				$importedImage = $this->ImportSession['ImportedImages'][$imageFile];
				if (is_array($importedImage)) {
					$updateFields = array_merge($updateFields, $importedImage);
				}
			} else {
				$this->ImportSession['ImportedImages'][$imageFile] = false;

				if (preg_match('#^(?P<scheme>[a-zA-Z0-9\.]+)://#i', $imageFile, $matches)) {
					// the filename is an external URL, import it against the calcualted product hash
					$imageAdmin->importImagesFromUrls(false, array($imageFile), $importImages, $importImageErrors, false);

					if (!empty($importImages)) {
						$variationImage = $importImages[0];
					}

					if (!empty($importImageErrors)) {
						// as this import works on one file only and importImagesFromWebUrls creates one error per file, can simply tack on the new error
						$importImageError = $importImageErrors[0];
						if (is_array($importImageError)) {
							$this->addImportResult('Warnings', $importImageError[1]);
						} else {
							$this->addImportResult('Warnings', $importImageError);
						}
					}

				} else {
					// the filename is a local file
					$importImageFilePath = ISC_BASE_PATH . "/" . GetConfig('ImageDirectory') . "/import/" . $imageFile;

					try {
						$variationImage = ISC_PRODUCT_IMAGE::importImage($importImageFilePath, basename($importImageFilePath), false, false, false, false);
					} catch (ISC_PRODUCT_IMAGE_SOURCEFILEDOESNTEXIST_EXCEPTION $exception) {
						// exception message may contain server path; present filtered message and log the original
						$this->addImportResult('Warnings', GetLang('ProductImageFileDoesNotExist'));
						trigger_error($exception->getMessage(), E_WARNING);
					} catch (ISC_PRODUCT_IMAGE_IMPORT_CANTCREATEDIR_EXCEPTION $exception) {
						// exception message may contain server path; present filtered message and log the original
						$this->addImportResult('Warnings', GetLang('ImportProductImageFilePermissionIssue'));
						trigger_error($exception->getMessage(), E_WARNING);
					} catch (ISC_PRODUCT_IMAGE_IMPORT_CANTMOVEFILE_EXCEPTION $exception) {
						// exception message may contain server path; present filtered message and log the original
						$this->addImportResult('Warnings', GetLang('ImportProductImageFilePermissionIssue'));
						trigger_error($exception->getMessage(), E_WARNING);
					} catch (Exception $exception) {
						// other exceptions should be ok to present
						$this->addImportResult('Warnings', $exception->getMessage());
					}
				}

				if ($variationImage !== false) {
					try {
						$importedImage = array(
							'vcimage' => $variationImage->getSourceFilePath(),
							'vcimagezoom' => $variationImage->getResizedFilePath(ISC_PRODUCT_IMAGE_SIZE_ZOOM, true, false),
							'vcimagestd' => $variationImage->getResizedFilePath(ISC_PRODUCT_IMAGE_SIZE_STANDARD, true, false),
							'vcimagethumb' => $variationImage->getResizedFilePath(ISC_PRODUCT_IMAGE_SIZE_THUMBNAIL, true, false),
						);
						$updateFields = array_merge($updateFields, $importedImage);
						$this->ImportSession['ImportedImages'][$imageFile] = $importedImage;
					} catch (ISC_PRODUCT_IMAGE_SOURCEFILEDOESNTEXIST_EXCEPTION $exception) {
						// exception message may contain server path; present filtered message and log the original
						$this->addImportResult('Warnings', GetLang('ProductImageFileDoesNotExist'));
						trigger_error($exception->getMessage(), E_WARNING);
					} catch (ISC_PRODUCT_IMAGE_IMPORT_CANTCREATEDIR_EXCEPTION $exception) {
						// exception message may contain server path; present filtered message and log the original
						$this->addImportResult('Warnings', GetLang('ImportProductImageFilePermissionIssue'));
						trigger_error($exception->getMessage(), E_WARNING);
					} catch (ISC_PRODUCT_IMAGE_IMPORT_CANTMOVEFILE_EXCEPTION $exception) {
						// exception message may contain server path; present filtered message and log the original
						$this->addImportResult('Warnings', GetLang('ImportProductImageFilePermissionIssue'));
						trigger_error($exception->getMessage(), E_WARNING);
					} catch (Exception $exception) {
						// other exceptions should be ok to present
						$this->addImportResult('Warnings', $exception->getMessage());
					}
				}
			}
		}


		// get the index of the last matched field...we assume that all the remaining fields are variations
		$lastindex = 0;
		foreach ($this->ImportSession['FieldList'] as $field => $index) {
			if ($index > $lastindex) {
				$lastindex = $index;
			}
		}

		$variationStartIndex = $lastindex + 1;

		// get the variation fields
		$variationFields = array_slice($record['original_record'], $variationStartIndex);

		// split the variation fields into key => value pairs
		$variationData = array();
		foreach ($variationFields as $field) {
			$varField = explode(":", $field, 2);
			// ensure we have a key and value...otherwise bad field
			if (count($varField) != 2) {
				$this->addImportResult('Failures', GetLang('CantExtractData', array('dataField' => $field)));
				return;
			}

			$varName = trim($varField[0]);
			$varValue = trim($varField[1]);
			$variationData[$varName] = $varValue;
		}

		// ensure we actually have variation data
		if (empty($variationData)) {
			// generate a failure
			$this->addImportResult('Failures', GetLang('NoVariationData', array('rowNum' => $this->ImportSession['DoneCount'] + 1)));
			return;
		}

		// are we choosing to update an existing variation combination or replacing with a new variation?
		// make sure this isn't a variation we've created this session
		if ($this->ImportSession['UpdateExisting'] && $variationID > 0 && !isset($this->ImportSession['NewVariations'][$productID])){
			// find the variation options so we can find the combination
			$query = "
			SELECT
				voptionid,
				voname,
				vovalue
			FROM
				[|PREFIX|]product_variation_options
			WHERE
				vovariationid = " . $variationID . " AND (";

			$where = "";
			foreach ($variationData as $varName => $varValue) {
				if ($where) {
					$where .= " OR ";
				}
				$where .= "
					(
						voname = '" . $GLOBALS['ISC_CLASS_DB']->Quote($varName) . "' AND
						vovalue = '" . $GLOBALS['ISC_CLASS_DB']->Quote($varValue) . "'
					)
				";
			}

			$query .= $where . ") ORDER BY vooptionsort, vovaluesort";

			$result = $GLOBALS['ISC_CLASS_DB']->Query($query);
			$optionList = array();
			$notFoundOptions = $variationData;
			// get the option id's
			if ($GLOBALS['ISC_CLASS_DB']->CountResult($result)) {
				while ($optionRow = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
					$optionList[] = $optionRow['voptionid'];
					unset($notFoundOptions[$optionRow['voname']]);
				}
			}

			$updatedName = false;

			// create any remaining options
			if (!empty($notFoundOptions)) {
				foreach ($notFoundOptions as $varName => $varValue) {
					// find whether it's the option name or value that doesn't exist
					$query = "
						SELECT
							COUNT(*) AS valuecount,
							vooptionsort,
							voname
						FROM
							[|PREFIX|]product_variation_options
						WHERE
							vovariationid = " . $variationID . " AND
							voname = '" . $GLOBALS['ISC_CLASS_DB']->Quote($varName) . "'
						GROUP BY
							voname
					";
					$result = $GLOBALS['ISC_CLASS_DB']->Query($query);
					// name exists, just create a new value
					if ($option = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
						$newOption = array(
							'vovariationid' => $variationID,
							'voname'		=> $option['voname'],
							'vovalue'		=> $varValue,
							'vooptionsort'	=> $option['vooptionsort'],
							'vovaluesort'	=> $option['valuecount'] + 1
						);

						$optionID = $GLOBALS['ISC_CLASS_DB']->InsertQuery('product_variation_options', $newOption);
						array_splice($optionList, $option['vooptionsort'] - 1, 0, $optionID);

						// we have a new value, we have to create new combinations for each of the rows using the existing data

						// get existing combinations but exclude the option name that we're on
						if ($this->ImportSession['CreateAllCombos']) {
							$combinations = GetVariationCombinations($productID, $varName);
							foreach ($combinations as $combination) {
								$newCombination = $combination;
								// insert the option at correct position
								array_splice($newCombination, $option['vooptionsort'] - 1, 0, $optionID);
								$newOptionList = implode(',', $newCombination);

								// create combination
								$newCombo = array(
									'vcproductid'	=> $productID,
									'vcvariationid'	=> $variationID,
									'vcoptionids'	=> $newOptionList,
									'vcenabled'		=> 1
								);
								$GLOBALS['ISC_CLASS_DB']->InsertQuery('product_variation_combinations', $newCombo);
							}
						}
					}
					else {
						// name not found, create it with the option

						// get total option names
						$query = "SELECT COUNT(DISTINCT voname) AS optioncount FROM [|PREFIX|]product_variation_options WHERE vovariationid = " . $variationID;
						$result = $GLOBALS['ISC_CLASS_DB']->Query($query);
						$optioncount = $GLOBALS['ISC_CLASS_DB']->FetchOne($result, 'optioncount');

						$newOption = array(
							'vovariationid' => $variationID,
							'voname'		=> $varName,
							'vovalue'		=> $varValue,
							'vooptionsort'	=> $optioncount + 1,
							'vovaluesort'	=> 1
						);

						$optionID = $GLOBALS['ISC_CLASS_DB']->InsertQuery('product_variation_options', $newOption);
						array_splice($optionList, $optioncount, 0, $optionID);

						// we have a new option name, so append the new option id to existing combinations
						$query = "
							UPDATE
								[|PREFIX|]product_variation_combinations
							SET
								vcoptionids = CONCAT(vcoptionids, '," . $optionID . "')
							WHERE
								vcvariationid = " . $variationID;

						$GLOBALS['ISC_CLASS_DB']->Query($query);

						// update the variation option count
						$query = "UPDATE [|PREFIX|]product_variations SET vnumoptions = vnumoptions + 1 WHERE variationid = " . $variationID;
						$GLOBALS['ISC_CLASS_DB']->Query($query);
					}
				}
			}

			$optionString = implode(",", $optionList);

			// attempt to find existing combination again using list of options
			$query = "
				SELECT
					combinationid
				FROM
					[|PREFIX|]product_variation_combinations
				WHERE
					vcproductid = " . $productID . " AND
					vcvariationid = " . $variationID . " AND
					vcoptionids = '" . $optionString . "'
			";

			$result = $GLOBALS['ISC_CLASS_DB']->Query($query);

			// update the combination
			if ($comboID = $GLOBALS['ISC_CLASS_DB']->FetchOne($result, 'combinationid')) {
				$GLOBALS['ISC_CLASS_DB']->UpdateQuery('product_variation_combinations', $updateFields, 'combinationid = ' . $comboID);

				$this->addImportResult('Updates', $prod['prodname']);
			}
			else {
				// couldn't update an existing combo, create a new one

				$newCombo = array(
					'vcproductid'	=> $productID,
					'vcvariationid'	=> $variationID,
					'vcoptionids'	=> $optionString
				);

				$newCombo = $newCombo + $updateFields;

				$GLOBALS['ISC_CLASS_DB']->InsertQuery('product_variation_combinations', $newCombo);

				$this->ImportSession['Results']['SuccessCount']++;
			}
		}
		else {
			// create a new variation for this product

			// have we already created a variation for this product in this import session?
			if (isset($this->ImportSession['NewVariations'][$productID])) {
				// we only need to create our new options and the combinations

				// find any options previously created
				$thisVar = $this->ImportSession['NewVariations'][$productID];
				$thisOptions = array();
				$thisOptionsL = array();
				foreach ($variationData as $varName => $varValue) {
					if (isset($thisVar[isc_strtolower($varName)][isc_strtolower($varValue)])) {
						$thisOptions[$varName] = $thisVar[isc_strtolower($varName)][isc_strtolower($varValue)];
						$thisOptionsL[isc_strtolower($varName)] = $thisVar[isc_strtolower($varName)][isc_strtolower($varValue)];
					}
				}

				// create any remaining uncreated options
				$remainingOptions = array_diff_key($variationData, $thisOptions);
				if (!empty($remainingOptions)) {
					foreach ($remainingOptions as $varName => $varValue) {
						$lvarName = isc_strtolower($varName);
						// get the option and value sort numbers

						// does this option name exist, but just not the value?
						if (isset($thisVar[$lvarName])) {
							$keyIndex = array_search($lvarName, array_keys($thisVar));

							$optionSort = $keyIndex + 1;
							$valueSort = count($thisVar[$lvarName]) + 1;
						}
						else {
							$valueSort = 1;
							$optionSort = count($thisVar) + 1;
						}

						$insertOption = array(
							'vovariationid'	=> $variationID,
							'voname' 		=> $varName,
							'vovalue'		=> $varValue,
							'vooptionsort'	=> $optionSort,
							'vovaluesort'	=> $valueSort
						);

						$optionID = $GLOBALS['ISC_CLASS_DB']->InsertQuery('product_variation_options', $insertOption);
						// add this new option to the list
						$thisVar[$lvarName][isc_strtolower($varValue)] = $optionID;
						$thisOptionsL[$lvarName] = $optionID;

						// is it a new option name?
						if (!$thisVar[isc_strtolower($varName)]) {
							// we have a new option name, so append the new option id to existing combinations
							$query = "
								UPDATE
									[|PREFIX|]product_variation_combinations
								SET
									vcoptionids = CONCAT(vcoptionids, '," . $optionID . "')
								WHERE
									vcvariationid = " . $variationID;

							$GLOBALS['ISC_CLASS_DB']->Query($query);
						}
					}
				}

				// store options back in session
				$this->ImportSession['NewVariations'][$productID] = $thisVar;

				// get the option ids for this combination. they must be in the order that the option names were created.
				$comboRows = array(array());
				foreach ($thisVar as $varName => $varData) {
					// is there an option that may have already been created but is missing for this record?
					if (isset($thisOptionsL[$varName])) {
						foreach ($comboRows as &$combo) {
							$combo[] = $thisOptionsL[$varName];
						}
					}
					else {
						$newRows = array();
						// missing option, iterate through all values for that option and create combinations
						foreach ($comboRows as $combo) {
							foreach ($varData as $varValue => $optionID) {
								$newRow = $combo;
								$newRow[] = $optionID;
								$newRows[] = $newRow;
							}
						}
						$comboRows = $newRows;
					}
				}

				// insert all our combinations
				foreach ($comboRows as $thisCombo) {
					$optionString = implode(",", $thisCombo);

					// now we can finally create the combination
					$newCombo = array(
						'vcproductid'	=> $prod['productid'],
						'vcvariationid'	=> $variationID,
						'vcoptionids'	=> $optionString
					);

					$newCombo = $newCombo + $updateFields;

					$GLOBALS['ISC_CLASS_DB']->InsertQuery('product_variation_combinations', $newCombo);
				}

				$this->ImportSession['Results']['SuccessCount']++;
			}
			else {
				// do we have an existing combinations for this product? we should delete any combinations for that first
				if ($variationID) {
					$GLOBALS['ISC_CLASS_DB']->DeleteQuery('product_variation_combinations', 'WHERE vcproductid = ' . $productID);
				}

				// name of our new variation .. check if it already exists
				$variationName = $prod['prodname'] . " Variations " . date('dmy');
				$query = "SELECT variationid FROM [|PREFIX|]product_variations WHERE vname = '" . $GLOBALS['ISC_CLASS_DB']->Quote($variationName) . "'";
				$result = $GLOBALS['ISC_CLASS_DB']->Query($query);
				if ($varRow = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
					// delete the old variation
					$GLOBALS['ISC_CLASS_DB']->DeleteQuery('product_variations', 'WHERE variationid = ' . $varRow['variationid']);
					$GLOBALS['ISC_CLASS_DB']->DeleteQuery('product_variation_options', 'WHERE vovariationid = ' . $varRow['variationid']);
					$GLOBALS['ISC_CLASS_DB']->DeleteQuery('product_variation_combinations', 'WHERE vcvariationid = ' . $varRow['variationid']);

					// update products that use this variation
					$updateProd = array(
						'prodvariationid' => 0
					);
					$GLOBALS['ISC_CLASS_DB']->UpdateQuery('products', $updateProd, 'prodvariationid = ' . $varRow['variationid']);
				}


				// create our new variation first
				$newVariation = array(
					'vname'		=> $variationName,
					'vvendorid' => $prod['prodvendorid']
				);

				$variationID = $GLOBALS['ISC_CLASS_DB']->InsertQuery('product_variations', $newVariation);

				$this->ImportSession['NewVariationIDs'][$productID] = $variationID;

				// update our product with the variation ID
				$updateProd = array(
					'prodvariationid' => $variationID
				);
				$GLOBALS['ISC_CLASS_DB']->UpdateQuery('products', $updateProd, 'productid = ' . $productID);

				$thisVar = array();
				$options = array();

				// now to create the options
				$optionCount = 0;
				foreach ($variationData as $varName => $varValue) {
					$newOption = array(
						'vovariationid'	=> $variationID,
						'voname'		=> $varName,
						'vovalue'		=> $varValue,
						'vooptionsort'	=> ++$optionCount,
						'vovaluesort'	=> 1
					);

					$optionID = $GLOBALS['ISC_CLASS_DB']->InsertQuery('product_variation_options', $newOption);

					$thisVar[isc_strtolower($varName)][isc_strtolower($varValue)] = $optionID;
					$options[] = $optionID;
				}

				$this->ImportSession['NewVariations'][$productID] = $thisVar;

				// create the combination
				$optionString = implode(",", $options);

				$newCombo = array(
					'vcproductid'	=> $productID,
					'vcvariationid'	=> $variationID,
					'vcoptionids'	=> $optionString
				);

				$newCombo = $newCombo + $updateFields;

				$GLOBALS['ISC_CLASS_DB']->InsertQuery('product_variation_combinations', $newCombo);

				$this->ImportSession['Results']['SuccessCount']++;
			}
		}

		// a stock or low stock is supplied, enable inventory tracking for the product
		if (!empty($record['prodvarstock']) || !empty($record['prodvarlowstock'])) {
			$updateProd = array(
				'prodinvtrack' => 2
			);

			$GLOBALS['ISC_CLASS_DB']->UpdateQuery('products', $updateProd, 'productid = ' . $productID);
		}

		/**
		 * This is a bit hackish but we need to update the product last modified time WITHOUT using the
		 * product entity class (shock horror). This is because we have nothing else to update it with
		 */
		$savedata = array(
			"prodlastmodified" => time()
		);

		$GLOBALS["ISC_CLASS_DB"]->UpdateQuery("products", $savedata, "productid = " . $productID);
	}