<?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>Tue, 24 Jan 2012 04:30:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Upcoming WordPress Security Book</title>
		<link>http://www.snipe.net/2010/11/wordpress-security-book/</link>
		<comments>http://www.snipe.net/2010/11/wordpress-security-book/#comments</comments>
		<pubDate>Fri, 05 Nov 2010 16:44:58 +0000</pubDate>
		<dc:creator>snipe</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[PHP/mySQL]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[book]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.snipe.net/?p=3234</guid>
		<description><![CDATA[I casually asked a few of you (okay, almost 8,000 of you) if there would be any interest in a commercial (but cheap) e-book on securing, monitoring, and un-hacking WordPress. I received enough &#8220;yes&#8221;, &#8220;absolutely&#8221; and &#8220;hell yeah!&#8221; replies that I decided to move forward with this project. I&#8217;m at the very early stages of [...]]]></description>
			<content:encoded><![CDATA[<p>I casually asked a few of you (okay, almost 8,000 of you) if there would be any interest in a commercial (but cheap) e-book on securing, monitoring, and un-hacking WordPress. I received enough &#8220;yes&#8221;, &#8220;absolutely&#8221; and &#8220;hell yeah!&#8221; replies that I decided to move forward with this project.<span id="more-3234"></span></p>
<p>I&#8217;m at the very early stages of writing a book about how to secure, monitor and un-hack WordPress. This book will be the culmination of everything I know about keeping WordPress hardened against attacks, how to keep an eye on your install so that you&#8217;re the first to know if something has happened, and how to handle the situation if you bought the book too late and got pwned anyway.</p>
<p><strong>Can I absolutely guarantee that you&#8217;ll never get hacked if you do everything in this book? </strong>Of course not &#8211; new exploits emerge all the time. However I can promise you that you&#8217;ll be a lot less likely to get hacked if you follow these instructions, and if you do get hacked, you&#8217;ll be a far better place to recovery quickly and completely &#8211; and if that&#8217;s not worth $5 to you, you deserve what you get.</p>
<p>I can also promise that if there is enough interest in this book, I will keep it updated and release new revisions for free for those people who have already purchased it. I don&#8217;t believe in having to re-purchase a book just because a paragraph or two was added. It pisses me off when I have to do it, and I&#8217;m sure it pisses you off, too.</p>
<p><strong>How much?</strong><br />
Right now, I&#8217;m toying with a $5-$10 price range, depending on how long and detailed it ends up being. It will not be more than $10, regardless of how long it is.</p>
<p><strong>Are you doing advanced sales?</strong><br />
YES! If you&#8217;d like to pre-order, <strong><a href="http://funds.gofundme.com/11u94" target="_blank">check out the page on GoFundMe</a></strong>. There are a few different options for pre-ordering, so it&#8217;s worth a look.</p>
<p><strong>Why an e-book instead of a &#8220;real&#8221; book?</strong><br />
I might make this available in hardcopy through Lulu or some such service &#8211; but there are several reasons I went with e-book. First, <a href="http://www.amazon.com/s/ref=ntt_athr_dp_sr_2?_encoding=UTF8&amp;sort=relevancerank&amp;search-alias=books&amp;field-author=Alison%20Gianotto" target="_blank">I&#8217;ve written &#8220;real&#8221; books before</a>. The process is frustrating, and there&#8217;s not a lot of money in it, in tech books anyway. By self-publishing, more of the money ends up in my pocket where it belongs. Second, as a recent convert to e-books (thanks to my iPad), I prefer to give folks the option of printing if they need to, but spare the trees for those that don&#8217;t. And third, stuff changes all the time in technology. Paper books have never seemed like a great way to cover tech topics, since half the book will be obsolete within a year or two, my own previous books included. Self-publishing via e-book means I can update the information as needed, and not feel like a jackass for making people buy a new edition of the book.</p>
<p><strong>What qualifies you to write this book anyway?</strong><br />
I&#8217;ve been working with WordPress since 2005, and have spent a considerable amount of time over the past several years doing forensic and recovery work on hacked WordPress blogs. For some idea of the type of content you can expect to find here, <a href="http://www.snipe.net/2010/01/when-wordpress-gets-hacked/">check out this blog post from Jan 2010</a>. I&#8217;ve helped folks like Chris Brogran, Scott Stratten, the Crave Network and others quickly recovery from a hack, and lock down their installs so they are less vulnerable to future attacks. Ask them &#8211; they&#8217;ll tell you. <img src='http://www.snipe.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><strong>How much swearing will there be?</strong><br />
Probably less than you expect. My goal is that this can be something some of you can recommend to your clients (the ones that won&#8217;t pay you to secure their sites), and as tempting as it is, that might not fly for the general public. I will, however, try to keep it entertaining in my own way.</p>
<p><strong>What&#8217;s the title going to be?</strong><br />
NFC. Suggestions welcome.</p>
<p><strong>How can I stay updated on the book&#8217;s progress?</strong><br />
I&#8217;ll be updating the official book website as new developments arise. <a href="http://secure-wp.com/">Check it out here</a>.</p>
<p><strong>I have another question!</strong><br />
Tag me on Twitter at <a href="http://twitter.com/snipeyhead">@snipeyhead</a> or leave a comment below and ask away.</p>

 <script type="text/javascript">
	<!--
		function onover(what){
	document.getElementById('blurbtext').innerHTML=''+what+'';
	}
	function onout(){
	document.getElementById('blurbtext').innerHTML='&nbsp;';
	}
	-->
	</script>



<h3 style="padding-bottom: 0px; margin-bottom: 0px;">Also check out: <br /><span id="blurbtext"><br /></span></h3>

<div id="relatedposts">




		
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2008/12/warcraft-security-better-than-banking-security/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/12/product.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Warcraft Security Better Than Banking Security?" height="90" width="90" onmouseover="onover('Warcraft Security Better Than Banking Security?')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2011/01/security-keynote/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2011/01/donkey_balls.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Security Keynote Download" height="90" width="90" onmouseover="onover('Security Keynote Download')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2010/01/when-wordpress-gets-hacked/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2010/01/cockpunch.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="When Your WordPress Blog Gets Hacked" height="90" width="90" onmouseover="onover('When Your WordPress Blog Gets Hacked')" onmouseout="onout()" /></a></div>

	</div>

]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2010/11/wordpress-security-book/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Firefox Addons for Penetration/XSS Testing</title>
		<link>http://www.snipe.net/2010/10/firefox-addons-xss-testing/</link>
		<comments>http://www.snipe.net/2010/10/firefox-addons-xss-testing/#comments</comments>
		<pubDate>Thu, 14 Oct 2010 19:25:48 +0000</pubDate>
		<dc:creator>snipe</dc:creator>
				<category><![CDATA[Featured]]></category>
		<category><![CDATA[PHP/mySQL]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[addons]]></category>
		<category><![CDATA[exploits]]></category>
		<category><![CDATA[firefox]]></category>
		<category><![CDATA[hacked]]></category>
		<category><![CDATA[penetration testing]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[xss]]></category>

		<guid isPermaLink="false">http://www.snipe.net/?p=2843</guid>
		<description><![CDATA[2010 was supposed to be the year of the Tiger, but it&#8217;s felt more like the year of Pwny so far. This article covers some Firefox add-ons that help you test your own apps, whether you&#8217;re working with a penetration tester, or by default, you are the penetration tester. I&#8217;ll start with the obvious candidates [...]]]></description>
			<content:encoded><![CDATA[<p>2010 was supposed to be the year of the Tiger, but it&#8217;s felt more like the year of Pwny so far. This article covers some Firefox add-ons that help you test your own apps, whether you&#8217;re working with a penetration tester, or by default, you <em>are</em> the penetration tester.<br />
<span id="more-2843"></span></p>
<p>I&#8217;ll start with the obvious candidates that you probably already have installed if you&#8217;re a developer. I&#8217;ve also added a few that are useful for post-hack diagnostics and recovery.</p>
<h4>General</h4>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/1843/">Firebug</a></strong> &#8211; Firebug is great for web development in general, but the debugging tools can help track down calls to rogue javascript on external servers, among many other things.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/60/">Web Developer Toolbar</a></strong> &#8211; Another great web dev tool, the Web Developer Toolbar makes it easy to turn javascript and cookies on and off selectively, view form fields and disable restrictions and much, much more. </p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/5914/">DNS Cache</a></strong> &#8211; simple addon that lets you clear or disable Firefox&#8217;s DNS cache. Not specifically for pen testing, but useful nonetheless.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/14503/">Notable</a> </strong>- love this addon, which lets you do a full-page screenshot with annotations over at <a href="https://www.notableapp.com/">notableapp.com</a>. As you&#8217;re testing, there&#8217;s a good chance you&#8217;re going to need to show your other devs or account managers a screenshot so they can see the vulnerability being exploited. While something simple like Fireshot would work fine (or native screenshots), I like using Notable for complex situations that require explanations on multiple points on the page. Exports to annotated PDF.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/46698/">Groundspeed</a></strong> &#8211; simple form toolkit that allows you to edit form fields (hidden to text, etc), remove length restrictions, change/remove javascript event handlers, and change form target so that it opens in a new tab.</p>
<h4>Code Injection</h4>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/7597/">SQL Inject Me</a></strong> &#8211; helps test for SQL injection vulnerabilities.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/7598/">XSS Me</a></strong> &#8211; used to test for reflected Cross-Site Scripting (XSS). It does NOT currently test for stored XSS.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/7595/">Access Me</a></strong> &#8211; used to test some access vulnerabilities related to web applications. The tool works by sending several versions of the last page request. A request with the session removed will be sent. A request using the HTTP HEAD verb and a request using a made up SECCOM verb will be sent. A combination of session and HEAD/SECCOM will also be sent.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/10345/">JS Deobfuscator</a></strong> &#8211; many attacks inject obfuscated javascript into a page so that it becomes harder for you to simply grep the source for something obvious, like the domain name to which the bad script is redirecting the user. This addon helps deobfuscate the javascript so you can see what&#8217;s really going on.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/3899/">Hackbar</a></strong> &#8211; helps with testing sql injections, XSS holes and site security. Ugly as sin, but it works well.</p>
<h4>Header and URL Monitoring/Tampering</h4>
<p>Note that some of these addons do similar things &#8211; try them and stick with whichever one you like best.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/6647/">HttpFox</a></strong> &#8211; monitors and analyzes all incoming and outgoing HTTP traffic between the browser and the web servers.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/3829/">Live HTTP Headers</a></strong> &#8211; view HTTP headers of a page and while browsing.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/967/">Modify Headers</a></strong> &#8211; add, modify and filter http request headers. You can modify the user-agent string, add headers to spoof a mobile request (e.g. x-up-calling-line-id) and much more.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/966/">Tamper Data</a></strong> &#8211; use tamperdata to view and modify HTTP/HTTPS headers and post parameters, trace and time http response/requests and security test web applications by modifying POST parameters.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/59/">User Agent Switcher</a></strong> &#8211; allows you to easily toggle between pre-set user agent strings, or set your own.</p>
<h4>Environment Detection</h4>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/4276/">Header Spy</a></strong> &#8211; lightweight addon that displays information about the website&#8217;s server in your statusbar. This is not as useful for pen testing as it is for impressing the crap out of clients who don&#8217;t know what server they&#8217;re running. <img src='http://www.snipe.net/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> </p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/8946/">Host Spy</a></strong> &#8211; integrated shortcut to show you who a website&#8217;s IP neighbors are on shared hosting.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/590/">ShowIP</a></strong> &#8211; Small addons that shows the IP address of the website in your statusbar and a link to some additional tools.</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/3572/">URL Flipper</a></strong> &#8211; quickly and easily increment and decrement numbers and strings in URLs for navigating through URL sequences (for example, user ids or session info in the query string.)</p>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/10229/">Wappalyzer</a></strong> &#8211; uncovers the technologies used on websites. It detects CMS and e-commerce systems, message boards, JavaScript frameworks, hosting panels, analytics tools and several more.</p>
<h4>Searching</h4>
<p><strong><a href="https://addons.mozilla.org/en-US/firefox/addon/49858/">Offensive Security Exploit Database</a> </strong> &#8211; this adds the excellent database of exploits at <a href="http://www.exploit-db.com">exploit-db.com</a> as one of your search engine options.</p>
<p>I&#8217;ve also just created a <strong>search plugin for XSSed.Com</strong>, <del datetime="2010-10-15T01:43:53+00:00">but it&#8217;s pending approval at Mozilla, so not sure when that will be ready for you</del> which can be <strong><a href="https://addons.mozilla.org/en-US/firefox/addon/241845/">downloaded here</a></strong>. It&#8217;s not exactly rocket science to add a new search site to your browser search bar, but I figured it was quick and easy to whip up. Feel free to check out the <a href="https://addons.mozilla.org/en-US/firefox/files/browse/101135">source code for the plugin here</a>.</p>
<h4>Too Many Addons Got You Down?</h4>
<p>If you&#8217;re finding your plugins are slowing down Firefox too much, you might want to create a separate Firefox profile specifically for testing, and switch to that profile when you&#8217;re ready to start hammering away. Also bear in mind that you might need to tweak some settings on these, or only enable them right before you use them, as the toolbars and sidebars can be a bit bulky.</p>
<p>Also keep in mind that the Net option on the web developers toolbar, or any of the header analyzer addons can be very helpful in general testing between dev and live environments (load the page on live and make sure nothing is being pulled from the dev address) and also to make sure your SSL requests are being handled correctly.</p>
<h4>Some Additional Thoughts&#8230;</h4>
<p>When folks ask me how I do penetration testing &#8211; whether I use software, or do it by hand &#8211; the best way I can answer is &#8220;both&#8221;. Software will only ever get you so far, but it&#8217;s a critical tool in helping you figure out where the vulnerabilities are. It&#8217;s not unlike using a metal detector to find treasure. When the metal detector is doing its job, it finds, well, metal. Not necessarily treasure, although fancier metal detectors have additional software that helps try to identify the buried object by shape and size. You still have to physically dig up the item and rely on your knowledge and experience to determine whether or not it really is treasure, or just junk. The metal detector simply finds something that meets a basic set of requirements, to save you from having to dig up every square inch of the beach. </p>
<p>When testing web applications for vulnerabilities, software does very much the same thing. It simply automates tasks that you could do by hand but that would take an unreasonable amount of time, but ultimately when it finds something, you still need to know enough about what you&#8217;re looking at to determine how big a threat it actually is. Most of the time the software will attempt the to try the lowest-level exploit, for example, the ability to execute arbitrary javascript in a page. Your testing tools may demonstrate that you can create a javascript alertbox on the page, but it&#8217;s your knowledge and experience that will help you determine the full extent of the vulnerability, for example whether that arbitrary javascript could be used to redirect a user to a new page, hijack the user&#8217;s session data, etc.</p>
<p>The reason I ended this post with a long-winded ramble is because I wanted to make it clear that just having the tools isn&#8217;t enough. Actually using them, and knowing what to do with the results are important. Understanding the basic mechanics of how exploits work is the only way you can make sure your applications are written to mitigate them. Having the tools installed but never understanding or using them is like buying a metal detector and keeping in the closet and then wondering why you haven&#8217;t found anything valuable yet.</p>
<p><strong>If you&#8217;re interested in learning more about web application penetration testing and security, check out the following books:</strong></p>
<ul>
<li><a href="http://www.amazon.com/gp/product/0470170778?ie=UTF8&#038;tag=snipenet&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0470170778">The Web Application Hacker&#8217;s Handbook: Discovering and Exploiting Security Flaws</a></li>
<li><a href="http://www.amazon.com/gp/product/0596514832?ie=UTF8&#038;tag=snipenet&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0596514832">Web Security Testing Cookbook: Systematic Techniques to Find Problems Fast</a></li>
<li><a href="http://www.amazon.com/gp/product/1590597842?ie=UTF8&#038;tag=snipenet&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=1590597842">Foundations of Security: What Every Programmer Needs to Know</a></li>
<li><a href="http://www.amazon.com/gp/product/1597495883?ie=UTF8&#038;tag=snipenet&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=1597495883">Ninja Hacking: Unconventional Penetration Testing Tactics and Techniques</a></li>
</ul>
<h4>How Do You XSS?</h4>
<p>These addons are not obviously meant to be a replacement for more capable and thorough penetration testing tools such as metaploit, netsparker, etc. They&#8217;re just meant to be a convenient way for developers to test code during and after development.</p>
<p>There is a  more comprehensive collection of addons listed <a href="https://addons.mozilla.org/en-US/firefox/collections/adammuntner/webappsec/?page=1">here</a>, but this is what I use. If you&#8217;ve got a favorite that I&#8217;ve missed, please be sure to share in the comments!</p>

 <script type="text/javascript">
	<!--
		function onover(what){
	document.getElementById('blurbtext').innerHTML=''+what+'';
	}
	function onout(){
	document.getElementById('blurbtext').innerHTML='&nbsp;';
	}
	-->
	</script>



<h3 style="padding-bottom: 0px; margin-bottom: 0px;">Also check out: <br /><span id="blurbtext"><br /></span></h3>

<div id="relatedposts">




		
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2009/01/easier-cross-browser-testing/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2009/01/cross_browser-compatible2.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Easier Cross-Browser Testing" height="90" width="90" onmouseover="onover('Easier Cross-Browser Testing')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2007/01/firefox-extensions-i-cant-live-without/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/06/firefox_eats_ie.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Firefox extensions I can&#8217;t live without" height="90" width="90" onmouseover="onover('Firefox extensions I can&#8217;t live without')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2008/06/hacking-firefox/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/06/firefox.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Hacking Firefox" height="90" width="90" onmouseover="onover('Hacking Firefox')" onmouseout="onout()" /></a></div>

	</div>

]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2010/10/firefox-addons-xss-testing/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<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[<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>

 <script type="text/javascript">
	<!--
		function onover(what){
	document.getElementById('blurbtext').innerHTML=''+what+'';
	}
	function onout(){
	document.getElementById('blurbtext').innerHTML='&nbsp;';
	}
	-->
	</script>



<h3 style="padding-bottom: 0px; margin-bottom: 0px;">Also check out: <br /><span id="blurbtext"><br /></span></h3>

<div id="relatedposts">




		
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2010/11/wordpress-security-book/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2010/11/secure-wordpress.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Upcoming WordPress Security Book" height="90" width="90" onmouseover="onover('Upcoming WordPress Security Book')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2009/01/essential-wordpress-plugins/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2009/01/blog_logo.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Essential WordPress Plugins" height="90" width="90" onmouseover="onover('Essential WordPress Plugins')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2009/01/creating-a-wordpress-theme/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2009/01/wordpress-logo.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Creating A WordPress Theme" height="90" width="90" onmouseover="onover('Creating A WordPress Theme')" onmouseout="onout()" /></a></div>

	</div>

]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2010/06/upgrading-to-wordpress-3/feed/</wfw:commentRss>
		<slash:comments>20</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[<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>

 <script type="text/javascript">
	<!--
		function onover(what){
	document.getElementById('blurbtext').innerHTML=''+what+'';
	}
	function onout(){
	document.getElementById('blurbtext').innerHTML='&nbsp;';
	}
	-->
	</script>



<h3 style="padding-bottom: 0px; margin-bottom: 0px;">Also check out: <br /><span id="blurbtext"><br /></span></h3>

<div id="relatedposts">




		
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2009/02/practical-mod_rewrite/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2009/02/dave.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Practical Mod_Rewrite for Web Developers" height="90" width="90" onmouseover="onover('Practical Mod_Rewrite for Web Developers')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2011/01/facebook-https-opt-in/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2011/01/Facebook-Needle.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Facebook Introduces HTTPS Opt-In for Users, Impacts App Developers" height="90" width="90" onmouseover="onover('Facebook Introduces HTTPS Opt-In for Users, Impacts App Developers')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2008/07/identify-and-fix-sql-injection-vulnerabilities-in-web-applications/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/07/screenshot.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Identify and Fix SQL Injection Vulnerabilities in Web Applications" height="90" width="90" onmouseover="onover('Identify and Fix SQL Injection Vulnerabilities in Web Applications')" onmouseout="onout()" /></a></div>

	</div>

]]></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[<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>
<p>[sourcecode lang=php]require_once $_SERVER['DOCUMENT_ROOT'].&#8217;/phpMailer/class.phpmailer.php&#8217;;</p>
<p>$mail             = new PHPMailer(); // defaults to using php &#8220;mail()&#8221;<br />
$body             = $mail->getFile(&#8216;../invites/email.html&#8217;);<br />
$mail->From       = $from_email;<br />
$mail->FromName   = $from_name;</p>
<p>$mail->Subject    = &#8220;Test email subject&#8221;;<br />
$mail->AltBody    = &#8220;To view the message, please use an HTML compatible email viewer!&#8221;;<br />
$mail->MsgHTML($body);</p>
<p>$mail->AddAddress($to_email, $to_name);		</p>
<p>if(!$mail->Send()) {<br />
	echo &#8220;
<li>Mailer Error: &#8221; . $mail->ErrorInfo;<br />
} else {<br />
	echo &#8220;</li>
<li>Message sent!&#8221;;<br />
}[/sourcecode]</p>
<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>

 <script type="text/javascript">
	<!--
		function onover(what){
	document.getElementById('blurbtext').innerHTML=''+what+'';
	}
	function onout(){
	document.getElementById('blurbtext').innerHTML='&nbsp;';
	}
	-->
	</script>



<h3 style="padding-bottom: 0px; margin-bottom: 0px;">Also check out: <br /><span id="blurbtext"><br /></span></h3>

<div id="relatedposts">




		
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2002/06/sending-htmlplain-text-mail-simultaneously-using-php/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/06/htmlmail.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Sending HTML/Plain Text Mail Simultaneously using PHP" height="90" width="90" onmouseover="onover('Sending HTML/Plain Text Mail Simultaneously using PHP')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2008/10/is-imappop3-gmail-or-gtalk-periodically-rejecting-your-password/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/10/gmail-soap.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Is IMAP/POP3 Gmail or Gtalk periodically rejecting your password?" height="90" width="90" onmouseover="onover('Is IMAP/POP3 Gmail or Gtalk periodically rejecting your password?')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2004/06/dynamic-watermarkstext-overlay-on-images-in-php/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/06/picture-31.png&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Dynamic Watermarks/Text Overlay on Images in PHP" height="90" width="90" onmouseover="onover('Dynamic Watermarks/Text Overlay on Images in PHP')" onmouseout="onout()" /></a></div>

	</div>

]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2009/10/funky-characters-in-html-mail-using-phpmailer/feed/</wfw:commentRss>
		<slash:comments>3</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[<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>
<p>[sourcecode language='php']function twitterify($ret) {<br />
  $ret = preg_replace(&#8220;#(^|[\n ])([\w]+?://[\w]+[^ \"\n\r\t< ]*)#&#8221;, &#8220;\\1<a href=\"\\2\" target=\"_blank\">\\2</a>&#8220;, $ret);<br />
  $ret = preg_replace(&#8220;#(^|[\n ])((www|ftp)\.[^ \"\t\n\r< ]*)#&#8221;, &#8220;\\1<a href=\"http://\\2\" target=\"_blank\">\\2</a>&#8220;, $ret);<br />
  $ret = preg_replace(&#8220;/@(\w+)/&#8221;, &#8220;<a href=\"http://www.twitter.com/\\1\" target=\"_blank\">@\\1</a>&#8220;, $ret);<br />
  $ret = preg_replace(&#8220;/#(\w+)/&#8221;, &#8220;<a href=\"http://search.twitter.com/search?q=\\1\" target=\"_blank\">#\\1</a>&#8220;, $ret);<br />
return $ret;<br />
}[/sourcecode]</p>
<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>

 <script type="text/javascript">
	<!--
		function onover(what){
	document.getElementById('blurbtext').innerHTML=''+what+'';
	}
	function onout(){
	document.getElementById('blurbtext').innerHTML='&nbsp;';
	}
	-->
	</script>



<h3 style="padding-bottom: 0px; margin-bottom: 0px;">Also check out: <br /><span id="blurbtext"><br /></span></h3>

<div id="relatedposts">




		
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2007/11/stupid-monsters-someone-was-paid-to-make/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/06/dd-beasts-senmurv-gay-pride.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Stupid Monsters Someone Was Paid to Make" height="90" width="90" onmouseover="onover('Stupid Monsters Someone Was Paid to Make')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2009/07/writing-your-first-twitter-application-with-oauth/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2009/07/Imonmonies128410148226846250.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Writing Your First Twitter Application with OAuth" height="90" width="90" onmouseover="onover('Writing Your First Twitter Application with OAuth')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2009/06/no-follow-back-girl/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2009/06/fail-lol.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Why I Won&#8217;t Follow You Back on Twitter" height="90" width="90" onmouseover="onover('Why I Won&#8217;t Follow You Back on Twitter')" onmouseout="onout()" /></a></div>

	</div>

]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2009/09/php-twitter-clickable-links/feed/</wfw:commentRss>
		<slash:comments>62</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[<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>
<p>[source lang='php']/* config.php */</p>
<p>/* Consumer key from twitter */<br />
$consumer_key = &#8216;xxhjgxhjxhhjgxjhjxgjyx768678xx&#8217;; </p>
<p>/* Consumer Secret from twitter */<br />
$consumer_secret = &#8216;jhgjdfgfgjhj76jgjgjhxxxjhxxx&#8217;;<br />
[/source]</p>
<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>
<p>[source lang='php']/* index.php */</p>
<p>session_start();</p>
<p>/* Destroy the session if the user is logging out */<br />
if ((isset($_GET['logout'])) &#038;&#038; ($_GET['logout']==&#8217;true&#8217;)) {<br />
    session_destroy();<br />
    session_unset();<br />
}</p>
<p>/* Include the config file */<br />
require_once(&#8216;config.php&#8217;);</p>
<p>/* include the twitter OAuth library files */<br />
require_once(&#8216;twitterOAuth/twitterOAuth.php&#8217;);<br />
require_once(&#8216;twitterOAuth/OAuth.php&#8217;);</p>
<p>    /*<br />
    Create a new TwitterOAuth object, and then<br />
    get a request token. The request token will be used<br />
    to build the link the user will use to authorize the<br />
    application. </p>
<p>     You should probably use a try/catch here to handle errors gracefully<br />
    */<br />
    $to = new TwitterOAuth($consumer_key, $consumer_secret);<br />
    $tok = $to->getRequestToken();</p>
<p>    $request_link = $to->getAuthorizeURL($tok);</p>
<p>    /*<br />
    Save tokens for later  &#8211; we need these on the callback page to ask for the<br />
    access tokens<br />
    */<br />
    $_SESSION['oauth_request_token'] = $token = $tok['oauth_token'];<br />
    $_SESSION['oauth_request_token_secret'] = $tok['oauth_token_secret'];</p>
<p>echo &#8216;
<p><a href="'.$request_link.'">login using twitter</a> | &#8216;;<br />
echo &#8216;<a href="index.php?logout=true">Logout</a></p>
<p>&#8216;;<br />
[/source]</p>
<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>
<p>[source lang='php']/* callback.php */</p>
<p>session_start();</p>
<p>/* Include the config file */<br />
require_once(&#8216;config.php&#8217;);</p>
<p>/* include the twitter OAuth library files */<br />
require_once(&#8216;twitterOAuth/twitterOAuth.php&#8217;);<br />
require_once(&#8216;twitterOAuth/OAuth.php&#8217;);</p>
<p>/* check for an auth access token. If there&#8217;s no auth token set, go ahead and fetch one from Twitter,<br />
* using the API call. */<br />
if ((!isset($_SESSION['oauth_access_token'])) || ($_SESSION['oauth_access_token'])==&#8221;) {</p>
<p>	$to = new TwitterOAuth($consumer_key, $consumer_secret, $_SESSION['oauth_request_token'], $_SESSION['oauth_request_token_secret']);<br />
	$tok = $to->getAccessToken();</p>
<p> 	/* Save tokens for later  &#8211; might be wise to<br />
        * store the oauth_token and secret in a database, and<br />
        * only store the oauth_token in a cookie or session for security purposes */<br />
	$_SESSION['oauth_access_token'] = $token = $tok['oauth_token'];<br />
	$_SESSION['oauth_access_token_secret'] = $tok['oauth_token_secret'];</p>
<p>} </p>
<p>/* Connect to the Twitter API */<br />
$to = new TwitterOAuth($consumer_key, $consumer_secret, $_SESSION['oauth_access_token'], $_SESSION['oauth_access_token_secret']);<br />
$content = $to->OAuthRequest(&#8216;https://twitter.com/account/verify_credentials.xml&#8217;, array(), &#8216;GET&#8217;);<br />
$user = simplexml_load_string($content);</p>
<p>if ($user->screen_name!=&#8221;) {<br />
	echo &#8216;<br />
<h2><img src="'.$user-/>profile_image_url.&#8217;&#8221; align=&#8221;left&#8221;>&#8217;;<br />
	echo &#8216;Hello, &#8216;.$user->screen_name.&#8217;</h2>
<p>&#8216;;<br />
	echo &#8216;
<p>You follow &#8216;.$user->friends_count.&#8217; people, &#8216;;<br />
	echo &#8216;you have &#8216;.$user->followers_count.&#8217; &#8216;;<br />
	echo &#8216;people following you, and you joined &#8216;;<br />
	echo &#8216;Twitter on &#8216;.$user->created_at.&#8217;. &#8216;;<br />
	echo &#8216;You have posted &#8216;.$user->statuses_count.&#8217; updates.</p>
<p>&#8216;;<br />
} else {<br />
	echo &#8216;Oops &#8211; an error has occurred.&#8217;;<br />
}</p>
<p>echo &#8216;
<pre>';
print_r($user);
echo '</pre>
<p>&#8216;;[/source]</p>
<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>
<p>[source lang='html']SimpleXMLElement Object<br />
(<br />
    [id] => 14246782<br />
    [name] => snipe<br />
    [screen_name] => snipeyhead<br />
    [location] => New York<br />
    [description] => Codemonkey, designer, author, speaker, blogger, swordfighter, Warcrafter, sarcasticgeek, scuba diver, blacksmith, crimefighter, Mentat, MBTI: ENTP, Totally NSFW<br />
    [profile_image_url] => http://s3.amazonaws.com/twitter_production/profile_images/303658881/Photo_4-rcrop2_normal.jpg<br />
    [url] => http://www.snipe.net<br />
    [protected] => false<br />
    [followers_count] => 4224<br />
    [profile_background_color] => 340100<br />
    [profile_text_color] => 3C3940<br />
    [profile_link_color] => 6C2125<br />
    [profile_sidebar_fill_color] => AEA797<br />
    [profile_sidebar_border_color] => 943A39<br />
    [friends_count] => 3756<br />
    [created_at] => Fri Mar 28 20:37:35 +0000 2008<br />
    [favourites_count] => 314<br />
    [utc_offset] => 12600<br />
    [time_zone] => Tehran<br />
    [profile_background_image_url] => http://s3.amazonaws.com/twitter_production/profile_background_images/22127710/twitterback2.jpg<br />
    [profile_background_tile] => false<br />
    [statuses_count] => 20570<br />
    [notifications] => false<br />
    [verified] => false<br />
    [following] => false<br />
    [status] => SimpleXMLElement Object<br />
        (<br />
            [created_at] => Mon Jul 27 01:50:36 +0000 2009<br />
            [id] => 2862508774<br />
            [text] => @elazar In case a name gets blocked/banned &#8211; when its reinstated (by someone claiming it, not spamming), it has a new ID#<br />
            [source] => Tweetie<br />
            [truncated] => false<br />
            [in_reply_to_status_id] => 2860170987<br />
            [in_reply_to_user_id] => 9105122<br />
            [favorited] => false<br />
            [in_reply_to_screen_name] => elazar<br />
        )</p>
<p>)[/source]</p>
<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>
<p>[source lang='php']$content = $to->OAuthRequest(&#8216;https://twitter.com/account/verify_credentials.xml&#8217;, array(), &#8216;GET&#8217;);[/source]</p>
<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>
<p>[source lang='php']<br />
$params = array(&#8216;user&#8217; => &#8216;username&#8217;, &#8216;text&#8217; => &#8216;this is a test message&#8217;);<br />
$do_dm = simplexml_load_string($to->OAuthRequest(&#8216;http://twitter.com/direct_messages/new.xml&#8217;, $params, &#8216;POST&#8217;));[/source]</p>
<p>To block a user, you&#8217;d do:</p>
<p>[source lang='php']$doblock = simplexml_load_string($to->OAuthRequest(&#8216;http://twitter.com/blocks/create/username.xml&#8217;, array(), &#8216;POST&#8217;));[/source]</p>
<p>To send a status update:<br />
[source lang='php']$content = simplexml_load_string($to->OAuthRequest(&#8216;https://twitter.com/statuses/update.xml&#8217;, array(&#8216;status&#8217; => &#8216;Test OAuth update. #testoauth&#8217;), &#8216;POST&#8217;));[/source]</p>
<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>
<p>[source lang='php']$params = array(&#8216;user&#8217; => &#8216;username&#8217;, &#8216;text&#8217; => &#8216;this is a test message&#8217;);<br />
$do_dm = simplexml_load_string($to->OAuthRequest(&#8216;http://twitter.com/direct_messages/new.xml&#8217;, $params, &#8216;POST&#8217;));</p>
<p>/* Check for an error response from Twitter */<br />
if ($do_dm->error!=&#8221;) {<br />
	echo &#8216;<br />
<h2>ERROR: &#8216;.$do_dm->error.&#8217;</h2>
<p>&#8216;;<br />
}[/source]</p>
<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>

 <script type="text/javascript">
	<!--
		function onover(what){
	document.getElementById('blurbtext').innerHTML=''+what+'';
	}
	function onout(){
	document.getElementById('blurbtext').innerHTML='&nbsp;';
	}
	-->
	</script>



<h3 style="padding-bottom: 0px; margin-bottom: 0px;">Also check out: <br /><span id="blurbtext"><br /></span></h3>

<div id="relatedposts">




		
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2008/09/planning-a-facebook-application/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/12/n40212040147_6720.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Planning Your Facebook Application" height="90" width="90" onmouseover="onover('Planning Your Facebook Application')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2008/12/planning-a-facebook-application-part-two/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/12/n40212040147_6720.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Planning a Facebook Application: Part Two" height="90" width="90" onmouseover="onover('Planning a Facebook Application: Part Two')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2009/12/twitter-business-contributors/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2009/12/love-twitter.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Twitter Gets Down to Business" height="90" width="90" onmouseover="onover('Twitter Gets Down to Business')" onmouseout="onout()" /></a></div>

	</div>

]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2009/07/writing-your-first-twitter-application-with-oauth/feed/</wfw:commentRss>
		<slash:comments>201</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[<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>
<p>[sourcecode language='sql']CREATE TABLE `stores` (<br />
`store_id` INT NOT NULL AUTO_INCREMENT ,<br />
`store_address` VARCHAR( 40 ) NULL ,<br />
`store_city` VARCHAR( 40 ) NULL ,<br />
`store_state` VARCHAR( 2 ) NULL ,<br />
`store_country` VARCHAR( 2 ) NULL ,<br />
`store_phone` VARCHAR( 15 ) NULL ,<br />
UNIQUE (`store_id`));[/sourcecode]</p>
<p>And then we add the new latitude and longitude fields to our stores table, using the DOUBLE datatype:</p>
<p>[sourcecode language='sql']ALTER TABLE `stores` ADD `latitude` DOUBLE NULL ,<br />
ADD `longitude` DOUBLE NULL ;[/sourcecode]</p>
<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>
<p>[sourcecode language='php']function curl_string ($url){<br />
$ch = curl_init();<br />
curl_setopt ($ch, CURLOPT_URL, $url);<br />
curl_setopt ($ch, CURLOPT_HEADER, 0);<br />
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);<br />
curl_setopt ($ch, CURLOPT_FOLLOWLOCATION, 1);<br />
curl_setopt ($ch, CURLOPT_TIMEOUT, 120);<br />
$result = curl_exec ($ch);<br />
curl_close($ch);<br />
return $result;<br />
[/sourcecode]</p>
<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>
<p>[sourcecode language='php']<br />
// set the url of the API using the address, city and state we want to query</p>
<p>$url_page = &#8220;http://&#8221;.&#8221;rpc.&#8221;.&#8221;.geocoder&#8221;.&#8221;.us/service/csv&#8221;;<br />
$url_page .=&#8221;?address=&#8221;.urlencode($address).&#8221;,&#8221;.urlencode($city).&#8221;,&#8221;.$state;</p>
<p>// execute the cURL request<br />
$string = curl_string($url_page);</p>
<p>// turn the comma separated csv data turned into an array,<br />
// so we can easily see that a match was found and use the pieces<br />
$address_pieces= explode(&#8220;,&#8221;, $string);</p>
<p>// make sure the array contains data<br />
if (count($address_pieces) > 0) {</p>
<p>// update the database<br />
$sql = &#8220;update stores set latitude=&#8217;&#8221;.$address_pieces[0].&#8221;&#8216;, &#8220;;<br />
$sql .=&#8221;longitude=&#8217;&#8221;.$address_pieces[1].&#8221;&#8216;, &#8220;;<br />
$sql .=&#8221;zip=&#8217;&#8221;.$address_pieces[5].&#8221;&#8216; where id=&#8217;&#8221;.$store_id.&#8221;&#8216;&#8221;;</p>
<p>if ($update_latlong = mysql_query($sql)) {<br />
echo &#8216;Lat/long updated!&#8217;;<br />
} else {<br />
// if the query failed, print out an error<br />
echo mysql_error();<br />
}</p>
<p>// 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<br />
} else {<br />
echo &#8216;No geolocation match.&#8217;;</p>
<p>}[/sourcecode]</p>
<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>
<p>[sourcecode language='php']// begin the session<br />
session_start();</p>
<p>$expireTime = 60*60*24*30; // 30 days<br />
session_set_cookie_params($expireTime);</p>
<p>if ((!isset($_SESSION['geo_country'])) || ($_SESSION['geo_country']==&#8221;))    {</p>
<p>// enter your MaxMind license key here<br />
$license_key=&#8217;XXXXXXXXXXXXXXX&#8217;;<br />
$ip = $_SERVER['REMOTE_ADDR'];</p>
<p>$query = &#8220;http://&#8221;.&#8221;geoip1.&#8221;.&#8221;maxmind&#8221;.&#8221;.com/b?l=&#8221; . $license_key . &#8220;&amp;amp;amp;amp;amp;amp;amp;amp;i=&#8221; . $ip;<br />
$url = parse_url($query);<br />
$host = $url["host"];<br />
$path = $url["path"] . &#8220;?&#8221; . $url["query"];<br />
$timeout = 1;<br />
$fp = fsockopen ($host, 80, $errno, $errstr, $timeout)<br />
or die(&#8216;Can not open connection to server.&#8217;);</p>
<p>if ($fp) {<br />
fputs ($fp, &#8220;GET $path HTTP/1.0\nHost: &#8221; . $host . &#8220;\n\n&#8221;);</p>
<p>while (!feof($fp)) {<br />
$buf .= fgets($fp, 128);<br />
} // endwhile</p>
<p>// split the output into an array<br />
$lines = split(&#8220;\n&#8221;, $buf);<br />
$data = $lines[count($lines)-1];<br />
fclose($fp);</p>
<p>$geo = explode(&#8220;,&#8221;,$data);<br />
$user_geo_country = $geo[0];<br />
$user_geo_state = $geo[1];<br />
$user_geo_city = $geo[2];<br />
$user_geo_lat = $geo[3];<br />
$user_geo_lon = $geo[4];</p>
<p>$_SESSION['geo_country'] = $user_geo_country;<br />
$_SESSION['geo_state'] = $user_geo_state;<br />
$_SESSION['geo_city'] = $user_geo_city;<br />
$_SESSION['geo_lat'] = $user_geo_lat;<br />
$_SESSION['geo_lon'] = $user_geo_lon;</p>
<p>setcookie(&#8220;geolocCookieCountry&#8221;, $user_geo_country, time()+$expireTime, &#8220;/&#8221;);<br />
setcookie(&#8220;geolocCookieCity&#8221;, $user_geo_city, time()+$expireTime, &#8220;/&#8221;);<br />
setcookie(&#8220;geolocCookieState&#8221;, $user_geo_state, time()+$expireTime, &#8220;/&#8221;);<br />
setcookie(&#8220;geolocCookieLat&#8221;, $user_geo_lat, time()+$expireTime, &#8220;/&#8221;);<br />
setcookie(&#8220;geolocCookieLon&#8221;, $user_geo_lon, time()+$expireTime, &#8220;/&#8221;);</p>
<p>} // endif $fp</p>
<p>} // endif session set<br />
[/sourcecode]</p>
<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>
<p>[sourcecode language='php']class RadiusCheck {</p>
<p>var $maxLat;<br />
var $minLat;<br />
var $maxLong;<br />
var $minLong;</p>
<p>function RadiusCheck($Latitude, $Longitude, $Miles) {<br />
global $maxLat,$minLat,$maxLong,$minLong;<br />
$EQUATOR_LAT_MILE = 69.172;<br />
$maxLat = $Latitude + $Miles / $EQUATOR_LAT_MILE;<br />
$minLat = $Latitude &#8211; ($maxLat &#8211; $Latitude);<br />
$maxLong = $Longitude + $Miles / (cos($minLat * M_PI / 180) * $EQUATOR_LAT_MILE);<br />
$minLong = $Longitude &#8211; ($maxLong &#8211; $Longitude);<br />
}</p>
<p>function MaxLatitude() {<br />
return $GLOBALS["maxLat"];<br />
}<br />
function MinLatitude() {<br />
return $GLOBALS["minLat"];<br />
}<br />
function MaxLongitude() {<br />
return $GLOBALS["maxLong"];<br />
}<br />
function MinLongitude() {<br />
return $GLOBALS["minLong"];<br />
}</p>
<p>}[/sourcecode]</p>
<p>and</p>
<p>[sourcecode language='php']class DistanceCheck {</p>
<p>function DistanceCheck() {<br />
}</p>
<p>function Calculate(<br />
$dblLat1,<br />
$dblLong1,<br />
$dblLat2,<br />
$dblLong2<br />
) {<br />
$EARTH_RADIUS_MILES = 3963;<br />
$dist = 0;</p>
<p>//convert degrees to radians<br />
$dblLat1 = $dblLat1 * M_PI / 180;<br />
$dblLong1 = $dblLong1 * M_PI / 180;<br />
$dblLat2 = $dblLat2 * M_PI / 180;<br />
$dblLong2 = $dblLong2 * M_PI / 180;</p>
<p>if ($dblLat1 != $dblLat2 || $dblLong1 != $dblLong2)<br />
{<br />
//the two points are not the same<br />
$dist =<br />
sin($dblLat1) * sin($dblLat2)<br />
+ cos($dblLat1) * cos($dblLat2)<br />
* cos($dblLong2 &#8211; $dblLong1);</p>
<p>$dist =<br />
$EARTH_RADIUS_MILES<br />
* (-1 * atan($dist / sqrt(1 &#8211; $dist * $dist)) + M_PI / 2);<br />
}<br />
return $dist;<br />
}</p>
<p>}[/sourcecode]</p>
<p>And then, to perform the actual query:</p>
<p>[sourcecode language='php']<br />
// set a default number of miles to search within<br />
$Miles = &#8217;50&#8242;;</p>
<p>// set the user&#8217;s latitude and longitude as the one to search against<br />
$Latitude = $user_geo_lat;<br />
$Longitude = $user_geo_lon;</p>
<p>$zcdRadius = new RadiusCheck($Latitude,$Longitude,$Miles);<br />
$minLat = $zcdRadius->MinLatitude();<br />
$maxLat = $zcdRadius->MaxLatitude();<br />
$minLong = $zcdRadius->MinLongitude();<br />
$maxLong = $zcdRadius->MaxLongitude();</p>
<p>$sql = &#8220;SELECT store_address, store_city, store_state, store_phone, &#8220;;<br />
$sql .= &#8220;SQRT((((69.1*(latitude-$Latitude))*(69.1*(latitude-$Latitude)))+((53*(longitude-$Longitude))*(53*(longitude-$Longitude))))) &#8220;;<br />
$sql .= &#8220;AS calc FROM stores where  &#8220;;<br />
$sql .= &#8220;latitude >= &#8216;$minLat&#8217; &#8220;;<br />
$sql .= &#8220;AND latitude <= '$maxLat' ";<br />
$sql .= "AND longitude >= &#8216;$minLong&#8217; &#8220;;<br />
$sql .= &#8220;AND longitude <= '$maxLong' ";<br />
$get_data = mysql_query($sql);</p>
<p>// loop through the matching database results<br />
while($storedata = mysql_fetch_assoc($get_data)) {</p>
<p>// calculate the number of miles away the result is<br />
$zcdDistance = new DistanceCheck;<br />
$Distance = $zcdDistance->Calculate($Latitude,$Longitude,$storedata['latitude'],$storedata['longitude']);</p>
<p>// and for the non-US people, here&#8217;s the km calculation<br />
$calc_km = round(($Distance * 1.609344),2);</p>
<p>echo &#8216;
<li>&#8216;.$storedata['store_address'].&#8217;<br />&#8216;.$storedata['store_city'].&#8217;, &#8216;;<br />
echo $storedata['store_state'].&#8217; &#8216;.$storedata['store_country'].&#8217;<br />&#8216;;<br />
echo $storedata['store_phone'].&#8217;<br />&#8216;;<br />
echo &#8216;Distance: &#8216;.$Distance.&#8217; (&#8216;.$calc_km.&#8217; km)&#8217;;<br />
}[/sourcecode]</p>
<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>

 <script type="text/javascript">
	<!--
		function onover(what){
	document.getElementById('blurbtext').innerHTML=''+what+'';
	}
	function onout(){
	document.getElementById('blurbtext').innerHTML='&nbsp;';
	}
	-->
	</script>



<h3 style="padding-bottom: 0px; margin-bottom: 0px;">Also check out: <br /><span id="blurbtext"><br /></span></h3>

<div id="relatedposts">




		
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2006/06/creating-a-multi-level-listbox-in-phpmysql/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/06/listbox.gif&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Creating a Multi-Level Listbox in PHP/mySQL" height="90" width="90" onmouseover="onover('Creating a Multi-Level Listbox in PHP/mySQL')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2002/06/alternating-row-colors-in-phpmysql/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/06/alternatingrowcolors1.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Alternating Row Colors in PHP/mySQL" height="90" width="90" onmouseover="onover('Alternating Row Colors in PHP/mySQL')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2002/06/phpmysql-breadcrumb-trail/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/06/breadcrumbs_editorial.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="PHP/mySQL Breadcrumb Trail" height="90" width="90" onmouseover="onover('PHP/mySQL Breadcrumb Trail')" onmouseout="onout()" /></a></div>

	</div>

]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2008/12/ip-geolocation-radius-search-in-php/feed/</wfw:commentRss>
		<slash:comments>10</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[<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>
<p>[sourcecode language='php']<br />
function convert_smart_quotes($string)<br />
{<br />
$search = array(chr(145),<br />
chr(146),<br />
chr(147),<br />
chr(148),<br />
chr(151));</p>
<p>$replace = array(&#8220;&#8216;&#8221;,<br />
&#8220;&#8216;&#8221;,<br />
&#8216;&#8221;&#8216;,<br />
&#8216;&#8221;&#8216;,<br />
&#8216;-&#8217;);</p>
<p>return str_replace($search, $replace, $string);<br />
}<br />
[/sourcecode]</p>

 <script type="text/javascript">
	<!--
		function onover(what){
	document.getElementById('blurbtext').innerHTML=''+what+'';
	}
	function onout(){
	document.getElementById('blurbtext').innerHTML='&nbsp;';
	}
	-->
	</script>



<h3 style="padding-bottom: 0px; margin-bottom: 0px;">Also check out: <br /><span id="blurbtext"><br /></span></h3>

<div id="relatedposts">




		
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2009/12/comment-count-bug-disqus/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2009/12/johnny.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Fixing Comment Count Bug in Disqus on WordPress" height="90" width="90" onmouseover="onover('Fixing Comment Count Bug in Disqus on WordPress')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2002/06/sending-htmlplain-text-mail-simultaneously-using-php/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/06/htmlmail.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Sending HTML/Plain Text Mail Simultaneously using PHP" height="90" width="90" onmouseover="onover('Sending HTML/Plain Text Mail Simultaneously using PHP')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2004/06/cropped-thumbnails-using-php-and-the-gd-library/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/06/monalisa.png&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Cropped Thumbnails using PHP and the GD Library" height="90" width="90" onmouseover="onover('Cropped Thumbnails using PHP and the GD Library')" onmouseout="onout()" /></a></div>

	</div>

]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2008/12/fixing-curly-quotes-and-em-dashes-in-php/feed/</wfw:commentRss>
		<slash:comments>17</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[<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>

 <script type="text/javascript">
	<!--
		function onover(what){
	document.getElementById('blurbtext').innerHTML=''+what+'';
	}
	function onout(){
	document.getElementById('blurbtext').innerHTML='&nbsp;';
	}
	-->
	</script>



<h3 style="padding-bottom: 0px; margin-bottom: 0px;">Also check out: <br /><span id="blurbtext"><br /></span></h3>

<div id="relatedposts">




		
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2008/09/planning-a-facebook-application/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2008/12/n40212040147_6720.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Planning Your Facebook Application" height="90" width="90" onmouseover="onover('Planning Your Facebook Application')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2009/02/advertising-on-facebook-part-two/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2009/02/facebook-office-glass.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Advertising on Facebook &#8211; Part Two" height="90" width="90" onmouseover="onover('Advertising on Facebook &#8211; Part Two')" onmouseout="onout()" /></a></div>

			
               

        
        
        
		<div class="yarppimg"><a href="http://www.snipe.net/2009/02/advertising-on-facebook-part-one/" rel="bookmark">
		<img src="http://www.snipe.net/wp-content/themes/snipe/thumb.php?src=http://www.snipe.net/wp-content/uploads/2009/02/chipdees.jpg&amp;h=90&amp;w=90&amp;zc=1&amp;q=95" alt="Advertising on Facebook &#8211; Part One" height="90" width="90" onmouseover="onover('Advertising on Facebook &#8211; Part One')" onmouseout="onout()" /></a></div>

	</div>

]]></content:encoded>
			<wfw:commentRss>http://www.snipe.net/2008/12/planning-a-facebook-application-part-two/feed/</wfw:commentRss>
		<slash:comments>40</slash:comments>
		</item>
	</channel>
</rss>

