public function removeAllImportedProducts()
 {
     global $wpdb;
     $listing_ids = $wpdb->get_col("\n            SELECT al.id\n            FROM {$wpdb->prefix}amazon_listings al\n            WHERE al.source = 'imported'\n               OR al.source = 'foreign_import'\n        ");
     $post_ids = $wpdb->get_col("\n            SELECT pm.post_id\n            FROM {$wpdb->postmeta} pm\n            WHERE pm.meta_key   = '_amazon_item_source'\n              AND pm.meta_value = 'imported'\n        ");
     // echo "<pre>";print_r($post_ids);echo"</pre>";die();
     $mode = isset($_REQUEST['mode']) ? $_REQUEST['mode'] : false;
     if ($mode == 'deletion_confirmed') {
         foreach ($post_ids as $post_id) {
             WPLA_ProductBuilder::deleteProduct($post_id);
         }
         foreach ($listing_ids as $listing_id) {
             $wpdb->delete($wpdb->prefix . 'amazon_listings', array('id' => $listing_id), array('%d'));
         }
         wpla_show_message('All imported products and listings have been removed.');
         return;
     }
     if (!empty($post_ids)) {
         $nonce = wp_create_nonce('wpla_tools_page');
         $btn_delete = '<a href="admin.php?page=wpla-tools&tab=developer&action=wpla_remove_all_imported_products&mode=deletion_confirmed&_wpnonce=' . $nonce . '" class="button button-small button-secondary">' . 'Yes, I want to remove all imported products' . '</a>';
         $buttons = ' &nbsp; ' . $btn_delete;
         wpla_show_message('Are you sure you want to remove ' . sizeof($post_ids) . ' products and ' . sizeof($listing_ids) . ' listings which were imported from Amazon? ' . $buttons, 'warn');
     } else {
         wpla_show_message('There are no imported products to remove.');
     }
 }
 public function displayAdvancedSettingsPage()
 {
     $wp_roles = new WP_Roles();
     // check import folder
     $upload_dir = wp_upload_dir();
     $basedir_name = self::getOption('import_images_basedir_name', 'imported/');
     $images_dir = $upload_dir['basedir'] . '/' . $basedir_name;
     if (!is_dir($images_dir)) {
         mkdir($images_dir);
     }
     if (!is_dir($images_dir)) {
         wpla_show_message('The folder for imported images <code>' . $images_dir . '</code> could not be created. Please check your folder permissions.', 'error');
     }
     $aData = array('plugin_url' => self::$PLUGIN_URL, 'message' => $this->message, 'dismiss_imported_products_notice' => self::getOption('dismiss_imported_products_notice'), 'enable_missing_details_warning' => self::getOption('enable_missing_details_warning'), 'enable_custom_product_prices' => self::getOption('enable_custom_product_prices', 1), 'enable_minmax_product_prices' => self::getOption('enable_minmax_product_prices', 0), 'enable_item_condition_fields' => self::getOption('enable_item_condition_fields', 2), 'enable_thumbs_column' => self::getOption('enable_thumbs_column'), 'autofetch_listing_quality_feeds' => self::getOption('autofetch_listing_quality_feeds', 1), 'autofetch_inventory_report' => self::getOption('autofetch_inventory_report', 0), 'product_gallery_first_image' => self::getOption('product_gallery_first_image'), 'product_gallery_fallback' => self::getOption('product_gallery_fallback', 'none'), 'pricing_info_expiry_time' => self::getOption('pricing_info_expiry_time', 24), 'repricing_use_lowest_offer' => self::getOption('repricing_use_lowest_offer', 0), 'repricing_margin' => self::getOption('repricing_margin', ''), 'import_parent_category_id' => self::getOption('import_parent_category_id', ''), 'enable_variation_image_import' => self::getOption('enable_variation_image_import', 1), 'enable_gallery_images_import' => self::getOption('enable_gallery_images_import', 1), 'import_images_subfolder_level' => self::getOption('import_images_subfolder_level', 0), 'import_images_basedir_name' => self::getOption('import_images_basedir_name', 'imported/'), 'default_matcher_selection' => self::getOption('default_matcher_selection', 'title'), 'available_attributes' => WPLA_ProductWrapper::getAttributeTaxonomies(), 'variation_attribute_map' => self::getOption('variation_attribute_map', array()), 'variation_merger_map' => self::getOption('variation_merger_map', array()), 'custom_shortcodes' => self::getOption('custom_shortcodes', array()), 'variation_meta_fields' => self::getOption('variation_meta_fields', array()), 'allowed_html_tags' => self::getOption('allowed_html_tags', '<b><i>'), 'process_shortcodes' => self::getOption('process_shortcodes', 'off'), 'remove_links' => self::getOption('remove_links', 'default'), 'variation_title_mode' => self::getOption('variation_title_mode', 'default'), 'profile_editor_mode' => self::getOption('profile_editor_mode', 'default'), 'option_uninstall' => self::getOption('uninstall'), 'available_roles' => $wp_roles->role_names, 'wp_roles' => $wp_roles->roles, 'settings_url' => 'admin.php?page=' . self::ParentMenuId . '-settings', 'form_action' => 'admin.php?page=' . self::ParentMenuId . '-settings' . '&tab=advanced');
     $this->display('settings_advanced', $aData);
 }
 public function checkMultisite()
 {
     if (is_multisite()) {
         // check for network activation
         if (!function_exists('is_plugin_active_for_network')) {
             require_once ABSPATH . '/wp-admin/includes/plugin.php';
         }
         if (function_exists('is_network_admin') && is_plugin_active_for_network(plugin_basename(WPLA_PATH . '/wp-lister-amazon.php'))) {
             wpla_show_message("network activated!");
         } else {
             wpla_show_message("not network activated!");
         }
         // $this->showMessage("
         // 	<b>Multisite installation detected</b><br>
         // 	<br>
         // 	This is a site network...<br>
         // ");
         return true;
     }
     return false;
 }
 function auto_match_ASIN($post_id)
 {
     // check if we have an ASIN
     $asin = get_post_meta($post_id, '_wpla_asin', true);
     if (!$asin) {
         return;
     }
     // check if this ASIN / ID already exist - skip if it does
     $lm = new WPLA_ListingsModel();
     if ($lm->getItemByASIN($asin, false)) {
         return;
     }
     if ($lm->getItemByPostID($post_id)) {
         return;
     }
     // get default account
     $default_account_id = get_option('wpla_default_account_id', 1);
     if (!$default_account_id) {
         return;
     }
     // insert matched listing
     $success = $lm->insertMatchedProduct($post_id, $asin, $default_account_id);
     if ($success) {
         $msg = isset($lm->lastError) ? $lm->lastError : '';
         WPLA()->logger->info("auto-matched product #{$post_id} - {$msg}");
     } else {
         // TODO: implement persistent admin messages
         $msg = isset($lm->lastError) ? $lm->lastError : '';
         wpla_show_message("Failed to match product #{$post_id}: {$msg}", 'error');
         // won't show because page is reloaded after saving
         WPLA()->logger->warn("Failed to match product #{$post_id} - please report this to support.");
         // echo "Failed to match product #$post_id - please report this to support.";
     }
 }
 function updateItemFromReportCSV($csv, $account_id)
 {
     global $wpdb;
     // skip if $csv is not the right format - seller-sku is required,
     // localized report headers would insert empty rows in listings table
     if (!isset($csv['seller-sku']) || empty($csv['seller-sku'])) {
         wpla_show_message('Error: Could not parse report row. Make sure to disable localized column headers in seller central.', 'error');
         return false;
     }
     // map CSV Report Row to DB columns
     $data = self::mapMerchantReportItemCSVToDB($csv);
     $data['account_id'] = $account_id;
     // WPLA()->logger->debug('data: '.print_r($data,1) );
     // check if SKU already exists
     if ($item = $this->getItemBySKU($data['sku'], false)) {
         // // foreign ASINs should not be updated when they already exist (disabled - SKUs should be updated)
         // if ( $data['source'] != 'foreign_import') {
         // 	$wpdb->update( $this->tablename, $data, array( 'sku' => $data['sku'] ) );
         // }
         // update found SKU
         $wpdb->update($this->tablename, $data, array('sku' => $data['sku']));
         $this->updated_count++;
     } else {
         $data['status'] = 'imported';
         $wpdb->insert($this->tablename, $data);
         $this->imported_count++;
     }
     echo $wpdb->last_error;
     #WPLA()->logger->info('sql: '.$wpdb->last_query );
     #WPLA()->logger->info( $wpdb->last_error );
     return $item;
 }
 /**
  * If a table only contains utf8 or utf8mb4 or latin1 columns, convert it to utf8mb4.
  * (modified version of maybe_convert_table_to_utf8mb4() in wp core)
  *
  * @since 0.9.6.5
  *
  * @param string $table The table to convert.
  * @return bool true if the table was converted, false if it wasn't.
  */
 static function convert_custom_table_to_utf8mb4($table)
 {
     global $wpdb;
     global $wp_version;
     // do nothing before wp42
     if (version_compare($wp_version, '4,2', '<')) {
         wpla_show_message('WordPress 4.2 or better required - your version is ' . $wp_version, 'error');
         return false;
     }
     // get column information
     $results = $wpdb->get_results("SHOW FULL COLUMNS FROM `{$table}`");
     if (!$results) {
         wpla_show_message("no columns found for {$table}", 'error');
         return false;
     }
     // check charset for each column
     foreach ($results as $column) {
         if ($column->Collation) {
             list($charset) = explode('_', $column->Collation);
             $charset = strtolower($charset);
             if ('utf8' !== $charset && 'utf8mb4' !== $charset && 'latin1' !== $charset) {
                 // Don't upgrade tables that have non-utf8 and non-latin1 columns.
                 wpla_show_message("skipped column {$column} in table {$table} with charset: {$charset}", 'error');
                 return false;
             }
         }
     }
     // convert
     return $wpdb->query("ALTER TABLE {$table} CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
 }
 public function addProductImage($post_id, $image, $title = 'default', $asin = false, $is_featured_image = true, $suffix = false)
 {
     // set image upload dir
     $imported_images_dir = $this->getImagesDir(basename($image), $asin);
     $imported_images_url = $this->getImagesUrl(basename($image), $asin);
     if ($suffix) {
         $asin = $asin . '-' . $suffix;
     }
     // skip empty image
     if (trim($image) == '') {
         return false;
     }
     // full url to source image
     $img_url = $image;
     // limit title to 120 characters - filenames/guids can only have 255 chars
     $title = strlen($title) > 120 ? trim(substr($title, 0, 120)) : $title;
     // append amazon id to listing title
     if ($asin) {
         $title .= '-' . $asin;
     }
     // build local image path - based on title
     $imgslug = sanitize_file_name($title);
     // sanitize listing title
     $imgslug = str_replace('%20', '-', $imgslug);
     // replace %20 with dash
     $imgslug = preg_replace('/[^A-Za-z0-9_\\-]/', '', $imgslug);
     // allow only alphanumeric chars, dashes and underscores
     $imgfile = sanitize_file_name($imgslug . '.jpg');
     // sanitize listing title
     // copy remote image
     $img_local_path = $imported_images_dir . $imgfile;
     // full path to destination image
     $img_local_url = $imported_images_url . $imgfile;
     // $is_new_image   = $this->copyRemoteImage( $img_url, $img_local_path );
     $copy_result = $this->copyRemoteImage($img_url, $img_local_path);
     // return values:
     // 	true   - new image downloaded
     // 	false  - filename already exists
     // 	string - matching image found by MD5 hash
     if (is_wp_error($copy_result)) {
         WPLA()->logger->error('image download failed: ' . $img_url);
         // TODO: notify user when image download failed
         return false;
     }
     // if an existing image was found by MD5 hash, try to find its attachment_id
     if (is_string($copy_result)) {
         // get new image file name
         $img_local_path = $copy_result;
         $imgfile = basename($img_local_path);
         $attachment_id = $this->get_attachment_id_for_filename($imgfile);
         if ($attachment_id) {
             WPLA()->logger->info('found existing attachment_id (md5): ' . $attachment_id);
             // set post thumbnail
             if ($is_featured_image) {
                 set_post_thumbnail($post_id, $attachment_id);
             }
             WPLA()->logger->info("set_post_thumbnail( {$post_id}, {$attachment_id} )");
             return $attachment_id;
         }
     }
     // if image file already exists, try to find attachment_id
     if ($copy_result === false) {
         $attachment_id = $this->get_attachment_id_for_filename($imgfile);
         if ($attachment_id) {
             WPLA()->logger->info('found existing attachment_id: ' . $attachment_id);
             // set post thumbnail
             if ($is_featured_image) {
                 set_post_thumbnail($post_id, $attachment_id);
             }
             WPLA()->logger->info("set_post_thumbnail( {$post_id}, {$attachment_id} )");
             return $attachment_id;
         }
     }
     // generate name from filename
     $name_parts = pathinfo($imgfile);
     $name = trim(substr($imgfile, 0, -(1 + strlen($name_parts['extension']))));
     // Construct the attachment array
     $wp_filetype = wp_check_filetype(basename($imgfile), null);
     $attachment = array('post_mime_type' => $wp_filetype['type'], 'guid' => $img_local_url, 'post_parent' => $post_id, 'post_title' => $name, 'post_content' => '', 'post_status' => 'inherit');
     // Save the attachment data
     // $attachment_id = wp_insert_attachment( $attachment, $img_local_path, $post_id );
     $attachment_id = self::wp_insert_attachment_with_error_handling($attachment, $img_local_path, $post_id);
     if (!is_wp_error($attachment_id)) {
         // if ( $is_new_image ) {
         // 	wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $img_local_path ) );
         // 	WPLA()->logger->info( 'wp_update_attachment_metadata()' );
         // }
         // if ( $is_new_image ) ... removed because it would leave image meta data empty, resulting a in 1px image in admin
         // WPLA()->logger->info( 'wp_update_attachment_metadata()' );
         WPLA()->logger->info('new attachment_id: ' . $attachment_id);
         // make sure we have wp_generate_attachment_metadata() available
         require_once ABSPATH . 'wp-admin/includes/image.php';
         // generate and update attachment meta data - will generate thumbnails as well
         wp_update_attachment_metadata($attachment_id, wp_generate_attachment_metadata($attachment_id, $img_local_path));
         // set post thumbnail
         if ($is_featured_image) {
             set_post_thumbnail($post_id, $attachment_id);
         }
         WPLA()->logger->info('set_post_thumbnail()');
         // mark attachment as imported
         update_post_meta($attachment_id, '_wpla_asin', $post_id);
         WPLA()->logger->info('product image: ' . $img_local_url);
     } else {
         wpla_show_message('Failed to create attachment from image ' . $img_local_path . '<br>Error: ' . $attachment_id->get_error_message(), 'warn');
         WPLA()->logger->error('Failed to create attachment from image ' . $img_local_path . '<br>Error: ' . $attachment_id->get_error_message());
     }
     return $attachment_id;
 }
 /**
  * Invoke request and return response
  */
 private function invoke(array $converted, $dataHandle = null, $contentMd5 = null)
 {
     $parameters = $converted[CONVERTED_PARAMETERS_KEY];
     $actionName = $parameters["Action"];
     $response = array();
     $responseBody = null;
     $statusCode = 200;
     /* Submit the request and read response body */
     try {
         // Ensure the endpoint URL is set.
         if (empty($this->config['ServiceURL'])) {
             throw new MarketplaceWebService_Exception(array('ErrorCode' => 'InvalidServiceUrl', 'Message' => "Missing serviceUrl configuration value. You may obtain a list of valid MWS URLs by consulting the MWS Developer's Guide, or reviewing the sample code published along side this library."));
         }
         /* Add required request parameters */
         $parameters = $this->addRequiredParameters($parameters);
         $converted[CONVERTED_PARAMETERS_KEY] = $parameters;
         // log to db - before request
         $this->dblogger->updateLog(array('callname' => $actionName, 'request' => maybe_serialize($converted), 'parameters' => maybe_serialize($parameters), 'account_id' => $this->account_id, 'market_id' => $this->market_id, 'success' => 'pending'));
         $shouldRetry = false;
         $retries = 0;
         do {
             try {
                 $response = $this->performRequest($actionName, $converted, $dataHandle, $contentMd5);
                 $httpStatus = $response['Status'];
                 // log to db - after request
                 $this->dblogger->updateLog(array('response' => maybe_serialize($response), 'http_code' => $response['Status'], 'success' => 'HTTP OK'));
                 switch ($httpStatus) {
                     case 200:
                         $shouldRetry = false;
                         break;
                     case 500:
                     case 503:
                         require_once 'MarketplaceWebService/Model/ErrorResponse.php';
                         $errorResponse = MarketplaceWebService_Model_ErrorResponse::fromXML($response['ResponseBody']);
                         // We will not retry throttling errors since this would just add to the throttling problem.
                         $shouldRetry = $errorResponse->getError()->getCode() === 'RequestThrottled' ? false : true;
                         if ($shouldRetry && $retries <= $this->config['MaxErrorRetry']) {
                             $this->pauseOnRetry(++$retries);
                         } else {
                             throw $this->reportAnyErrors($response['ResponseBody'], $response['Status'], null, $response['ResponseHeaderMetadata']);
                         }
                         break;
                     default:
                         $shouldRetry = false;
                         throw $this->reportAnyErrors($response['ResponseBody'], $response['Status'], null, $response['ResponseHeaderMetadata']);
                         break;
                 }
                 /* Rethrow on deserializer error */
             } catch (Exception $e) {
                 require_once 'MarketplaceWebService/Exception.php';
                 // log to db - error
                 $success = 'Error';
                 $this->dblogger->updateLog(array('result' => $e->getMessage(), 'success' => $success));
                 wpla_show_message($actionName . ' request failed with error: ' . $e->getMessage(), 'error');
                 throw new MarketplaceWebService_Exception(array('Exception' => $e, 'Message' => $e->getMessage()));
             }
         } while ($shouldRetry);
     } catch (MarketplaceWebService_Exception $se) {
         throw $se;
     } catch (Exception $t) {
         throw new MarketplaceWebService_Exception(array('Exception' => $t, 'Message' => $t->getMessage()));
     }
     // log to db - parsed request
     // $success = 'Success';
     // $this->dblogger->updateLog( array(
     //     'result'    => json_encode( $response['ResponseBody'] ),
     //     'success'   => $success
     // ));
     return array('ResponseBody' => $response['ResponseBody'], 'ResponseHeaderMetadata' => $response['ResponseHeaderMetadata']);
 }
 function sendSignedRequest($action, $section = false, $params = array(), $version = '2009-01-01', $return_raw = false)
 {
     // $base_url = "https://mws.amazonservices.de/Products/2011-01-01";
     // $host = "mws.amazonservices.de";
     $api_section = $section ? $section . '/' . $version : '';
     $base_params = array('AWSAccessKeyId' => $this->AccessKey, 'Action' => $action, 'SellerId' => $this->SellerId, 'SignatureMethod' => "HmacSHA256", 'SignatureVersion' => "2", 'Timestamp' => gmdate("Y-m-d\\TH:i:s.\\0\\0\\0\\Z", time()), 'Version' => $version);
     $params = array_merge($base_params, $params);
     // debug
     // echo "<pre>params: ";print_r($params);echo"</pre>";#die();
     // Sort the URL parameters
     $url_parts = array();
     foreach (array_keys($params) as $key) {
         $url_parts[] = $key . "=" . str_replace('%7E', '~', rawurlencode($params[$key]));
     }
     sort($url_parts);
     // Construct the string to sign
     $url_string = implode("&", $url_parts);
     $string_to_sign = "GET\n" . $this->api_host . "\n/{$api_section}\n" . $url_string;
     // Sign the request
     $signature = hash_hmac("sha256", $string_to_sign, $this->SecretKey, TRUE);
     // Base64 encode the signature and make it URL safe
     $signature = urlencode(base64_encode($signature));
     $url = 'https://' . $this->api_host . '/' . $api_section . '?' . $url_string . "&Signature=" . $signature;
     // log to db - before request
     $this->dblogger->updateLog(array('callname' => $action, 'request' => $string_to_sign, 'parameters' => maybe_serialize($params), 'request_url' => $url, 'account_id' => $this->account_id, 'market_id' => $this->market_id, 'success' => 'pending'));
     $ch = curl_init();
     curl_setopt($ch, CURLOPT_URL, $url);
     curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
     // curl_setopt($ch, CURLOPT_TIMEOUT, 300);
     curl_setopt($ch, CURLOPT_TIMEOUT, 600);
     curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
     // If you are having problems, try adding this to the end of the curl-setopt block:
     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
     $response = curl_exec($ch);
     $curlinfo = curl_getinfo($ch);
     // echo "<pre>response: ";print_r( htmlspecialchars( $response ) );echo"</pre>";#die();
     // echo "<pre>curl: ";print_r( $curlinfo );echo"</pre>";#die();
     // log to db - after request
     $this->dblogger->updateLog(array('response' => $response, 'http_code' => $curlinfo['http_code'], 'success' => 'HTTP OK'));
     curl_close($ch);
     if ($response === false) {
         // throw new Exception('Server connection is failed. Please try again later.');
         // log to db - parsed request
         $this->dblogger->updateLog(array('result' => 'The response from Amazon was empty. (Server connection is failed. Please try again later.)', 'curl' => maybe_serialize($curlinfo), 'success' => 'Error'));
         wpla_show_message("There was a problem communicating with Amazon. Please try again later. (Response to {$action} request was empty)", 'error');
         return false;
     } else {
         if ($return_raw) {
             return $response;
         }
         $parsed_xml = $this->parseXML($response);
         // echo "<pre>XML: ";print_r( $parsed_xml );echo"</pre>";#die();
         if (isset($parsed_xml->Error)) {
             $success = 'Error';
         } elseif (isset($parsed_xml->GetMatchingProductForIdResult->Error)) {
             $success = 'Error';
         } elseif (isset($parsed_xml->GetCompetitivePricingForASINResult->Error)) {
             $success = 'Error';
         } elseif (isset($parsed_xml->GetLowestOfferListingsForASINResult->Error)) {
             $success = 'Error';
         } elseif (isset($parsed_xml->ListMatchingProductsResult->Error)) {
             $success = 'Error';
         } elseif (isset($parsed_xml->GetOrderResult->Error)) {
             $success = 'Error';
         } elseif (isset($parsed_xml->ListOrderItemsResult->Error)) {
             $success = 'Error';
         } else {
             $success = 'Success';
         }
         // log to db - parsed request
         $this->dblogger->updateLog(array('result' => json_encode($parsed_xml), 'curl' => maybe_serialize($curlinfo), 'success' => $success));
     }
     return $parsed_xml;
 }
 public function createProductFromAmazonListing($listing)
 {
     $lm = new WPLA_ListingsModel();
     if (is_numeric($listing)) {
         $listing = $lm->getItem($listing);
     }
     if (!$listing) {
         return false;
     }
     $listing_id = $listing['id'];
     WPLA()->logger->info('--- createProductFromAmazonListing() - ID: ' . $listing['id'] . ' / ASIN ' . $listing['asin']);
     $account = WPLA_AmazonAccount::getAccount($listing['account_id']);
     if (!$account) {
         return false;
     }
     // init api
     $api = new WPLA_AmazonAPI($account->id);
     // get product details from amazon
     $result = $api->getMatchingProductForId($listing['asin'], 'ASIN');
     $this->request_count++;
     // echo "<pre>getMatchingProductForId() returned: ";print_r($result);echo"</pre>";#die();
     // handle Import Variations As Simple option
     if ($result->success && get_option('wpla_import_variations_as_simple', 0)) {
         $result->product->variation_type = '_single_';
     }
     // handle empty result error
     if ($result->success && empty($result->product->AttributeSets->ItemAttributes->Title)) {
         if (!empty($result->product->GetMatchingProductForIdResult->Error->Message)) {
             $this->lastError = sprintf(__('There was a problem fetching product details for %s.', 'wpla'), $listing['asin']) . '<br><code>' . $result->product->GetMatchingProductForIdResult->Error->Message . '</code>';
         } else {
             $this->lastError = sprintf(__('There was a problem fetching product details for %s.', 'wpla'), $listing['asin']) . ' The product data received from Amazon was empty.';
         }
         return false;
     }
     // first check if product already exists in WooCommerce...
     $post_id = WPLA_ProductBuilder::getProductIdBySKU($listing['sku']);
     if ($post_id) {
         WPLA()->logger->info('found existing product by SKU ' . $listing['sku'] . ' - post_id: ' . $post_id);
         $this->message = "Found existing product for SKU " . $listing['sku'];
         // if this SKU exists, check whether it is a variation or not
         $_product = get_product($post_id);
         // echo "<pre>";print_r($_product);echo"</pre>";#die();
         // handle child variations
         if ('variation' == $_product->product_type) {
             // set parent_id
             $data = array('post_id' => $_product->variation_id, 'parent_id' => $_product->parent->id, 'product_type' => $_product->product_type);
             $lm->updateListing($listing_id, $data);
             $this->message = "Found existing variation for SKU " . $listing['sku'];
         }
     } else {
         // SKU does not exist in WC...
         $variation_type = $result->success ? $result->product->variation_type : '_unknown_';
         $variation_type = is_string($variation_type) ? $variation_type : '_none_';
         // convert empty object to string
         WPLA()->logger->info('no WC product found for SKU ' . $listing['sku'] . ' - type: ' . $variation_type);
         // process child variation - fetch parent item instead
         if ($result->success && $result->product->variation_type == 'child') {
             // foreign imports should have their title updated first
             // $data = array();
             // $data['listing_title'] = $result->product->AttributeSets->ItemAttributes->Title;
             // $lm->updateListing( $listing_id, $data );
             // update listing attributes and title from result
             $lm->updateItemAttributes($result->product->AttributeSets->ItemAttributes, $listing_id);
             // get parent item - new request
             $parent_asin = $result->product->VariationParentASIN;
             $api = new WPLA_AmazonAPI($account->id);
             // new log record
             $parent_result = $api->getMatchingProductForId($parent_asin, 'ASIN');
             $parent_node = $parent_result->success ? $parent_result->product : false;
             $this->request_count++;
             // check for "variations without attributes"
             // if there are no variation attributes on the parent, fall back to creating a simple product instead
             if (0 == self::countVariationChildNodes($parent_node)) {
                 $msg = $listing['asin'] . " seems to be a child of parent ASIN {$parent_asin} - but that parent has no variation attributes set, so it will be imported as a simple product.";
                 WPLA()->logger->warn($msg);
                 wpla_show_message($msg, 'warn');
                 $result->product->variation_type = '_invalid_parent_';
             } else {
                 WPLA()->logger->info($listing['asin'] . " is a child of parent ASIN {$parent_asin}");
                 $result = $parent_result;
             }
         }
         // process parent variation
         if ($result->success && $result->product->variation_type == 'parent') {
             // get parent listing - or create if it doesn't exist yet
             $parent_listing = $this->getOrCreateParentVariation($result, $listing, $account);
             // all further processing should be using the parent variation - including children
             $result->product = $this->parseVariationChildNodes($result->product, $parent_listing, $account);
             // $listing      = $parent_listing; // $listing is supposed to be an array, $parent listing is an object
             $listing_id = $parent_listing->id;
             $listing = $lm->getItem($listing_id);
         }
     }
     // SKU does not exist in WC
     // import product...
     if ($result->success) {
         // update price for foreign imports (imported by ASIN)
         if ('foreign_import' == $listing['source'] && !$listing['price']) {
             $this->updateListingWithLowestPrice($listing, $account);
         }
         // update listing attributes and title from result
         $lm->updateItemAttributes($result->product->AttributeSets->ItemAttributes, $listing_id);
         $listing = $lm->getItem($listing_id);
         // create product
         $woo = new WPLA_ProductBuilder();
         $woo->importSingleProduct($listing, $result->product);
         // post-process variations - add post_id
         if ($result->product->variation_type == 'parent' && isset($result->product->variations)) {
             $this->fixNewVariationListings($result->product->variations, $parent_listing);
         }
         $success = true;
         $errors = '';
         $this->lastPostID = $woo->last_insert_id;
     } elseif (@$result->Error->Message) {
         $errors = sprintf(__('There was a problem fetching product details for %s.', 'wpla'), $listing['asin']) . '<br>Error: ' . $result->Error->Message;
         $success = false;
     } else {
         $errors = sprintf(__('There was a problem fetching product details for %s.', 'wpla'), $listing['asin']);
         $success = false;
     }
     $this->lastError = $errors;
     return $success;
 }
 public function fetchPageContent($listing_url)
 {
     WPLA()->logger->info('fetching URL: ' . $listing_url);
     // fetch HTML content
     $response = wp_remote_get($listing_url, array('timeout' => 15, 'user-agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36'));
     // WPLA()->logger->info("BODY: ".$response['body']);
     // handle errors
     if (is_wp_error($response)) {
         // echo "<pre>";print_r($response);echo"</pre>";
         // $this->showMessage( "Couldn't fetch URL $listing_url - ".$response->get_error_message(), 1, 1 );
         WPLA()->logger->error("Couldn't fetch URL {$listing_url} - " . $response->get_error_message());
         wpla_show_message("Couldn't fetch URL {$listing_url} - " . $response->get_error_message());
         $this->errors[] = "Couldn't fetch URL {$listing_url} - " . $response->get_error_message();
         // doesn't show
         return false;
     }
     if ($response['response']['code'] != 200) {
         // echo "<pre>Couldn't fetch URL $listing_url - server returned error code ".$response['response']['code']."</pre>";
         // $this->showMessage( "Couldn't fetch URL $listing_url - server returned error code ".$response['response']['code'], 1, 1 );
         WPLA()->logger->error("Couldn't fetch URL {$listing_url} - server returned error code " . $response['response']['code']);
         wpla_show_message("Couldn't fetch URL {$listing_url} - server returned error code " . $response['response']['code']);
         $this->errors[] = "Couldn't fetch URL {$listing_url} - server returned error code " . $response['response']['code'];
         return false;
     }
     // log to db - enable only for debugging (limited to 64k)
     // $this->dblogger = new WPLA_AmazonLogger();
     // $this->dblogger->updateLog( array(
     //     'callname'    => 'wp_remote_get',
     //     'request'     => 'internal action hook',
     //     'parameters'  => maybe_serialize( $listing_url ),
     //     'request_url' => $listing_url,
     //     'account_id'  => '',
     //     'market_id'   => '',
     //     'response'    => $response['body'],
     //     'result'      => json_encode( $response ),
     //     'success'     => 'Success'
     // ));
     // return HTML content
     $html_content = $response['body'];
     $this->success = true;
     // echo "<pre>";htmlspecialchars($html_content);echo"</pre>";#die();
     return $html_content;
 }
 public function importOrder($order, $account)
 {
     global $wpdb;
     $table = $wpdb->prefix . self::TABLENAME;
     // skip processing if requests are throttled already
     if ($this->throttling_is_active == true) {
         return false;
     }
     // if ( ! is_object($order) )
     // 	echo "<pre>order is not an object: ";print_r($order);echo"</pre>";die();
     // check if order exists in WPLA and is already up to date (TODO: optimize)
     if ($id = $this->order_id_exists($order->AmazonOrderId)) {
         $om = new WPLA_OrdersModel();
         $amazon_order = $om->getItem($id);
         if ($amazon_order['LastTimeModified'] == $this->convertIsoDateToSql($order->LastUpdateDate)) {
             WPLA()->logger->info('Order ' . $order->AmazonOrderId . ' has not been modified since ' . $amazon_order['LastTimeModified'] . ' and is up to date.');
             wpla_show_message('Order ' . $order->AmazonOrderId . ' has not been modified since ' . $amazon_order['LastTimeModified'] . ' and is up to date.');
             return null;
         }
     }
     $data = array('order_id' => $order->AmazonOrderId, 'status' => $order->OrderStatus, 'total' => isset($order->OrderTotal->Amount) ? $order->OrderTotal->Amount : '', 'currency' => isset($order->OrderTotal->CurrencyCode) ? $order->OrderTotal->CurrencyCode : '', 'buyer_name' => isset($order->BuyerName) ? $order->BuyerName : '', 'buyer_email' => isset($order->BuyerEmail) ? $order->BuyerEmail : '', 'PaymentMethod' => isset($order->PaymentMethod) ? $order->PaymentMethod : '', 'ShippingAddress_City' => isset($order->ShippingAddress->City) ? $order->ShippingAddress->City : '', 'date_created' => $this->convertIsoDateToSql($order->PurchaseDate), 'LastTimeModified' => $this->convertIsoDateToSql($order->LastUpdateDate), 'account_id' => $account->id, 'details' => json_encode($order));
     // fetch order line items from Amazon - required for both new and updated orders
     $this->api = new WPLA_AmazonAPI($account->id);
     $items = $this->api->getOrderLineItems($order->AmazonOrderId);
     $data['items'] = maybe_serialize($items);
     // check if ListOrderItems request is throttled
     // if true, skip ALL further requests / order processing until next cron run
     if (is_object($items) && isset($items->Error->Message)) {
         $this->throttling_is_active = true;
         wpla_show_message('ListOrderItems requests are throttled. Skipping further order processing until next run.', 'warn');
         return false;
     }
     // check if order exists in WPLA
     if ($id = $this->order_id_exists($order->AmazonOrderId)) {
         // load existing order record from wp_amazon_orders
         $ordersModel = new WPLA_OrdersModel();
         $wpla_order = $ordersModel->getItem($id);
         // check if order status was updated
         // if pending -> Canceled: revert stock reduction by processing history records
         // if pending -> Shipped / Unshipped: create WooCommerce order if enabled (done in createOrUpdateWooCommerceOrder())
         if ($order->OrderStatus != $wpla_order['status']) {
             $old_order_status = $wpla_order['status'];
             $new_order_status = $order->OrderStatus;
             // add history record
             $history_message = "Order status has changed from " . $old_order_status . " to " . $new_order_status;
             $history_details = array('id' => $id, 'new_status' => $new_order_status, 'old_status' => $old_order_status, 'LastTimeModified' => $data['LastTimeModified']);
             $this->addHistory($data['order_id'], 'order_status_changed', $history_message, $history_details);
             // if pending -> Canceled: revert stock reduction by processing history records
             if ($old_order_status == 'Pending' && $new_order_status == 'Canceled') {
                 // revert stock reduction
                 $this->revertStockReduction($wpla_order);
                 // add history record
                 $history_message = "Stock levels have been replenished";
                 $history_details = array('id' => $id);
                 $this->addHistory($data['order_id'], 'revert_stock', $history_message, $history_details);
             }
         }
         // if status changed
         // update existing order
         $wpdb->update($table, $data, array('order_id' => $order->AmazonOrderId));
         $this->updated_count++;
         // TODO: update WooCommerce order!
         // add history record
         $history_message = "Order details were updated - " . $data['LastTimeModified'];
         $history_details = array('id' => $id, 'status' => $data['status'], 'LastTimeModified' => $data['LastTimeModified']);
         $this->addHistory($data['order_id'], 'order_updated', $history_message, $history_details);
     } else {
         // insert new order
         $wpdb->insert($table, $data);
         $this->imported_count++;
         $id = $wpdb->insert_id;
         echo $wpdb->last_error;
         // add history record
         $history_message = "Order was added with status: " . $data['status'];
         $history_details = array('id' => $id, 'status' => $data['status'], 'LastTimeModified' => $data['LastTimeModified']);
         $this->addHistory($data['order_id'], 'order_inserted', $history_message, $history_details);
         // process ordered items - unless order has been cancelled
         if ($data['status'] != 'Canceled') {
             foreach ($items as $item) {
                 // process each item and reduce stock level
                 $success = $this->processListingItem($item, $order);
             }
         }
     }
     // if order does not exist
     return $id;
 }
 static function updateAmazonPrice($item, $target_price, $verbose)
 {
     // make sure we don't go below min_price
     if ($item->min_price) {
         $target_price = max($target_price, $item->min_price);
     }
     // make sure we don't go above max_price (prevent feed error)
     if ($item->max_price) {
         $target_price = min($target_price, $item->max_price);
     }
     // skip if there is no change in price
     if ($target_price == $item->price) {
         return false;
     }
     // update amazon price in WooCommerce
     update_post_meta($item->post_id, '_amazon_price', $target_price);
     // update price in listings table
     $data = array('price' => $target_price, 'pnq_status' => 1);
     WPLA_ListingsModel::updateWhere(array('id' => $item->id), $data);
     // show message
     if ($verbose) {
         wpla_show_message($item->sku . ': price was changed from ' . $item->price . ' to <b>' . $target_price . '</b>');
     }
     // price was changed
     return true;
 }
 static function checkForDeletedProducts()
 {
     global $wpdb;
     $items = $wpdb->get_var("\n            SELECT count(a.id)\n            FROM {$wpdb->prefix}amazon_listings a\n            LEFT JOIN {$wpdb->posts} p ON a.post_id = p.ID\n            WHERE a.post_id <> 0\n              AND p.ID IS NULL\n            ORDER BY a.post_id\n        ");
     if (!empty($items)) {
         $link_url = wp_nonce_url('admin.php?page=wpla-tools&tab=developer&action=wpla_fix_deleted_products', 'wpla_tools_page');
         $link_button = '&nbsp;&nbsp;<a href="' . $link_url . '" class="button button-small button-primary">Fix It Now</a>';
         wpla_show_message(sprintf('Warning: There are %s listings linked to missing WooCommerce products.<br>These items need to be removed from WP-Lister to be able to list or import them again.', $items) . $link_button, 'error');
     }
 }
 function updateMarketplaceParticipations()
 {
     if (!$this->id) {
         return;
     }
     $api = new WPLA_AmazonAPI($this->id);
     $result = $api->listMarketplaceParticipations();
     if (@$result->success) {
         $this->allowed_markets = maybe_serialize($result->allowed_markets);
         $this->update();
     } elseif ($result->ErrorMessage) {
         wpla_show_message($result->ErrorMessage, 'error');
     }
     return $result;
 }