table_options = $wpdb->prefix . "amazonpress_options";
$this->table_log = $wpdb->prefix . "amazonpress_log";
$this->table_cache = $wpdb->prefix . "amazonpress_cache";
$this->table_products = $wpdb->prefix . "amazonpress_products";
// Load Options
$this->options = get_option('amazonPressOptions');
$this->setValidation();
if(defined('AUTH_KEY'))
$this->encKey = AUTH_KEY;
else
$this->encKey = ABSPATH . 'fwa CGkK-5{ao[XYn7hq7wLvDMN#^PSIZ)$19lPI+UpH51vD|gYe%9s)j#5E-.lu';
// Default REST Parameters (can be over-ridden)
$this->params = array(
'Operation' => 'ItemSearch',
'SearchIndex' => 'Books',
'ResponseGroup' => 'Medium,Images'
);
// If we're ready to run live, activate the controls.
if( isset($this->options['ServicePath'])
AND isset($this->options['AWSAccessKeyId'])
AND (
isset($this->options['AWSSecretAccessKeyId'])
OR time() < 1250294400
)
AND isset($this->options['AssociateTag'])
AND isset($this->options['DefaultTags'])
AND isset($this->options['DefaultSearchField'])
AND isset($this->options['MaxResults'])
AND isset($this->options['Version'])
AND function_exists('simplexml_load_string') )
{
$this->live = true;
}
}
/**
* Define the validation parameters used to check submitted admin form code.
*/
function setValidation()
{
$this->validate['SortBy'] = array(
'random' => true,
'salesrank' => true,
'-salesrank' => true,
'listprice' => true,
'-listprice' => true
);
$this->validate['DisplayPosition'] = array(
'0' => true,
'1' => true
);
}
function checkInstall($autoInstall = false)
{
// Plugin options are not installed, implying that the plugin itself has not yet been installed either.
if(!isset($this->options['Version']) OR $this->options['Version'] < $this->version)
{
if($autoInstall) return($this->doInstall());
else
{
if(!strpos($_SERVER['REQUEST_URI'], 'amazonpress/php/amazonpress.class.php'))
$this->admin_notices[] = 'AmazonPress is almost ready. Please visit the management page (Tools > AmazonPress) to complete the ' .
'installation process and enter your Amazon credentials.';
return(false);
}
}
else
return(true);
}
function unInstall()
{
global $wpdb;
$sql = "DROP TABLE `" . $this->table_cache . "`;";
$wpdb->query($sql);
$sql = "DROP TABLE `" . $this->table_log . "`;";
$wpdb->query($sql);
delete_option('amazonPressOptions');
}
function doInstall()
{
global $wpdb;
if(!function_exists('simplexml_load_string')) {
$this->admin_alert("WARNING: AmazonPress currently only works on servers running PHP v 5.x or higher.");
return(false);
}
// Plugin options are not installed, implying that the plugin itself has not yet been installed either.
if(!isset($this->options['Version'])) {
$this->admin_alert("Previous installation not found. Installing necessary tables now.");
$sql = "CREATE TABLE IF NOT EXISTS `" . $this->table_cache . "` (
`keyword` varchar(255) NOT NULL,
`timestamp` bigint(20) unsigned zerofill NOT NULL,
`data` longblob NOT NULL,
`blocked` blob NOT NULL,
PRIMARY KEY (`keyword`)
) ENGINE = MYISAM ;";
$result = $wpdb->query($sql);
if($result === false) {
$this->admin_alert("Failed to create table `" . $this->table_cache . "`.");
return(false);
}
$sql = "CREATE TABLE IF NOT EXISTS `" . $this->table_log . "` (
`id` bigint(20) NOT NULL auto_increment,
`timestamp` bigint(20) unsigned zerofill NOT NULL,
`message` text NOT NULL,
PRIMARY KEY (`id`),
KEY `timestamp` (`timestamp`)
) ENGINE=MyISAM ;";
$result = $wpdb->query($sql);
if($result === false) {
$this->admin_alert("Failed to create table `" . $this->table_log . "`.");
return(false);
}
$sql = "CREATE TABLE IF NOT EXISTS `" . $this->table_products . "` (
`id` bigint(20) unsigned zerofill NOT NULL auto_increment,
`cache_id` varchar(255) NOT NULL,
`data` longblob NOT NULL,
`asin` varchar(255) NOT NULL,
`blocked` tinyint(1) NOT NULL,
`sticky` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;";
$result = $wpdb->query($sql);
if($result === false) {
$this->admin_alert("Failed to create table `" . $this->table_products . "`.");
return(false);
}
$this->options = array(
'Locale' => 'United States',
'LocaleTipTag' => 'CJ7COUK-20',
'ServicePath' => 'http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService',
'AWSAccessKeyId' => '',
'AssociateTag' => '',
'SearchFrom' => 'categories',
'DefaultTags' => '',
'ShowOnPosts' => true,
'ShowOnPages' => true,
'ShowOnHome' => true,
'ShowOnCategories' => true,
'ShowOnTags' => true,
'ShowOnSearch' => true,
'TitleText' => '
Related Reading:
',
'DefaultSearchField'=> 'Keywords',
'MaxResults' => 5,
'ShowText' => true,
'ShowImages' => true,
'CacheExpiry' => 60*24,
'AllowTip' => true,
'StyleSheet' => 'style.css',
'WidgetOptions' => array(
'Title' => false,
'DefaultTags' => false),
'Version' => $this->version
);
update_option('amazonPressOptions', $this->options);
}
elseif($this->options['Version'] < $this->version)
{
$this->admin_alert("Plugin files version do not match installed version. Running upgrade scripts now.");
if($this->options['AllowTip'] != true) $this->admin_alert("I notice you don't have the 'Tip' option enabled. If you'd like to help " .
"support the ongoing development of this plugin, you can find that option under the 'Options->Display' tab. " .
"Enabling the tip option is a nice way you could say thank you.");
if($this->options['Version'] < '1.1')
{
$this->options['Version'] = '1.1';
$this->options['ShowOnPosts'] = true;
$this->options['ShowOnPages'] = true;
$this->options['ShowOnHome'] = true;
$this->options['ShowOnCategories'] = true;
$this->options['ShowOnTags'] = true;
$this->options['ShowOnSearch'] = true;
update_option('amazonPressOptions', $this->options);
$this->admin_alert("Plugin upgraded to v. 1.1 This version allows you to limit display of products on various categories of blog pages. Some CSS tweaks were also added.");
}
if($this->options['Version'] < '1.2')
{
$this->options['Version'] = '1.2';
$this->options['Locale'] = 'United States';
$this->options['LocaleTipTag'] = 'CJ7COUK-20';
update_option('amazonPressOptions', $this->options);
$this->admin_alert("Plugin upgraded to v. 1.2 This version allows you to select the locale from where you wish your products to be selected. Please be aware that in order to collect referral rewards, your associate account must be registered in the same locale as you are using to pull products from.");
}
if($this->options['Version'] < '1.3')
{
/*
* Bug fixes
* Limit hits to amazon
* Select Search index to be displayed
* Clear cache button
* Pretty icon for notices
*/
$this->options['Version'] = '1.3';
if(!$this->options['SearchIndex']) $this->options['SearchIndex'] = $this->params['SearchIndex'];
update_option('amazonPressOptions', $this->options);
$this->admin_alert("Plugin upgraded to v. 1.3 -- Tired of promoting just books? This version gives you access to promote many different types of products from Amazon. Simply check off the products you would like to promote in the Items to Feature field below.");
}
if($this->options['Version'] < '1.4')
{
/*
* Updates to Amazon security and authentication protocol.
*/
$this->options['Version'] = '1.4';
update_option('amazonPressOptions', $this->options);
$this->admin_alert("Plugin upgraded to v. 1.4 -- Updated to support new Amazon security protocols. You will need to enter your AWS Secret Access Key in the appropriate field below.");
}
if($this->options['Version'] < '1.5')
{
/*
* Minor bug fixes and tweaks.
*/
$this->options['Version'] = '1.5';
update_option('amazonPressOptions', $this->options);
$this->admin_alert("Plugin upgraded to v. 1.5 -- Minor bug fixes and tweaks to support a wider range of Amazon keys.");
}
if($this->options['Version'] < '1.6')
{
/*
* Minor bug fixes and tweaks.
*/
$this->options['Version'] = '1.6';
$this->options['StyleSheet'] = 'style.css';
$this->options['ShowText'] = true;
update_option('amazonPressOptions', $this->options);
$this->admin_alert("Plugin upgraded to v. 1.6 " .
"Minor bug fixes when zero results are returned. " .
"Better admin screen organization. " .
"Upgraded admin security. " .
"Allowed display of only images or only text. " .
"Allowed for custom stylesheets.
" .
"NOTE: If products are not showing on your blog, you may need to clear the built-in product cache.");
}
if($this->options['Version'] < '1.7')
{
/*
* Minor bug fixes and tweaks.
*/
$this->options['Version'] = '1.7';
update_option('amazonPressOptions', $this->options);
$this->admin_alert("Plugin upgraded to v. 1.7 " .
"Expanded debugging and error control. " .
"Allowed display of cached Amazon data in admin console.");
}
if($this->options['Version'] < '1.8')
{
/*
* Changing error logging to use separate table, rather than wp-options.
*/
$sql = "CREATE TABLE IF NOT EXISTS `" . $this->table_log . "` (
`id` bigint(20) NOT NULL auto_increment,
`timestamp` bigint(20) unsigned zerofill NOT NULL,
`message` text NOT NULL,
PRIMARY KEY (`id`),
KEY `timestamp` (`timestamp`)
) ENGINE=MyISAM ;";
$result = $wpdb->query($sql);
if($result === false) {
$this->admin_alert("Failed to create table `" . $this->table_log . "`.");
return(false);
}
$this->options['WidgetOptions'] = array(
'Title' => false,
'DefaultTags' => false);
$this->options['ImageSize'] = '';
$this->options['Version'] = '1.8';
unset($this->options['error_log']);
update_option('amazonPressOptions', $this->options);
$this->admin_alert("Plugin upgraded to v. 1.8 " .
"Added sidebar Widget controls. " .
"Enabled display of product descriptions. " .
"Updated display CSS for better theme compatibility. " .
"Expanded debugging and error control. " .
"");
}
if($this->options['Version'] < '1.9')
{
$this->options['SortBy'] = 'random';
$this->options['DisplayPosition'] = '0';
$sql = "ALTER TABLE `" . $this->table_cache . "` ADD `blocked` BLOB NOT NULL";
$result = $wpdb->query($sql);
if($result === false) {
$this->admin_alert("Failed to create table `" . $this->table_log . "`.");
return(false);
}
$this->options['Version'] = '1.9';
update_option('amazonPressOptions', $this->options);
$this->admin_alert("Plugin upgraded to v. " . $this->options['Version'] . " " .
"Added ability to sort results on the whole blog, as well as on individual posts. " .
"Added ability to chose display position relative to page/post content. " .
"Reworked product caching for more robust storage and more advanced potential future features. " .
"Added ability to block specific products from being displayed. " .
"");
}
if($this->options['Version'] < '2.0')
{
$this->options['Version'] = '2.0';
update_option('amazonPressOptions', $this->options);
$this->admin_alert("Plugin upgraded to v. " . $this->options['Version'] . " " .
"Removed Amazon products from appearing in blog feeds in order to better comply with Amazon regulations. " .
"");
}
return(true);
}
}
// Function to show notices using the built-in wp controls when appropriate.
function wp_admin_notices()
{
foreach($this->admin_notices as $msg)
{
$this->admin_alert($msg);
$this->debug($msg);
}
}
// Allow showing an alert to the user when necessary.
function admin_alert($msg = '', $log = true)
{
if($msg) echo "
$msg
";
if($log) $this->debug($msg);
}
function error_handler($errno, $errstr, $errfile=false, $errline=false)
{
$msg = 'PHP Error: ' . $errstr;
switch($errno)
{
case E_ERROR:
case E_USER_ERROR:
$this->debug($msg);
die();
break;
case E_WARNING:
case E_USER_WARNING:
$this->debug($msg);
break;
default:
break;
}
}
// Allow limited logging of errors.
function log_error($msg = '')
{
if(!isset($this->options['Version'])) return;
// Legacy code to support error logging until upgrade can complete.
if($this->options['Version'] < '1.8')
{
if(!is_array($this->options['error_log'])) $this->options['error_log'] = array();
array_unshift($this->options['error_log'], date('F j, Y, g:i a ') . $msg);
if(count($this->options['error_log']) > 100)
{
array_pop($this->options['error_log']);
}
update_option('amazonPressOptions', $this->options);
}
else
{
global $wpdb;
$sql = "INSERT INTO `" . $this->table_log . "` (`timestamp`, `message`) " .
"VALUES (" .
"'" . time() . "', " .
"'" . addslashes($msg) . "');";
$wpdb->query($sql);
}
}
function show_error($msg = '')
{
if($this->debug_visible == true)
{
echo "\n
$msg
";
}
}
function debug($msg = '', $level = '1')
{
switch($this->debug_mode)
{
case 'basic':
if($level <= '1') {
$this->log_error($msg);
if($this->debug_visible == true) $this->show_error($msg);
}
break;
case 'all':
if($level <= '2') {
$this->log_error($msg);
if($this->debug_visible == true) $this->show_error($msg);
}
break;
default:
break;
}
}
function getpath($path, $username = false, $password = false)
{
$this->debug("Using built-in getpath function to load data. Slower, but should work.");
// Test URL and ensure that it is valid.
if(false !== $username AND false !== $password)
$match = "^([a-z]{2,10})\://" . $username . "\:" . $password . "([a-z0-9\.\-]+)/?([^\?]*)(.*)$";
else
$match = "^([a-z]{2,10})\://([a-z0-9\.\-]+)(/?[^\?]*)(.*)$";
// Return false if the path does not look like a url.
if(!eregi($match, $path, $regs)) {
return(false);
}
else {
list($path, $protocol, $hostname, $request, $query) = $regs;
// Determine port protocol.
switch(strtoupper($protocol))
{
case "HTTPS":
$port = 443;
break;
case "FTP":
$port = 21;
break;
default:
$port = 80;
break;
}
}
// Load url data
$fp = fsockopen($hostname, $port, $errno, $errstr, 10);
if (!$fp) {
echo "$errstr ($errno) \n";
return(false);
} else {
$out = "GET " . $request . $query . " HTTP/1.0\r\n";
$out .= "Host: $hostname\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
$data = '';
while (!feof($fp)) {
$data .= fgets($fp);
}
fclose($fp);
$data_start = strpos($data, "\r\n\r\n");
$header = substr($data, 0, $data_start);
$body = substr($data, $data_start + 4, strlen($data));
$regs = "";
if(eregi("[\r\n]+Location\: *([^\r\n]+)", $header, $regs) AND eregi("HTTP/[0-9]*\.[0-9]*[ ]*3[0-9]{2}", $header))
{
$location = $regs[1];
return($this->getpath($location));
}
else
{
return($body);
}
}
}
// Main data loader.
function request_data($new_params = array())
{
// Only run a request from Amazon once per page load in order to comply with amazon regs.
if($this->done_request == true) {
$this->debug("Do not run request for '" . implode(",", $new_params) . "' in order to comply " .
"with Amazon speed limit regulations");
return(false);
}
if($this->options['SearchIndex']) $this->params['SearchIndex'] = $this->options['SearchIndex'];
// Update the options with anything passed on the function.
$params = array_merge($this->params, $new_params);
// Create the request
# $request = $this->options['ServicePath']
# . "&AWSAccessKeyId=" . $this->options['AWSAccessKeyId']
# . "&AssociateTag=" . $this->options['AssociateTag'];
// Determine region based on predefined service path.
if(eregi('ecs\.amazonaws\.([^/]+)/', $this->options['ServicePath'], $regs))
{
$region = $regs[1];
}
$pubKey = $this->options['AWSAccessKeyId'];
$priKey = $this->options['AWSSecretAccessKeyId'];
$request['AssociateTag'] = $this->options['AssociateTag'];
// Iterate through the parameters adding to the request.
foreach($params as $key=>$param)
{
if($param != "")
{
# $request .= "&" . $key . "=" . $param;
$request[$key] = $param;
}
}
$xml_data = $this->aws_signed_request($region, $request, $pubKey, $priKey);
$this->done_request = true;
if($xml_data) return($xml_data);
}
// Load related reading either from database table, or from Amazon.com
function load($keyword)
{
global $wpdb;
$keyword = addslashes($keyword);
$sql = "SELECT * FROM " . $this->table_cache . " WHERE `keyword` = '" . $keyword . "' LIMIT 0,1";
$data = $wpdb->get_row($sql, ARRAY_A);
if($data !== false AND $data['keyword'] != "")
{
$data['keyword'] = stripslashes($data['keyword']);
$data['data'] = stripslashes($data['data']);
return($data);
}
}
// Save related reading to cache when necessary
function save($keyword, $xml)
{
global $wpdb;
$keyword = trim(addslashes($keyword));
$data = trim(addslashes($xml));
$timestamp = time() + ($this->options['CacheExpiry']*60);
$sql = "SELECT * FROM " . $this->table_cache . " WHERE `keyword` = '" . $keyword . "' LIMIT 0,1";
$existing_data = $wpdb->get_row($sql, ARRAY_A);
if($existing_data['keyword'] != "")
$sql = "UPDATE " . $this->table_cache . " SET `timestamp` = '$timestamp', `data` = '$data' WHERE `keyword` = '" . $keyword . "' LIMIT 1;";
else
$sql = "INSERT INTO " . $this->table_cache . " (`keyword`, `timestamp`, `data`) VALUES ('$keyword', '$timestamp', '$data');";
$results = $wpdb->query($sql);
return;
}
/**
* Update the cached entry to block a given ASIN.
*/
function block($keyword, $asin)
{
global $wpdb;
$keyword = trim(addslashes($keyword));
$asin = trim(addslashes($asin));
$sql = "SELECT * FROM " . $this->table_cache . " WHERE `keyword` = '" . $keyword . "' LIMIT 0,1";
$existing_data = $wpdb->get_row($sql, ARRAY_A);
if($existing_data['keyword'] != "")
{
$blocked = stripslashes($existing_data['blocked']);
if(trim($blocked) != "") $blocked = explode('|', $blocked);
else $blocked = array();
$key = array_search($asin, $blocked);
if($key === false)
{
$blocked[] = $asin;
}
$blocked = implode('|', $blocked);
$blocked = addslashes($blocked);
$sql = "UPDATE " . $this->table_cache . " SET `blocked`='$blocked' WHERE `keyword` = '" . $keyword . "' LIMIT 1;";
$wpdb->query($sql);
}
else
$this->admin_alert("Unable to block this product. The keyword doesn't exist in the cache.");
return;
}
/**
* Remove a previous block on an ASIN
*/
function unblock($keyword, $asin)
{
global $wpdb;
$keyword = trim(addslashes($keyword));
$asin = trim(addslashes($asin));
$sql = "SELECT * FROM " . $this->table_cache . " WHERE `keyword` = '" . $keyword . "' LIMIT 0,1";
$existing_data = $wpdb->get_row($sql, ARRAY_A);
if($existing_data['keyword'] != "")
{
$blocked = stripslashes($existing_data['blocked']);
if(trim($blocked) != "") $blocked = explode('|', $blocked);
else $blocked = array();
$key = array_search($asin, $blocked);
if(false !== $key)
unset($blocked[$key]);
if(count($blocked) > 0) $blocked = implode('|', $blocked);
else $blocked = '';
$blocked = addslashes($blocked);
$sql = "UPDATE " . $this->table_cache . " SET `blocked`='$blocked' WHERE `keyword` = '" . $keyword . "' LIMIT 1;";
$wpdb->query($sql);
}
else
$this->admin_alert("Unable to block this product. The keyword doesn't exist in the cache.");
return;
}
// Specific search functions
function search($search_keywords, $searchResults=false, $searchField=false, $searchIndex=false, $options = array())
{
global $wpdb;
$this->debug("Searching For: '$search_keywords'", 2);
if($searchResults == false) $searchResults = $this->options['MaxResults'];
if($searchField == false) $searchField = $this->options['DefaultSearchField'];
if($searchIndex == false) $searchIndex = $this->params['SearchIndex'];
$keywords = explode(",", $search_keywords);
$tmp_items = array();
foreach($keywords as $word)
{
if(trim($word) != "")
{
$xml_data = false;
$data = false;
$data = $this->load(trim($word));
$blocked = array();
if($data['data'])
{
$blocked = stripslashes($data['blocked']);
if(trim($blocked) != "") $blocked = explode('|', $blocked);
else $blocked = array();
}
if($data['data'] != "" AND ($data['timestamp'] > time() OR $this->done_request == true)) {
$this->debug("Loading from memory for keyword: '$word'.", 2);
$xml_data = $data['data'];
$memCached = true;
}
elseif(!$this->done_request)
{
$new_params = array(
$searchField => urlencode(trim($word))
);
$this->debug("Sending request to amazon for keyword: '$word'.");
$xml_data = $this->request_data($new_params);
if($xml_data)
{
libxml_use_internal_errors(true);
$xml = simplexml_load_string($xml_data);
if($xml)
{
$this->debug("Received XML data from Amazon for word '$word'. Saving to cache.", 2);
$this->save($word, $xml_data);
$memCached = false;
}
else
{
libxml_clear_errors();
$this->debug("Unable to properly process XML data received for word '$word'. Attempting to use pre-cached data if available.");
$xml_data = $data['data'];
$memCached = true;
}
}
else
$this->debug("Failed to receive any valid XML from Amazon for word '$word'.");
}
else
{
$this->debug("Skipped searching for word '$word' because we should only send one request to Amazon per page load. Right now, we're done.", 2);
}
if($xml_data)
{
$xml = simplexml_load_string($xml_data);
if(isset($xml))
{
$items = $xml->Items->Item;
if(count($items) > 0)
{
$counter = 0;
foreach($xml->Items->Item as $item) {
if(array_search($item->ASIN, $blocked) === false)
$tmp_items[] = $item;
}
}
elseif(isset($xml->Items->Request->Errors))
{
if(!$memCached)
{
$this->debug("Error returned from Amazon for word '$word'.");
$this->debug($xml->Items->Request->Errors->Error->Message);
}
else
{
$this->debug("Detected previously cached errors for this word '$word' from Amazon.");
$this->debug($xml->Items->Request->Errors->Error->Message);
}
}
else
{
$this->debug("No results or errors were returned for word '$word'.");
}
}
else
$this->debug("Unable to properly load XML string for word '$word'.");
}
else
$this->debug("No XML data detected for word '$word'.", 2);
}
}
if(!$tmp_items) {
$this->debug('No results found related to the keyword(s): ' . $search_keywords);
return(false);
}
// Sort the products according to the requested value.
$items = array();
$counter = 0;
if(!isset($options['SortBy'])) $options['SortBy'] = '';
switch($options['SortBy'])
{
case 'salesrank':
foreach($tmp_items as $tmp_item)
{
$id = (int) $tmp_item->SalesRank;
if($id)
{
$items[$id] = $tmp_item;
}
}
ksort($items);
$items = array_slice($items, 0, $searchResults);
break;
case '-salesrank':
foreach($tmp_items as $tmp_item)
{
$id = (int) $tmp_item->SalesRank;
if($id)
{
$items[$id] = $tmp_item;
}
}
krsort($items);
$items = array_slice($items, 0, $searchResults);
break;
case 'listprice':
foreach($tmp_items as $tmp_item)
{
if($tmp_item->OfferSummary->LowestNewPrice->Amount)
$id = (int) $tmp_item->OfferSummary->LowestNewPrice->Amount;
else
$id = (int) $tmp_item->ItemAttributes->ListPrice->Amount;
if($id)
{
$items[$id] = $tmp_item;
}
}
ksort($items);
$items = array_slice($items, 0, $searchResults);
break;
case '-listprice':
foreach($tmp_items as $tmp_item)
{
if($tmp_item->OfferSummary->LowestNewPrice->Amount)
$id = (int) $tmp_item->OfferSummary->LowestNewPrice->Amount;
else
$id = (int) $tmp_item->ItemAttributes->ListPrice->Amount;
if($id)
{
$items[$id] = $tmp_item;
}
}
krsort($items);
$items = array_slice($items, 0, $searchResults);
break;
case 'random':
default:
while($counter++ < count($tmp_items)*2)
{
$rand = rand(0, count($tmp_items)-1);
$tmp_item = $tmp_items[$rand];
$id = $tmp_item->ASIN;
if(!$items["$id"]) $items["$id"] = $tmp_item;
if(count($items) >= $searchResults) break;
}
break;
}
return($items);
}
/**
* Main function to display related Amazon products.
*/
function display($keywords, $echo = true, $params = array())
{
$options = $this->options;
if(!isset($params['class'])) $params['class'] = 'amazonpress';
foreach($params as $key=>$value)
{
$options[$key] = $value;
}
$old_error_handler = set_error_handler(array($this, 'error_handler'));
if($old_error_handler === false) $this->debug('Unable to set custom error handler.');
$items = $this->search($keywords, false, false, false, $options);
if($items) $numBooks = count($items);
if($numBooks == 0 AND $keywords != $options['DefaultTags'])
{
$this->debug("No items found for keywords '$keywords'. Searching with default tags: '" . $options['DefaultTags'] . "'");
$items = $this->search($options['DefaultTags'], false, false, false, $options);
$numBooks = count($items);
$this->debug("Number of Items: $numBooks", 2);
}
$result = '';
if($numBooks > 0)
{
// Allow for tipping the author, if enabled
if($options['AllowTip'] == true)
{
// If there is only one result, show the author's link 50% of the time.
if($numBooks == 1)
$tip_random_number = rand(1, 2);
else
$tip_random_number = rand(1, $numBooks);
}
$result = "