<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Snipe.Net &#187; PHP/mySQL</title>
	<atom:link href="http://www.snipe.net/topics/web-development/php-mysql/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.snipe.net</link>
	<description>Bitterness never tasted so sweet</description>
	<lastBuildDate>Thu, 29 Jul 2010 05:03:36 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Upgrading to WordPress 3.0 and Adding Multi-Site</title>
		<link>http://www.snipe.net/2010/06/upgrading-to-wordpress-3/</link>
		<comments>http://www.snipe.net/2010/06/upgrading-to-wordpress-3/#comments</comments>
		<pubDate>Sat, 19 Jun 2010 06:09:49 +0000</pubDate>
		<dc:creator>snipe</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[PHP/mySQL]]></category>
		<category><![CDATA[blogging]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[upgrade]]></category>
		<category><![CDATA[wordpress]]></category>
		<category><![CDATA[wpmu]]></category>

		<guid isPermaLink="false">http://www.snipe.net/?p=3071</guid>
		<description><![CDATA[WordPress 3.0, code name “Thelonious”, has been released, and it brings multi-site functionality as part of the core. As someone with far too many blogs of my own, I thought this would be a great time to start switching them all over, and let you know what you&#8217;re in for if you choose to do [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: left; margin-right: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.snipe.net%2F2010%2F06%2Fupgrading-to-wordpress-3%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.snipe.net%2F2010%2F06%2Fupgrading-to-wordpress-3%2F&amp;source=snipeyhead&amp;style=normal&amp;service=bit.ly&amp;service_api=R_92bd97f4f8b9fa8a40675b36ea291223" height="61" width="50" /><br />
			</a>
		</div>
<p>WordPress 3.0, code name “Thelonious”, has been released, and it brings multi-site functionality as part of the core. As someone with far too many blogs of my own, I thought this would be a great time to start switching them all over, and let you know what you&#8217;re in for if you choose to do the same.<br />
<span id="more-3071"></span><br />
Previously, if you wanted to run multiple sites from one core installation of WordPress, you would install <a href="http://mu.wordpress.org/">WPMU</a>. </p>
<p>I had tossed that idea around a lot over the past year, since I run several websites that run on WordPress, but I had heard from enough people who ran into plugin/MU conflict issues that made things go &#8216;splody &#8216;splody that I opted not to. So instead, every time a new version of WordPress came out, I&#8217;d end up upgrading around 20 installs. Blech.</p>
<p>With version 3.0 of WordPress, the ability to create multiple sites using one install of WordPress is built right into the core, so no need to fool around with WPMU. The temptation was too great this time, so I decided to give it a whack. It was not what I would call a smooth process, but it wasn&#8217;t terrible either.</p>
<blockquote><p><strong>STOP: </strong>If you are already running WPMU and you just want to figure out how to upgrade your existing WPMU sites to WordPress 3.0, you&#8217;re reading the wrong article.  <a href="http://developersmind.com/2010/06/17/upgrading-wordpress-mu-2-9-2-to-wordpress-3-0/">Try this one instead</a>.</p></blockquote>
<h3>Goals</h3>
<p>What I wanted to get out of this was to have one main core install, but run multiple sites on their own domains that all pulled from that main core, so upgrading to later versions would mean upgrading one core instead of a dozen or two.  These properties remaining at their current separate domain names (such as www.crankyhaiku.com, www.geekhaiku.com etc) was critical, both because of search engine optimization and for branding reasons.</p>
<h3>Upgrading</h3>
<p>The normal upgrade part was flawless, as WordPress upgrades tend to be these days. Automatic upgrade has never quite worked for me, so I always do a manual upgrade. It takes longer to upload the files, but it&#8217;s a pretty painless process. So to upgrade to 3.0, I did the usual: </p>
<ul>
<li>backup (which I didn&#8217;t actually have to do, since I automatically backup to the Amazon Cloud every night using <a href="http://www.webdesigncompany.net/automatic-wordpress-backup/">Automatic WordPress Plugin</a>) but I&#8217;m paranoid</li>
<li>delete the wp-admin directory</li>
<li> delete the wp-includes directory</li>
<li>upload everything in the WordPress package &#8211; except for wp-content &#8211; to the web root</li>
<li>hit the upgrade script to trigger the database updates</li>
</ul>
<p>Flawless, as usual. Not so much as a hiccup. Now came the trickier part &#8211; adding the &#8220;Network&#8221; functionality previously available in WPMU to start to consolidate sites.</p>
<h3>Creating a Multi-Site Network</h3>
<p>I can&#8217;t speak for how easy or difficult this normally was with WPMU, so unfortunately I can&#8217;t tell you how this process compares to a normal WPMU setup. It wasn&#8217;t awful, but it was definitely buggy.</p>
<p>The WordPress documentation on <a href="http://codex.wordpress.org/Create_A_Network">Creating a Network</a> walks through the basics well enough, so I suggest you start there so you know what to expect.</p>
<p><strong>Note: You will not be able to go through the wizard in your WordPress admin until you deactivate ALL of your plugins. You can obviously re-enable them later, but I found that many of them did not keep their original settings.</strong> </p>
<p>I suspect this might be because I chose &#8220;network activate&#8221; instead of just plain &#8220;activate&#8221;. I had wanted to make those plugins available for all sites in the network, and didn&#8217;t realize that it would wipe out my existing snipe.net settings when I did so. Oh well. (Incidentally, that explains why you might see some weird stuff on the site until I have a chance to go through everything one by one. Double &#8220;related posts&#8221; bits at the end of the articles, Apture wasn&#8217;t working, etc.) All of the settings are fixable, but it may take you a little time to figure out what&#8217;s been lost, and what you have to do to set it back to the way it was before.</p>
<h4>Editing Your wp-config.php</h4>
<p>Beyond the setup in your WordPress admin, you&#8217;ll need to make a few changes to your wp-config.php file and your htaccess file. I hadn&#8217;t updated my wp-config for several versions, so I decided to use the wp-config-sample.php file and just pull my existing database variables over. Whether you use your old wp-config.php or start fresh with the stock WordPress sample, you&#8217;ll need to add the following to your wp-config.php, just <em>above</em> the comment that says &#8220;/* That&#8217;s all, stop editing! Happy blogging. */&#8221;</p>
<p><code>define( 'MULTISITE', true );<br />
define( 'SUBDOMAIN_INSTALL', true );<br />
$base = '/';<br />
define( 'DOMAIN_CURRENT_SITE', 'www.yoursite.com' );<br />
define( 'PATH_CURRENT_SITE', '/' );<br />
define( 'SITE_ID_CURRENT_SITE', 1 );<br />
define( 'BLOG_ID_CURRENT_SITE', 1 );</code></p>
<p>If you followed my suggestion and read the <a href="http://codex.wordpress.org/Create_A_Network">WordPress documentation on creating a network</a> (you did read that, right?), you&#8217;ll see that you have two choices for how your network will be set up: sub-domain (blah1.yourdomain.com, blah2.yourdomain.com) or directory-based (yourdomain.com/blah1, yourdomain.com/blah2). Make sure you think this one through before you get started, since there doesn&#8217;t seem to be an easy way to switch between the two.</p>
<p>As I mentioned, I didn&#8217;t want my sites to live at subdomain.snipe.net, or snipe.net/blogname &#8211; I wanted them to live at their own urls. I also didn&#8217;t want a bunch of crap littering up my document root. The easiest way to do this on Rackspace Cloud Sites is through a combination of setting up a site alias, and using mod_rewrite to handle domains:</p>
<ul>
<li>Set up a <a href="http://help.rackspacecloud.com/article.php?id=077">domain alias</a>, like secondblog.com, and point it to originalblog.com</li>
<li>Modify the mod_rewrite rules in your htaccess access file </li>
<li>In your site preferences, point the blog url to the aliased domain name </li>
</ul>
<p>If you&#8217;re not on Rackspace Cloud Sites, you can just follow the directions in the WordPress documentation.</p>
<h4>Tweaking Your .htaccess</h4>
<p>You&#8217;ll need to make sure the bit below is in your htaccess file &#8211; but your WordPress Network Setup wizard will point that out to you anyway <img src='http://www.snipe.net/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p><code>RewriteCond %{REQUEST_FILENAME} -f [OR]<br />
RewriteCond %{REQUEST_FILENAME} -d<br />
RewriteRule ^ - [L]<br />
RewriteRule . index.php [L]</code></p>
<p>One thing to look out for besides having to reset your plugin preferences: when I created my Network, setting this site as the default, it automatically tried to set the url as snipe.net/blog. I&#8217;m not sure why it did this, and I&#8217;m certain I didn&#8217;t add it anywhere, but when I committed the changeover to Network, all of my urls were broken (since snipe.net/blog/ doesn&#8217;t exist). It was a quick change that you can handle via the Settings menu, but watch out for it and be sure to test your links once you&#8217;ve made the switch. </p>
<h3>Importing Blogs</h3>
<p>Now that you&#8217;ve got a Network set up, you have actually add them to the Network so that they&#8217;re using the same core. I expected this to be a much bigger pain in the ass than it ended up being. All I had to do was go to the original admin, go to TOOLS > EXPORT and download the XML file. Then go into my WordPress 3.0 admin, select the site I wanted to admin, and go to TOOLS > IMPORT > WORDPRESS, and upload the XML file. Worked perfectly, so far as I can tell.</p>
<h3>Security Notes</h3>
<p>Consolidating all of your WordPress sites into one multi-site install has many benefits, the most obvious one being that it&#8217;s easier to maintain one core install than updating every single instance of WordPress you run. That said, you may want to consider a few things:</p>
<p>While one install is probably more &#8220;secure&#8221; than multi-installs in the real world simply because you&#8217;re more likely to keep one site updated than dozens, there are a few things to consider.</p>
<p>If you run multiple WordPress blogs under the same user (the same account, in Rackspace Cloud Sites), all of the files are owned by the same linux user and group. This means that if one of your WordPress installs ends up compromised, either because you forgot to upgrade one of them, or because of a <a href="http://blog.unmaskparasites.com/2010/06/14/attack-on-wordpress-blogs-on-rackspace/">vulnerability in your hosting company</a>, once an attacker has access to one of your blog installs, they have access to any other files owned by that user. Which means all of your other blogs, even the ones that are running current WordPress versions.</p>
<p>Along this same line of thought, if you&#8217;re running multiple WordPress installs under different users and you end up consolidating them to take advantage of the multi-site functionality, do so understanding that in this scenario, all of your blogs will be owned by the same user/group in the same webspace, so one vulnerability could easily turn into a much bigger problem. </p>
<p>Conversely, <a href="http://blog.unmaskparasites.com/2010/06/14/attack-on-wordpress-blogs-on-rackspace/">tracking down backdoors and maliciously modified files</a> could potentially be easier, since you have fewer installs to search through.</p>
<p>WordPress has been much better about quickly patching holes, and being proactive about finding vulnerabilities. If your site ends up getting hacked, these days it&#8217;s more likely to be a vulnerable plugin, an outdated install you forgot all about, or a PC virus that added your FTP login to a botnet &#8211; not the core WordPress install itself. I say this with a certain amount of confidence, since I have restored <em>at least</em> two-dozen hacked WordPress sites (not mine) since the beginning of the year, and have therefore spent countless hours investigating the attack, identifying the vector, and writing up summaries to post to <a href="http://badwarebusters.org/">badwarebusters.org</a> in an effort to help other people facing the same hack.</p>
<p>To be clear, running a multi-site install isn&#8217;t any riskier than running multiple blogs under the same user. But if you&#8217;re currently running your blogs under different users, you should at least be aware of how that could potentially impact you. </p>
<h3>Final Thoughts</h3>
<p>My thought is that it might have been smarter to install WPMU, and then upgrade to 3.0, since the upgrade process for a WPMU setup to 3.0 seems like it was a little less wonky, but I don&#8217;t really know.</p>
<p>I&#8217;ve really only just started playing with this during the fragment of free time I had today (work has been brutal for the past month or so). So far, pulling the theme in has been as simple as downloading them from their respective old WordPress installs and uploading them to the new 3.0 themes directory and activating them so that they&#8217;re available to the rest of the sites in the network. </p>
<p>And certainly, if you&#8217;ve found an easier way to get this done, please let me know in the comments. <img src='http://www.snipe.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>


<p>Possibly related posts:<ol><li><a href='http://www.snipe.net/2009/01/essential-wordpress-plugins/' rel='bookmark' title='Permanent Link: Essential WordPress Plugins'>Essential WordPress Plugins</a> <small>Many WordPress bloggers have taken the time to share the...</small></li>
<li><a href='http://www.snipe.net/2010/01/when-wordpress-gets-hacked/' rel='bookmark' title='Permanent Link: When Your WordPress Blog Gets Hacked'>When Your WordPress Blog Gets Hacked</a> <small>It happens to most bloggers at some point &#8211; your...</small></li>
<li><a href='http://www.snipe.net/2009/01/creating-a-wordpress-theme/' rel='bookmark' title='Permanent Link: Creating A WordPress Theme'>Creating A WordPress Theme</a> <small>If you&#8217;ve already got some design chops and a WordPress...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2010/06/upgrading-to-wordpress-3/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Microsoft Web Developer&#8217;s Summit 2009</title>
		<link>http://www.snipe.net/2009/12/mswds09/</link>
		<comments>http://www.snipe.net/2009/12/mswds09/#comments</comments>
		<pubDate>Sun, 06 Dec 2009 03:20:31 +0000</pubDate>
		<dc:creator>snipe</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[PHP/mySQL]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[microsoft]]></category>
		<category><![CDATA[mswds]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[webdev]]></category>
		<category><![CDATA[windows]]></category>

		<guid isPermaLink="false">http://www.snipe.net/?p=2557</guid>
		<description><![CDATA[I had the opportunity this week to go out to Redmond, Washington to attend the Microsoft Web Developer&#8217;s Summit at the MS headquarters. For this summit, about 25 leaders in the PHP (and PHP project) community were invited out to sit down with members of the MS product development teams and provide critical, honest feedback [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: left; margin-right: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.snipe.net%2F2009%2F12%2Fmswds09%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.snipe.net%2F2009%2F12%2Fmswds09%2F&amp;source=snipeyhead&amp;style=normal&amp;service=bit.ly&amp;service_api=R_92bd97f4f8b9fa8a40675b36ea291223" height="61" width="50" /><br />
			</a>
		</div>
<p>I had the opportunity this week to go out to Redmond, Washington to attend the Microsoft Web Developer&#8217;s Summit at the MS headquarters. For this summit, about 25 leaders in the PHP (and PHP project) community were invited out to sit down with members of the MS product development teams and provide critical, honest feedback about Microsoft.<br />
<span id="more-2557"></span></p>
<h3>Background</h3>
<p>The MSWDS is one of only four significant annual events within the PHP community (others include tek, DPC and ZendCon), and this summit is a bit harder to get invited to. Unlike most other conferences, where all you need is the cash to pony up for a conference pass and a hotel room to crash in, invites are very limited and attendees are selected because they have had some interaction with the folks at Microsoft, and are believed to be leaders and influencers within the open source community. To be blunt, these summits cost Microsoft a lot of money, so they need to make sure they&#8217;re getting the best bang for their buck.</p>
<p><img src="http://www.snipe.net/wp-content/uploads/2009/12/IMG_0065-sm.jpg" alt="IMG_0065-sm" title="IMG_0065-sm" width="200" height="126" class="alignright size-full wp-image-2577" />Keeping that in mind, it would be easy to assume that we were being brought out there so that Microsoft could pitch us on the latest and greatest Microsoft products, trying to get the movers and shakers of open source to drink the corporate kool-aid and switch to Microsoft products. While more acceptance of Microsoft products within the open source community is obviously a goal, they are making a concerted effort to learn from us &#8211; what we need, where they are falling short, and how we can move forward together.</p>
<h3>Discussions and Format</h3>
<p>The summit itself was a total of three days, with the last day being optional for those open source developers who were willing to sign an NDA to discuss some of Microsoft&#8217;s emerging technology. During the three days, different representatives from Microsoft&#8217;s product teams sat down with us and asked for our comments, thoughts and ideas about where they&#8217;re at, and where we think they should be going. We met with folks from the <a href="http://microsoft.com/web/">IIS Web Platform</a> team, the <a href="http://www.microsoft.com/sqlserver/2008/en/us/default.aspx">SQL server</a> team, as well as some representatives from <a href="http://www.codeplex.com/">Codeplex</a>, <a href="http://silverlight.net/">Silverlight</a>, <a href="http://technet.microsoft.com/en-us/library/bb978526.aspx">Powershell</a>, <a href="http://www.asp.net/%28S%28waglea45zymnbmbli4vgme45%29%29/ajax/">ASP.NET Ajax</a> (which is not exclusive to ASP.NET, despite the name), and <a href="http://www.bing.com/maps/explore/">Bing maps</a>. </p>
<p>We had a chance to air grievances, which was cathartic in some ways, but I think it was more important to us to be able to sit down with the actual teams who are working on this technology at Microsoft, and really get into the specific challenges we face. The approach was not generally pitchy, and with very few exceptions, a great deal of effort was put into making all of us from the open source community feel like respected authorities in our field whose opinions really matter. </p>
<p>Something they did this year which was apparently not done last year was to include representatives from well-known PHP-based projects who are not normally parts of the PHP community. I honestly hadn&#8217;t realized that the many of the folks over Joomla, WordPress and Drupal often don&#8217;t consider themselves as part of the greater PHP community, and getting a chance to discuss that with them brought up some interesting perspectives. I don&#8217;t think the guys representing these projects were there in an official capacity, but their point of view was one that had honestly not occurred to me before, so that was a really interesting and unexpected benefit. There was some debate on whether or not these types of projects should be a more involved part of the PHP community, with good points on both sides, but I think most walked away with some ideas on how to move forward in making those lines of communication more accessible and open.</p>
<h3>My Perspective</h3>
<p>Of course the ultimate question from Microsoft was &#8220;What would it take for you to switch to Microsoft products for your clients?&#8221; My smartass remark was, of course &#8220;A fucking miracle.&#8221; But everyone in the room knew I was joking. I hope. If we weren&#8217;t willing to work with Microsoft on improving their products to work with open source better, we wouldn&#8217;t have been there. </p>
<p>As often as Microsoft has been an easy target in the past, and as much bad blood as there may have been in the past, there <em>are</em> people at Microsoft that care about working with the open source community, and who are making progress to get there. It is our job as technology professionals to fairly evaluate technology and make recommendations based on what makes the most sense technologically and financially. It is NOT our job to make religious decisions based on zealotry. </p>
<p>That means that if and when Microsoft can meet my needs and/or the needs of my clients, it can and should be part of that evaluation or I&#8217;m not doing my job. Does that mean I&#8217;m ready to switch back? No. Not yet, anyway. But I believe they are listening, and I saw some things during this summit that make me far more likely to start including some parts of Microsoft&#8217;s products into the technology I suggest as being potentially viable for client projects, which is a far cry closer than I was last week. Specifically, some of the stuff I learned about Silverlight, Bing&#8217;s geolocation products and Windows Azure (Microsoft&#8217;s cloud hosting platform) was pretty impressive. As I get to play with these products a little more, I&#8217;ll be blogging about them with my fair evaluation of pros and cons, so stay tuned.</p>
<p>I&#8217;m also excited to see where the <a href="http://www.microsoft.com/web/Downloads/platform.aspx">Microsoft Web Platform Installer product</a> heads. Right now, the WebPI product is a very easy to use, slick solution for the less techy individual who wants to, for example, deploy a WordPress blog in 5 minutes or less and may not have the savvy to do the install themselves &#8211; basically a MS version of Cpanel/Fantastico, which we have had available to us as web administrators for over a decade. That product is less interesting to me right now, but some of the directions they could go in for more advanced users like us hold real potential. We had some suggestions that were well-received, and if they are actually implemented in the way I envision them, it could honestly turn the table and make some of the Microsoft web server products something that I could consider recommending, or even using myself. (I should also mention that Cpanel is the most horrific, insecure, hack-prone web control panel I&#8217;ve ever used, and I am NOT endorsing it as a solution.)</p>
<p>The reality is that competition inspires innovation, and Microsoft getting better means progress for everyone. I saw a post on Twitter that basically implied that open source representatives attending this conference were traitors or sellouts. I don&#8217;t see it that way at all. We have amazing open source products like Firefox because the open source community worked together to create a better product, and Microsoft responded by making vast improvements to Internet Explorer, building in more security and standards compliance. <strong>When we work together to innovate, everybody wins.</strong></p>
<p>Another transition I&#8217;ve been seeing in Microsoft which was really made more obvious by this summit is that there is a less omnipresent feeling of &#8220;all or nothing&#8221; within many Microsoft departments. As open source advocates, we enjoy having choices. Previously with Microsoft, you&#8217;d get the most benefit from their products by committing to an entirely Microsoft development process (&#8220;drinking <em>all</em> of the kool-aid, since the best stuff is the sugary goop at the bottom&#8221;), with benefits sharply falling off if you opted to pick and choose. This philosophy has always been distinctly in opposition with the open source philosophy, and I believe was likely the cause for some of the distrust coming from the open source community. Seeing this transition into a paradigm of being able to cherry-pick what we like for some things and sticking with open source solutions we like better for others is a step in the right direction, in my opinion. </p>
<p>An additional unexpected benefit to sitting down with all these MS product people was that I got a chance to better understand some of the legal/licensing challenges Microsoft faces. I&#8217;m not making excuses for them, but I hadn&#8217;t considered some of the obstacles in the way of people at MS who care about working with us. Microsoft is a big target with deep pockets, and they have to cover their own asses. I was quicker to dismiss some of the corporate decisions as being &#8220;evil&#8221; prior to sitting down with some of them and understanding why they do what they do. Don&#8217;t get me wrong &#8211; some of their decisions (*cough*sudo*cough*) still don&#8217;t make sense to me and I believe they are wrong, but I think I have a better understanding of where they sit than I did before</p>
<h3>Wrapping Up</h3>
<p>Overall, I would consider this summit a great success, and I hope I get to participate again in the future. There are several people who really deserve a shout-out for all of the hard work that went into this and are directly responsible for it&#8217;s success. From the PHP community, <a href="http://blog.calevans.com/">Cal Evans</a> was a co-host and an absolute rock star, always quick to make sure things ran smoothly and kick-start conversations and redirect us back when we went off on tangents. From Microsoft, Karri Dunn, Tonya Young, Josh Holmes, Peter Laudati, Lauren Cooney, and others were amazing. I may be forgetting a few &#8211; I am still a little wiped from the week and the traveling.</p>
<p>Was this an instant fix? Certainly not. Do we all have a lot more work to do before we&#8217;re &#8220;there&#8221;? Absolutely. But as Cal Evans put it on his own blog roundup, &#8220;The more people I get to know at Microsoft, the less I’m able to despise the company.&#8221; They took the time to find out what we think, even when it may not have been what they wanted to hear. Time will tell whether or not they actually act on it. </p>
<h3>Other PHP Representatives Blog Post Roundups</h3>
<p>I&#8217;ll be updating this list as more people finish their blog post roundups, so you can get take their on the summit. Many of them are far smarter than I am, so it&#8217;s worth reading what they have to say.</p>
<ul>
<li><a href="http://blog.calevans.com/2009/12/05/mswds09/">Cal Evans</a> (<a href="http://twitter.com/CalEvans">@CalEvans</a>)</li>
<li><a href="http://blog.phpdeveloper.org/?p=246">Chris Cornutt</a> (<a href="http://twitter.com/enygma">@enygma</a>)</li>
<li><a href="http://blog.maartenballiauw.be/post/2009/12/07/Microsoft-Web-Development-Summit-2009.aspx">Maarten Balliauw</a> (<a href="http://twitter.com/maartenballiauw">@maartenballiauw</a>)</li>
<li><a href="http://www.rafaeldohms.com.br/2009/12/04/microsoft-web-developer-summit-2009-in-review/en/">Rafael Dohms</a> (<a href="http://twitter.com/rdohms">@rdohms</a>)</li>
<li><a href="http://blueparabola.com/blog/microsoft-web-developer-summit-2009">Keith Casey</a> (<a href="http://twitter.com/CaseySoftware">@CaseySoftware</a>)</li>
<li><a href="http://blog.wampserver.com/index.php/2009/12/05/microsoft-web-development-summit-2009/">Romain Bourdon</a> (in French) (<a href="http://twitter.com/le_vrai_roms">@le_vrai_roms</a>)</li>
<li><a href="http://blog.tabini.ca/2009/12/09/microsoft-is-and-microsoft-does/">Marco Tabini</a> (<a href="http://twitter.com/mtabini">@mtabini</a>)</li>
<li><a href="http://community.joomla.org/blogs/community/1088-slowing-back-down-mswds-and-jdc09-reflection.html">Sam Moffatt</a> (<a href="http://twitter.com/Pasamio">@Pasamio</a>)</li>
<li><a href="http://blog.echolibre.com/2009/12/microsoft-web-developer-summit/">Helgi Þormar Þorbjörnsson</a> (<a href="http://twitter.com/h">@h</a>)</li>
</ul>
<h3>MS Representative Blogs</h3>
<p>If you&#8217;d like to see more about the fabulous people at MS who are working hard to move the company forward in a way that works with open source, check out their blogs. I&#8217;m proud to call these guys friends, and as long as we continue to have people like this working for Microsoft, I think the lines of communication and cooperation between both sides of the aisle will keep moving forward.</p>
<ul>
<li><a href="http://www.davebost.com/blog/">Dave Bost</a> (<a href="http://twitter.com/DaveBost">@DaveBost</a>)  &#8211; Developer Evangelist</li>
<li><a href="http://www.joshholmes.com/blog/">Josh Holmes</a> (<a href="http://twitter.com/JoshHolmes">@JoshHolmes</a>) &#8211; UX Architect Evangelist</li>
<li><a href="http://blogs.iis.net/tobintitus/">Tobin Titus</a> (<a href="http://twitter.com/tobint">@tobint</a>) &#8211; MSDN Site Manager</li>
<li><a href="http://blogs.msdn.com/peterlau/">Peter Laudati</a> (<a href="http://twitter.com/jrzyshr">@jrzyshr</a>) &#8211; Developer Evangelist</li>
<li><a href="http://blogs.msdn.com/markbrown/">Mark Brown</a> (<a href="http://twitter.com/markjbrown">@MarkJBrown</a>) &#8211; Product Manager for Microsoft Web Platform</li>
<li>William Coleman (<a href="http://twitter.com/will_coleman">@will_coleman</a>) &#8211; Developer Evangelist</li>
<li>Lauren Cooney (<a href="http://twitter.com/lcooney ">@lcooney</a>) &#8211; GPM for Web Platforms at Microsoft</li>
<li>Jas Sandhu (<a href="http://twitter.com/jassand">@jassand</a>) &#8211; Interop Strategy Evangelist</li>
<li><a href="http://ruslany.net/">Ruslan Yakushev</a> (<a href="http://twitter.com/ruslany">@ruslany</a>) &#8211; Program Manager on IIS team in charge of FastCGI and PHP support</li>
<li><a href="http://hanselman.com">Scott Hanselman</a> (<a href="http://twitter.com/shanselman">@shanselman</a>)  &#8211; Principal Program Manager Lead</li>
</ul>
<p>Funnily, as I&#8217;m writing this, <em>Futurama: Into the Wild Green Yonder</em> has been on television, and the scene that just played is the one where Calculon says &#8220;I&#8217;d like to thank the academy, my agent, and most of all my operating system &#8211; Windows 7, for everything it &#8211;&#8221; at which point his OS locks up. Windows 7 is actually a great product, and I run it on my Mac using Bootcamp and VM Fusion, but I thought the timing was amusing.</p>
<p><em>Kool-aid photo taken in <a href="http://www.snipe.net/2009/02/getting-to-know-belize/">Belize</a> by me &#8211; just thought it was funny. Lead post image by <a href="http://eliw.com/">Eli White</a>.</em></p>


<p>Possibly related posts:<ol><li><a href='http://www.snipe.net/2009/12/final-fail-of-the-year/' rel='bookmark' title='Permanent Link: Final Fail of the Year'>Final Fail of the Year</a> <small>This is just a quickie to let you know that...</small></li>
<li><a href='http://www.snipe.net/2009/01/trying-out-facebook-connect/' rel='bookmark' title='Permanent Link: Trying out Facebook Connect'>Trying out Facebook Connect</a> <small>After much deliberation, I have decided to give Facebook Connect...</small></li>
<li><a href='http://www.snipe.net/2008/07/identify-and-fix-sql-injection-vulnerabilities-in-web-applications/' rel='bookmark' title='Permanent Link: Identify and Fix SQL Injection Vulnerabilities in Web Applications'>Identify and Fix SQL Injection Vulnerabilities in Web Applications</a> <small>Scrawlr is a free software for scanning SQL injection vulnerabilities...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2009/12/mswds09/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Funky characters in HTML mail using PHPMailer</title>
		<link>http://www.snipe.net/2009/10/funky-characters-in-html-mail-using-phpmailer/</link>
		<comments>http://www.snipe.net/2009/10/funky-characters-in-html-mail-using-phpmailer/#comments</comments>
		<pubDate>Sat, 10 Oct 2009 17:44:51 +0000</pubDate>
		<dc:creator>snipe</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[PHP/mySQL]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[bom]]></category>
		<category><![CDATA[byte order mark]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[html email]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[phpmailer]]></category>

		<guid isPermaLink="false">http://www.snipe.net/?p=2223</guid>
		<description><![CDATA[While working on a client project, I ended up having to send HTML email notifications to users. During testing, I discovered some stray characters at the beginning of the email. If you have to send HTML email, there are certainly lots of good libraries available for download to make your life easier. I opted to [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: left; margin-right: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.snipe.net%2F2009%2F10%2Ffunky-characters-in-html-mail-using-phpmailer%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.snipe.net%2F2009%2F10%2Ffunky-characters-in-html-mail-using-phpmailer%2F&amp;source=snipeyhead&amp;style=normal&amp;service=bit.ly&amp;service_api=R_92bd97f4f8b9fa8a40675b36ea291223" height="61" width="50" /><br />
			</a>
		</div>
<p>While working on a client project, I ended up having to send HTML email notifications to users. During testing, I discovered some stray characters at the beginning of the email.</p>
<p><span id="more-2223"></span>If you have to send HTML email, there are certainly lots of good libraries available for download to make your life easier. I opted to use <strong><a href="http://phpmailer.worxware.com/" target="_blank">PHPMailer by Worx International</a></strong>, since it&#8217;s one I have used in the past, and getting up and running with it takes about 3 minutes, tops.</p>
<p>What makes it particularly nice is that you don&#8217;t have to do anything special to format your HTML to be ready for mailing. If your workflow is anything like mine, you design an HTML email and code it into HTML, and then post it somewhere for the client to review. Since this HTML file already exists on your server, using PHPMailer is a breeze, since all you have to do to start sending HTML email is point the program to the HTML file you already created.</p>
<p>After you upload the PHPMailer library, all it takes is a few lines of code, and you&#8217;re done:</p>
<pre class="brush: php">require_once $_SERVER[&#039;DOCUMENT_ROOT&#039;].&#039;/phpMailer/class.phpmailer.php&#039;;

$mail             = new PHPMailer(); // defaults to using php &quot;mail()&quot;
$body             = $mail-&gt;getFile(&#039;../invites/email.html&#039;);
$mail-&gt;From       = $from_email;
$mail-&gt;FromName   = $from_name;

$mail-&gt;Subject    = &quot;Test email subject&quot;;
$mail-&gt;AltBody    = &quot;To view the message, please use an HTML compatible email viewer!&quot;;
$mail-&gt;MsgHTML($body);

$mail-&gt;AddAddress($to_email, $to_name);		

if(!$mail-&gt;Send()) {
	echo &quot;&lt;li&gt;Mailer Error: &quot; . $mail-&gt;ErrorInfo;
} else {
	echo &quot;&lt;/li&gt;&lt;li&gt;Message sent!&quot;;
}</pre>
<p>One thing I noticed while sending out the test emails is that there were weird characters, specifically <strong><em>ï»¿</em></strong>)  showing up at the very beginning of the HTML email, despite there not being any stray characters or empty spaces at the top of the HTML email source file.</p>
<p><img class="aligncenter size-full wp-image-2225" title="email" src="http://www.snipe.net/wp-content/uploads/2009/10/email.gif" alt="email" width="540" height="595" /></p>
<p>I  verified that the HTML email file was in UTF-8 with Unix line endings &#8211; still those funky characters remained. They were appearing in the emails regardless of email client &#8211; Entourage, Thunderbird, Postbox, everything.</p>
<p>I removed the encoding and doctype declarations from the HTML email file. No joy. Googling led me to lots if interesting articles on encoding and character sets for PHPMailer, but nothing particularly useful.</p>
<p>Finally I happened upon an FAQ article on the W3C website related to &#8220;<a href="http://www.w3.org/International/questions/qa-utf8-bom" target="_blank">Display problems caused by the UTF-8 BOM</a>&#8220;. Of course! The byte order mark. I haven&#8217;t had to send out HTML emails in a long time and had just switched from using Coda to BBedit for code editing, and completely forgot about that.</p>
<p>Some applications insert a particular combination of bytes at the beginning of a file to indicate that the text contained in the file is Unicode. This combination of bytes is known as a signature or Byte Order Mark (BOM). Some applications &#8211; such as a text editor or a browser &#8211; will display the BOM as an extra line in the file, others will display unexpected characters, such as ï»¿.</p>
<p>The BOM is always at the beginning of the file, and so you would normally expect to see the display issues at the top of a page. However, you may also find blank lines appearing within the page if you include text from a separate file that begins with a UTF-8 signature.</p>
<p>After further investigation in BBedit, I realized that BBedit offers an encoding type of &#8220;Unicode (UTF-8, no BOM)&#8221;, I switched to this encoding for the HTML source email file, re-saved, sent another test, and all was right with the world.</p>
<p><img src="http://www.snipe.net/wp-content/uploads/2009/10/editor-560x392.png" alt="editor" title="editor" width="560" height="392" class="aligncenter size-large wp-image-2227" /></p>
<p>(Pardon all the blurring &#8211; this project was for a high-profile Facebook application, and I am paranoid about exposing database or file structures to the outside world.)</p>
<h3>Some additional notes from W3C</h3>
<p>If you have an editor which shows the characters that make up the UTF-8 signature you may be able to delete them by hand. <strong>Chances are, however, that the BOM is there in the first place because you didn&#8217;t see it.</strong></p>
<p><strong>Check whether your editor allows you to specify whether a UTF-8 signature is added or kept during a save. </strong>Such an editor provides a way of removing the signature by simply reading the file in then saving it out again. For example, if Dreamweaver detects a BOM the Save As dialogue box will have a check mark alongside the text &#8220;Include Unicode Signature (BOM)&#8221;. Just uncheck the box and save.</p>
<p>You will find that some text editors such as Windows Notepad will automatically add a UTF-8 signature to any file you save as UTF-8.</p>
<p><strong>A UTF-8 signature at the beginning of a CSS file can sometimes cause the initial rules in the file to fail on certain user agents.</strong></p>
<p>In some browsers, the presence of a UTF-8 signature will cause the browser to interpret the text as UTF-8 regardless of any character encoding declarations to the contrary.</p>
<p>So there you have it. Not exactly rocket surgery, but it was frustrating for the short time I was trying to troubleshoot, so I&#8217;m putting it into the internet ether to hopefully save someone else a few minutes.</p>
<h3>Related Links</h3>
<ul>
<li><a href=http://www.w3.org/International/questions/qa-utf8-bom">W3C UTF-8 Byte Order Mark FAQ</a>
	</li>
<li><a href="http://www.unicode.org/unicode/faq/utf_bom.html">Unicode.Org UTF-8 Byte Order Mark FAQ</a>
</li>
</ul>
</li>


<p>Possibly related posts:<ol><li><a href='http://www.snipe.net/2002/06/sending-htmlplain-text-mail-simultaneously-using-php/' rel='bookmark' title='Permanent Link: Sending HTML/Plain Text Mail Simultaneously using PHP'>Sending HTML/Plain Text Mail Simultaneously using PHP</a> <small>Although I expect this article to cause a few ruffled...</small></li>
<li><a href='http://www.snipe.net/2009/01/upside-down-text/' rel='bookmark' title='Permanent Link: Confuse and Impress Friends with Upside-Down Text'>Confuse and Impress Friends with Upside-Down Text</a> <small>˙ǝʇɐpdn ʞooqǝɔɐɟ ɹo ʇǝǝʍʇ ɹǝʇʇıʍʇ &#8216;ʇsod ƃoןq ʇxǝu ɹnoʎ uı...</small></li>
<li><a href='http://www.snipe.net/2007/01/firefox-extensions-i-cant-live-without/' rel='bookmark' title='Permanent Link: Firefox extensions I can&#8217;t live without'>Firefox extensions I can&#8217;t live without</a> <small>The full list of Firefox extensions that I consider absolutely...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2009/10/funky-characters-in-html-mail-using-phpmailer/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>PHP Regex to Make Twitter Links Clickable</title>
		<link>http://www.snipe.net/2009/09/php-twitter-clickable-links/</link>
		<comments>http://www.snipe.net/2009/09/php-twitter-clickable-links/#comments</comments>
		<pubDate>Thu, 10 Sep 2009 09:44:42 +0000</pubDate>
		<dc:creator>snipe</dc:creator>
				<category><![CDATA[PHP/mySQL]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[regex]]></category>
		<category><![CDATA[snippets]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://www.snipe.net/?p=2169</guid>
		<description><![CDATA[This is just a quicky post, not one of my usual long, rambling diatribes. This week is madness, even by my own absurd standards, but I didn&#8217;t want to miss jotting this down in case it might be helpful to others. I&#8217;ve had varying degrees of success trying to find a series of preg_replace statements [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: left; margin-right: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.snipe.net%2F2009%2F09%2Fphp-twitter-clickable-links%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.snipe.net%2F2009%2F09%2Fphp-twitter-clickable-links%2F&amp;source=snipeyhead&amp;style=normal&amp;service=bit.ly&amp;service_api=R_92bd97f4f8b9fa8a40675b36ea291223" height="61" width="50" /><br />
			</a>
		</div>
<p>This is just a quicky post, not one of my usual long, rambling diatribes. This week is madness, even by my own absurd standards, but I didn&#8217;t want to miss jotting this down in case it might be helpful to others.<br />
<span id="more-2169"></span><br />
I&#8217;ve had varying degrees of success trying to find a series of preg_replace statements that would correctly replace output generated by Twitter&#8217;s RSS feeds (which do not contain any linking HTML) to autolink hyperlinks, @replies and hashtags, so I finally sat down and sorted it out myself. </p>
<p>The code below should correctly autolink all of the autolinkables in your PHP script:</p>
<ul>
<li>links @username to the user&#8217;s Twitter profile page</li>
<li>links regular links to wherever they should link to</li>
<li>links hashtags to a Twitter search on that hashtag</li>
</ul>
<pre class="brush: php">function twitterify($ret) {
  $ret = preg_replace(&quot;#(^|[\n ])([\w]+?://[\w]+[^ \&quot;\n\r\t&lt; ]*)#&quot;, &quot;\\1&lt;a href=\&quot;\\2\&quot; target=\&quot;_blank\&quot;&gt;\\2&lt;/a&gt;&quot;, $ret);
  $ret = preg_replace(&quot;#(^|[\n ])((www|ftp)\.[^ \&quot;\t\n\r&lt; ]*)#&quot;, &quot;\\1&lt;a href=\&quot;http://\\2\&quot; target=\&quot;_blank\&quot;&gt;\\2&lt;/a&gt;&quot;, $ret);
  $ret = preg_replace(&quot;/@(\w+)/&quot;, &quot;&lt;a href=\&quot;http://www.twitter.com/\\1\&quot; target=\&quot;_blank\&quot;&gt;@\\1&lt;/a&gt;&quot;, $ret);
  $ret = preg_replace(&quot;/#(\w+)/&quot;, &quot;&lt;a href=\&quot;http://search.twitter.com/search?q=\\1\&quot; target=\&quot;_blank\&quot;&gt;#\\1&lt;/a&gt;&quot;, $ret);
return $ret;
}</pre>
<p>Someday I&#8217;ll try to find the time to write a regex primer/tutorial. Regex is another one of the things, <a href="http://www.snipe.net/series/subversion-primer/">like SVN</a>, that seems scary and incomprehensible to many people, but eventually it clicks and makes sense. In the meantime, if you&#8217;re interested in learning more about using Regex in PHP, check out the following resources:</p>
<h3>More Regexy Goodness:</h3>
<ul>
<li><a href="http://www.regular-expressions.info/php.html">Using Regular Expressions in PHP</a> via regular-expressions.info</li>
<li><a href="http://www.phpro.org/tutorials/Introduction-to-PHP-Regex.html">Introduction to PHP Regex</a> via phpro.org</li>
<li><a href="http://www.webcheatsheet.com/php/regular_expressions.php">Using Regular Expressions in PHP</a> via webcheatsheets.com</li>
<li><a href="http://www.catswhocode.com/blog/15-php-regular-expressions-for-web-developers">15 PHP Regular Expressions for Web Developers</a> via catswhocode.com</li>
</ul>
<p>Make sure you leave yourself some time to actually try out some examples, and dissect the examples they give so that you really grok what&#8217;s happening. Once it clicks, it seems so simple, you won&#8217;t believe you ever let it beat you up and take your lunch money.</p>
<p>Image by <a href="http://www.xkcd.com/">xkcd</a>, via <a href="http://store.xkcd.com/xkcd/#RegularExpressionsShirt">their awesome web store</a>. The comic rocks my geeky face off. I buy my clothes there. So should you. </p>
<p>PS &#8211; still really, really hate posting code in WordPress. Even in HTML mode, it keeps converting my fscking special characters, which then get double/triple/etc converted.  </p>


<p>Possibly related posts:<ol><li><a href='http://www.snipe.net/2009/03/giantblueglowingcock-meme/' rel='bookmark' title='Permanent Link: The&#8230; Er&#8230; Anatomy of a Twitter Meme'>The&#8230; Er&#8230; Anatomy of a Twitter Meme</a> <small>This is a transcript of the #giantblueglowingcock Twitter meme from...</small></li>
<li><a href='http://www.snipe.net/2009/05/comparison-of-desktop-twitter-clients/' rel='bookmark' title='Permanent Link: Comparison of Desktop Twitter Clients'>Comparison of Desktop Twitter Clients</a> <small>In my never-ending quest for the perfect Twitter client, I&#8217;ve...</small></li>
<li><a href='http://www.snipe.net/2009/07/writing-your-first-twitter-application-with-oauth/' rel='bookmark' title='Permanent Link: Writing Your First Twitter Application with OAuth'>Writing Your First Twitter Application with OAuth</a> <small>If you&#8217;re interested in writing a web-based Twitter application but...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2009/09/php-twitter-clickable-links/feed/</wfw:commentRss>
		<slash:comments>46</slash:comments>
		</item>
		<item>
		<title>Writing Your First Twitter Application with OAuth</title>
		<link>http://www.snipe.net/2009/07/writing-your-first-twitter-application-with-oauth/</link>
		<comments>http://www.snipe.net/2009/07/writing-your-first-twitter-application-with-oauth/#comments</comments>
		<pubDate>Thu, 23 Jul 2009 23:26:12 +0000</pubDate>
		<dc:creator>snipe</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[PHP/mySQL]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[application development]]></category>
		<category><![CDATA[oauth]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://www.snipe.net/?p=2070</guid>
		<description><![CDATA[If you&#8217;re interested in writing a web-based Twitter application but aren&#8217;t sure where to start, the Twitter OAuth library from Abraham Wiliams makes authenticating with OAuth and Twitter a breeze. Please note: Use of the information in this article is conditional on the fact that you swear NOT to to make any of those goddamned [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: left; margin-right: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.snipe.net%2F2009%2F07%2Fwriting-your-first-twitter-application-with-oauth%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.snipe.net%2F2009%2F07%2Fwriting-your-first-twitter-application-with-oauth%2F&amp;source=snipeyhead&amp;style=normal&amp;service=bit.ly&amp;service_api=R_92bd97f4f8b9fa8a40675b36ea291223" height="61" width="50" /><br />
			</a>
		</div>
<p>If you&#8217;re interested in writing a web-based Twitter application but aren&#8217;t sure where to start, the <a href="http://twitter.abrah.am/" target="_blank">Twitter OAuth library from Abraham Wiliams</a> makes authenticating with OAuth and Twitter a breeze.<br />
<span id="more-2070"></span><br />
<strong>Please note: </strong>Use of the information in this article is conditional on the fact that you swear NOT to to make any of those goddamned Twitter games that spam Twitter timelines or send DMs like Spymaster or Quizzes. If you&#8217;re reading this to learn how to create one of those, please fuck right off. Do not pass go, do not collect $200. Those apps are the anal cancer of Twitter and the people who write them should be clubbed like baby seals.</p>
<p>Right then. Moving on.</p>
<p><a href="http://oauth.net/" target="_blank">OAuth</a> is an open protocol to allow secure API authorization  in a simple and standard method from desktop and web applications. In layman&#8217;s terms, it is a system by which you can allow a user to authenticate with an OAuth-enabled service without providing you with their credentials to that service.</p>
<p>In my Twitter anti-social media douchebag service, <a href="http://www.douchenuker.com" target="_blank">DoucheNuker.Com</a>, we use Twitter&#8217;s OAuth to validate the user and make Twitter API requests on their behalf, specifically sending a DM to the douchebag they are nuking, another DM to @spam to report them to Twitter as a spammer, and then a block request to block the spammer&#8217;s account from being able to follow them in the future.</p>
<h3>Why OAuth?</h3>
<p>Using OAuth allows you to write applications that access the Twitter API but do not require your users to give you their Twitter username and password. This is important for a variety of reasons:</p>
<ul>
<li>If the user changes their Twitter login, they do not have to update that information with you for your application to continue working for them</li>
<li>Using OAuth puts the user in control &#8211; if they ever wish to stop using your application, they can <a href="https://twitter.com/account/connections" target="_blank">disable it through Twitter</a> instead of trusting your application to stop using their login information. Once they disable it through Twitter, any requests by your application will require them to manually approve the connection again.</li>
<li>Increased sense of trust, since the user doesn&#8217;t have to worry about your application stealing their Twitter credentials and using it for nefarious purposes. I personally wouldn&#8217;t trust any web-based application that asks for my Twitter username and password, and given <a href="http://mashable.com/2009/07/15/twitter-security-meltdown/" target="_blank">Twitter&#8217;s recent history of bad press regarding their security</a>, more and more users are following that lead.</li>
</ul>
<h3>Definitions</h3>
<p style="text-align: left;">Before I show you how to use Abraham&#8217;s shmancy library to connect to Twitter&#8217;s OAuth, you should understand the basics of how OAuth works and what it&#8217;s doing. And before we get too caught up in <em>that</em>, it&#8217;s important that we establish some definitions that you&#8217;ll see if you do any additional research into OAuth:</p>
<p style="text-align: left;"><img class="aligncenter size-full wp-image-2084" title="chartkey-2" src="http://www.snipe.net/wp-content/uploads/2009/07/chartkey-2.png" alt="chartkey-2" width="464" height="110" /></p>
<p style="text-align: left;">
<p><strong>User:</strong> The users of your application.<br />
<strong>Consumer:</strong> Your application, which you have registered with Twitter<br />
<strong>Service Provider: </strong>The third-party service the <em>consumer</em> (your application) is authenticating against &#8211; in this case, Twitter.</p>
<p>These terms are used in much of the OAuth documentation, so they&#8217;re worth remembering.</p>
<p>So now that you know the lingo, how does OAuth actually work? For a detailed technical view of what gets passed back and forth, check out the <a href="http://oauth.googlecode.com/svn/spec/branches/1.0/drafts/4/spec.html" target="_blank">core spec documentation on OAuth</a>. Included in that documentation is the detailed chart below.</p>
<p><img class="aligncenter size-large wp-image-2088" title="diagram" src="http://www.snipe.net/wp-content/uploads/2009/07/diagram-560x411.png" alt="diagram" width="560" height="411" /></p>
<p>As you can see, the documentation frequently uses the terms defined above.</p>
<p>If that flow diagram seems a little overwhelming, don&#8217;t sweat it. I have a simplified version just for you (featuring a stoner Twitter user and a Twitter bird with a Thyroid problem), specifically with respect to the bits you need to know to set up your first Twitter application with OAuth. The other things OAuth does <em>are</em> important, but this is the stuff that directly impacts you, and that you need to grok to get started with your app.</p>
<p><img class="aligncenter size-full wp-image-2086" title="chart" src="http://www.snipe.net/wp-content/uploads/2009/07/chart.png" alt="chart" width="550" height="435" /></p>
<p><img class="alignleft size-full wp-image-2090" title="boba_fett" src="http://www.snipe.net/wp-content/uploads/2009/07/boba_fett.png" alt="boba_fett" width="128" height="128" />I was absurdly and inexplicably tempted to randomly throw a Boba Fett icon into that diagram, but was afraid it might confuse people. That said, I have poor impulse control, so here&#8217;s a random Boba Fett icon, so I can sleep tonight. As my friend <a href="http://twitter.com/jramboz" target="_blank">Jason Ramboz</a> says, &#8220;Step 4, Boba Fett freezes the key in carbonite for transport.&#8221;</p>
<p>Moving on.</p>
<p>Now that you&#8217;ve got a good idea of how the basics of OAuth work, you&#8217;re ready to get started with Abraham&#8217;s great Twitter OAuth library. He does provide an example script in the downloadable code, but it might be confusing for people just starting out.</p>
<h3>Getting Started &#8211; Registering Your Application with Twitter</h3>
<p>Before you even start mucking around in any code, you have to <strong><a href="https://twitter.com/oauth_clients/new" target="_blank">register your new application with Twitter</a></strong>. You&#8217;ll need a name and url for your application in order to register it, and you&#8217;ll need to define a callback url. The callback url is the full url of the page Twitter should send the user to after it&#8217;s done authenticating. This file can be named anything you want, but make sure the one you create on your server matches the one you register with Twitter. All of these details can be changed later if you change your mind or need to update something.</p>
<p>Once you&#8217;ve registered your application, Twitter will issue you a <strong>Consumer Key</strong> and a <strong>Consumer Secret</strong> for your new app. You&#8217;ll need these to get your sample code from the Twitter OAuth library working. As you can probably tell by the name, your Consumer Secret should remain private and you should never give it out to anyone. It&#8217;s used in your code so that Twitter can identify your application when you&#8217;re making API calls.</p>
<p>By forcing you to send your consumer key and secret with your API calls, Twitter is able to determine which application is sending the API calls, and can verify that the Twitter user you are attempting to send API requests on behalf of has actually authorized your application to access their account. If the user decides they no longer want to allow your application, they can edit their allowed application preferences and your application will no longer be able to make API calls on their behalf.</p>
<p>You can access a list of all of the applications you have registered with Twitter &#8211; and links to edit their details or view the consumer key and consumer secret &#8211; by going to <a href="https://twitter.com/oauth_clients/" target="_blank">your oauth clients page on Twitter</a>.</p>
<h4>The Twitter OAuth PHP Library Code</h4>
<p><strong>You&#8217;ve got your consumer keys from Twitter, so now you&#8217;re ready to download <a href="https://docs.google.com/View?docID=dcf2dzzs_2339fzbfsf4" target="_blank">Abraham&#8217;s Twitter OAuth library</a> code. </strong>You can pull the code from <a href="http://github.com/abraham/twitteroauth" target="_blank">http://github.com/abraham/twitteroauth</a>. As I mentioned, he does provide an example script, but there&#8217;s not a lot of explanation given to it, so some people might be a little confused by it if its their first foray into Twitter applications with OAuth. We&#8217;re going to whip up something a little more straightforward and simple, so you can easily modify it to suit your needs.</p>
<p>Unpack/unzip the archive you downloaded from github. You&#8217;ll see the two main files, OAuth.php and twitterOAuth.php are in the top level directory, and there is a directory called &#8216;example&#8217;, that has the included example script.</p>
<p><strong>For our example, we&#8217;re going to put the two OAuth files into a directory called &#8216;twitterOAuth&#8217;, which is a sub-directory of where the index.php and callback.php files live. </strong>As you may have guessed, the callback.php file is the one we&#8217;ve registered with Twitter as being our callback url. We&#8217;ll keep common configuration options such as the consumer key and consumer secret, and database credentials in a config.php file.</p>
<pre class="brush: php">/* config.php */

/* Consumer key from twitter */
$consumer_key = &#039;xxhjgxhjxhhjgxjhjxgjyx768678xx&#039;; 

/* Consumer Secret from twitter */
$consumer_secret = &#039;jhgjdfgfgjhj76jgjgjhxxxjhxxx&#039;;
</pre>
<p>Now we create the index.php file, which will be used to generate the authentication link, inviting users to authorize and login using Twitter.</p>
<pre class="brush: php">/* index.php */

session_start();

/* Destroy the session if the user is logging out */
if ((isset($_GET[&#039;logout&#039;])) &amp;&amp; ($_GET[&#039;logout&#039;]==&#039;true&#039;)) {
    session_destroy();
    session_unset();
}

/* Include the config file */
require_once(&#039;config.php&#039;);

/* include the twitter OAuth library files */
require_once(&#039;twitterOAuth/twitterOAuth.php&#039;);
require_once(&#039;twitterOAuth/OAuth.php&#039;);

    /*
    Create a new TwitterOAuth object, and then
    get a request token. The request token will be used
    to build the link the user will use to authorize the
    application. 

     You should probably use a try/catch here to handle errors gracefully
    */
    $to = new TwitterOAuth($consumer_key, $consumer_secret);
    $tok = $to-&gt;getRequestToken();

    $request_link = $to-&gt;getAuthorizeURL($tok);

    /*
    Save tokens for later  - we need these on the callback page to ask for the
    access tokens
    */
    $_SESSION[&#039;oauth_request_token&#039;] = $token = $tok[&#039;oauth_token&#039;];
    $_SESSION[&#039;oauth_request_token_secret&#039;] = $tok[&#039;oauth_token_secret&#039;];

echo &#039;&lt;p&gt;&lt;a href=&quot;&#039;.$request_link.&#039;&quot;&gt;login using twitter&lt;/a&gt; | &#039;;
echo &#039;&lt;a href=&quot;index.php?logout=true&quot;&gt;Logout&lt;/a&gt;&lt;/p&gt;&#039;;
</pre>
<p>The callback.php file is the script that Twitter sends the user back to after authenticating. Here you&#8217;ll probably want to set some cookies, store some user data in the database, and start letting the user do whatever it is your application does.</p>
<pre class="brush: php">/* callback.php */

session_start();

/* Include the config file */
require_once(&#039;config.php&#039;);

/* include the twitter OAuth library files */
require_once(&#039;twitterOAuth/twitterOAuth.php&#039;);
require_once(&#039;twitterOAuth/OAuth.php&#039;);

/* check for an auth access token. If there&#039;s no auth token set, go ahead and fetch one from Twitter,
* using the API call. */
if ((!isset($_SESSION[&#039;oauth_access_token&#039;])) || ($_SESSION[&#039;oauth_access_token&#039;])==&#039;&#039;) {

	$to = new TwitterOAuth($consumer_key, $consumer_secret, $_SESSION[&#039;oauth_request_token&#039;], $_SESSION[&#039;oauth_request_token_secret&#039;]);
	$tok = $to-&gt;getAccessToken();

 	/* Save tokens for later  - might be wise to
        * store the oauth_token and secret in a database, and
        * only store the oauth_token in a cookie or session for security purposes */
	$_SESSION[&#039;oauth_access_token&#039;] = $token = $tok[&#039;oauth_token&#039;];
	$_SESSION[&#039;oauth_access_token_secret&#039;] = $tok[&#039;oauth_token_secret&#039;];

} 

/* Connect to the Twitter API */
$to = new TwitterOAuth($consumer_key, $consumer_secret, $_SESSION[&#039;oauth_access_token&#039;], $_SESSION[&#039;oauth_access_token_secret&#039;]);
$content = $to-&gt;OAuthRequest(&#039;https://twitter.com/account/verify_credentials.xml&#039;, array(), &#039;GET&#039;);
$user = simplexml_load_string($content);

if ($user-&gt;screen_name!=&#039;&#039;) {
	echo &#039;&lt;h2&gt;&lt;img src=&quot;&#039;.$user-/&gt;profile_image_url.&#039;&quot; align=&quot;left&quot;&gt;&#039;;
	echo &#039;Hello, &#039;.$user-&gt;screen_name.&#039;&lt;/h2&gt;&#039;;
	echo &#039;&lt;p&gt;You follow &#039;.$user-&gt;friends_count.&#039; people, &#039;;
	echo &#039;you have &#039;.$user-&gt;followers_count.&#039; &#039;;
	echo &#039;people following you, and you joined &#039;;
	echo &#039;Twitter on &#039;.$user-&gt;created_at.&#039;. &#039;;
	echo &#039;You have posted &#039;.$user-&gt;statuses_count.&#039; updates.&lt;/p&gt;&#039;;
} else {
	echo &#039;Oops - an error has occurred.&#039;;
}

echo &#039;&lt;pre&gt;&#039;;
print_r($user);
echo &#039;&lt;/pre&gt;&#039;;</pre>
<p><strong>So we&#8217;ve connected to Twitter&#8217;s API to authenticate a session on behalf of the user, and then put the XML response of the user&#8217;s information into an array called $user, using <a href="http://us3.php.net/simplexml">SimpleXML</a>.</strong> Using SimpleXML, we can call up any node values within the XML using $user->field_name, as you can see above. </p>
<p>I&#8217;ve included a print_r($user) so that you can see the full details of the array being returned, but you&#8217;ll obviously want to comment that out in your live code.</p>
<p>The output array will contain the following fields:</p>
<pre class="brush: html">SimpleXMLElement Object
(
    [id] =&gt; 14246782
    [name] =&gt; snipe
    [screen_name] =&gt; snipeyhead
    [location] =&gt; New York
    [description] =&gt; Codemonkey, designer, author, speaker, blogger, swordfighter, Warcrafter, sarcasticgeek, scuba diver, blacksmith, crimefighter, Mentat, MBTI: ENTP, Totally NSFW
    [profile_image_url] =&gt; http://s3.amazonaws.com/twitter_production/profile_images/303658881/Photo_4-rcrop2_normal.jpg
    [url] =&gt; http://www.snipe.net
    [protected] =&gt; false
    [followers_count] =&gt; 4224
    [profile_background_color] =&gt; 340100
    [profile_text_color] =&gt; 3C3940
    [profile_link_color] =&gt; 6C2125
    [profile_sidebar_fill_color] =&gt; AEA797
    [profile_sidebar_border_color] =&gt; 943A39
    [friends_count] =&gt; 3756
    [created_at] =&gt; Fri Mar 28 20:37:35 +0000 2008
    [favourites_count] =&gt; 314
    [utc_offset] =&gt; 12600
    [time_zone] =&gt; Tehran
    [profile_background_image_url] =&gt; http://s3.amazonaws.com/twitter_production/profile_background_images/22127710/twitterback2.jpg
    [profile_background_tile] =&gt; false
    [statuses_count] =&gt; 20570
    [notifications] =&gt; false
    [verified] =&gt; false
    [following] =&gt; false
    [status] =&gt; SimpleXMLElement Object
        (
            [created_at] =&gt; Mon Jul 27 01:50:36 +0000 2009
            [id] =&gt; 2862508774
            [text] =&gt; @elazar In case a name gets blocked/banned - when its reinstated (by someone claiming it, not spamming), it has a new ID#
            [source] =&gt; Tweetie
            [truncated] =&gt; false
            [in_reply_to_status_id] =&gt; 2860170987
            [in_reply_to_user_id] =&gt; 9105122
            [favorited] =&gt; false
            [in_reply_to_screen_name] =&gt; elazar
        )

)</pre>
<p>We&#8217;re not actually doing anything magical here yet, since that information is all available publicly via a user&#8217;s RSS feed, but the key line of code you want to look at in callback.php is this one:</p>
<pre class="brush: php">$content = $to-&gt;OAuthRequest(&#039;https://twitter.com/account/verify_credentials.xml&#039;, array(), &#039;GET&#039;);</pre>
<p>The OAuthRequest function is what actually sends the requests to the API, so you&#8217;ll be using this a lot. In the example above, all we were doing was getting the access tokens, but you&#8217;ll use OAuthRequest for just about everything else, too. For example, to send a Direct Message in Twitter, you&#8217;d use:</p>
<pre class="brush: php">
$params = array(&#039;user&#039; =&gt; &#039;username&#039;, &#039;text&#039; =&gt; &#039;this is a test message&#039;);
$do_dm = simplexml_load_string($to-&gt;OAuthRequest(&#039;http://twitter.com/direct_messages/new.xml&#039;, $params, &#039;POST&#039;));</pre>
<p>To block a user, you&#8217;d do:</p>
<pre class="brush: php">$doblock = simplexml_load_string($to-&gt;OAuthRequest(&#039;http://twitter.com/blocks/create/username.xml&#039;, array(), &#039;POST&#039;));</pre>
<p>To send a status update:</p>
<pre class="brush: php">$content = simplexml_load_string($to-&gt;OAuthRequest(&#039;https://twitter.com/statuses/update.xml&#039;, array(&#039;status&#039; =&gt; &#039;Test OAuth update. #testoauth&#039;), &#039;POST&#039;));</pre>
<h3>Important! Storing user IDs</h3>
<p>Whenever you&#8217;re storing Twitter IDs in a database, be sure to store the Twitter ID number <em>in addition</em> to (or instead of) the Twitter username. While it may seem obvious to use a numeric value over a mixed alphanumeric, Twitter doesn&#8217;t expose user&#8217;s ID numbers without a little digging, so it might be easy to forget that they exist.</p>
<p>There are two main reasons why using the numeric ID is critical:</p>
<ul>
<li>Users can change their Twitter usernames. If they did this, your entire database could potentially be screwed up, since username key you&#8217;re looking for won&#8217;t match any longer.</li>
<li>If an account has been suspended due to spam or imposters, it can potentially be available for registration again after a grace period. If a spammer had a username before, and then a legitimate user reclaimed it, your records could potentially have old data from the previous user&#8217;s account. </li>
</ul>
<p>The second point above became crystal clear while working on DoucheNuker.Com. If a user account was suspended due to spamming, and then a legitimate user took it over, that new, legitimate user could potentially be considered a spammer in our database if we didn&#8217;t store (and query against) the ID number, too. When a username is reissued or reclaimed, it gets a new user ID number, so as long as you store and use the Twitter user&#8217;s ID number, your database can remain agnostic to name changes and reissues. </p>
<p>You&#8217;ll note in the <a href="http://apiwiki.twitter.com/Twitter-API-Documentation" target="_blank">Twitter REST API documentation</a> that almost all API requests allow the option of using the username or the user ID, and some actually require the user ID and cannot be used with just a username.</p>
<h3>Important! Error Messages and Throttling</h3>
<p><strong>You do not want to authenticate against Twitter every single time you load the page, but will instead want to store the request tokens in a database or session so that you don&#8217;t keep hammering Twitter&#8217;s API each time the page loads.</strong> </p>
<p>Remember that the although the <strong>Request Token</strong> you used to generate the authorization link will change often, a user&#8217;s <strong>Access Token </strong>and<strong> Access Secret Token</strong> do not, so you can safely store those in a database and use those instead of re-validating every time.</p>
<p><strong>As of right now, Twitter is throttling validation requests to 15 <em>per Twitter account</em> per hour.</strong> This was implemented to improve Twitter&#8217;s security and make it harder for bad guys to brute force their way into someone else&#8217;s Twitter account. There is discussion about rolling this change back, or only throttling to 15 <em>failed attempts</em> per hour, but as of this moment, if you attempt to authenticate more than 15 times in an hour, you&#8217;ll get a message that says &#8220;Too many requests in this time period. Try again later.&#8221; There is no way around this message for now, so plan your application accordingly. </p>
<p><strong>This limit is entirely separate from the <a href="http://apiwiki.twitter.com/Rate-limiting">Twitter Rate Limit</a> that throttles the number of times you can hit the API.</strong> <a href="http://twitter.com/help/request_whitelisting">Whitelisting your account and IP address with Twitter</a> will NOT circumvent this rate limit, so make sure you design your app in a smart way that will not attempt to authenticate more than absolutely necessary.</p>
<p>The default rate limit for calls to the REST API is 150 requests per hour. The REST API does account- and IP-based rate limiting. Authenticated API calls are charged to the authenticating user&#8217;s limit while unauthenticated API calls are deducted from the calling IP address&#8217; allotment. </p>
<p><strong>You&#8217;ll notice in all of API requests, we&#8217;re using SimpleXML to capture the value of the XML that&#8217;s returned. </strong>We need to do this in order to make sure we&#8217;re capturing any error messages that Twitter returns to us. Without error messages, when stuff doesn&#8217;t work as expected, we&#8217;re flying completely blind. Always make sure to plan your application in a way that handles errors intelligently. Let&#8217;s take a look at the API call to send a Direct Message again:</p>
<pre class="brush: php">$params = array(&#039;user&#039; =&gt; &#039;username&#039;, &#039;text&#039; =&gt; &#039;this is a test message&#039;);
$do_dm = simplexml_load_string($to-&gt;OAuthRequest(&#039;http://twitter.com/direct_messages/new.xml&#039;, $params, &#039;POST&#039;));

/* Check for an error response from Twitter */
if ($do_dm-&gt;error!=&#039;&#039;) {
	echo &#039;&lt;h2&gt;ERROR: &#039;.$do_dm-&gt;error.&#039;&lt;/h2&gt;&#039;;
}</pre>
<p><strong>Now we&#8217;re capturing the error returned from Twitter, and can handle this appropriately with our users. </strong>The error might be indicating that the user cannot send a Direct Message to someone they&#8217;re not following. Or there might be something else amiss &#8211; so you&#8217;ll want to make provisions in your script to help the user understand why something might not be working.</p>
<p><strong>And that&#8217;s honestly all there is to it.</strong> Now that you&#8217;ve got the OAuthRequest function sussed, you just need to check with the <a href="http://twitterapi.pbworks.com/browse/#view=ViewFolder&#038;param=API%20Methods">Twitter API Wiki</a> to determine the correct urls and parameters to send, based on what you&#8217;re trying to do.</p>
<p>I have to say, having worked with a LOT of APIs, including Facebook, Amazon, and at least a half-dozen others, Twitter&#8217;s API is actually the most well-documented and simplest to use. Surprising, really, since Facebook and Amazon have actual business models, so you&#8217;d think they&#8217;d invest just an iota of time into documenting their shit. I&#8217;ve gone into long tirades here on my blog about how miserably awful the Facebook API documentation is, and Amazon&#8217;s API is probably 10x worse. Twitter&#8217;s API is, overall, pretty accurate and up to date. If its your first foray into writing an application with an API, I think Twitter is actually a good place to start &#8211; before you graduate to Facebook and wish you were dead.</p>
<h3>Recap &#8211; Important Links</h3>
<ul>
<li><a href="https://twitter.com/oauth_clients/new" target="_blank">Register your application with Twitter</a></li>
<li><a href="https://twitter.com/oauth_clients/" target="_blank">List of all of your registered apps on Twitter</a></li>
<li><a href="http://apiwiki.twitter.com/Twitter-API-Documentation" target="_blank">Twitter API Documentation</a></li>
<li><a href="http://apiwiki.twitter.com/Rate-limiting" target="_blank">Twitter API Rate Limiting Documentation</a></li>
<li><a href="https://docs.google.com/View?docID=dcf2dzzs_2339fzbfsf4" target="_blank">Download &amp; Docs for Abraham&#8217;s OAuth PHP library</a></li>
<li><a href="http://oauth.net/" target="_blank">OAuth official website</a></li>
</ul>
<p>And that&#8217;s all there is to it. Please use your new powers for good and not evil. No annoying games, no &#8220;increase your followers&#8221; services, etc. If you have any questions, leave &#8216;em in the comments.</p>


<p>Possibly related posts:<ol><li><a href='http://www.snipe.net/2008/09/planning-a-facebook-application/' rel='bookmark' title='Permanent Link: Planning Your Facebook Application'>Planning Your Facebook Application</a> <small>This is part one of a series &#8211; the technical...</small></li>
<li><a href='http://www.snipe.net/2009/12/twitter-business-contributors/' rel='bookmark' title='Permanent Link: Twitter Gets Down to Business'>Twitter Gets Down to Business</a> <small>Twitter has taken the first steps of what will no...</small></li>
<li><a href='http://www.snipe.net/2008/12/planning-a-facebook-application-part-two/' rel='bookmark' title='Permanent Link: Planning a Facebook Application: Part Two'>Planning a Facebook Application: Part Two</a> <small>I know I promised you that we&#8217;d get into some...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2009/07/writing-your-first-twitter-application-with-oauth/feed/</wfw:commentRss>
		<slash:comments>123</slash:comments>
		</item>
		<item>
		<title>Using IP Geolocation and Radius Searching with PHP/MySQL</title>
		<link>http://www.snipe.net/2008/12/ip-geolocation-radius-search-in-php/</link>
		<comments>http://www.snipe.net/2008/12/ip-geolocation-radius-search-in-php/#comments</comments>
		<pubDate>Mon, 15 Dec 2008 19:05:50 +0000</pubDate>
		<dc:creator>snipe</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[PHP/mySQL]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[geolocation]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[web dev]]></category>

		<guid isPermaLink="false">http://www.snipe.net/?p=538</guid>
		<description><![CDATA[Delivering content relative to the physical location of your users is an excellent (and fairly easy) way to fine-tune the content you&#8217;re delivering to be most relevent to the people visiting your site. Two simple ways of doing this are to use an IP-based geolocation lookup, or to do a manual radius search (like a [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: left; margin-right: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.snipe.net%2F2008%2F12%2Fip-geolocation-radius-search-in-php%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.snipe.net%2F2008%2F12%2Fip-geolocation-radius-search-in-php%2F&amp;source=snipeyhead&amp;style=normal&amp;service=bit.ly&amp;service_api=R_92bd97f4f8b9fa8a40675b36ea291223" height="61" width="50" /><br />
			</a>
		</div>
<p>Delivering content relative to the physical location of your users is an excellent (and fairly easy) way to fine-tune the content you&#8217;re delivering to be most relevent to the people visiting your site. Two simple ways of doing this are to use an IP-based geolocation lookup, or to do a manual radius search (like a &#8220;store finder&#8221; type of functionality), where the user manually enters a postal code. Both function on the same logic &#8211; the real difference is that one requires a third-party service that can fetch the user&#8217;s latitude and longitude based on IP address. This functionality can be used to show content such as local news, local store branches, etc &#8211; as soon as the user visits your page.</p>
<p><span id="more-538"></span></p>
<p>The nuts and bolts of IP-based geolocation is as follows:</p>
<ol>
<li>User lands on the web page</li>
<li>User&#8217;s IP is captured and posted to a third-party geolocation service, which returns a latitude and longitude for the IP address</li>
<li>Website performs a radius database search using the latitude and longitude provided by the third party service and returns content relative to that location</li>
</ol>
<p>Bear in mind, your database must contain data that is stored with a latitude and longitude for this to work. You cannot compare a user&#8217;s latitude and longitude to the lat/long of a piece of data if you have no lat/long associated to your content.</p>
<p>Also, please note: the services mentioned here &#8211; and the code provided &#8211; is intended for US-based geolocation. The code will remain basically the same for non-US lookups, but you may need to use alternate third-party services if your country is not covered by the ones mentioned here.</p>
<h2>1. Getting Latitude/Longitude for Your Database Data</h2>
<p>The first part of this process starts with you tagging your existing or newly added database data with the correct latitude and longitude. Each item you wish to include in the radius search should have a valid lat/long value in the latitude and longitude fields. If you are modifying an existing database, you would execute an alter table command to add the new lat/long fields. For this example, we&#8217;ll be using a table called stores:</p>
<pre class="brush: sql">CREATE TABLE `stores` (
`store_id` INT NOT NULL AUTO_INCREMENT ,
`store_address` VARCHAR( 40 ) NULL ,
`store_city` VARCHAR( 40 ) NULL ,
`store_state` VARCHAR( 2 ) NULL ,
`store_country` VARCHAR( 2 ) NULL ,
`store_phone` VARCHAR( 15 ) NULL ,
UNIQUE (`store_id`));</pre>
<p>And then we add the new latitude and longitude fields to our stores table, using the DOUBLE datatype:</p>
<pre class="brush: sql">ALTER TABLE `stores` ADD `latitude` DOUBLE NULL ,
ADD `longitude` DOUBLE NULL ;</pre>
<p>Now our table is set up to store the latitude and longitude data, but from where do we actually get the data, short of manually looking it up for each row of data in our stores table?  Easy. There is a fabulous free service available at <a href="http://rpc.geocoder.us/" target="_blank">rpc.geocoder.us</a> that lets you post an address to their API, to which it responds with the latitude and longitude values for that address. The easiest way to handle this process is to set up a cURL request in the admin area where you&#8217;re managing your data &#8211; otherwise you&#8217;ll need to write a script to cycle through the rows of data, fetching the latitude and longitude.(You will, of course, need to have PHP configured with cURL support for this to work.) Using this service, free lookups are throttled by your IP address to one request every 15 seconds &#8211; this may cause issues when you&#8217;re initially trying to get the lat/long data into your database for existing records, but shouldn&#8217;t be much of an issue afterwards.</p>
<p>First let&#8217;s set up a small function to handle the cURL request:</p>
<pre class="brush: php">function curl_string ($url){
$ch = curl_init();
curl_setopt ($ch, CURLOPT_URL, $url);
curl_setopt ($ch, CURLOPT_HEADER, 0);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt ($ch, CURLOPT_TIMEOUT, 120);
$result = curl_exec ($ch);
curl_close($ch);
return $result;
</pre>
<p>The format you will be sending the query to the rpc.geocoder.us API is: <span class="linkification-ext"><a class="linkification-ext" title="Linkification: http://rpc.geocoder.us/service/csv?address=1600+Pennsylvania+Ave%2C+Washington+DC" href="http://rpc.geocoder.us/service/csv?address=1600+Pennsylvania+Ave%2C+Washington+DC">http://rpc.geocoder.us/service/csv?address=1600+Pennsylvania+Ave%2C+Washington+DC</a></span></p>
<pre class="brush: php">
// set the url of the API using the address, city and state we want to query

$url_page = &quot;http://&quot;.&quot;rpc.&quot;.&quot;.geocoder&quot;.&quot;.us/service/csv&quot;;
$url_page .=&quot;?address=&quot;.urlencode($address).&quot;,&quot;.urlencode($city).&quot;,&quot;.$state;

// execute the cURL request
$string = curl_string($url_page);

// turn the comma separated csv data turned into an array,
// so we can easily see that a match was found and use the pieces
$address_pieces= explode(&quot;,&quot;, $string);

// make sure the array contains data
if (count($address_pieces) &gt; 0) {

// update the database
$sql = &quot;update stores set latitude=&#039;&quot;.$address_pieces[0].&quot;&#039;, &quot;;
$sql .=&quot;longitude=&#039;&quot;.$address_pieces[1].&quot;&#039;, &quot;;
$sql .=&quot;zip=&#039;&quot;.$address_pieces[5].&quot;&#039; where id=&#039;&quot;.$store_id.&quot;&#039;&quot;;

if ($update_latlong = mysql_query($sql)) {
echo &#039;Lat/long updated!&#039;;
} else {
// if the query failed, print out an error
echo mysql_error();
}

// if the array does not contain data, no match was found in geocoder.us, so suggest using google maps to find the lat/long manually
} else {
echo &#039;No geolocation match.&#039;;

}</pre>
<p>The code above will help you update your existing data if you were to use it in a script that cycles through your database records, or you can use it as part of your store administration area, doing the cURL request every time a new store is saved to the database.</p>
<h2>2. Getting the Latitude and Longitude of the User</h2>
<p>Now that you have base lat/long data for the data in your database, you have to obtain the lat/long for the user visiting the site. For this next part, you will again need to access a third-party service, this time to get the latitude and longitude based on the user&#8217;s IP address. I use a commercial service available from <a href="http://www.maxmind.com/app/city" target="_blank">MaxMind.Com</a>. It&#8217;s not free, but their prices are very reasonable ($20 per 50,000 queries) &#8211; and by using a cookie to store whether or not the user&#8217;s lat/long has already been returned, you can save on the number of accesses you use up on a busy site.</p>
<pre class="brush: php">// begin the session
session_start();

$expireTime = 60*60*24*30; // 30 days
session_set_cookie_params($expireTime);

if ((!isset($_SESSION[&#039;geo_country&#039;])) || ($_SESSION[&#039;geo_country&#039;]==&#039;&#039;))    {

// enter your MaxMind license key here
$license_key=&#039;XXXXXXXXXXXXXXX&#039;;
$ip = $_SERVER[&#039;REMOTE_ADDR&#039;];

$query = &quot;http://&quot;.&quot;geoip1.&quot;.&quot;maxmind&quot;.&quot;.com/b?l=&quot; . $license_key . &quot;&amp;amp;amp;amp;amp;amp;amp;amp;amp;i=&quot; . $ip;
$url = parse_url($query);
$host = $url[&quot;host&quot;];
$path = $url[&quot;path&quot;] . &quot;?&quot; . $url[&quot;query&quot;];
$timeout = 1;
$fp = fsockopen ($host, 80, $errno, $errstr, $timeout)
or die(&#039;Can not open connection to server.&#039;);

if ($fp) {
fputs ($fp, &quot;GET $path HTTP/1.0\nHost: &quot; . $host . &quot;\n\n&quot;);

while (!feof($fp)) {
$buf .= fgets($fp, 128);
} // endwhile

// split the output into an array
$lines = split(&quot;\n&quot;, $buf);
$data = $lines[count($lines)-1];
fclose($fp);

$geo = explode(&quot;,&quot;,$data);
$user_geo_country = $geo[0];
$user_geo_state = $geo[1];
$user_geo_city = $geo[2];
$user_geo_lat = $geo[3];
$user_geo_lon = $geo[4];

$_SESSION[&#039;geo_country&#039;] = $user_geo_country;
$_SESSION[&#039;geo_state&#039;] = $user_geo_state;
$_SESSION[&#039;geo_city&#039;] = $user_geo_city;
$_SESSION[&#039;geo_lat&#039;] = $user_geo_lat;
$_SESSION[&#039;geo_lon&#039;] = $user_geo_lon;

setcookie(&quot;geolocCookieCountry&quot;, $user_geo_country, time()+$expireTime, &quot;/&quot;);
setcookie(&quot;geolocCookieCity&quot;, $user_geo_city, time()+$expireTime, &quot;/&quot;);
setcookie(&quot;geolocCookieState&quot;, $user_geo_state, time()+$expireTime, &quot;/&quot;);
setcookie(&quot;geolocCookieLat&quot;, $user_geo_lat, time()+$expireTime, &quot;/&quot;);
setcookie(&quot;geolocCookieLon&quot;, $user_geo_lon, time()+$expireTime, &quot;/&quot;);

} // endif $fp

} // endif session set
</pre>
<p>Now, you&#8217;ll only be querying MaxMind if the user hasn&#8217;t already accessed the site before and had their latitude and longtude stored in the cookie. Note the $license variable in the code above. When you sign up for the MaxMind Web Service, you will be given a license number, which you&#8217;ll insert there.</p>
<h2>3. Putting the Two Together to Return Results Within X Miles</h2>
<p>To tie the two together and query the database, returning only results that are within a specified number of miles of the user&#8217;s IP address, we&#8217;ll need two classes:</p>
<pre class="brush: php">class RadiusCheck {

var $maxLat;
var $minLat;
var $maxLong;
var $minLong;

function RadiusCheck($Latitude, $Longitude, $Miles) {
global $maxLat,$minLat,$maxLong,$minLong;
$EQUATOR_LAT_MILE = 69.172;
$maxLat = $Latitude + $Miles / $EQUATOR_LAT_MILE;
$minLat = $Latitude - ($maxLat - $Latitude);
$maxLong = $Longitude + $Miles / (cos($minLat * M_PI / 180) * $EQUATOR_LAT_MILE);
$minLong = $Longitude - ($maxLong - $Longitude);
}

function MaxLatitude() {
return $GLOBALS[&quot;maxLat&quot;];
}
function MinLatitude() {
return $GLOBALS[&quot;minLat&quot;];
}
function MaxLongitude() {
return $GLOBALS[&quot;maxLong&quot;];
}
function MinLongitude() {
return $GLOBALS[&quot;minLong&quot;];
}

}</pre>
<p>and</p>
<pre class="brush: php">class DistanceCheck {

function DistanceCheck() {
}

function Calculate(
$dblLat1,
$dblLong1,
$dblLat2,
$dblLong2
) {
$EARTH_RADIUS_MILES = 3963;
$dist = 0;

//convert degrees to radians
$dblLat1 = $dblLat1 * M_PI / 180;
$dblLong1 = $dblLong1 * M_PI / 180;
$dblLat2 = $dblLat2 * M_PI / 180;
$dblLong2 = $dblLong2 * M_PI / 180;

if ($dblLat1 != $dblLat2 || $dblLong1 != $dblLong2)
{
//the two points are not the same
$dist =
sin($dblLat1) * sin($dblLat2)
+ cos($dblLat1) * cos($dblLat2)
* cos($dblLong2 - $dblLong1);

$dist =
$EARTH_RADIUS_MILES
* (-1 * atan($dist / sqrt(1 - $dist * $dist)) + M_PI / 2);
}
return $dist;
}

}</pre>
<p>And then, to perform the actual query:</p>
<pre class="brush: php">
// set a default number of miles to search within
$Miles = &#039;50&#039;;

// set the user&#039;s latitude and longitude as the one to search against
$Latitude = $user_geo_lat;
$Longitude = $user_geo_lon;

$zcdRadius = new RadiusCheck($Latitude,$Longitude,$Miles);
$minLat = $zcdRadius-&gt;MinLatitude();
$maxLat = $zcdRadius-&gt;MaxLatitude();
$minLong = $zcdRadius-&gt;MinLongitude();
$maxLong = $zcdRadius-&gt;MaxLongitude();

$sql = &quot;SELECT store_address, store_city, store_state, store_phone, &quot;;
$sql .= &quot;SQRT((((69.1*(latitude-$Latitude))*(69.1*(latitude-$Latitude)))+((53*(longitude-$Longitude))*(53*(longitude-$Longitude))))) &quot;;
$sql .= &quot;AS calc FROM stores where  &quot;;
$sql .= &quot;latitude &gt;= &#039;$minLat&#039; &quot;;
$sql .= &quot;AND latitude &lt;= &#039;$maxLat&#039; &quot;;
$sql .= &quot;AND longitude &gt;= &#039;$minLong&#039; &quot;;
$sql .= &quot;AND longitude &lt;= &#039;$maxLong&#039; &quot;;
$get_data = mysql_query($sql);

// loop through the matching database results
while($storedata = mysql_fetch_assoc($get_data)) {

// calculate the number of miles away the result is
$zcdDistance = new DistanceCheck;
$Distance = $zcdDistance-&gt;Calculate($Latitude,$Longitude,$storedata[&#039;latitude&#039;],$storedata[&#039;longitude&#039;]);

// and for the non-US people, here&#039;s the km calculation
$calc_km = round(($Distance * 1.609344),2);

echo &#039;&lt;li&gt;&#039;.$storedata[&#039;store_address&#039;].&#039;&lt;br /&gt;&#039;.$storedata[&#039;store_city&#039;].&#039;, &#039;;
echo $storedata[&#039;store_state&#039;].&#039; &#039;.$storedata[&#039;store_country&#039;].&#039;&lt;br /&gt;&#039;;
echo $storedata[&#039;store_phone&#039;].&#039;&lt;br /&gt;&#039;;
echo &#039;Distance: &#039;.$Distance.&#039; (&#039;.$calc_km.&#039; km)&#039;;
}</pre>
<p>And that&#8217;s really all there is to it.</p>
<h2>Store Locator Only</h2>
<p>If you want to create a store-locator style script without automagically getting the user&#8217;s current location, you&#8217;d use the code above, almost verbatim. The difference is that you&#8217;d need an additional table of zipcodes with associated lat/long, available for purchase (again, not very expensive) from <a href="http://www.zipcodedownload.com/" target="_blank">ZipCodeDownload.Com</a>. The process would go as follows:</p>
<ol>
<li>User arrives at your site, and enters a zip code and mile radius they wish to search using your search form &#8211; clicks submit</li>
<li>The script queries the zip code table to find the latitude and longitude associated with the postal code the user has entered, and uses THAT latitude and longitude as the values for $Latitude and $Longitude in the code above, and uses the user-entered values for $Miles from the search form.</li>
</ol>
<p>Everything else stays exactly the same. Naturally, you&#8217;ll probably want to add some sanity checks in your own code (gracefully displaying default content if no latitude and longitude is returned, etc) but I decided to skip that here so as not to confuse anyone.</p>
<p>Enjoy, and if you end up using this anywhere, let me know how it works out.</p>


<p>Possibly related posts:<ol><li><a href='http://www.snipe.net/2002/06/alternating-row-colors-in-phpmysql/' rel='bookmark' title='Permanent Link: Alternating Row Colors in PHP/mySQL'>Alternating Row Colors in PHP/mySQL</a> <small>Using alternating row colors in a PHP database application is...</small></li>
<li><a href='http://www.snipe.net/2006/06/creating-a-multi-level-listbox-in-phpmysql/' rel='bookmark' title='Permanent Link: Creating a Multi-Level Listbox in PHP/mySQL'>Creating a Multi-Level Listbox in PHP/mySQL</a> <small>This lets you create a nested multi-level category menu through...</small></li>
<li><a href='http://www.snipe.net/2002/06/phpmysql-breadcrumb-trail/' rel='bookmark' title='Permanent Link: PHP/mySQL Breadcrumb Trail'>PHP/mySQL Breadcrumb Trail</a> <small>This lets you automatically create a breadcrumb trail navigation through...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2008/12/ip-geolocation-radius-search-in-php/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Fixing Curly Quotes and Em Dashes in PHP</title>
		<link>http://www.snipe.net/2008/12/fixing-curly-quotes-and-em-dashes-in-php/</link>
		<comments>http://www.snipe.net/2008/12/fixing-curly-quotes-and-em-dashes-in-php/#comments</comments>
		<pubDate>Thu, 11 Dec 2008 15:23:31 +0000</pubDate>
		<dc:creator>snipe</dc:creator>
				<category><![CDATA[PHP/mySQL]]></category>
		<category><![CDATA[bytecode]]></category>
		<category><![CDATA[data validation]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.snipe.net/?p=476</guid>
		<description><![CDATA[The curly quotes, or &#8220;smart quotes&#8221; generated by Microsoft Word and other applications can be a real headache to developers. If you&#8217;ve built an administration area for your content publishers, and the publishers frequently compose their posts in Word and then copy+paste into your form to publish to the web, you may run into the [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: left; margin-right: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.snipe.net%2F2008%2F12%2Ffixing-curly-quotes-and-em-dashes-in-php%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.snipe.net%2F2008%2F12%2Ffixing-curly-quotes-and-em-dashes-in-php%2F&amp;source=snipeyhead&amp;style=normal&amp;service=bit.ly&amp;service_api=R_92bd97f4f8b9fa8a40675b36ea291223" height="61" width="50" /><br />
			</a>
		</div>
<p>The curly quotes, or &#8220;smart quotes&#8221; generated by Microsoft Word and other applications can be a real headache to developers. If you&#8217;ve built an administration area for your content publishers, and the publishers frequently compose their posts in Word and then copy+paste into your form to publish to the web, you may run into the situation where the curly quotes are replaced by your browser&#8217;s version of an unrecognized symbol, often a question mark. This can be particularly frustrating when Word-generated characters such as these curly quotes or em dashes break content-generated XML feeds, even after you&#8217;ve been careful enough to convert &#8220;normal&#8221; HTML special characters so that your XML would be valid. Fortunately, there is an easy workaround.</p>
<p><span id="more-476"></span><br />
Rather than try to convince your publishers to stop using Word to compose their content, the easier (and more effective) solution will be to replace the curly quotes with &#8220;normal&#8221; quotes before the data is inserted into the database.</p>
<p>The function below will convert curly quotes and em dashes into standard quotes and dashes &#8220;-&#8221;. If you&#8217;ve got a handful of classes or functions that you routinely use as part of your data scrubbing process (to clean data before it gets sent to the server), you may want to include this function in that group, that way you don&#8217;t ever have to think about it again.</p>
<pre class="brush: php">
function convert_smart_quotes($string)
{
$search = array(chr(145),
chr(146),
chr(147),
chr(148),
chr(151));

$replace = array(&quot;&#039;&quot;,
&quot;&#039;&quot;,
&#039;&quot;&#039;,
&#039;&quot;&#039;,
&#039;-&#039;);

return str_replace($search, $replace, $string);
}
</pre>


<p>Possibly related posts:<ol><li><a href='http://www.snipe.net/2009/12/comment-count-bug-disqus/' rel='bookmark' title='Permanent Link: Fixing Comment Count Bug in Disqus on WordPress'>Fixing Comment Count Bug in Disqus on WordPress</a> <small>My final post for 2009 should probably have been more...</small></li>
<li><a href='http://www.snipe.net/2006/06/checkboxesmultiple-select-boxes-in-php/' rel='bookmark' title='Permanent Link: Checkboxes/Multiple Select Boxes in PHP'>Checkboxes/Multiple Select Boxes in PHP</a> <small>For the PHP newbie, checkboxes and/or multiple select listboxes can...</small></li>
<li><a href='http://www.snipe.net/2009/09/php-twitter-clickable-links/' rel='bookmark' title='Permanent Link: PHP Regex to Make Twitter Links Clickable'>PHP Regex to Make Twitter Links Clickable</a> <small>This is just a quicky post, not one of my...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2008/12/fixing-curly-quotes-and-em-dashes-in-php/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Planning a Facebook Application: Part Two</title>
		<link>http://www.snipe.net/2008/12/planning-a-facebook-application-part-two/</link>
		<comments>http://www.snipe.net/2008/12/planning-a-facebook-application-part-two/#comments</comments>
		<pubDate>Fri, 05 Dec 2008 06:42:25 +0000</pubDate>
		<dc:creator>snipe</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[PHP/mySQL]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[social networking]]></category>

		<guid isPermaLink="false">http://www.snipe.net/?p=430</guid>
		<description><![CDATA[I know I promised you that we&#8217;d get into some code in the next part in this series, but the article is coming out much longer than I anticipated, as I expect it to be one of the most thorough articles out there regarding Facebook application design. Part two of this series will walk through [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: left; margin-right: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.snipe.net%2F2008%2F12%2Fplanning-a-facebook-application-part-two%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.snipe.net%2F2008%2F12%2Fplanning-a-facebook-application-part-two%2F&amp;source=snipeyhead&amp;style=normal&amp;service=bit.ly&amp;service_api=R_92bd97f4f8b9fa8a40675b36ea291223" height="61" width="50" /><br />
			</a>
		</div>
<p>I know I promised you that we&#8217;d get into some code in the next part in this series, but the article is coming out much longer than I anticipated, as I expect it to be one of the most thorough articles out there regarding Facebook application design. Part two of this series will walk through designing a real-life Facebook application &#8211; one I wrote specifically for this article series. <span id="more-430"></span></p>
<p>The first article discussed what features and screens you have available to you in Facebook apps in general &#8211; and in this article, we&#8217;re going to go through the process of actually planning a real one. You&#8217;ll see some pitfalls and hopefully learn to think about your applications in a new, more complete way, harnessing all of the aspects of Facebook&#8217;s platform.</p>
<p>Although we won&#8217;t be delving into code in this article either, I strongly encourage you to read this article (<a href="http://www.snipe.net/2008/09/planning-a-facebook-application/" target="_blank">and the previous article</a>, if you haven&#8217;t yet) for your own sanity&#8217;s sake. For all the frustrations that come with writing Facebook applications, I can say from experience that at least 75% of my frustration was a direct result of devoting enough time towards the planning phase of each specific application. Plus, the application we spec out in this article is going to be the base that we use in the next part &#8211; the one where we actually do start touching code. That article is about 60% written, but breaking it up makes far more sense, since you SHOULD be planning your applications before you start touching code anyway. I promise you, getting this part right will save you days, if not weeks, of re-writing code and fixing issues. I&#8217;ve done it. It sucks.</p>
<p><!--more--></p>
<h2>Determine the Social Action Points Before You Start Development</h2>
<p>One thing I have learned with regard to Facebook application development is that its very easy to rush into development and inadvertently miss some key integration and social action points.Â  It&#8217;s easy to do, since they are not &#8220;visible&#8221; as you&#8217;re developing the app. When the user interacts with your application, you may forget to consider all of the potential notifications that can be triggered, because you can&#8217;t physically see them. Out of site, out of mind means that you&#8217;re throwing them in haphazardly after the app is built, or worse yet, not using them at all.</p>
<p>&#8220;Invisible&#8221; (but very important) things to consider are:</p>
<ul>
<li>Newsfeeds &#8211; when a user directly interacts with your application, you can post that action to the user&#8217;s newsfeed.</li>
<li>Email notifications &#8211; if a user has permitted your application to send them email, you can send plain text or (limited) HTML emails to notify them of action items</li>
</ul>
<p>Newsfeeds are an absolutely critical part of your Facebook application, so spending a little time planning what actions a user can take on each screen of your app is a great start. The actions the user takes that are entered as newsfeed items are prominently visible on the user&#8217;s profile page, and will be visible to their friends on the Facebook &#8220;home&#8221; page that shows recent actions friends have taken.</p>
<div id="attachment_433" class="wp-caption aligncenter" style="width: 510px"><a href="http://www.snipe.net/wp-content/uploads/2008/12/picture-6.png"><img class="size-full wp-image-433" title="Newsfeed" src="http://www.snipe.net/wp-content/uploads/2008/12/picture-6.png" alt="Sample newsfeed appllication items" width="500" height="57" /></a><p class="wp-caption-text">Sample newsfeed application items</p></div>
<p>The main goals of your newsfeed items should be to highlight worthy user actions &#8211; the benefit of this will be that the user&#8217;s friends see these newsfeed items and may be interested in checking out your application.</p>
<p>Emails are important, but since your users must opt-in to receive them, you may not reach as many people with them. Still, its important to plan them out the same way you plan newsfeeds.</p>
<p>As we go through the sample application, we&#8217;ll figure out where newsfeed items would be appropriate. Each application is allowed x number of newsfeed posts per user per day, so you want to <strong>choose them wisely and make sure they are truly relevant and not spammy</strong>. Each application&#8217;s cap is different, and that number changes based on the way people interact with it, but applications start off with around 20 max newsfeed posts per user per day. (This can make testing a bitch, which is another reason its important to get them into the application early in development.)</p>
<p>Also bear in mind that <strong>Facebook&#8217;s newsfeeds MUST be triggered by an action the app user has taken.</strong> You cannot have your application send newsfeed entries without the user clicking on something or interacting in some way. The format must be &#8220;&lt;user&gt; has done xyz&#8221;, so the newsfeed reflects on the action that specific user took.</p>
<h2>Three Sizes of Newsfeeds</h2>
<p>Facebook allows you to create three versions of a newsfeed item &#8211; a headline, a short story and a fullsize story. Headlines are the default view, so they should be well-crafted. If a user likes the headline, they can click to see the short story or fullsize, but your headline version has to be great to make them click.</p>
<h2>Our Sample Application</h2>
<p>Because no application should be started without a game plan, we need a plan for what the sample application weâ€™re discussing here will do. For the sake of this tutorial, our application will behave similarly to the â€œpokeâ€ feature of Facebook, only weâ€™ll be blowing kisses. (I realize this is a cheesy idea for an application, but it will keep it simple, and will allow us to demonstrate actions that can only be invoked when a user takes action on another user.) <a href="http://www.new.facebook.com/apps/application.php?id=47515775916" target="_blank">You can see this app live in action here</a>.</p>
<p>In the <a href="http://www.snipe.net/2008/09/planning-a-facebook-application/" target="_blank">Planning Your Facebook Application</a> article, we discussed planning out each of the boxes that will be available to you in the application. So for this application, weâ€™re going to plan for this:</p>
<ol>
<li><strong>Application canvas page</strong> &#8211; listing of recent incoming and outgoing kisses for the viewing application user</li>
<li><strong>Profile box </strong>- display of recent received kisses for the profile owner, with action item for viewer to send the profile owner a kiss</li>
<li><strong>Boxes tab</strong>: <strong>Wide</strong> -Â  listing of recent incoming and outgoing kisses for the profile owner user, tailored for non-application users, since Facebook users other than the profile owner can see this page</li>
<li><strong>Boxes tab</strong>: <strong>Narrow</strong> &#8211; Smaller version of the wide display, for users who opt to use the narrow box column</li>
<li><strong>Fan page</strong> â€“ Although I opted not to create a fan page view of this app in the real app, for the sake of argument we could include a list of most recent back-and-forth kiss activity, or a leaderboard of the most active kissers &#8211; with an action item inviting users to start blowing kisses. Remember that not everyone viewing the fan page that your app gets added to will have added the application, so the view should be tailored towards viewers who are not application users. Since it will be on a fan page, and could get decent exposure depending on the popularity of the page to which it&#8217;s added, you&#8217;ll want to make sure this view encourages sharing and competition to compel new people to add it.</li>
<li><strong>Application tab</strong> â€“ same display as the canvas page</li>
</ol>
<p>I encourage you to actually draw these screens and boxes out on paper, or in your favorite graphics program. It won&#8217;t take long, and will be a huge help in thinking through the interface and making sure you&#8217;re writing the app in a way that will make sense to the user. The mockups (often called wireframes) don&#8217;t have to be complicated or pretty, but they should roughly represent all of the key interface elements in position and placement.</p>
<p>Our application is very simple, so we only have two main screens: the homepage that will display the incoming and outgoing kisses, and the page that allows the user to pick from a list of their friends and blow kisses to them. A more complicated application could require a dozen or more screens, with each screen representing a page of functionality within the app.</p>
<p><strong>Canvas page (displays recent kisses, sent and received)</strong></p>
<div id="attachment_439" class="wp-caption aligncenter" style="width: 510px"><a href="http://www.snipe.net/wp-content/uploads/2008/12/home1.gif"><img class="size-full wp-image-439" title="Homepage mockup" src="http://www.snipe.net/wp-content/uploads/2008/12/home1.gif" alt="Homepage mockup" width="500" height="375" /></a><p class="wp-caption-text">Homepage mockup</p></div>
<p><strong>Send kisses page</strong></p>
<div id="attachment_440" class="wp-caption aligncenter" style="width: 510px"><a href="http://www.snipe.net/wp-content/uploads/2008/12/blow.gif"><img class="size-full wp-image-440" title="Blow kisses page" src="http://www.snipe.net/wp-content/uploads/2008/12/blow.gif" alt="Blow kisses page" width="500" height="375" /></a><p class="wp-caption-text">Blow kisses page</p></div>
<p>As we&#8217;re creating these wireframes, it becomes easier to figure out where newsfeeds and app-generated emails will be most useful.</p>
<p>One the canvas page, the page listing incoming and outgoing smooches, the user isn&#8217;t taking any direct action, so there is no direct newsfeed trigger for newsfeeds or emails. The blow kisses page is a good place for both.</p>
<p>When the user blows someone (or several someones) a kiss, it should trigger a newsfeed item: &#8220;&lt;user&gt; has blown &lt;recipient&gt; a kiss!&#8221; Because of the rule that the user has to directly trigger the newsfeed item, it would NOT be permitted to also add a newsfeed item into the recipient&#8217;s newsfeed such as &#8220;&lt;recipient&gt; has received a kiss from &lt;user&gt;&#8221;. You could trigger that second newsfeed item when the user picks up their kiss though, since the user (in this case, the recipient) took direct action with the application. This may seem like nuance, but its important to understand the difference in order to remain compliant with Facebook&#8217;s terms of service.</p>
<p>The recipient should then receive an email notification letting them know someone has blown them a kiss. If the user has not yet allowed the application and agreed to let the application send them emails, they will not receve this notification, however, so its not a form of communication you should rely on.</p>
<h2>Another Example</h2>
<p>As I have mentioned, this application is extremely simple. It was designed to be simple to keep the coding part (coming soon, I swear) simple &#8211; but it doesn&#8217;t give us the opportunity to really demonstrate places where newsfeeds will serve you best in your application.</p>
<p>Another application I wrote (which is a work in progress) might be a better example. The <a href="http://www.facebook.com/apps/application.php?id=4882584027" target="_blank">WoW Toons application</a>, which is a simple application that allows users to display their World of Warcraft characters on their profile, has a few more potential interaction points.</p>
<p><strong>Canvas Page:</strong> Displays the user&#8217;s current WoW characters (also known as &#8216;toons&#8217;) with level, race, class, etc. It also contains the functionality for the user to force an update to their character, so if they have gained a level, the app will reach out to the WoW database and fetch new data. It then compares the newly fetched character level with the one stored in the database, and if the new level is higher, it updates the database AND triggers a newsfeed item: &#8220;&lt;user&gt; has reached level &lt;level&gt; on &lt;pronoun&gt; &lt;race&gt; &lt;class&gt; &#8211; Ding!&#8221;</p>
<p>When a user adds a new character to their profile, a newsfeed item gets inserted: &#8220;&lt;user&gt; has added their level &lt;level&gt; &lt;race&gt; &lt;class&gt; to their Warcraft Toons profile&#8221;</p>
<p>Newsfeeds also allow you to add an action item link (although its small and arguably not that noticeable.) For the Blow Kisses app, a &#8220;Blow &lt;user&gt; a Kiss Now&#8221; link with a link to the WoW app would be appropriate. For the WoW Toons app, something like &#8220;Add your own Warcraft toons now&#8221; link makes sense.</p>
<div id="attachment_447" class="wp-caption aligncenter" style="width: 510px"><a href="http://www.snipe.net/wp-content/uploads/2008/12/picture-8.png"><img class="size-full wp-image-447" title="WoW Toons canvas page" src="http://www.snipe.net/wp-content/uploads/2008/12/picture-8.png" alt="WoW Toons canvas page" width="500" height="366" /></a><p class="wp-caption-text">WoW Toons canvas page</p></div>
<p>The WoW database also provides data on specific gear (armor, weapons, pvp stats, etc), and although I don&#8217;t currently track that data (might tho), when a user refreshes their gear, it would be appropriate to add a newsfeed item for new weapons, stats, etc. &#8220;&lt;user&gt; has reached 1045 pvp kills&#8221;.</p>
<p><strong>Your Friends Toons Page: </strong>In the WoW Toons application, you can see a listing of the toons for your friends who have added the app.</p>
<div id="attachment_450" class="wp-caption aligncenter" style="width: 510px"><a href="http://www.snipe.net/wp-content/uploads/2008/12/picture-9.png"><img class="size-full wp-image-450" title="Friends page" src="http://www.snipe.net/wp-content/uploads/2008/12/picture-9.png" alt="Friends page" width="500" height="508" /></a><p class="wp-caption-text">Friends page</p></div>
<p>One possible feature that could be added would be to show the &#8220;last updated&#8221; date on this page, and allow users to &#8220;nudge&#8221; their friends if the friend in question hasn&#8217;t updated their characters recently. &#8220;&lt;user&gt; has nudged &lt;recipient&gt; to update &lt;pronoun&gt; Warcraft toon.&#8221;</p>
<p>In the Mr. Right application mentioned in the first article, we wanted to use newsfeeds to show the gifts that Mr. Right (a fictional boyfriend avatar) has sent the user. Because it is not allowed to automatically generate newsfeed items that a user has not triggered, we had to change the app to let the user click on a button labeled &#8220;Send me a Gift&#8221;. Since the user took direct action by clicking the button, we were able to insert a newsfeed item: &#8220;&lt;user&gt; has received a &lt;gift name&gt; from Mr. Right.&#8221; The action text was then &#8220;Get your own Mr. Right now&#8221;, linking to the application.</p>
<div id="attachment_452" class="wp-caption aligncenter" style="width: 510px"><a href="http://www.snipe.net/wp-content/uploads/2008/12/picture-10.png"><img class="size-full wp-image-452" title="Mr. Right gift newsfeed" src="http://www.snipe.net/wp-content/uploads/2008/12/picture-10.png" alt="Mr. Right gift newsfeed" width="500" height="124" /></a><p class="wp-caption-text">Mr. Right gift newsfeed</p></div>
<p>The above is an example of the short story newsfeed format. &#8220;Alison received a puppy from Mr. Right&#8221; is the headline, and then the short story body contain the image and additional text.</p>
<h2>Conclusion</h2>
<p>I&#8217;m sure I sound like a broken record by now, encouraging you to take the extra time to create wireframes and plan out every part of your application before you start coding. I promise you that it will be worth it in the end &#8211; you will end up with a better application that users will find easier to use and that more effectively uses the social parts of the social networking platform. I have personally cut corners and skipped this part too often &#8211; its so tempting. &#8220;Oh, I know what its supposed to do. Its a simple application.&#8221; And it never, ever is as simple as it first seemed.</p>
<p>Coding comes next, so be sure to <a href="http://feeds.feedburner.com/snipenet" target="_blank">subscribe to our RSS</a> feed to make sure you donâ€™t miss the next part in this series, where we walk through creating a simple Facebook application based on the dicussion in this article.</p>


<p>Possibly related posts:<ol><li><a href='http://www.snipe.net/2008/09/planning-a-facebook-application/' rel='bookmark' title='Permanent Link: Planning Your Facebook Application'>Planning Your Facebook Application</a> <small>This is part one of a series &#8211; the technical...</small></li>
<li><a href='http://www.snipe.net/2010/01/facebook-lite-default/' rel='bookmark' title='Permanent Link: Unclutter Your Facebook Feed: Set FB Lite As Your Default'>Unclutter Your Facebook Feed: Set FB Lite As Your Default</a> <small>Whether you&#8217;re on Facebook for fun or for work, chances...</small></li>
<li><a href='http://www.snipe.net/2009/04/changes-to-facebooks-newsfeed/' rel='bookmark' title='Permanent Link: Changes to Facebook&#8217;s Newsfeed/Wall'>Changes to Facebook&#8217;s Newsfeed/Wall</a> <small>With the most recent API changes, specifically the one that...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2008/12/planning-a-facebook-application-part-two/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>Planning Your Facebook Application</title>
		<link>http://www.snipe.net/2008/09/planning-a-facebook-application/</link>
		<comments>http://www.snipe.net/2008/09/planning-a-facebook-application/#comments</comments>
		<pubDate>Sat, 27 Sep 2008 05:38:27 +0000</pubDate>
		<dc:creator>snipe</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[PHP/mySQL]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[social networks]]></category>
		<category><![CDATA[web dev]]></category>

		<guid isPermaLink="false">http://www.snipe.net/?p=277</guid>
		<description><![CDATA[This is part one of a series &#8211; the technical how-to of creating the application will be discussed in a separate article. This article is intended to help you plan out your application to best prepare for coding and best leverage the new aspects of Facebook for exposure and social interaction. Having created several Facebook [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: left; margin-right: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.snipe.net%2F2008%2F09%2Fplanning-a-facebook-application%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.snipe.net%2F2008%2F09%2Fplanning-a-facebook-application%2F&amp;source=snipeyhead&amp;style=normal&amp;service=bit.ly&amp;service_api=R_92bd97f4f8b9fa8a40675b36ea291223" height="61" width="50" /><br />
			</a>
		</div>
<p>This is part one of a series &#8211; the technical how-to of creating the application will be discussed in a separate article. This article is intended to help you plan out your application to best prepare for coding and best leverage the new aspects of Facebook for exposure and social interaction.</p>
<p><span id="more-277"></span></p>
<p>Having created several Facebook applications, I had this article in the back of my head for quite some time now.</p>
<p>The code and Facebook functionality mentioned in this series is current as of September 26, 2008, and deals primarily with the &#8220;new&#8221; Facebook. I will try to keep it updated in the future as they continue to change things.</p>
<p><strong>The planning phase is absolutely critical and directly relates to the success of your application. </strong>This is <em>especially</em> important in Facebook applications, since <em>good planning can literally make or break the popularity of your application</em>. If you overlook one of the profile boxes, or fail to implement social actions such as inviting friends or posting actions to the user&#8217;s newsfeed, you&#8217;re going to lose out on some of the key viral aspects that make Facebook applications become popular in the first place.</p>
<h2>Application Canvas Page</h2>
<p>The application canvas page is the standalone page that your application lives on. It has the widest available page real estate, since the application doesn&#8217;t have to share the page with anything else, other than a narrow column of ads on the right-hand side (not shown).</p>
<div id="attachment_299" class="wp-caption aligncenter" style="width: 510px"><a href="http://www.snipe.net/wp-content/uploads/2008/09/fb_canvas.jpg"><img class="size-full wp-image-299" title="Facebook application canvas page" src="http://www.snipe.net/wp-content/uploads/2008/09/fb_canvas.jpg" alt="Facebook application canvas page" width="500" height="336" /></a><p class="wp-caption-text">Facebook application canvas page</p></div>
<p>You can use a few built-in features of Facebook&#8217;s scripting language, called FBML, to easily create the ability for users to invite their friends to the application. We&#8217;ll go into more of the built-in tab tags in the next article, but its important to make it easy for users to get their friends engaged. Here&#8217;s some sample code:</p>
<pre class="brush: php">&lt;fb :tabs&gt;
&lt;fb :tab-item href=&quot;invite.php&quot; title=&quot;Invite Friends&quot;/&gt;
&lt;/fb&gt;</pre>
<blockquote><p><strong>NOTE: </strong><strong>Never require that your friends invite people in order to use your app.</strong> It&#8217;s lame, and although it may boost the number of people who install the application, your number of active users will not benefit as much as if you use honest methods to encourage people to involve their friends. Its a lousy business practice, and you will drive away users who don&#8217;t enjoy being forced into spamming their friends with an application that they haven&#8217;t even been able to try yet.</p>
<p>Genuine, organic interaction is what will provide you with long-term active users, not underhanded tricks. When users trust your application, they will be more likely to stay active and invite all of their friends, instead of just the ones they are required to invite to check out your application.</p></blockquote>
<p>Since FBML will also allow you to deliver specific content based on whether or not the user has added your application to their profile or boxes page (discussed below), be sure to include a button that prompts the user to add it. Many users are still confused by Facebook&#8217;s new layout and functionality, so the easier to make it for people to add your app to their profile, the better.</p>
<p><a href="http://www.snipe.net/wp-content/uploads/2008/09/add_popup.jpg"><img class="aligncenter size-full wp-image-315" title="add_popup" src="http://www.snipe.net/wp-content/uploads/2008/09/add_popup.jpg" alt="" width="500" height="367" /></a></p>
<p>To create this button, simple include the following code where you want the button to appear:</p>
<pre class="brush: php">&lt;fb :if-section-not-added section=&quot;profile&quot;&gt;
&lt;fb :add-section-button section=&quot;profile&quot; /&gt;
&lt;/fb&gt;</pre>
<h2>Application Content Boxes</h2>
<p>Let&#8217;s get started by discussing the various places your application content can live on Facebook. The main application lives on a canvas page in Facebook &#8211; that&#8217;s the page at <em><a class="linkification-ext" title="Linkification: http://apps.facebook.com/" href="http://apps.facebook.com/">http://apps.facebook.com/</a>&lt;yourappname&gt;</em> &#8211; but that&#8217;s not the only place your application can appear. There are several content boxes available to you &#8211; or more specifically, to your potential application users &#8211; and each one serves a slightly different purpose. To further complicate (or arguably improve) matters, the content you deliver in those boxes can be customized for the user who has added the application AND for a user who is looking at the box on someone else&#8217;s profile or fan page, delivering specific content for each state.</p>
<h3>1. Application Profile Boxes &#8211; Old Vs. New</h3>
<p>Application profile boxes are the boxes where your application has a presence on the profile of the user who has interacted with it.</p>
<p>With the &#8220;new&#8221; Facebook launch, the location and specs of these boxes have changed drastically. Prior to the Facebook redesign, when a user added an application, it would appear on their profile page, either as a wide box or a narrow box, depending on the application settings. Users could move the box from its default location into the opposite column if they so chose. When a user added an application, the application&#8217;s profile box would show up <em>by default</em> in the user&#8217;s profile. They always had the option to remove the box, but it showed up by default.</p>
<div id="attachment_290" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.snipe.net/wp-content/uploads/2008/09/old_facebook.jpg"><img class="size-medium wp-image-290" title="Old Facebook application profile box" src="http://www.snipe.net/wp-content/uploads/2008/09/old_facebook-300x276.jpg" alt="Old Facebook application profile box" width="300" height="276" /></a><p class="wp-caption-text">Old Facebook application profile box</p></div>
<p>This layout has since changed &#8211; in fact, if you use Facebook at all, you&#8217;ll have noticed that the entire profile layout and interface has changed. The change has resulted in a reduced presence of applications on the profile pages. On the new Facebook, users no longer &#8220;add&#8221; an application, they &#8220;allow&#8221; it &#8211; and the application doesn&#8217;t show up anywhere in their profile without them deliberately going out of their way to add it. This means that you have more of an obligation to give the user a compelling reason to add your application to their main profile page.</p>
<p>Additionally, the old wider boxes in old profiles are no more. <strong>Profile application boxes are 200 pixels, with 8 pixels of padding on each side for an actual usable width of 184 pixels &#8211; and are limited to 250 pixels in height</strong>. If your application box is longer than 250 pixels, everything below the 250 mark will be hidden from view, so plan around these dimensions.</p>
<div id="attachment_293" class="wp-caption aligncenter" style="width: 452px"><a href="http://www.snipe.net/wp-content/uploads/2008/09/new_fb.jpg"><img class="size-full wp-image-293" title="New Facebook application profile box" src="http://www.snipe.net/wp-content/uploads/2008/09/new_fb.jpg" alt="New Facebook application profile box" width="442" height="604" /></a><p class="wp-caption-text">New Facebook application profile box</p></div>
<p>If your application is created properly, the user will also have the option to move the application box to their (new) &#8220;Boxes&#8221; screen.</p>
<p><a href="http://www.snipe.net/wp-content/uploads/2008/09/new_bf_closeup.jpg"><img class="aligncenter size-full wp-image-295" title="new_bf_closeup" src="http://www.snipe.net/wp-content/uploads/2008/09/new_bf_closeup.jpg" alt="" width="356" height="301" /></a></p>
<h3>2. User Profile &#8220;Boxes&#8221; Screen</h3>
<p>The new Facebook includes a tab labeled &#8220;Boxes&#8221;, that serves as a shortcut to the applications they choose to include in that area. Applications that the user frequently interacts with might be added here, so the user can get to them quickly. Users can opt to drag their box to the wide or narrow column in the boxes screen.</p>
<div id="attachment_296" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.snipe.net/wp-content/uploads/2008/09/boxes_tab.jpg"><img class="size-medium wp-image-296" title="New Facebook Boxes tab" src="http://www.snipe.net/wp-content/uploads/2008/09/boxes_tab-300x201.jpg" alt="New Facebook Boxes tab" width="300" height="201" /></a><p class="wp-caption-text">New Facebook Boxes tab</p></div>
<p>Because the user has the option of narrow or wide, you should make sure you account for both wide and narrow views for the boxes tab. As you&#8217;re designing your application, take into consideration that there are dramatic size differences between the wide and narrow layouts, and <em>design appropriate displays for both versions</em>.</p>
<h3>3. Fan Pages</h3>
<p>Fan pages and application pages also received an overhaul in the new Facebook design. Previously, application&#8217;s &#8220;about&#8221; page &#8211; the page that shows the user information about your application, the developers, and allows users to leave reviews of your application &#8211; did not allow you to add external applications to an existing application about page. The new design allows you to do so. Previously, if you were running a marketing effort that included a Facebook application to advertise a product or event, you needed to create a separate fan page where users could upload photos and video, &#8220;fan&#8221; the page, and so on. Because Facebook has changed this format to allow application administrators to add external applications to application fan pages, you may decide to use the application fan page as the one destination for fans. This can cut out confusion on the part of the user, who may not understand why there are two fan pages for the same product or service.</p>
<p><a href="http://www.snipe.net/wp-content/uploads/2008/09/fanpage.jpg"><img class="aligncenter size-full wp-image-300" title="fanpage" src="http://www.snipe.net/wp-content/uploads/2008/09/fanpage.jpg" alt="" width="499" height="505" /></a></p>
<p>To encourage engagement, you can enable discussion boards, reviews, photo and video galleries, wall posts and more, allowing your application fan page to be a community portal for the people who interact with your application.</p>
<h3>4. Application Tab</h3>
<p>In the new Facebook, users have the option of adding a shortcut to their favorite apps right on their profile. This allows them to jump right to the application, without having to dig through their application menu. You MUST set this option up in the application settings page. (It will ask you for a url for your tab page. If you leave this blank, the user will not be able to add your application as an application tab.)</p>
<div id="attachment_308" class="wp-caption aligncenter" style="width: 510px"><a href="http://www.snipe.net/wp-content/uploads/2008/09/app_tab.jpg"><img class="size-full wp-image-308" title="Application tabs" src="http://www.snipe.net/wp-content/uploads/2008/09/app_tab.jpg" alt="Application tabs" width="500" height="476" /></a><p class="wp-caption-text">Application tabs</p></div>
<p>The contents of the tab page (for example, tab.php) are not unlike the contents of your main canvas page. No special code is required, so you can basically just copy the code from your canvas page (minus the wide and narrow specs) and echo it out to the page.</p>
<h3>Social Actions</h3>
<p>The user&#8217;s mini-feed (or newsfeed) is a critical aspect of your Facebook application. Many people never browse the application directory, but instead primarily learn about new applications solely by seeing what their friends are interacting with. Some will be more apt to accept a direct invitation, and others won&#8217;t even need an invitation. They will see social actions posted to their friends newsfeed, and if they are compelled by the action or message, they will frequently add the application based simply on the fact that their friends are using it, and they trust their friends, so &#8220;it must be good&#8221;.</p>
<div id="attachment_301" class="wp-caption aligncenter" style="width: 370px"><a href="http://www.snipe.net/wp-content/uploads/2008/09/picture-8.png"><img class="size-medium wp-image-301" title="Social actions are displayed in the user's newsfeed and profile wall" src="http://www.snipe.net/wp-content/uploads/2008/09/picture-8-300x180.png" alt="Social actions are displayed in the user's newsfeed and profile wall" width="360" height="216" /></a><p class="wp-caption-text">Social actions are displayed in the user</p></div>
<p>Social actions are displayed on the user&#8217;s profile &#8220;wall&#8221;, and also in the mini-feed page of their friends.</p>
<p>Facebook does have some restrictions on social action messages. For example, every application is limited to 10 social newsfeed messages per user per 24 hour period. If the user interacts with your application in a way that triggers newsfeed messages more than 10 times in a day, the subsequent newsfeed messages will simply not be displayed.</p>
<p>Additionally, the user must take an action that triggers the social action newsfeed item. For example, you could not have an application that adds a newsfeed item every 4 hours regardless of whether or not the user has interacted with the application that day. In the Mr. Right application, which was the source for many of the screenshots in this article, we wanted the fictional &#8220;boyfriend&#8221;, Mr. Right, to send the users gifts. Those gifts would then be inserted into the user&#8217;s minifeed, encouraging social actions. However since it would be against the Facebook terms of service to automatically fire off newsfeed items, we had to create a button in the application that allowed users to request that Mr. Right send them a gift. When they clicked on the &#8220;Let me give you something&#8221; button, the newsfeed was allowed, since the user took a specific action in the application to trigger it.</p>
<p>When planning your newsfeed items, be sure to make them interesting, relevant and FUN. If users feel as though your application is spamming them or their friends, they will be quick to uninstall it and never look back.</p>
<h2>Conclusion</h2>
<p>So in conclusion, there are a total of 6 application views you should be planning for:</p>
<ol>
<li>Widest &#8211; application canvas page</li>
<li>Narrow &#8211; profile box  (184 x 250)</li>
<li>Wide &#8211; boxes tab (380 wide, can expand in height)</li>
<li>Narrow &#8211; boxes tab  (184 wide, can expand in height)</li>
<li>Wide &#8211; fan page</li>
<li>Widest &#8211; application tab</li>
</ol>
<p>Additionally, each of these application views can deliver content based on:</p>
<ol>
<li>Whether or not the user has added (or &#8220;allowed&#8221;) the application</li>
<li>Whether or not the user has added the application to their profile box</li>
</ol>
<p>Ideally, you&#8217;ll want to <strong>tailor each of the box view variations based on the user states mentioned above.</strong> This potentially means designing 12 different box display variations. This may seem like a lot of work, however by taking the time to plan this out in advance, you&#8217;ll be sure to deliver truly tailored content to your users, making it more compelling for them to interact with your application and add it to their profile, putting it in a place where their friends will see it.</p>
<p>Customizing the view will deliver meaningful messages to users who have already allowed the application, and perhaps more importantly, can deliver a call to action for users who haven&#8217;t. For example:</p>
<ul>
<li>The user who has allowed the application will see a leaderboard of their friends, or information directly related to the way they have previously interacted with the application. (&#8220;Friends Leaderboard&#8221;, or &#8220;This is your Mr. Right&#8221;)</li>
<li>The user who has not allowed the application and is viewing it on a friend&#8217;s profile will be given a call to action. (&#8220;To get your own Mr. Right, click here&#8221;)</li>
</ul>
<p>Including <strong>social actions into the user&#8217;s newsfeed </strong>will also be a very effective way to leverage the viral aspects of Facebook applications. Carefully planned social action messages will pique the curiosity of the friends of the user who is interacting with it, making it far more likely that they will interact with it themselves.</p>
<p>And of course, you should <strong>make it as easy as possible for users to invite and engage their friends</strong> by integrating built-in &#8220;invite your friends&#8221; functionality, and (if it makes sense for your application) leaderboards or galleries that invite friendly competition between friends.</p>
<p>For more information on application planning, visit the <a href="http://developers.facebook.com/get_started.php?tab=anatomy" target="_blank">Anatomy of an Application page on the Facebook Developer&#8217;s website</a>. The Anatomy of an Application page also addresses some additional functions such as a media publisher that allows user to publish rich content to their profiles, although it may not be appropriate for all applications.</p>
<p>Don&#8217;t miss <strong><a href="http://www.snipe.net/2008/12/planning-a-facebook-application-part-two/" target="_blank">part two of this series</a></strong>, where we walk through creating a simple Facebook application based on the dicussion in this article.</p>


<p>Possibly related posts:<ol><li><a href='http://www.snipe.net/2008/12/planning-a-facebook-application-part-two/' rel='bookmark' title='Permanent Link: Planning a Facebook Application: Part Two'>Planning a Facebook Application: Part Two</a> <small>I know I promised you that we&#8217;d get into some...</small></li>
<li><a href='http://www.snipe.net/2009/04/facebook-application-tabs/' rel='bookmark' title='Permanent Link: %$#^%$* Facebook Application Tabs'>%$#^%$* Facebook Application Tabs</a> <small>So this is new. And by new I mean painfully...</small></li>
<li><a href='http://www.snipe.net/2009/06/fb-fanpages-fbml-box/' rel='bookmark' title='Permanent Link: Static FBML: Not Every Facebook Fan Page Needs An Application'>Static FBML: Not Every Facebook Fan Page Needs An Application</a> <small>You don&#8217;t always need a custom application for your Facebook...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2008/09/planning-a-facebook-application/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Identify and Fix SQL Injection Vulnerabilities in Web Applications</title>
		<link>http://www.snipe.net/2008/07/identify-and-fix-sql-injection-vulnerabilities-in-web-applications/</link>
		<comments>http://www.snipe.net/2008/07/identify-and-fix-sql-injection-vulnerabilities-in-web-applications/#comments</comments>
		<pubDate>Tue, 01 Jul 2008 16:15:34 +0000</pubDate>
		<dc:creator>snipe</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[PHP/mySQL]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[Windows Downloads]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[freeware]]></category>
		<category><![CDATA[hacking]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[security]]></category>

		<guid isPermaLink="false">http://www.snipe.net/?p=157</guid>
		<description><![CDATA[Scrawlr is a free software for scanning SQL injection vulnerabilities on your web applications, developed by HP Web Security Research Group in coordination with Microsoft Security Response Center. Scrawlr crawls a website while simultaneously analyzing the parameters of each individual web page for SQL Injection vulnerabilities. After the scanning process, if it can find vulnerabilities, [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style="float: left; margin-right: 10px;">
			<a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fwww.snipe.net%2F2008%2F07%2Fidentify-and-fix-sql-injection-vulnerabilities-in-web-applications%2F"><br />
				<img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fwww.snipe.net%2F2008%2F07%2Fidentify-and-fix-sql-injection-vulnerabilities-in-web-applications%2F&amp;source=snipeyhead&amp;style=normal&amp;service=bit.ly&amp;service_api=R_92bd97f4f8b9fa8a40675b36ea291223" height="61" width="50" /><br />
			</a>
		</div>
<p>Scrawlr is a free software for scanning SQL injection vulnerabilities on your web applications, developed by HP Web Security Research Group in coordination with Microsoft Security Response Center.</p>
<p><span id="more-157"></span></p>
<p><a href="http://www.snipe.net/wp-content/uploads/2008/07/screenshot.jpg"><img class="aligncenter size-full wp-image-156" title="screenshot" src="http://www.snipe.net/wp-content/uploads/2008/07/screenshot.jpg" alt="" width="400" height="308" /></a></p>
<p>Scrawlr crawls a website while simultaneously analyzing the parameters of each individual web page for SQL Injection vulnerabilities.</p>
<p>After the scanning process, if it can find vulnerabilities, it will display your database table names as a proof of the possible SQL injection vulnerabilities.</p>
<p>From the <a href="http://www.communities.hp.com/securitysoftware/blogs/spilabs/archive/2008/06/23/finding-sql-injection-with-scrawlr.aspx" target="_blank">HP Scrawlr website</a>:</p>
<blockquote><p><strong>Technical details for Scrawlr</strong></p>
<ul>
<li>Identify Verbose SQL Injection vulnerabilities in URL parameters</li>
<li>Can be configured to use a Proxy to access the web site</li>
<li>Will identify the type of SQL server in use</li>
<li>Will extract table names (verbose only) to guarantee no false positives</li>
</ul>
<p>Scrawlr does have some limitations versus our professional solutions and our fully functional SQL Injector tool</p>
<ul>
<li>Will only crawls up to 1500 pages</li>
<li>Does not support sites requiring authentication</li>
<li>Does not perform Blind SQL injection</li>
<li>Cannot retrieve database contents</li>
<li>Does not support JavaScript or flash parsing</li>
<li>Will not test forms for SQL Injection (POST Parameters)</li>
</ul>
</blockquote>
<p>There are some limitations, as noted in the above bulleted list, however this is certainly a good start to help web developers find and correct vulnerabilities in their applications. <a href="https://download.spidynamics.com/Products/scrawlr/" target="_blank">Download Scrawlr now</a> &#8211; Windows Only.</p>


<p>Possibly related posts:<ol><li><a href='http://www.snipe.net/2008/07/generate-lists-of-banned-words-for-forums-and-other-applications/' rel='bookmark' title='Permanent Link: Generate lists of banned words for forums and other applications'>Generate lists of banned words for forums and other applications</a> <small>If you develop software for a living, or if you...</small></li>
<li><a href='http://www.snipe.net/2009/01/advertising-on-facebook-applications-an-experiment/' rel='bookmark' title='Permanent Link: Advertising on Facebook Applications &#8211; An Experiment'>Advertising on Facebook Applications &#8211; An Experiment</a> <small>This article has been deprecated, and has been replaced by...</small></li>
<li><a href='http://www.snipe.net/2002/06/alternating-row-colors-in-phpmysql/' rel='bookmark' title='Permanent Link: Alternating Row Colors in PHP/mySQL'>Alternating Row Colors in PHP/mySQL</a> <small>Using alternating row colors in a PHP database application is...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2008/07/identify-and-fix-sql-injection-vulnerabilities-in-web-applications/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
