PageRenderTime 25ms CodeModel.GetById 36ms RepoModel.GetById 1ms app.codeStats 1ms

/wp-content/plugins/wp-lister-for-ebay/classes/model/ItemBuilderModel.php

https://bitbucket.org/sanders_nick/my-maxi-skirt
PHP | 1007 lines | 554 code | 241 blank | 212 comment | 145 complexity | 78977e63961ee9811245cdaa559e61a6 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-1.0, GPL-3.0, LGPL-2.1
  1. <?php
  2. /**
  3. * ItemBuilderModel class
  4. *
  5. * responsible for building listing items
  6. *
  7. */
  8. class ItemBuilderModel extends WPL_Model {
  9. // var $_session;
  10. // var $_cs;
  11. var $variationAttributes = array();
  12. var $result = false;
  13. function ItemBuilderModel()
  14. {
  15. global $wpl_logger;
  16. $this->logger = &$wpl_logger;
  17. // provide listings model
  18. $this->lm = new ListingsModel();
  19. }
  20. function buildItem( $id, $session, $isFixedPriceItem = false, $reviseItem = false )
  21. {
  22. // fetch record from db
  23. $p = $this->lm->getItem( $id );
  24. $profile_details = $p['profile_data']['details'];
  25. $images = $this->getProductImagesURL( $p['post_id'] );
  26. $main_image = $this->getProductMainImageURL( $p['post_id'] );
  27. $product_sku = ProductWrapper::getSKU( $p['post_id'] );
  28. if ( trim($product_sku) == '' ) $product_sku = false;
  29. $hasVariations = ProductWrapper::hasVariations( $p['post_id'] );
  30. // price has been calculated when applying the profile
  31. $start_price = $p['price'];
  32. // build item
  33. $item = new ItemType();
  34. // Set Listing Properties
  35. $item->ListingDuration = $p['listing_duration'];
  36. $item->Quantity = $p['quantity'];
  37. // omit ListingType when revising item
  38. if ( ! $reviseItem ) {
  39. $item->ListingType = $p['auction_type'];
  40. }
  41. // Set the Listing Starting Price and Buy It Now Price
  42. $item->StartPrice = new AmountType();
  43. $item->StartPrice->setTypeValue( $start_price );
  44. $item->StartPrice->setTypeAttribute('currencyID', $profile_details['currency'] );
  45. // optional BuyItNow price
  46. if ( intval($profile_details['fixed_price']) != 0) {
  47. $buynow_price = $this->lm->applyProfilePrice( $p['price'], $profile_details['fixed_price'] );
  48. $item->BuyItNowPrice = new AmountType();
  49. $item->BuyItNowPrice->setTypeValue( $buynow_price );
  50. $item->BuyItNowPrice->setTypeAttribute('currencyID', $profile_details['currency'] );
  51. }
  52. // Set the Item Title
  53. $item->Title = $this->prepareTitle( $p['auction_title'] );
  54. // SKU - omit if empty
  55. if ($product_sku) $item->SKU = $product_sku;
  56. // handle product image
  57. $item->PictureDetails = new PictureDetailsType();
  58. $item->PictureDetails->addPictureURL( str_replace(' ', '%20', $main_image ) );
  59. if ( $profile_details['with_gallery_image'] ) $item->PictureDetails->GalleryType = 'Gallery';
  60. // handle VAT (percent)
  61. if ( $profile_details['tax_mode'] == 'fix' ) {
  62. $item->VATDetails = new VATDetailsType();
  63. $item->VATDetails->VATPercent = $profile_details['vat_percent'];
  64. }
  65. // use Sales Tax Table
  66. if ( $profile_details['tax_mode'] == 'ebay_table' ) {
  67. $item->UseTaxTable = true;
  68. }
  69. // Set Local Info
  70. $item->Currency = $profile_details['currency'];
  71. $item->Country = $profile_details['country'];
  72. $item->Location = $profile_details['location'];
  73. $item->DispatchTimeMax = $profile_details['dispatch_time'];
  74. $item->ConditionID = $profile_details['condition_id'];
  75. // postal code
  76. if ( $profile_details['postcode'] != '' ) {
  77. $item->PostalCode = $profile_details['postcode'];
  78. }
  79. // set eBay site from global site iD
  80. // http://developer.ebay.com/DevZone/XML/docs/Reference/eBay/types/SiteCodeType.html
  81. $site_id = $session->getSiteId();
  82. $sites = EbayController::getEbaySites();
  83. $site_name = $sites[$site_id];
  84. $item->Site = $site_name;
  85. #$item->setSubTitle('Brandneuer iPod Mini!');
  86. #$item->setListingEnhancement('Highlight');
  87. $item->setHitCounter( $profile_details['counter_style'] );
  88. // ReturnPolicy
  89. $item->ReturnPolicy = new ReturnPolicyType();
  90. if ( $profile_details['returns_accepted'] == 1 ) {
  91. $item->ReturnPolicy->ReturnsAcceptedOption = 'ReturnsAccepted';
  92. $item->ReturnPolicy->ReturnsWithinOption = $profile_details['returns_within'];
  93. $item->ReturnPolicy->Description = stripslashes( $profile_details['returns_description'] );
  94. } else {
  95. $item->ReturnPolicy->ReturnsAcceptedOption = 'ReturnsNotAccepted';
  96. }
  97. // Set Payment Methods
  98. // $item->PaymentMethods[] = 'PersonalCheck';
  99. // $item->PaymentMethods[] = 'PayPal';
  100. // $item->PayPalEmailAddress = 'youraccount@yourcompany.com';
  101. foreach ( $profile_details['payment_options'] as $payment_method ) {
  102. # BuyerPaymentMethodCodeType
  103. $item->addPaymentMethods( $payment_method['payment_name'] );
  104. if ( $payment_method['payment_name'] == 'PayPal' ) {
  105. $item->PayPalEmailAddress = get_option( 'wplister_paypal_email' );
  106. }
  107. }
  108. // add subtitle if enabled
  109. if ( @$profile_details['subtitle_enabled'] == 1 ) {
  110. // check for custom subtitle from profile
  111. $subtitle = @$profile_details['custom_subtitle'];
  112. // if empty use product excerpt
  113. if ( $subtitle == '' ) {
  114. $the_post = get_post( $p['post_id'] );
  115. $subtitle = strip_tags( $the_post->post_excerpt );
  116. }
  117. // limit to 55 chars
  118. $subtitle = substr( $subtitle, 0, 55 );
  119. $item->setSubTitle( $subtitle );
  120. $this->logger->debug( 'setSubTitle: '.$subtitle );
  121. }
  122. // private listing
  123. if ( @$profile_details['private_listing'] == 1 ) {
  124. $item->setPrivateListing( true );
  125. }
  126. // add shipping services and options
  127. $item = $this->buildShipping( $id, $item, $p['post_id'], $profile_details );
  128. // add ebay categories and store categories
  129. $item = $this->buildCategories( $id, $item, $p['post_id'], $profile_details );
  130. // add variations
  131. if ( $hasVariations ) {
  132. if ( @$profile_details['variations_mode'] == 'flat' ) {
  133. // don't build variations - list as flat item
  134. $item = $this->flattenVariations( $id, $item, $profile_details );
  135. } else {
  136. // default: list as variations
  137. $item = $this->buildVariations( $id, $item, $profile_details );
  138. }
  139. }
  140. // add item specifics (attributes) - after variations
  141. $item = $this->buildItemSpecifics( $id, $item );
  142. // Set the Item Description
  143. $item->Description = $this->getFinalHTML( $id );
  144. return $item;
  145. } /* end of buildItem() */
  146. public function buildCategories( $id, $item, $post_id, $profile_details ) {
  147. // handle primary category
  148. if ( intval($profile_details['ebay_category_1_id']) > 0 ) {
  149. $item->PrimaryCategory = new CategoryType();
  150. $item->PrimaryCategory->CategoryID = $profile_details['ebay_category_1_id'];
  151. } else {
  152. // get ebay categories map
  153. $categories_map_ebay = get_option( 'wplister_categories_map_ebay' );
  154. // fetch products local category terms
  155. $terms = wp_get_post_terms( $post_id, ProductWrapper::getTaxonomy() );
  156. // $this->logger->info('terms: '.print_r($terms,1));
  157. $ebay_category_id = false;
  158. $primary_category_id = false;
  159. $secondary_category_id = false;
  160. foreach ( $terms as $term ) {
  161. // look up ebay category
  162. if ( isset( $categories_map_ebay[ $term->term_id ] ) ) {
  163. $ebay_category_id = @$categories_map_ebay[ $term->term_id ];
  164. }
  165. // check ebay category
  166. if ( intval( $ebay_category_id ) > 0 ) {
  167. if ( ! $primary_category_id ) {
  168. $primary_category_id = $ebay_category_id;
  169. } else {
  170. $secondary_category_id = $ebay_category_id;
  171. }
  172. }
  173. }
  174. $this->logger->info('mapped primary_category_id: '.$primary_category_id);
  175. $this->logger->info('mapped secondary_category_id: '.$secondary_category_id);
  176. if ( intval( $primary_category_id ) > 0 ) {
  177. $item->PrimaryCategory = new CategoryType();
  178. $item->PrimaryCategory->CategoryID = $primary_category_id;
  179. }
  180. if ( intval( $secondary_category_id ) > 0 ) {
  181. $item->SecondaryCategory = new CategoryType();
  182. $item->SecondaryCategory->CategoryID = $secondary_category_id;
  183. }
  184. }
  185. // optional secondary category
  186. if ( intval($profile_details['ebay_category_2_id']) > 0 ) {
  187. $item->SecondaryCategory = new CategoryType();
  188. $item->SecondaryCategory->CategoryID = $profile_details['ebay_category_2_id'];
  189. }
  190. // handle optional store category
  191. if ( intval($profile_details['store_category_1_id']) > 0 ) {
  192. $item->Storefront = new StorefrontType();
  193. $item->Storefront->StoreCategoryID = $profile_details['store_category_1_id'];
  194. } else {
  195. // get store categories map
  196. $categories_map_store = get_option( 'wplister_categories_map_store' );
  197. // fetch products local category terms
  198. $terms = wp_get_post_terms( $post_id, ProductWrapper::getTaxonomy() );
  199. // $this->logger->info('terms: '.print_r($terms,1));
  200. $store_category_id = false;
  201. $primary_store_category_id = false;
  202. $secondary_store_category_id = false;
  203. foreach ( $terms as $term ) {
  204. // look up store category
  205. if ( isset( $categories_map_store[ $term->term_id ] ) ) {
  206. $store_category_id = @$categories_map_store[ $term->term_id ];
  207. }
  208. // check store category
  209. if ( intval( $store_category_id ) > 0 ) {
  210. if ( ! $primary_store_category_id ) {
  211. $primary_store_category_id = $store_category_id;
  212. } else {
  213. $secondary_store_category_id = $store_category_id;
  214. }
  215. }
  216. }
  217. $this->logger->info('mapped primary_store_category_id: '.$primary_store_category_id);
  218. $this->logger->info('mapped secondary_store_category_id: '.$secondary_store_category_id);
  219. if ( intval( $primary_store_category_id ) > 0 ) {
  220. $item->Storefront = new StorefrontType();
  221. $item->Storefront->StoreCategoryID = $primary_store_category_id;
  222. }
  223. if ( intval( $secondary_store_category_id ) > 0 ) {
  224. $item->Storefront->StoreCategory2ID = $secondary_store_category_id;
  225. }
  226. }
  227. // optional secondary store category
  228. if ( intval($profile_details['store_category_2_id']) > 0 ) {
  229. $item->Storefront->StoreCategory2ID = $profile_details['store_category_2_id'];
  230. }
  231. return $item;
  232. } /* end of buildCategories() */
  233. public function buildShipping( $id, $item, $post_id, $profile_details ) {
  234. $this->logger->info('shipping_service_type: '.$profile_details['shipping_service_type'] );
  235. $isFlat = $profile_details['shipping_service_type'] != 'calc' ? true : false;
  236. $isCalc = $profile_details['shipping_service_type'] == 'calc' ? true : false;
  237. $shippingDetails = new ShippingDetailsType();
  238. $shippingDetails->ShippingType = $isFlat ? 'Flat' : 'Calculated';
  239. $this->logger->info('shippingDetails->ShippingType: '.$shippingDetails->ShippingType );
  240. // local shipping options
  241. $localShippingOptions = $profile_details['loc_shipping_options'];
  242. $this->logger->debug('localShippingOptions: '.print_r($localShippingOptions,1));
  243. $pr = 1;
  244. foreach ($localShippingOptions as $opt) {
  245. $price = $this->getDynamicShipping( $opt['price'], $post_id );
  246. $add_price = $this->getDynamicShipping( $opt['add_price'], $post_id );
  247. $ShippingServiceOptions = new ShippingServiceOptionsType();
  248. $ShippingServiceOptions->setShippingService( $opt['service_name'] );
  249. $ShippingServiceOptions->setShippingServicePriority($pr);
  250. // set shipping costs for flat services
  251. if ( $isFlat ) {
  252. $ShippingServiceOptions->setShippingServiceCost( $price );
  253. if ( trim( $add_price ) == '' ) {
  254. $ShippingServiceOptions->setShippingServiceAdditionalCost( $price );
  255. } else {
  256. $ShippingServiceOptions->setShippingServiceAdditionalCost( $add_price );
  257. }
  258. }
  259. $localShippingServices[]=$ShippingServiceOptions;
  260. $pr++;
  261. $EbayShippingModel = new EbayShippingModel();
  262. $lastShippingCategory = $EbayShippingModel->getShippingCategoryByServiceName( $opt['service_name'] );
  263. $this->logger->debug('ShippingCategory: '.print_r($lastShippingCategory,1));
  264. }
  265. $shippingDetails->setShippingServiceOptions($localShippingServices, null);
  266. // $intlShipping = array(
  267. // 'UK_RoyalMailAirmailInternational' => array (
  268. // 'Europe' => 1,
  269. // 'Worldwide' => 1.50
  270. // ),
  271. // 'UK_RoyalMailInternationalSignedFor' => array (
  272. // 'Europe' => 5,
  273. // )
  274. // );
  275. $intlShipping = $profile_details['int_shipping_options'];
  276. $this->logger->debug('intlShipping: '.print_r($intlShipping,1));
  277. $pr = 1;
  278. foreach ($intlShipping as $opt) {
  279. // foreach ($opt as $loc=>$price) {
  280. $price = $this->getDynamicShipping( $opt['price'], $post_id );
  281. $add_price = $this->getDynamicShipping( $opt['add_price'], $post_id );
  282. $InternationalShippingServiceOptions = new InternationalShippingServiceOptionsType();
  283. $InternationalShippingServiceOptions->setShippingService( $opt['service_name'] );
  284. $InternationalShippingServiceOptions->setShippingServicePriority($pr);
  285. $InternationalShippingServiceOptions->setShipToLocation( $opt['location'] );
  286. // set shipping costs for flat services
  287. if ( $isFlat ) {
  288. $InternationalShippingServiceOptions->setShippingServiceCost( $price );
  289. if ( trim( $add_price ) == '' ) {
  290. $InternationalShippingServiceOptions->setShippingServiceAdditionalCost( $price );
  291. } else {
  292. $InternationalShippingServiceOptions->setShippingServiceAdditionalCost( $add_price );
  293. }
  294. }
  295. $shippingInternational[]=$InternationalShippingServiceOptions;
  296. $pr++;
  297. // }
  298. }
  299. // only set international shipping if $intlShipping array contains one or more valid items
  300. if ( @$intlShipping[0]['service_name'] != '' )
  301. $shippingDetails->setInternationalShippingServiceOption($shippingInternational,null);
  302. // set CalculatedShippingRate
  303. if ( $isCalc ) {
  304. $calculatedShippingRate = new CalculatedShippingRateType();
  305. $calculatedShippingRate->setOriginatingPostalCode( $profile_details['postcode'] );
  306. $calculatedShippingRate->setShippingPackage( $localShippingOptions[0]['ShippingPackage'] );
  307. list( $weight_major, $weight_minor ) = ProductWrapper::getEbayWeight( $post_id );
  308. $calculatedShippingRate->setWeightMajor( floatval( $weight_major) );
  309. $calculatedShippingRate->setWeightMinor( floatval( $weight_minor) );
  310. $dimensions = ProductWrapper::getDimensions( $post_id );
  311. if ( trim( @$dimensions['width'] ) != '' ) $calculatedShippingRate->setPackageWidth( $dimensions['width'] );
  312. if ( trim( @$dimensions['length'] ) != '' ) $calculatedShippingRate->setPackageLength( $dimensions['length'] );
  313. if ( trim( @$dimensions['height'] ) != '' ) $calculatedShippingRate->setPackageDepth( $dimensions['height'] );
  314. // debug
  315. // $weight = ProductWrapper::getWeight( $post_id ) ;
  316. // $this->logger->info('weight: '.print_r($weight,1));
  317. // $this->logger->info('dimensions: '.print_r($dimensions,1));
  318. $shippingDetails->setCalculatedShippingRate( $calculatedShippingRate );
  319. }
  320. // check if we have local pickup only
  321. if ( ( count($localShippingOptions) == 1 ) && ( $lastShippingCategory == 'PICKUP' ) ) {
  322. $item->setShipToLocations( 'None' );
  323. $item->setDispatchTimeMax( null );
  324. $this->logger->info('PICKUP ONLY mode');
  325. // don't set ShippingDetails for pickup-only in UK!
  326. if ( $item->Site != 'UK' ) {
  327. $item->setShippingDetails($shippingDetails);
  328. }
  329. } else {
  330. $item->setShippingDetails($shippingDetails);
  331. }
  332. return $item;
  333. } /* end of buildShipping() */
  334. public function buildItemSpecifics( $id, $item ) {
  335. // new ItemSpecifics
  336. $ItemSpecifics = new NameValueListArrayType();
  337. // get listing data
  338. $listing = $this->lm->getItem( $id );
  339. // get product attributes
  340. $attributes = ProductWrapper::getAttributes( $listing['post_id'] );
  341. $this->logger->info('product attributes: '.print_r($attributes,1));
  342. // apply item specifics from profile
  343. $specifics = $listing['profile_data']['details']['item_specifics'];
  344. $this->logger->info('item_specifics: '.print_r($specifics,1));
  345. foreach ($specifics as $spec) {
  346. if ( $spec['value'] != '' ) {
  347. $value = $spec['value'];
  348. $NameValueList = new NameValueListType();
  349. $NameValueList->setName ( $spec['name'] );
  350. $NameValueList->setValue( $value );
  351. if ( ! in_array( $spec['name'], $this->variationAttributes ) ) {
  352. $ItemSpecifics->addNameValueList( $NameValueList );
  353. }
  354. $this->logger->info("specs: added custom value: {$spec['name']} - $value");
  355. } elseif ( $spec['attribute'] != '' ) {
  356. $value = $attributes[ $spec['attribute'] ];
  357. $NameValueList = new NameValueListType();
  358. $NameValueList->setName ( $spec['name'] );
  359. $NameValueList->setValue( $value );
  360. if ( ! in_array( $spec['name'], $this->variationAttributes ) ) {
  361. $ItemSpecifics->addNameValueList( $NameValueList );
  362. }
  363. $this->logger->info("specs: added product attribute: {$spec['name']} - $value");
  364. }
  365. }
  366. // skip if item has no attributes
  367. // if ( count($attributes) == 0 ) return $item;
  368. // add ItemSpecifics from product attributes
  369. /* disabled for now, since it causes duplicates and it's not actually required anymore
  370. foreach ($attributes as $name => $value) {
  371. $NameValueList = new NameValueListType();
  372. $NameValueList->setName ( $name );
  373. $NameValueList->setValue( $value );
  374. // only add attribute to ItemSpecifics if not already present in variations
  375. if ( ! in_array( $name, $this->variationAttributes ) ) {
  376. $ItemSpecifics->addNameValueList( $NameValueList );
  377. }
  378. } */
  379. if ( count($ItemSpecifics) > 0 ) {
  380. $item->setItemSpecifics( $ItemSpecifics );
  381. $this->logger->info("item specifics were added.");
  382. }
  383. return $item;
  384. } /* end of buildItemSpecifics() */
  385. public function buildVariations( $id, $item, $profile_details ) {
  386. // build variations
  387. $item->Variations = new VariationsType();
  388. // get product variations
  389. $p = $this->lm->getItem( $id );
  390. $variations = ProductWrapper::getVariations( $p['post_id'] );
  391. // loop each combination
  392. foreach ($variations as $var) {
  393. $newvar = new VariationType();
  394. // handle price
  395. $newvar->StartPrice = $this->lm->applyProfilePrice( $var['price'], $profile_details['start_price'] );
  396. // handle variation quantity - if no quantity set in profile
  397. if ( intval( $item->Quantity ) == 0 ) {
  398. $newvar->Quantity = intval( $var['stock'] );
  399. } else {
  400. $newvar->Quantity = $item->Quantity;
  401. }
  402. // handle sku
  403. if ( $var['sku'] != '' ) {
  404. $newvar->SKU = $var['sku'];
  405. }
  406. // add VariationSpecifics (v2)
  407. $VariationSpecifics = new NameValueListArrayType();
  408. foreach ($var['variation_attributes'] as $name => $value) {
  409. $NameValueList = new NameValueListType();
  410. $NameValueList->setName ( $name );
  411. $NameValueList->setValue( $value );
  412. $VariationSpecifics->addNameValueList( $NameValueList );
  413. }
  414. $newvar->setVariationSpecifics( $VariationSpecifics );
  415. $item->Variations->addVariation( $newvar );
  416. }
  417. // build temporary array for VariationSpecificsSet
  418. $tmpVariationSpecificsSet = array();
  419. foreach ($variations as $var) {
  420. foreach ($var['variation_attributes'] as $name => $value) {
  421. if ( ! is_array($tmpVariationSpecificsSet[ $name ]) ) {
  422. $tmpVariationSpecificsSet[ $name ] = array();
  423. }
  424. if ( ! in_array( $value, $tmpVariationSpecificsSet[ $name ] ) ) {
  425. $tmpVariationSpecificsSet[ $name ][] = $value;
  426. }
  427. }
  428. }
  429. // build VariationSpecificsSet
  430. $VariationSpecificsSet = new NameValueListArrayType();
  431. foreach ($tmpVariationSpecificsSet as $name => $values) {
  432. $NameValueList = new NameValueListType();
  433. $NameValueList->setName ( $name );
  434. foreach ($values as $value) {
  435. $NameValueList->addValue( $value );
  436. }
  437. $VariationSpecificsSet->addNameValueList( $NameValueList );
  438. }
  439. $item->Variations->setVariationSpecificsSet( $VariationSpecificsSet );
  440. // build array of variation attributes, which will be needed in builtItemSpecifics()
  441. $this->variationAttributes = array();
  442. foreach ($tmpVariationSpecificsSet as $key => $value) {
  443. $this->variationAttributes[] = $key;
  444. }
  445. $this->logger->info('variationAttributes'.print_r($this->variationAttributes,1));
  446. // select *one* VariationSpecificsSet for Pictures set
  447. // currently the first one is selected automatically, but there will be preferences for this later
  448. $VariationValuesForPictures = reset($tmpVariationSpecificsSet);
  449. $VariationNameForPictures = key($tmpVariationSpecificsSet);
  450. // build Pictures
  451. $Pictures = new PicturesType();
  452. $Pictures->setVariationSpecificName ( $VariationNameForPictures );
  453. foreach ($variations as $var) {
  454. $VariationValue = $var['variation_attributes'][$VariationNameForPictures];
  455. if ( in_array( $VariationValue, $VariationValuesForPictures ) ) {
  456. $VariationSpecificPictureSet = new VariationSpecificPictureSetType();
  457. $VariationSpecificPictureSet->setVariationSpecificValue( $VariationValue );
  458. $VariationSpecificPictureSet->addPictureURL( str_replace(' ', '%20', $var['image'] ) );
  459. // only list variation images if enabled
  460. if ( @$profile_details['with_variation_images'] != '0' ) {
  461. $Pictures->addVariationSpecificPictureSet( $VariationSpecificPictureSet );
  462. }
  463. // remove value from VariationValuesForPictures to avoid duplicates
  464. unset( $VariationValuesForPictures[ array_search( $VariationValue, $VariationValuesForPictures ) ] );
  465. }
  466. }
  467. $item->Variations->setPictures( $Pictures );
  468. // ebay doesn't allow different weight and dimensions for varations
  469. // so for calculated shipping services we just fetch those from the first variation
  470. // and overwrite
  471. $isCalc = $profile_details['shipping_service_type'] == 'calc' ? true : false;
  472. if ( $isCalc ) {
  473. // get weight and dimensions from first variation
  474. $weight_major = $variations[0]['weight_major'];
  475. $weight_minor = $variations[0]['weight_minor'];
  476. $dimensions = $variations[0]['dimensions'];
  477. $item->ShippingDetails->CalculatedShippingRate->setWeightMajor( floatval( $weight_major ) );
  478. $item->ShippingDetails->CalculatedShippingRate->setWeightMinor( floatval( $weight_minor ) );
  479. if ( trim( @$dimensions['width'] ) != '' ) $item->ShippingDetails->CalculatedShippingRate->setPackageWidth( $dimensions['width'] );
  480. if ( trim( @$dimensions['length'] ) != '' ) $item->ShippingDetails->CalculatedShippingRate->setPackageLength( $dimensions['length'] );
  481. if ( trim( @$dimensions['height'] ) != '' ) $item->ShippingDetails->CalculatedShippingRate->setPackageDepth( $dimensions['height'] );
  482. // debug
  483. $this->logger->info('first variations weight: '.print_r($weight,1));
  484. $this->logger->info('first variations dimensions: '.print_r($dimensions,1));
  485. }
  486. // remove some settings from single item
  487. $item->SKU = null;
  488. $item->Quantity = null;
  489. $item->StartPrice = null;
  490. $item->BuyItNowPrice = null;
  491. return $item;
  492. /* this we should get:
  493. <Variations>
  494. <Variation>
  495. <SKU />
  496. <StartPrice>15</StartPrice>
  497. <Quantity>1</Quantity>
  498. <VariationSpecifics>
  499. <NameValueList>
  500. <Name>Size</Name>
  501. <Value>large</Value>
  502. </NameValueList>
  503. </VariationSpecifics>
  504. </Variation>
  505. <Variation>
  506. <SKU />
  507. <StartPrice>10</StartPrice>
  508. <Quantity>1</Quantity>
  509. <VariationSpecifics>
  510. <NameValueList>
  511. <Name>Size</Name>
  512. <Value>small</Value>
  513. </NameValueList>
  514. </VariationSpecifics>
  515. </Variation>
  516. <Pictures>
  517. <VariationSpecificName>Size</VariationSpecificName>
  518. <VariationSpecificPictureSet>
  519. <VariationSpecificValue>large</VariationSpecificValue>
  520. <PictureURL>http://www.example.com/wp-content/uploads/2011/09/grateful-dead.jpg</PictureURL>
  521. </VariationSpecificPictureSet>
  522. <VariationSpecificPictureSet>
  523. <VariationSpecificValue>small</VariationSpecificValue>
  524. <PictureURL>www.example.com/wp-content/uploads/2011/09/grateful-dead.jpg</PictureURL>
  525. </VariationSpecificPictureSet>
  526. </Pictures>
  527. <VariationSpecificsSet>
  528. <NameValueList>
  529. <Name>Size</Name>
  530. <Value>large</Value>
  531. <Value>small</Value>
  532. </NameValueList>
  533. </VariationSpecificsSet>
  534. </Variations>
  535. */
  536. }
  537. public function flattenVariations( $id, $item, $profile_details ) {
  538. // get product variations
  539. $p = $this->lm->getItem( $id );
  540. $variations = ProductWrapper::getVariations( $p['post_id'] );
  541. $this->logger->info("flattenVariations($id)");
  542. // fetch first variations start price
  543. if ( intval($item->StartPrice->value) == 0 ) {
  544. $start_price = $variations[0]['price'];
  545. $start_price = $this->lm->applyProfilePrice( $start_price, $profile_details['start_price'] );
  546. $item->StartPrice->setTypeValue( $start_price );
  547. $this->logger->info("using first variations price: ".print_r($item->StartPrice->value,1));
  548. }
  549. // ebay doesn't allow different weight and dimensions for varations
  550. // so for calculated shipping services we just fetch those from the first variation
  551. // and overwrite
  552. $isCalc = $profile_details['shipping_service_type'] == 'calc' ? true : false;
  553. if ( $isCalc ) {
  554. // get weight and dimensions from first variation
  555. $weight_major = $variations[0]['weight_major'];
  556. $weight_minor = $variations[0]['weight_minor'];
  557. $dimensions = $variations[0]['dimensions'];
  558. $item->ShippingDetails->CalculatedShippingRate->setWeightMajor( floatval( $weight_major ) );
  559. $item->ShippingDetails->CalculatedShippingRate->setWeightMinor( floatval( $weight_minor ) );
  560. if ( trim( @$dimensions['width'] ) != '' ) $item->ShippingDetails->CalculatedShippingRate->setPackageWidth( $dimensions['width'] );
  561. if ( trim( @$dimensions['length'] ) != '' ) $item->ShippingDetails->CalculatedShippingRate->setPackageLength( $dimensions['length'] );
  562. if ( trim( @$dimensions['height'] ) != '' ) $item->ShippingDetails->CalculatedShippingRate->setPackageDepth( $dimensions['height'] );
  563. // debug
  564. $this->logger->info('first variations weight: '.print_r($weight,1));
  565. $this->logger->info('first variations dimensions: '.print_r($dimensions,1));
  566. }
  567. return $item;
  568. } /* end of flattenVariations() */
  569. public function checkItem( $item ) {
  570. $success = true;
  571. $this->VariationsHaveStock = false;
  572. // check StartPrice, Quantity and SKU
  573. if ( is_object( $item->Variations ) ) {
  574. // item has variations
  575. $VariationsHaveStock = false;
  576. $VariationsSkuArray = array();
  577. $VariationsSkuAreUnique = true;
  578. // check each variation
  579. foreach ($item->Variations->Variation as $var) {
  580. // StartPrice must be greater than 0
  581. if ( intval($var->StartPrice) == 0 ) {
  582. $longMessage = __('Some variations seem to have no price.','wplister');
  583. $success = false;
  584. }
  585. // Quantity must be greater than 0 - at least for one variation
  586. if ( intval($var->Quantity) > 0 ) $VariationsHaveStock = true;
  587. // SKUs must be unique - if present
  588. if ( ($var->SKU) != '' ) {
  589. if ( in_array( $var->SKU, $VariationsSkuArray )) {
  590. $VariationsSkuAreUnique = false;
  591. } else {
  592. $VariationsSkuArray[] = $var->SKU;
  593. }
  594. }
  595. }
  596. if ( ! $VariationsSkuAreUnique ) {
  597. foreach ($item->Variations->Variation as &$var) {
  598. $var->SKU = '';
  599. }
  600. $longMessage = __('You are using the same SKU for more than one variations which is not allowed by eBay.','wplister');
  601. $longMessage .= '<br>';
  602. $longMessage .= __('To circumvent this issue, your item will be listed without SKU.','wplister');
  603. // $success = false;
  604. }
  605. if ( ! $VariationsHaveStock ) {
  606. $longMessage = __('None of these variations are in stock.','wplister');
  607. $success = false;
  608. }
  609. // make this info available to reviseItem()
  610. $this->VariationsHaveStock = $VariationsHaveStock;
  611. } else {
  612. // item has no variations
  613. // StartPrice must be greater than 0
  614. if ( intval($item->StartPrice) == 0 ) {
  615. $longMessage = __('Price can not be zero.','wplister');
  616. $success = false;
  617. }
  618. }
  619. // PrimaryCategory->CategoryID must be greater than 0
  620. if ( intval( @$item->PrimaryCategory->CategoryID ) == 0 ) {
  621. $longMessage = __('There has been no primary category assigned.','wplister');
  622. $success = false;
  623. }
  624. if ( ! $success && ! $this->is_ajax() ) {
  625. $this->showMessage( $longMessage, 1, true );
  626. } elseif ( ( $longMessage != '' ) && ! $this->is_ajax() ) {
  627. $this->showMessage( $longMessage, 0, true );
  628. }
  629. $htmlMsg = '<div id="message" class="error"><p>';
  630. $htmlMsg .= '<b>' . 'This item did not pass the validation check' . ':</b>';
  631. $htmlMsg .= '<br>' . $longMessage . '';
  632. $htmlMsg .= '</p></div>';
  633. // save error as array of objects
  634. $errorObj = new stdClass();
  635. $errorObj->SeverityCode = 'Validation';
  636. $errorObj->ErrorCode = '42';
  637. $errorObj->ShortMessage = $longMessage;
  638. $errorObj->LongMessage = $longMessage;
  639. $errorObj->HtmlMessage = $htmlMsg;
  640. $errors = array( $errorObj );
  641. // save results as local property
  642. $this->result = new stdClass();
  643. $this->result->success = $success;
  644. $this->result->errors = $errors;
  645. return $success;
  646. } /* end of checkItem() */
  647. public function getDynamicShipping( $price, $post_id ) {
  648. // return price if no mapping
  649. if ( ! substr( $price, 0, 1 ) == '[' ) return floatval($price);
  650. // split values list
  651. $values = substr( $price, 1, -1 );
  652. $values = explode( '|', $values );
  653. // first item is mode
  654. $mode = array_shift($values);
  655. // weight mode
  656. if ( $mode == 'weight' ) {
  657. $product_weight = ProductWrapper::getWeight( $post_id );
  658. foreach ($values as $val) {
  659. list( $limit, $price ) = explode(':', $val);
  660. if ( $product_weight >= $limit) $shipping_cost = $price;
  661. }
  662. return floatval($shipping_cost);
  663. }
  664. // convert '0.00' to '0' - ebay api doesn't like '0.00'
  665. if ( $price == 0 ) $price = '0';
  666. return floatval($price);
  667. }
  668. public function prepareTitleAsHTML( $title ) {
  669. $this->logger->debug('prepareTitleAsHTML() in: ' . $title );
  670. $title = htmlentities( $title, ENT_QUOTES, 'UTF-8', false );
  671. $this->logger->debug('prepareTitleAsHTML() out: ' . $title );
  672. return $title;
  673. }
  674. public function prepareTitle( $title ) {
  675. $this->logger->info('prepareTitle() in: ' . $title );
  676. // replace some specials chars with harmless versions
  677. // $title = str_replace('&#8211;', '-', $title );
  678. // $title = str_replace('&ndash;', '-', $title );
  679. // $title = str_replace('�', '"', $title );
  680. // $title = str_replace('�', '"', $title );
  681. // $title = str_replace('�', '\'', $title );
  682. // $title = str_replace('&#8220;', '"', $title );
  683. // $title = str_replace('&#8221;', '"', $title );
  684. // $title = str_replace('&#8217;', '\'', $title );
  685. // $title = str_replace('&#8230;', '...', $title );
  686. // $this->logger->info('prepareTitle() s1: ' . $title );
  687. $title = html_entity_decode( $title, ENT_QUOTES, 'UTF-8' );
  688. $this->logger->info('prepareTitle() out: ' . $title );
  689. return $title;
  690. }
  691. public function getFinalHTML( $id ) {
  692. // get item data
  693. $item = $this->lm->getItem( $id );
  694. // use latest post_content from product
  695. $post = get_post( $item['post_id'] );
  696. if ( ! empty($post->post_content) ) $item['post_content'] = $post->post_content;
  697. // load template
  698. $template = new TemplatesModel( $item['template'] );
  699. $html = $template->processItem( $item );
  700. // return html
  701. return $html;
  702. }
  703. public function getProductMainImageURL( $post_id, $checking_parent = false ) {
  704. // this seems to be neccessary for listing previews on some installations
  705. if ( ! function_exists('get_post_thumbnail_id'))
  706. require_once( ABSPATH . 'wp-includes/post-thumbnail-template.php');
  707. $large_image_url = ProductWrapper::getImageURL( $post_id );
  708. if ( $large_image_url ) {
  709. $image_url = $large_image_url;
  710. } else {
  711. $images = $this->getProductImagesURL( $post_id );
  712. $image_url = @$images[0];
  713. }
  714. // check if featured image comes from nextgen gallery
  715. if ( $this->is_plugin_active('nextgen-gallery/nggallery.php') ) {
  716. $thumbnail_id = get_post_meta($post_id, '_thumbnail_id', true);
  717. if ( 'ngg' == substr($thumbnail_id, 0, 3) ) {
  718. $imageID = str_replace('ngg-', '', $thumbnail_id);
  719. $picture = nggdb::find_image($imageID);
  720. $image_url = $picture->imageURL;
  721. // $this->logger->info( "NGG - picture: " . print_r($picture,1) );
  722. $this->logger->info( "NGG - image_url: " . print_r($image_url,1) );
  723. }
  724. }
  725. if ( ( $image_url == '' ) && ( ! $checking_parent ) ) {
  726. // $parents = get_post_ancestors( $post_id );
  727. $parent_id = get_post($post_id)->post_parent;
  728. if ( $parent_id ) {
  729. return $this->getProductMainImageURL( $parent_id, true);
  730. }
  731. }
  732. // ebay doesn't accept https - only http and ftp
  733. $image_url = $this->removeHttpsFromUrl( $image_url );
  734. return $image_url;
  735. }
  736. public function getProductImagesURL( $id ) {
  737. global $wpdb;
  738. $results = $wpdb->get_col(
  739. "
  740. SELECT guid
  741. FROM {$wpdb->prefix}posts
  742. WHERE post_type = 'attachment'
  743. AND post_parent = '$id'
  744. ORDER BY menu_order
  745. "
  746. );
  747. $this->logger->debug( "getProductImagesURL( $id ) : " . print_r($results,1) );
  748. // Shopp stores images in db by default...
  749. if ( ProductWrapper::plugin == 'shopp' ) {
  750. $results = ProductWrapper::getAllImages( $id );
  751. // $this->logger->info( "SHOPP - getAllImages( $id ) : " . print_r($results,1) );
  752. }
  753. $filenames = array();
  754. foreach($results as $row) {
  755. $filenames[] = $this->removeHttpsFromUrl( $row );
  756. }
  757. return $filenames;
  758. }
  759. // ebay doesn't accept image urls using https - only http and ftp
  760. function removeHttpsFromUrl( $url ) {
  761. $url = str_replace( 'https://', 'http://', $url );
  762. $url = str_replace( ':443', '', $url );
  763. return $url;
  764. }
  765. }