Share Recent Facebook Changes Impacting Businesses

August 23rd, 2010 by Erik

Last week Facebook announced a number of significant changes that are going to impact how companies market on Facebook. Changes to Profile Tabs and Places are the two key topics that marketing teams need to pay close attention to. Developers should be happy about the paring down of FBML and future support of IFRAME profile tabs.

Profile Tabs
Tabs are going away for user profiles. Facebook has been killing off customization for the user profile steadily over the last few years. This summer has been especially brutal, removing boxes, publisher attachments and now tabs. This leaves developers with only one remaining area with which to establish a permanent presence on the profile. Applications can still create an entry on the seldom used info tab, although I would expect this to go away soon as well. Facebook has cited lack of usage as the main reason for the changes. For application and game developers, this is one less area for users to help promote your application.

Additionally, tabs are also being changed for Facebook Pages (Fan Pages). Today begins the transition from the wide format tab to the narrower 520 pixel version. Profile tabs are especially useful for companies wishing to better market their services using a uniquely branded design. Companies can also use Tabs to offering functionality to a Page that isn’t part of the default suite of Facebook tools.

Most designers will likely find the new narrow format limiting. The tab resides as one of three columns instead of two and is surrounded with significantly more of the Facebook color palette. Expect to see a number of changes to major brands over the next few hours.

A potential before and after screenshot of the Nike Just Do It Page clearly illustrates the change.


Before


After

FBML vs. IFRAME
Facebook is finally laying to rest the FBML vs. IFRAME debate. Facebook has announced that new apps will no longer have the option to be FBML based as of the end of this year. Missing from the post is a date when the legacy apps will no longer be supported. Developers should begin transitioning as soon as possible. Expect Facebook to drop support for FBML canvas apps in mid 2011. Also, as part of this transition, Facebook is allowing developers to use IFRAME for profile tabs. This gives developers better control over how they create tabs and access to a full and unrestricted range of tools with which to build tabs, the timeline for this is in “the next few months.” Once the switch is made, developers will be able to enjoy:

  • Full JavaScript SDK integration
  • Better Metrics (Google Analytics etc)
  • Support for unrestricted JavaScript (jQuery etc)
  • Rich Media Players (Flash, Silverlight, HTML 5)
  • Freedom from creating an “app” or using FBML Profile Tab app

While this is very exciting, a word of caution, expect some changes to the developer guidelines to come at the same time. The transition to IFRAMES from FBML is the most interesting news as a developer, it moves the Facebook Platform one step closer to becoming an OpenSocial container.

Facebook Places
This has far and away received the most coverage as Facebook wades into location with it’s 500 million users. Foursquare and Gowalla, two established players, will of course be impacted and now the rest of the world will see what location is all about. Business should work to claim their “Place” page immediately as people are already likely checking in. Also be sure to read the following articles on Places.

Businesses need to provide documentation to claim their Place on Facebook. This is likely to combat the number of fake Pages that were created for businesses during the early days of Pages. Keep an eye on Places over the next two months, I expect some significant feature announcements from Facebook as well as the other location providers. If anyone business has been on the fence about dabbling in Social Media, Places is the reason to get started.

Share Validating Credit Card Numbers

August 18th, 2010 by Erik

The easiest way to pre-verify a credit card value is entered correctly is to use the Luhn algorithm to check for mistakes. This is commonly referred to as a mod-10, mod10 or mod 10 check. It’s always important when handling user data to ensure it validates not only on the client, saving a trip to the server, but also server side should the client not support JavaScript. For that reason, we test on both ends.

There are more “elegant” implementations of this algorithm – see the Wikipedia entry referenced above – but they will likely be more difficult for a novice developer to understand. Further, the functions provided here are tolerant of common data entry conventions, such as using spaces and dashes to delimit blocks of numbers as is common with credit card numbers.

View Code JAVASCRIPT
function mod10_check(val){
	var nondigits = new RegExp(/[^0-9]+/g);
	var number = val.replace(nondigits,'');
	var pos, digit, i, sub_total, sum = 0;
	var strlen = number.length;
	if(strlen < 13){ return false; }
	for(i=0;i<strlen;i++){
		pos = strlen - i;
		digit = parseInt(number.substring(pos - 1, pos));
		if(i % 2 == 1){
			sub_total = digit * 2;
			if(sub_total > 9){
				sub_total = 1 + (sub_total - 10);
			}
		} else {
			sub_total = digit;
		}
		sum += sub_total;
	}
	if(sum > 0 && sum % 10 == 0){
		return true;
	}
	return false;
}

You can use the JavaScript version to validate a field on any javascript event. The following snippet illustrates how you might use the onblur event to notify the user the card number appears to be invalid with jQuery.

...
<input type="text" 
	name="cc_number" 
	onblur="if(mod10_check(this.value)){$('#cc_error').hide(); } else { $('#cc_error').show(); }" 
	value="" />
<span id="cc_error" style="display:none;">The card number is invalid.</span>
...

You can then use the following test in PHP to validate the data once it gets to your server.

function mod10_check($val){
	$number = preg_replace('/[^0-9]+/', '', $val);
	$sum = 0;
	$strlen = strlen($number);
	if($strlen < 13){ return false; }
	for($i=0;$i<$strlen;$i++){
		$digit = substr($number, $strlen - $i - 1, 1);
		if($i % 2 == 1){
			$sub_total = $digit * 2;
			if($sub_total > 9){
				$sub_total = 1 + ($sub_total - 10);
			}
		} else {
			$sub_total = $digit;
		}
		$sum += $sub_total;
	}
	if($sum > 0 && $sum % 10 == 0){ 
		return true; 
	}
	return false;
}

You can see the JavaScript implementation in action here. The only difference from the above example is the addition of an onkeyup event and the inclusion of a valid card message.

The card number appears to be invalid.

Some card numbers you can use to test the code.

1234-5678-9012-3456 - Invalid
4111 1111 1111 1111 - Valid (MC/Visa Like)
3111 111111 11117 - Valid (Amex Like)
6011111111111117 - Valid (Discover Like)

Share Escaping Unicode Characters to HTML Entities in PHP

August 17th, 2010 by Erik

PHP LogoThis is a severe hack I used to get around some character translation issues encountered with non-latin character sets. You should at all costs use a character set that supports international characters whenever possible, probably UTF-8 if your application language supports it. That said, sometimes you need a work around, and this will at least make your pages readable for your users. This is NOT necessary if you are on a single unified platform.

I was working with mixed platforms. One application utilized PHP and UTF-8, while the other leveraged .NET and displayed in HTML with the ISO-8895-1 charset. Behind both was a shared Microsoft SQL Server database. The problem arose when UTF-8 data stored from the PHP application was displayed in the .NET app. Changing the .NET app over to UTF-8 wasn’t an option, so conversion to a character set that would work was required.

PHP has a number of tools to assist developers converting from one character set to another. iconv and the multi-byte string (mb_*) library both have utilities for handling exactly this type of conversion. The mb_* functions were not installed on the server and in this case, the iconv() function did not work as well as expected.

In practice, rather common characters like Microsoft single right “smart quotes” didn’t work with iconv(). Further, accented characters from languages like French and Spanish didn’t convert properly despite having suitable replacements in the ISO-8895-1 character set. This would leave my string littered with ? characters where suitable replacements weren’t found. So the hack I opted for was to convert all multi-byte characters to their HTML escaped equivalents. This places the burden on the browser to decide if it can display a symbol or not. It also turns out that scripting this conversion is much simpler than trying to handle the numerous potential characters and their named HTML equivalents. For any hexadecimal unicode character, \u2019 the right single quotation mark for example, can be re-written as ’ which will then show as a single right quote.

It also turns out that the built-in json_encode() function converts multi-byte characters to unicode notation when serializing, so it’s easy to hand the curly quotes like \u2018 and \u2019. A simple regular expression with preg_replace() the string for the \u#### pattern and replaces it with the correct HTML escape sequence. This was tested with western european languages and it works sufficient for my purpose.

For small strings, such as values entered as part of a typical web based tool, the additional processing time is minimal. It may be faster to strip the json encoding quotes from the beginning and end of the string using substr(). However, for the length of data, the number of times this routine executes and for clarity, this was sufficient.

function unicode_escape_sequences($str){
	$working = json_encode($str);
	$working = preg_replace('/\\\u([0-9a-z]{4})/', '&#x$1;', $working);
	return json_decode($working);
}

Share Working with Big Integers in PHP

August 10th, 2010 by Erik

This is the start of a class I developed for working with URL shortening and doing math with large numbers such as Facebook User Ids.

There are some issues with the implementation of base convert, but if you are going between base 10 and higher it’s fine.

<?php
/**
* @author Erik Giberti
* @package Big Integer Math
*/
 
class bigint {
 
	public static function base_convert($value_in = false, $source_base = 10, $target_base = 36){
		// We use these values for our conversions
		$values = array('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','-','_');
		$characters = array_flip($values);
		$string_out = '';
 
		// Easy answers
		if($source_base	== $target_base){ return $value_in; }
 
		$len = strlen($value_in);
		$sum = array();
		if($source_base > $target_base){
			for($i=0;$i<$len;$i++){
				$char = substr($value_in, $len - $i - 1, 1);
				$sum[$i] = $characters[$char] * pow($source_base, $i);
			}
		} else {
			if($source_base != 10){
				$value = bigint::base_convert($value_in, $source_base, 10);
			} else {
				$value = $value_in;
			}
 
			$cnt = 0;
			while($value > 0){
				$times = $value / $target_base;
				$sum[] = bigint::mod($value, $target_base);
				$value = bigint::floor($times); // get rid of our remainder
			}
 
			for($i=count($sum);$i>0;$i--){
				$string_out .= $values[$sum[$i-1]];
			}
 
			return $string_out;
		}
 
		if($target_base == 10){ return array_sum($sum); }
		return implode($sum);
	}
 
	public static function mod($val, $mod){ return $val - floor($val/$mod) * $mod; }
 
	public static function floor($val){
		$bits = explode('.',$val);
		return $bits[0];
	}
}

Share Simple File Based Caching in PHP

July 30th, 2010 by Erik


Depending on the access pattern for the your data, file based caches are often more economical, despite being slower, than an in memory cache like Memcached or APC. Disk space is cheap and is therefore a good option for caching data that’s not frequently accessed, but is expensive to generate. That’s exactly where this class comes in. On a small EC2 instance, I was able to cache API calls, time was reduced from 0.4 seconds to 0.001 seconds per call. The reduced the overall latency of the application and freed up system resources to work on other tasks.

Because API result sets can be very large, consider a friend list from Facebook which may be 10-50K per person, caching them in memory can often push out more valuable data reducing the effectiveness of the cache. The file based cache is of course persistent and can also be distributed across multiple servers using rsync (or if you’re feeling adventurous, you alter this code to store it in an S3 bucket).

One drawback of this approach is the cache is not limited in size, leaving the files on disk until they’re deleted or replaced with new data. For that reason, I recommend you store data on a different drive where possible. File base caches, also have a potential security risk if the data is stored in a web accessible directory. This risk is not unique to this implementation. The potential exists for a clever hacker to discover your cache and gain access to sensitive data or worse compromise your server by storing executable code and executing it. You can reduce this risk by encrypting your data before storing it in the cache and by moving the data to a directory outside of the web root.

To use this cache, save this source to disk, I recommend a filename like FileCache.php and include it in your existing code files where you will be using it. Then, start caching your data! See the usage example in the comments for how to get started. Because the files are not encrypted, you can easily look in them to see what your data looks like.

<?php
 
	/**
	*
	* @package FileCache - A simple file based cache
	* @author Erik Giberti
	* @copyright 2010 Erik Giberti, all rights reserved
	* @license http://opensource.org/licenses/gpl-license.php GNU Public License
	*
	* Class to implement a file based cache. This is useful for caching large objects such as
	* API/Curl responses or HTML results that aren't well suited to storing in small memory caches 
	* or are infrequently accessed but are still expensive to generate.
	*
	* For security reasons, it's *strongly* recommended you set your cache directory to be outside
	* of your web root and on a drive independent of your operating system.
	*
	* Uses JSON to serialize the data object.
	*
	* Sample usage:
	*
	* $cache = new FileCache('/var/www/cache/');
	* $data = $cache->get('sampledata');
	* if(!$data){
	*      $data = array('a'=>1,'b'=>2,'c'=>3);
	*      $cache->set('sampledata', $data, 3600);
	* }
	* print $data['a'];
	*
	*/
 
 
// Requires the native JSON library
if (!function_exists('json_decode') || !function_exists('json_encode')) {
  throw new Exception('Cache needs the JSON PHP extensions.');
}
 
class FileCache {
 
	/**
	* Value is pre-pended to the cache, should be the full path to the directory
	*/
	protected $root = null;
 
	/**
	* For holding any error messages that may have been raised
	*/
	protected $error = null;
 
	/**
	* @param string $root The root of the file cache. 
	*/
	function __construct($root = '/tmp/'){
		$this->root = $root;
	}
 
	/**
	* Saves data to the cache. Anything that evaluates to false, null, '', boolean false, 0 will 
	* not be saved.
	* @param string $key An identifier for the data
	* @param mixed $data The data to save
	* @param int $ttl Seconds to store the data
	* @returns boolean True if the save was successful, false if it failed
	*/
	public function set($key, $data = false, $ttl = 3600){
		if(!$key) {
			$this->error = "Invalid key";
			return false; 
		}
		if(!$data){
			$this->error = "Invalid data";
			return false;
		}
		$key = $this->_make_file_key($key);
		$store = array(
			'data' => $data,
			'ttl'  => time() + $ttl,
		);
		$status = false;
		try {
			$fh = fopen($key, "w+");
			if(flock($fh, LOCK_EX)){
				ftruncate($fh, 0);
				fwrite($fh, json_encode($store));
				flock($fh, LOCK_UN);
				$status = true;
			}
			fclose($fh); 
		} catch (Exception $e) { 
			$this->error = "Exception caught: " . $e->getMessage();
			return false; 
		}
		return $status;
	}
 
	/**
	* Reads the data from the cache
	* @param string $key An identifier for the data
	* @returns mixed Data that was stored
	*/
	public function get($key){
		if(!$key) {
			$this->error = "Invalid key";
			return false; 
		}
 
		$key = $this->_make_file_key($key);
		$file_content = null;
 
		// Get the data from the file
		try {
			$fh = fopen($key, "r");
			if(flock($fh, LOCK_SH)){
				$file_content = fread($fh, filesize($key));
			}
			fclose($fh);
		} catch (Exception $e) { 
			$this->error = "Exception caught: " . $e->getMessage();
			return false; 
		}
 
		// Assuming we got something back...
		if($file_content){
			$store = json_decode($file_content, true);
			if($store['ttl'] < time()){ 
				unlink($key);	// remove the file
				$this->error = "Data expired";
				return false; 
			}
		}
		return $store['data'];
	}
 
	/**
	* Remove a key, regardless of it's expire time
	* @param string $key An identifier for the data
	*/
	public function delete($key){
		if(!$key) {
			$this->error = "Invalid key";
			return false; 
		}
 
		$key = $this->_make_file_key($key);
 
		try {
			unlink($key);	// remove the file
		} catch (Exception $e) { 
			$this->error = "Exception caught: " . $e->getMessage();
			return false; 
		}
 
		return true;
	}
 
	/**
	* Reads and clears the internal error
	* @returns string Text of the error raised by the last process
	*/
	public function get_error(){
		$message = $this->error;
		$this->error = null;
		return $message;
	}
 
	/**
	* Can be used to inspect internal error
	* @returns boolean True if we have an error, false if we don't 
	*/
	public function have_error(){
		return ($this->error !== null) ? true : false;
	}
 
	/**
	* Create a key for the cache
	* @todo Beef up the cleansing of the file.
	* @param string $key The key to create
	* @returns string The full path and filename to access
	*/	
	private function _make_file_key($key){
		$safe_key = str_replace(array('.','/',':','\''), array('_','-','-','-'), trim($key));
		return $this->root . $safe_key;
	}
}

Share Testing Your AWS Elastic Load Balancer

July 27th, 2010 by Erik

Vijay Ramachandran asked me, via twitter, how to test if an Amazon Elastic Load Balancer is really doing it’s job. Because 140 characters really isn’t sufficient space to handle this answer, I’ve created this post. Feel free to use any of this in any of your environment.

First, I’ll assume you’ve covered some of the basics with ELB.

The default configuration you’ll end up with following my guides above is a stateless system that distributes the requests more or less evenly across all configured servers. However, when you do it the first time, it’s nice to see that it’s actually doing what you think it should be. The steps are simple

  1. Verify each instance is working as expected
  2. Verify the load balancer is distributing the requests across multiple instances
  3. Verify the instances are working behind the load balancer

1. Verify each instance is working

This is far and away the easiest step. You can simply access each machine by the amazon assigned IP address for that specific instance and ensure that it’s doing what you expect. The only potential issue here is you might jump from one machine to a different machine if you are not watching your URL. For example, if you are on ec2-123-123-123-123.compute-1.amazonaws.com, access your application at that address and ensure it works as expected, if it jumps to a domain name because you’ve hard coded a link somewhere, you may not be testing the new server at all.

2. Verify the load balancer is distributing the requests across multiple instances

To test that requests are being distributed across multiple machines, I use a test file. I generate my test file automatically by running the following script as part of the boot-up routine. This simply saves the instance-id from the metadata into a text file. If you are uncomfortable placing this information in the web root, you can optionally place it behind basic authentication, put it into a script that hashes it (md5 or sha1) or some other application based logic to access it.

/usr/local/bin/curl http://169.254.169.254/latest/meta-data/instance-id
 > /var/www/html/instance-id.txt

Check the path for curl and the web root for your local system and adjust accordingly. This should work from RedHat flavored distributions.

Once you’ve run this on each of your instances, you can tell that requests are being distributed to both machines by simply requesting your load balancer address and verifying that it changes. (Obviously replace the following request with the correct address for your machine.)

http://applicationservers-123456789.us-east-1.elb.amazonaws.com/instance-id.txt

3. Verify the instances are working behind the load balancer

Now for the last and final test. Confident that your requests are being distributed across both machines, test that your application works as expected. First under the Amazon assigned name, applicationservers-123456789.us-east-1.elb.amazonaws.com in this example, then under your CNAME’d alias.

If everything still works, you can assume all is good.

4. Bonus Check

If you really, really, really want to know… you can also verify using your access logs. Check in /var/log/httpd/access_log or wherever your web server logs are kept to see that requests are being distributed to each machine.

DNS Tips:

1. Never use the real IP returned from dig or nslookup as an A record in DNS unless you automate checking it (and even still I wouldn’t) because the actual IP changes from time to time. Only use CNAME entries.

2. If you are using GoDaddy’s DNS tool, you can’t CNAME the root of a domain (ie .example.com). For this case I use one instance as a permanent instance with an elastic IP and point the root A record for my domains to this. I then assign www. as a CNAME for the load balancer’s AWS assigned domain. Last but not least, I use .htaccess and mod_rewrite to ensure requests are sent to www.example.com. This ensures traffic is being sent to the load balancer address.

Share Camera Ergonomics

July 15th, 2010 by Erik

Apple’s iPhone 4 antennae problems have no doubt made me more aware of the way we hold things. Specifically, it made me think about the way we hold a camera for capturing stills vs. a capturing motion and the ergonomic design of those devices. How much of the variation in the hardware design of image capture devices comes from convention and restraints of bygone technology?

For example, movie cameras used to require two large spools to hold the film. I’m sure a number of physical constraints generally forced cameras to have a certain design. For example, a spool may fill more uniformly when its axis is horizontal to gravity vs. perpendicular. As cameras became more portable, I’m sure weight and fatige were factors in moving to the shoulder mounted design. Now, the modern camera is even more compact despite the addition of other technology, such as the microphone. However, notice that method for “holding” it is generally the same. Support the camera from the bottom, either using a palm, shoulder or a tripod to support it. The basic form factor remains unchanged.

Modern cameras use CCD or CMOS technology to capture the light and record it to a digital medium. These sensors are very thin so for sake of this discussion add very little depth to the camera beyond the length of the lens. So unlike traditional film that had to be protected from light, transported into the path of the light, removed and protected again, a modern camera doesn’t require this. Beyond the lens, the other large component a camera is the battery. However, batteries are not constrained in their placement nor their shape, so they are not the limiting factor in the ergonomic design despite adding weight to the camera. The only component of a camera (video or otherwise) that cannot be changed is the lens, everything else can be arranged to place it in the best possible location for holding the device steady, working the controls and so on.

Still cameras (at least smaller format ones) have gone through similar technological changes, yet at a distance look very similar to their film based ancestors. They do of course differ in the way in which a user interacts with them. The idea being the user will hold the camera with two hands, the right holding the body while the left supports lens. Notice this is different than how a video camera is typically held. Is this difference still necessary? Many SLR cameras can now capture HD video as well as stills. The additional features of a digital camera have more to do with software than hardware design.

Given that the physical space constraints have been lifted and battery technology has come a long way and will continue to evolve: what form factor really is the best way to hold a device for capturing high quality motion and stills?

Share Zend Framework’s Podcast Reader Documentation

June 11th, 2010 by Erik

Using the Zend Framework can be incredibly frustrating due to the lack of clear documentation. Today I ran into an issue while trying to display an additional field from a RSS feed. The RSS feed is fairly simple, it was originally built to feed iTunes and we’re simply repurposing it. Originally, the client only needed the title and mp3 file. However, a new podcast with multiple contributors made it necessary to include the author field in the display that we’re now generating as well.

The following is a snippet of the feed that’s being generated by the other service.

...
<item> 
<title>i GaGa - Podcast #1</title> 
<author>Steve Jobs</author> 
<itunes:author>Steve Jobs</itunes:author> 
<enclosure url="http://www.example.com/d/pc001.mp3" length="2500000" type="audio\/mpeg" /> 		<guid>http://www.example.com/d/pc001.mp3</guid> 
<pubDate>Fri, 28 May 2010 00:00:00 GMT</pubDate> 
<itunes:keywords>apple, iphone, twitter</itunes:keywords> 
</item> 
...

As you can see it’s incomplete and there are some unnecessary fields included, but overall, the data is there and should be easy to scrape out. The basic code to grab the title and attachment was already scripted using the Zend Feed Reader.

function parse($xml_string){
	$feed = Zend_Feed_Reader::importString($string_data);
	$data = array(
		'title' => $feed->getTitle(),
		'description' => $feed->getDescription(),
		'link' => $feed->getLink(),
		'entries' => array(),
	);
	foreach($feed as $entry){
		$item = array(
				'id' => $entry->getId(),
				'title'=>$entry->getTitle(),
				'enclosure' => $entry->getEnclosure(),
		);
		$data['entries'][] = $item;
	}
	return $data;
}

So just adding the getAuthor() method, should grab the author field, at least that’s what the official documentation says. An alternate method getAuthors() should return an associative array. However, this is clearly overkill in this scenario because we already know that data doesn’t exist. Ultimately, neither of these actually worked with my content. Both methods simply returned NULL.

It turns out it’s VERY simple to get the author value from the feed above. The ability to extend the Zend Framework easily is a real strength, but it’s not intuitive. After skimming a few blog articles about extending the reader classes, I decided to go back and re-read the documentation on extending the reader classes. I thought if I could find a working extension, I could use that as a starting point for writing my own. It turns out that there’s already a Podcast extension which is excatly what I had set out to write. Oh, and it’s also enabled by default!

Great! it’s there, I don’t even have to register it because it’s registered by default… W00t! It just isn’t working, or more accurately, I have no idea how to use it. Each of the built in extensions have their own methods for grabbing information. Finding documentation on these methods is difficult. This is one place the php.net documentation really excels and I had expected Zend would provide something similar in nature. Unfortunately I was wrong. I did figure out what methods existed (and what they did) after grep’ng the code. The change I needed was simple, use getCastAuthor() to set my $item['author'] field, my code was ready.

function parse($xml_string){
	...
	foreach($feed as $entry){
		$item = array(
				'id' => $entry->getId(),
				'title'=>$entry->getTitle(),
				'author'=>$entry->getCastAuthor(),
				'enclosure' => $entry->getEnclosure(),
		);
		$data['entries'][] = $item;
	}
	return $data;
}

I suspect using the Zend Studio product includes much of this documentation making it easier to write code leveraging the power of the Framework.

The Missing Manual

To save some of you the hassle of having to grep the code and read it yourself, I offer this guide to the methods added with the Podcast extension. This is current as of revision 22418 from the subversion standard trunk. If you are attempting to create an iTunes compliant feed, be aware that there is a separate iTunes writer extension built directly into the Framework as well. Also be sure to read the Apple RSS Specification documents that outlines exactly how they use each field.

Feed (Zend_Feed_Reader_Extension_Podcast_Feed)

These additional methods are made available to the feed, which is what’s returned from the call to Zend_Feed_Reader::importString() or whatever import method you are using.

getCastAuthor()
This will return the contents of <itunes:author> tag.

getBlock()
This will return the contents of <itunes:block> tag.

getCategories()
This will return the contents of the <itunes:category> as an array of values.

getExplicit()
This will return value the of the <itunes:explicit> tag.

getImage()
This will return the href value of the <itunes:image>, suitable for use in an <img> tag.

getKeywords()
This will return the contents of the <itunes:keywords> tag.

getNewFeedUrl()
This will return the contents of <itunes:new-feed-url> tag.

getOwner()
This will return a string that concatenates intelligently the values contained in <itunes:owner>’s <itunes:email> and <itunes:name> values.
For example, the following XML would return: tony@bp.com (Tony Hayward)

<itunes:owner>
   <itunes:name>Tony Hayward</itunes:name>
   <itunes:email>tony@bp.com</itunes:email>
</itunes:owner>

getSubtitle()
This will return the contents of <itunes:subtitle> tag.

getSummary()
This will return the contents of <itunes:summary> tag.

Entry (Zend_Feed_Reader_Extension_Podcast_Entry)

These additional methods are made available on a per entry item level.

getCastAuthor()
This will return the contents of <itunes:author> tag.

getBlock()
This will return the contents of <itunes:block> tag.

getDuration()
Unique to the item field, this will return the contents of <itunes:duration> tag.

getExplicit()
This will return value the of the <itunes:explicit> tag.

getKeywords()
This will return the contents of the <itunes:keywords> tag.

getSubtitle()
This will return the contents of <itunes:subtitle> tag.

getSummary()
This will return the contents of <itunes:summary> tag.

Share Using Data Pipelining to Improve Gadget Speed

June 2nd, 2010 by Erik

Beginning with OpenSocial 0.9, support for a new featured called Data Pipelining was introduced. Pipelining is really simple to implement and can bring a lot of speed and power to your applications. Pipelined requests are sent before a gadget has been loaded so they are significantly faster.

Data pipelining can be used to load all sorts of information from friends lists, remote json data and plain HTML. For this example, I’m showing how to use pipelining to load in HTML to generate a home view more quickly than using gadgets.io.makeRequest() or osapi.http() after the gadget loads. Another option for quickly loading content is to use tag which provides essentially the same functionality as the data pipelining example here, but may or may not be supported by your container. To really get into the power of these alternatives to fetching data after load, be sure to read the developer wiki notes on remote data requests.

This simple gadget that will load the contents of a file and inject them into the DOM using simple JavaScript.

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
	<ModulePrefs>
		<Require feature="opensocial-0.9" />
		<Require feature="opensocial-data"/>
		<Require feature="views" />
		<Require feature="dynamic-height" />
	</ModulePrefs>
	<Content type="html" view="home">
	<![CDATA[
<script xmlns:os="http://ns.opensocial.org/2008/markup" type="text/os-data">
	<os:HttpRequest key="home" href="http://example.com/data_pipeline.php" method="post" format="text" authz="signed" />
</script>
<script type="text/javascript" src="http://example.com/display.js"></script>
<div id="target_div">
</div>
}]>
	</Content>
</Module>

The content of display.js simply loads the pre-fetched content into the target_div on the home page view.

View Code JAVASCRIPT
function init(){
	var home = opensocial.data.getDataContext().getDataSet('home');
	if(home && !home.error){
		if(home.content != undefined){
			document.getElementById('target_div').innerHTML = home.content;
		} else {
			document.getElementById('target_div').innerHTML = home.result.content;
		}
	}
}
gadgets.util.registerOnLoadHandler(init);

The entire “page” that you would want to generate for this display should be passed back as the output from the file http://example.com/data_pipeline.php. The file will be called with as a signed post containing the current viewer $_POST['opensocial_viewer_id'] and owner $_POST['opensocial_owner_id'] user id values along with the other required signature fields.

You can now query your systems to generate the appropriate display. For example, if the owner and viewer are the same, you might show a summary of activity to their account since they last visited the application. If the viewer and owner are not the same, you might display a public view of the owners activity within the application (and possibly promotional material if the viewer isn’t currently a user of the app).

Share Monitoring Like and Recommendations

May 25th, 2010 by Erik

Now that you understand how to add the Facebook likes and shares to your site it’s time to start understanding how to leverage these to get cursory metrics and improve the user experience. Facebook provides two tools for you and your visitors to looking at what’s happening on your site right now.

Recommendations Widget

This uses Facebook’s news feed algorithm to help elevate the most interesting content for your visitors. The experience is personalized for each user. However, on smaller sites such as this one, the recommendations are going to be your most valuable content and you can use it to see what is resonating with your audience. The Facebook algorithm selects content based on the number of recent likes and shares and elevates that to the top.

You’ll notice while working with the Facebook configuration tool, that you only need to provide your domain name. There is however a gotcha, if you have multiple sub-domains, you’ll need to create a separate widget for each. Facebook isn’t currently enabling aggregation across domains. The line breaks were added for clarity.

<iframe src="http://www.facebook.com/plugins/recommendations.php?
     site=af-design.com&amp;width=300&amp;height=300&amp;header=true&amp;colorscheme=light&amp;font&amp;border_color" 
     scrolling="no" 
     frameborder="0" 
     style="border:none; overflow:hidden; width:300px; height:300px;" 
     allowTransparency="true"></iframe>

The code is very simple to add to your site and the Facebook configuration tool walks you through the assorted options. The end result should create something like the following tailored to your site.

Activity Feed

This will show your visitors what content their friends are liking and using, back filled with the most recommended content from your site. The main differentiator here is the addition of the like content. This is important because visitors will see what their friends are finding valuable on your site.

<iframe src="http://www.facebook.com/plugins/activity.php?
     site=af-design.com&amp;width=300&amp;height=300&amp;header=true&amp;colorscheme=light&amp;font&amp;border_color" 
     scrolling="no" 
     frameborder="0" 
     style="border:none; overflow:hidden; width:300px; height:300px;" 
     allowTransparency="true"></iframe>

Again, the Facebook configuration tool creates the code quickly and easily for you with a few simple options. The end result should create something like the following tailored to your site.

REAL Analytics

As of yet, Facebook is not providing any hard numbers on how many people like a piece of content unless you create a page or an application. We’ll get into that next time. However, right now, you can indirectly infer the number of shares if you choose to use a share widget with a counter and of course with the recommend widget highlighting your most shared content.

© 1998-2008 AF-Design, All rights reserved.