Signing Amazon Requests with PHP

By Jeff Denton Sep 3, 2009

On August 15, 2009 Amazon changed the way it accepts API requests. API requests must now be signed using a painful and error prone 10-step process outlined at the Amazon Developer Guide website. There are a few examples for PHP floating around the web right now but I had to try them all and do a little tweaking myself to get this to work.  The code below is based on this blog post over at Every Good Path.

I'm not going to explain this line by line but I will say that the following request is designed to search for books by ISBN number and return the book image, author, Amazon link, etc.  Have fun!

 

$request =  'Service=AWSECommerceService&'.
				'AWSAccessKeyId='.AMAZON_ACCESS_KEY_ID.'&'.
				'Operation=ItemSearch&'.					
				'Keywords='.$itemISBN.'&'.
				'SearchIndex=Books&'.
				'ResponseGroup=Images,ItemAttributes,Small&'.
				'Version=2009-01-06&'.
				'Timestamp='.gmdate("Y-m-d\TH:i:s\Z");
	
	// encode url - replace commas w/ %2C, replace colon w/ %3A
	// Could use urlencode($request) here, but $request may already be partially encoded
	$request = str_replace(',','%2C', $request);
	$request = str_replace(':','%3A', $request);
	
	// break request string into key/value pairs,
	$reqarr = explode('&',$request);

	// sort on byte value
	sort($reqarr);

	// tie back together w/ &'s
	$string_to_sign = implode("&", $reqarr);
	$string_to_sign = "GET\nwebservices.amazon.com\n/onca/xml\n".$string_to_sign;

	$signature = urlencode(base64_encode(hash_hmac("sha256", $string_to_sign, AMAZON_SECRET_ACCESS_KEY, True)));

	$request .= '&Signature='.$signature;
	$request = 'http://webservices.amazon.com/onca/xml?'.$request;
	
	$response  = file_get_contents($request);
	$amazonXML = simplexml_load_string($response);	

 

0
In coding

The Sciptaculous Autocompleter Disappears in IE!

By Jeff Denton Jun 30, 2009

Recently, a client was having trouble with an autocompleter implementation I had built for them.  This particular application contains a call log module that Autocomplete screenshotallows the client to log every contact made with a customer: phone, walk-in, email, etc.  In order to speed up call log entries while on the phone, I created a customer lookup feature that includes an autocompleter on the last name field.  The client begins typing in the last name field and a list of potential matches pops up. The client selects the correct customer if they exist, the customer information populates and the client can then begin entering information about this particular contact.

The contact log has reached over 2000 entries by now and there are many customers in the database with similar/same last name. This causes the autocomplete list to extend past the defined height of the container div in many instances which in turn causes scroll bars to appear.  Great - everything working as it should.  But...in IE 7 and 8 clicking on the scroll bars to view the hidden content causes the div and the autocomplete list to vanish!  Oops! In Firefox, everything works fine.

Here's a great forum post on how to fix this problem but I'll lay it out here a little more cleanly. The application UI is built on Prototype and Scriptaculous so we're using Ajax.Autocompleter to make this happen.

We're assuming the autocomplete textbox id is nameLast and the autocomplete div id is nameLastAutocompleter. First, replace your autocompete instantiation code with something like this:

var autocompleteLastName = new Ajax.Autocompleter('nameLast', 'nameLastAutocompleter', memberLookupLastNameURL, {paramName: "autoText", minChars: 3, updateElement: this.returnAutocompleterFieldsMember});
		
Event.observe('nameLastAutocompleter', "mouseover", autocompleteLastName.onHover.bindAsEventListener(autocompleteLastName));
Event.observe('nameLastAutocompleter', "click", autocompleteLastName.onClick.bindAsEventListener(autocompleteLastName));
			
		if (Prototype.Browser.IE) {
			$('nameLastAutocompleter').observe('mousedown', function(e) {
				autocompleteLastName.dontBlur = true;
				e.stop();
			});
			$('nameLastAutocompleter').observe('blur', (function(e) {
			setTimeout((function() {
			if (! $('nameLast').focused)
			this.onBlur(e);
			}).bind(this), 100);
			e.stop();
			}).bindAsEventListener(autocompleteLastName));

			$('nameLast').observe('focus', function() { $('nameLast').focused = true; });
			$('nameLast').observe('blur', function() { $('nameLast').focused = false; });
		} else {
			$('nameLastAutocompleter').observe('mousedown', function(e) {
				e.stop();
			});
		} 

 Then, at the bottom of the Scriptaculous controls.js file (or any other globally included js file), add the following:

Autocompleter.Base.prototype.render = function() {
	if(this.entryCount > 0) {
		if (this.selected_item)
			Element.removeClassName(this.getEntry(this.selected_item-1),"selected");
			Element.addClassName(this.getEntry(this.index),"selected");
			this.selected_item = this.index+1;
			if(this.hasFocus) {
				this.show();
			this.active = true;
		}
	} else {
		this.active = false;
		this.hide();
	}
};

Autocompleter.Base.prototype.onHover = function(event) {
	var element = Event.findElement(event, 'LI');
	if(element && defined(element.autocompleteIndex) && this.index != element.autocompleteIndex) {
		this.index = element.autocompleteIndex;
		this.render();
	}
	Event.stop(event);
};

Autocompleter.Base.prototype.addObservers = Prototype.emptyFunction;

if (Prototype.Browser.IE) {
Autocompleter.Base.prototype.onBlur = function(event) {
	setTimeout((function(e) {
		if (this.dontBlur) {
			this.dontBlur = false;
			return;
		}
		this.hasFocus = false;
		this.active = false;
		this.hide();
		}).bind(this), 100);
	};
}

 It's a lot of code, I know - but it did the trick for me.

 

Passing Extra Data to an Ext AJAX Request Callback

By Jeff Denton Sep 16, 2008

I’m not ashamed to admin that I’ve fought with this problem for months. I’ve always put it on the back burner for a more convenient time, but today I just hammered away until I found a solution.

Now, to some of you (most???) this may seem trivially obvious. The scenario is this…let’s say you have a JavaScript method containing an Ext.Ajax.Request with a callback function. Furthermore, you need to pass a couple of variables to this method and have them processed in the callback function. The scenario in code looks like…

var someClass = {
     someMethod: function(var1,var2) {
            Ext.Ajax.request({
                 url: 'someURL.php',
                 method: 'POST',
                 params: {param1:'param1',param2:'param2'},
                 success: function(response) {
                 // Do something with response.responseText AND var1, var2 here.
           }
        });
     }
}

At first glance this presents a big scoping problem since var1 and var2 are local and available only to someMethod. The answer is the ’scope’ parameter of the Ajax.request object. If we set the scope parameter to someClass.someMethod we now have access to var1 and var2 inside the success callback function.

var someClass = {
     someMethod: function(var1,var2) {
          Ext.Ajax.request({
               url: 'someURL.php',
               method: 'POST',
               scope: someClass.someMethod,
               params: {param1:'param1',param2:'param2'},
               success: function(response) {
                    // We now have access to var1 and var2.
                    alert('Response = '+response.responseText+' var1 = '+var1+' and var2 = +var2);
               }     
          });
     }
}