/** * Imports an actual product record in to the database. * * @param array Array of record data */ protected function _ImportRecord($record) { if(empty($record['prodname'])) { $this->ImportSession['Results']['Failures'][] = implode(",", $record['original_record'])." ".GetLang('ImportProductsMissingName'); return; } if ($message = strtokenize($_REQUEST, '#')) { $this->ImportSession['Results']['Failures'][] = implode(",", $record['original_record'])." ".GetLang(B('UmVhY2hlZFByb2R1Y3RMaW1pdA==')); return; } $record = $this->normalizeInventoryTracking($record); $productHash = uniqid('IMPORT', true); $productId = 0; $hasThumb = false; $productFiles = array(); $productImages = array(); $existing = null; $isOverrideDuplicates = !empty($this->ImportSession['OverrideDuplicates']); $dupeCheckWhere = ''; // Is there an existing product with this product ID ? if (!empty($record['productid'])) { $query = "SELECT * FROM [|PREFIX|]products WHERE productid = " . (int)$record['productid']; $result = $GLOBALS["ISC_CLASS_DB"]->Query($query); if($existing = $GLOBALS["ISC_CLASS_DB"]->Fetch($result)) { // Overriding existing products, set the product id if($isOverrideDuplicates) { $productId = $existing['productid']; $this->addImportResult('Updates', $record['prodname']); } else { // a product was found, but we're not updating existing record: skip $this->addImportResult('Duplicates', $record['prodname']); return; } // merge existing product details with the incoming record $record = $this->mergeExistingRecord($record, $existing); } else { // no product for this id was found, skip $this->addImportResult('Failures', $record['productid'] . " " . GetLang('ImportProductNotFound')); return; } $dupeCheckWhere = " AND productid != " . (int)$record['productid']; } // Check if there is a different product with the same name $query = "SELECT * FROM [|PREFIX|]products WHERE prodname = '" . $GLOBALS['ISC_CLASS_DB']->Quote($record['prodname']) . "'" . $dupeCheckWhere; $result = $GLOBALS["ISC_CLASS_DB"]->Query($query); $differentProductWithSameName = $GLOBALS['ISC_CLASS_DB']->Fetch($result); if($differentProductWithSameName) { if($existing || !$isOverrideDuplicates) { $this->addImportResult('Duplicates', $record['prodname']); return; } $existing = $differentProductWithSameName; $productId = $existing['productid']; $this->addImportResult('Updates', $record['prodname']); $record = $this->mergeExistingRecord($record, $existing); } // Apply any default data $defaults = array( 'prodprice' => 0, 'prodcostprice' => 0, 'prodretailprice' => 0, 'prodsaleprice' => 0, 'prodweight' => 0, 'prodheight' => 0, 'prodwidth' => 0, 'proddepth' => 0, 'prodsearchkeywords' => '', 'prodsortorder' => 0, 'prodvisible' => 1, 'prodfeatured' => 0, 'prodrelatedproducts' => '-1', 'prodoptionsrequired' => 0, 'prodfreeshipping' => 0, 'prodlayoutfile' => '', 'prodtags' => '', 'prodcondition' => 'New', 'prodshowcondition' => 0, 'prodallowpurchases' => 1, 'prodeventdaterequired' => 0, 'prodeventdatefieldname' => '', 'prodeventdatelimited' => 0, 'prodeventdatelimitedtype' => 0, 'prodeventdatelimitedstartdate' => 0, 'prodeventdatelimitedenddate' => 0, 'prodbrandid' => 0, 'tax_class_name' => '', 'upc' => '', 'category' => null, ); $record += $defaults; // check validity of price columns $priceFields = array( 'prodprice', 'prodcostprice', 'prodsaleprice', 'prodretailprice' ); foreach ($priceFields as $field) { // price was invalid if (!IsPrice($record[$field])) { if ($productId) { // using existing price $record[$field] = $existing[$field]; } else { $record[$field] = 0; } $this->addImportResult('Warnings', $record['prodname']." ".GetLang('ImportProductInvalidPrice')); } } // Do we have a product file? $productFiles = array(); if (!$this->ImportSession['IsBulkEdit']) { if (!empty($record['prodfile'])) { $productFile = $this->_ImportFile($record); if ($productFile) { $productFiles[] = $productFile; } } } else { // bulk import files for ($x = 1; $x <= $this->ImportSession['MultiFieldCount']['files']; $x++) { if (empty($record['prodfile' . $x])) { continue; } $productFile = $this->_ImportFile($record, $x); if ($productFile) { $productFiles[] = $productFile; } } } // Do we have an image? $productImages = array(); if (!$this->ImportSession['IsBulkEdit']) { if(!empty($record['prodimagefile'])) { $importedImage = $this->_ImportImage($productId, $record); if ($importedImage) { $productImages[] = $importedImage; } } } else { // bulk import images for ($x = 1; $x <= $this->ImportSession['MultiFieldCount']['images']; $x++) { if (empty($record['prodimagefile' . $x])) { if (empty($record['prodimageid' . $x])) { continue; } // image file is empty but an ID was supplied, we should delete the image if ($productId) { try { $image = new ISC_PRODUCT_IMAGE($record['prodimageid' . $x]); // ensure this image is associated with this product if ($image->getProductId() == $productId) { $image->delete(); } } catch (Exception $ex) { } } continue; } $importedImage = $this->_ImportImage($productId, $record, $x); if ($importedImage) { $productImages[] = $importedImage; } } } // a category is not required if we have an existing record and ignore blanks is enabled $requireCatsField = !(!empty($record['productid']) && $this->ignoreBlankFields()); $cats = $this->getImportRecordCategories($record); if($requireCatsField && empty($cats)) { $this->addImportResult('Failures', implode(",", $record['original_record'])." ".GetLang('ImportProductsMissingCategory')); return; } // If there's a tax class, we need to fetch it now $record['tax_class_id'] = 0; if(!empty($record['tax_class_name'])) { static $taxClassCache = array(); if(!isset($taxClassCache[$record['tax_class_name']])) { $query = " SELECT id FROM [|PREFIX|]tax_classes WHERE name='".$GLOBALS['ISC_CLASS_DB']->quote($record['tax_class_name'])."' "; $taxClassCache[$record['tax_class_name']] = $GLOBALS['ISC_CLASS_DB']->fetchOne($query); } // Still don't have a matching tax class? Must be new. if(!$taxClassCache[$record['tax_class_name']]) { $newTaxClass = array( 'name' => $record['tax_class_name'] ); $taxClassCache[$record['tax_class_name']] = $GLOBALS['ISC_CLASS_DB']->insertQuery('tax_classes', $newTaxClass); } $record['tax_class_id'] = $taxClassCache[$record['tax_class_name']]; } // check the condition is valid $validConditions = array('new', 'used', 'refurbished'); if (!isset($record['prodcondition']) || !in_array(isc_strtolower($record['prodcondition']), $validConditions)) { $record['prodcondition'] = 'New'; } // Does the brand already exist? if(isset($record['brandname']) && $record['brandname'] != '') { $query = sprintf("select brandid from [|PREFIX|]brands where brandname='%s'", $GLOBALS['ISC_CLASS_DB']->Quote($record['brandname'])); $result = $GLOBALS['ISC_CLASS_DB']->Query($query); if($row = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) { $brandId = $row['brandid']; } // Create new brand else { // do we have permission to create brands? if ($GLOBALS["ISC_CLASS_ADMIN_AUTH"]->HasPermission(AUTH_Add_Brands)) { $newBrand = array( "brandname" => $record['brandname'] ); $brandId = $GLOBALS['ISC_CLASS_DB']->InsertQuery("brands", $newBrand); } else { // no brand creation permission, abort this record $this->addImportResult('Failures', $record['prodname'] . " " . GetLang('ImportNoPermissionCreateBrand')); return; } } $record['prodbrandid'] = $brandId; } else if(!$this->ignoreBlankFields()){ $record['prodbrandid'] = 0; } if (isset($record['prodfile']) && $record['prodfile'] != '') { $productType = 2; } else if (isset($existing['prodtype']) && isId($existing['prodtype'])) { $productType = (int)$existing['prodtype']; } else { $productType = 1; } // event date $record['prodeventdaterequired'] = $this->StringToYesNoInt($record['prodeventdaterequired']); if ($record['prodeventdaterequired']) { // we must have an event name if (empty($record['prodeventdatefieldname'])) { $record['prodeventdaterequired'] = 0; $this->addImportResult('Warnings', $record['prodname'] . ' ' . GetLang('ImportNoEventDateName')); } else { $record['prodeventdatelimited'] = $this->StringToYesNoInt($record['prodeventdatelimited']); if ($record['prodeventdatelimited']) { if (!empty($record['prodeventdatelimitedstartdate'])) { $record['prodeventdatelimitedstartdate'] = (int)@ConvertDateToTime($record['prodeventdatelimitedstartdate']); } if (!empty($record['prodeventdatelimitedenddate'])) { $record['prodeventdatelimitedenddate'] = (int)@ConvertDateToTime($record['prodeventdatelimitedenddate']); } // determine what type of event date it is if ($record['prodeventdatelimitedstartdate'] > 0 && $record['prodeventdatelimitedenddate'] == 0) { $record['prodeventdatelimitedtype'] = 2; // start date } elseif ($record['prodeventdatelimitedstartdate'] == 0 && $record['prodeventdatelimitedenddate'] > 0) { $record['prodeventdatelimitedtype'] = 3; // end date } elseif ($record['prodeventdatelimitedenddate'] > $record['prodeventdatelimitedstartdate']) { $record['prodeventdatelimitedtype'] = 1; // date range } else { $record['prodeventdatelimited'] = 0; $this->addImportResults('Warnings', $record['prodname'] . ' ' . GetLang('ImportEventDateInvalid')); } } } } // Verify the inventory tracking method is valid. if($record['prodinvtrack'] == 2 && !($existing && $existing['prodvariationid'])) { $this->addImportResult('Warnings', $record['prodname'] . ' ' . GetLang('ImportProductTrackInventoryNoVariations')); $record['prodinvtrack'] = $existing['prodinvtrack']; } // This is our product $productData = array( "prodname" => $record['prodname'], "prodcode" => @$record['prodcode'], "proddesc" => @$record['proddesc'], "prodsearchkeywords" => @$record['prodsearchkeywords'], "prodtype" => $productType, "prodprice" => DefaultPriceFormat($record['prodprice']), "prodcostprice" => DefaultPriceFormat($record['prodcostprice']), "prodretailprice" => DefaultPriceFormat($record['prodretailprice']), "prodsaleprice" => DefaultPriceFormat($record['prodsaleprice']), "prodavailability" => @$record['prodavailability'], "prodsortorder" => $record['prodsortorder'], "prodvisible" => (int)$record['prodvisible'], "prodfeatured" => $record['prodfeatured'], "prodrelatedproducts" => $record['prodrelatedproducts'], "prodinvtrack" => (int)@$record['prodinvtrack'], "prodcurrentinv" => (int)@$record['prodcurrentinv'], "prodlowinv" => (int)@$record['prodlowinv'], "prodoptionsrequired" => $record['prodoptionsrequired'], "prodwarranty" => @$record['prodwarranty'], "prodheight" => DefaultDimensionFormat(@$record['prodheight']), "prodweight" => DefaultDimensionFormat(@$record['prodweight']), "prodwidth" => DefaultDimensionFormat(@$record['prodwidth']), "proddepth" => DefaultDimensionFormat(@$record['proddepth']), "prodfreeshipping" => (int)$record['prodfreeshipping'], "prodfixedshippingcost" => DefaultPriceFormat(@$record['prodfixedshippingcost']), "prodbrandid" => (int)$record['prodbrandid'], "prodcats" => $cats, "prodpagetitle" => @$record['prodpagetitle'], "prodmetakeywords" => @$record['prodmetakeywords'], "prodmetadesc" => @$record['prodmetadesc'], "prodlayoutfile" => $record['prodlayoutfile'], 'prodtags' => $record['prodtags'], 'prodmyobasset' => '', 'prodmyobincome' => '', 'prodmyobexpense' => '', 'prodpeachtreegl' => '', 'prodcondition' => $record['prodcondition'], 'prodshowcondition' => (bool)$record['prodshowcondition'], 'prodallowpurchases' => (bool)$record['prodallowpurchases'], 'prodeventdaterequired' => $record['prodeventdaterequired'], 'prodeventdatefieldname' => $record['prodeventdatefieldname'], 'prodeventdatelimited' => $record['prodeventdatelimited'], 'prodeventdatelimitedtype' => $record['prodeventdatelimitedtype'], 'prodeventdatelimitedstartdate' => $record['prodeventdatelimitedstartdate'], 'prodeventdatelimitedenddate' => $record['prodeventdatelimitedenddate'], 'tax_class_id' => $record['tax_class_id'], 'upc' => $record['upc'], 'last_import' => $this->ImportSession['StartTime'], ); /** * The variation is part of the product record, so it will have to be attached to the record if this is an * update AND the existing product already has a variation */ if (isset($existing) && is_array($existing) && isId($existing['prodvariationid'])) { $productData['prodvariationid'] = $existing['prodvariationid']; } $empty = array(); // Save it $err = ''; if (!$GLOBALS['ISC_CLASS_ADMIN_PRODUCT']->_CommitProduct($productId, $productData, $empty, $empty, $empty, $err, $empty, true)) { $this->addImportResult('Failures', $record['prodname'] . " " . GetLang('ImportDatabaseError')); return; } if($productId == 0) { $productId = $GLOBALS['NewProductId']; } // Post process images $existingImages = new ISC_PRODUCT_IMAGE_ITERATOR("SELECT * FROM `[|PREFIX|]product_images` WHERE imageprodid = " . (int)$productId); $maxSort = count($existingImages); if ($this->ImportSession['DeleteImages']) { foreach ($existingImages as $existingImage) { $existingImage->delete(false); } $maxSort = 0; } if(!empty($productImages)) { // sort the images usort($productImages, array($this, "_compare_images")); // update our images with the product id foreach ($productImages as $image) { $image->setProductId($productId); // ensure that an image doesn't have a sort set higher than max, or if no sort specified, then also set it to the highest. if ($image->getSort() > $maxSort || $image->getSort() === null) { $image->setSort($maxSort); $maxSort++; } $image->saveToDatabase(false); } } // Delete existing files if ($this->ImportSession['DeleteDownloads']) { $query = " SELECT * FROM [|PREFIX|]product_downloads WHERE productid = " . $productId; $result = $GLOBALS['ISC_CLASS_DB']->Query($query); while ($download = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) { // Remove the file from the file system @unlink(GetConfig('DownloadDirectory') . "/" . $download['downfile']); // Delete from the database $GLOBALS['ISC_CLASS_DB']->DeleteQuery('product_downloads', 'WHERE downloadid = ' . $download['downloadid']); } } // Process product files if(!empty($productFiles)) { foreach($productFiles as $file) { $file['productid'] = $productId; $GLOBALS['ISC_CLASS_DB']->InsertQuery("product_downloads", $file); } } ++$this->ImportSession['Results']['SuccessCount']; }
/** * Takes a product id and product image id and modifies the sorting values of all affected product images to "move this image after another image" * * @param ISC_ADMIN_REMOTE $remote */ public function remoteMoveImageAfterOtherImage(ISC_ADMIN_REMOTE $remote) { // this method is used instead of simply receiving a full serialize of the new product order, it allows us to update more efficiently by knowing which image was moved and only updating the affected sort orders $response = array(); $productId = false; $productHash = false; if (isset($_POST['product'])) { $productId = (int)@$_POST['product']; if (!isId($productId) || !ProductExists($productId)) { $response[] = $remote->MakeXMLTag('error', GetLang('ProductDoesntExist'), true); } else if (!$GLOBALS["ISC_CLASS_ADMIN_AUTH"]->HasPermission(AUTH_Edit_Products)) { $response[] = $remote->MakeXMLTag('error', GetLang('Unauthorized'), true); } } else if (isset($_POST['hash']) && $_POST['hash']) { $productHash = $_POST['hash']; if (!$GLOBALS["ISC_CLASS_ADMIN_AUTH"]->HasPermission(AUTH_Create_Product)) { $response[] = $remote->MakeXMLTag('error', GetLang('Unauthorized'), true); } } else { $response[] = $remote->MakeXMLTag('error', GetLang('ProductDoesntExist'), true); } if (!empty($response)) { $remote->SendXMLHeader(); $remote->SendXMLResponse($response); die(); } $moveId = (int)$_POST['move']; try { $moveImage = new ISC_PRODUCT_IMAGE($moveId); } catch (ISC_PRODUCT_IMAGE_INVALIDID_EXCEPTION $e) { $response[] = $remote->MakeXMLTag('error', sprintf(GetLang('ProductImageInvalidId'), $moveId), true); } catch (ISC_PRODUCT_IMAGE_RECORDNOTFOUND_EXCEPTION $e) { $response[] = $remote->MakeXMLTag('error', sprintf(GetLang('ProductImageNotFound'), $moveId), true); } catch (Exception $e) { $response[] = $remote->MakeXMLTag('error', GetLang('ProductImageMoveDatabaseError'), true); } if (!empty($response)) { $remote->SendXMLHeader(); $remote->SendXMLResponse($response); die(); } $moveSort = $moveImage->getSort(); if ($productId && $moveImage->getProductId() !== $productId || $productHash && $moveImage->getProductHash() !== $productHash) { // provided image id does not belong to provided product id $response[] = $remote->MakeXMLTag('error', sprintf(GetLang('ProductImageMismatchError'), $moveId, $productId), true); $remote->SendXMLHeader(); $remote->SendXMLResponse($response); die(); } if (isset($_POST['after'])) { $afterId = (int)$_POST['after']; try { $afterImage = new ISC_PRODUCT_IMAGE($afterId); } catch (ISC_PRODUCT_IMAGE_INVALIDID_EXCEPTION $e) { $response[] = $remote->MakeXMLTag('error', sprintf(GetLang('ProductImageInvalidId'), $afterId), true); $remote->SendXMLHeader(); $remote->SendXMLResponse($response); die(); } catch (ISC_PRODUCT_IMAGE_RECORDNOTFOUND_EXCEPTION $e) { $response[] = $remote->MakeXMLTag('error', sprintf(GetLang('ProductImageNotFound'), $afterId), true); $remote->SendXMLHeader(); $remote->SendXMLResponse($response); die(); } catch (Exception $e) { $response[] = $remote->MakeXMLTag('error', GetLang('ProductImageMoveDatabaseError'), true); $remote->SendXMLHeader(); $remote->SendXMLResponse($response); die(); } if ($productId && $afterImage->getProductId() !== $productId || $productHash && $afterImage->getProductHash() !== $productHash) { // provided image id does not belong to provided product id $response[] = $remote->MakeXMLTag('error', sprintf(GetLang('ProductImageMismatchError'), $afterId, $productId), true); $remote->SendXMLHeader(); $remote->SendXMLResponse($response); die(); } $afterSort = $afterImage->getSort(); } else { $after = false; $afterSort = -1; } if ($moveImage->getProductHash()) { if (!$GLOBALS["ISC_CLASS_ADMIN_AUTH"]->HasPermission(AUTH_Create_Product)) { $response[] = GetLang('Unauthorized'); $remote->SendXMLHeader(); $remote->SendXMLResponse($response); die(); } } else { if (!$GLOBALS["ISC_CLASS_ADMIN_AUTH"]->HasPermission(AUTH_Edit_Products)) { $response[] = GetLang('Unauthorized'); $remote->SendXMLHeader(); $remote->SendXMLResponse($response); die(); } } // create an sql query to shift all sorting values between the two anchor points if ($moveSort > $afterSort) { $sql = "UPDATE `[|PREFIX|]product_images` SET imagesort = imagesort + 1 WHERE imageprodid = " . $moveImage->getProductId() . " AND imagesort > " . $afterSort . " AND imagesort < " . $moveSort; $newSort = $afterSort + 1; } else { $sql = "UPDATE `[|PREFIX|]product_images` SET imagesort = imagesort - 1 WHERE imageprodid = " . $moveImage->getProductId() . " AND imagesort > " . $moveSort . " AND imagesort <= " . $afterSort; $newSort = $afterSort; } $db = $GLOBALS['ISC_CLASS_DB']; $db->Query("SET autocommit = 0"); $db->Query("LOCK TABLES `[|PREFIX|]product_images` WRITE"); $result = $db->Query($sql); if ($result) { $moveImage->setSort($newSort); try { $moveImage->saveToDatabase(false); $db->Query("COMMIT"); $response[] = $remote->MakeXMLTag('success', GetLang('ProductImagesSortOrderChanged'), true); } catch (Exception $e) { $db->Query("ROLLBACK"); $response[] = $remote->MakeXMLTag('success', GetLang('ProductImageMoveDatabaseError'), true); } $db->Query("UNLOCK TABLES"); } else { $db->Query("ROLLBACK"); $db->Query("UNLOCK TABLES"); $response[] = $remote->MakeXMLTag('success', GetLang('ProductImageMoveDatabaseError'), true); } $remote->SendXMLHeader(); $remote->SendXMLResponse($response); die(); }