<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Anant Garg | on web development</title>
	
	<link>http://anantgarg.com</link>
	<description>on web development</description>
	<pubDate>Mon, 20 Feb 2012 08:40:47 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/anantgarg" /><feedburner:info uri="anantgarg" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>anantgarg</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><item>
		<title>Busting the cookies and privacy myth</title>
		<link>http://feedproxy.google.com/~r/anantgarg/~3/0nOULh2Ee0g/</link>
		<comments>http://anantgarg.com/2012/02/18/busting-the-cookies-and-privacy-myth/#comments</comments>
		<pubDate>Sat, 18 Feb 2012 10:10:45 +0000</pubDate>
		<dc:creator>Anant Garg</dc:creator>
		
		<category><![CDATA[News]]></category>

		<guid isPermaLink="false">http://anantgarg.com/?p=345</guid>
		<description><![CDATA[A lot of misinformation has been floating around since the Wall Street Journal article was published. Here is a simplified explanation of what really happened and how this affects you.]]></description>
			<content:encoded><![CDATA[<p>I am going to try and make today&#8217;s post as simple as possible. Note some explanations maybe a little too simple for my technical audience.</p>
<p>I was referenced in the WSJ article- <a href="http://j.mp/ag-wsj" target="_blank">Google&#8217;s iPhone tracking</a> regarding my <a href="http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/">cross-domain cookies</a> post. </p>
<p>For my technical audience, here is a <a href="http://webpolicy.org/2012/02/17/safari-trackers/" target="_blank">detailed version of how my code was actually used</a>. </p>
<p><strong>So let us begin from scratch&#8230; what are cookies?</strong></p>
<p>Cookies (data on your computer used to identify you) are stored whenever you visit a site. Usually, this data is used to keep you logged into a site or to figure out if you are a repeat visitor.</p>
<p>This is fine and in-order for the web to function in it&#8217;s current state, in many ways, essential.</p>
<p><img src="http://anantgarg.com/wp-content/uploads/2012/02/1.jpg" alt="Site's store cookies" title="Site's store cookies" width="604" height="234" class="alignnone size-full wp-image-350" /></p>
<p>In the above diagram, when you visit siteA.com, a cookie is set for siteA.com on your computer.</p>
<p>When you visit siteB.com, a cookie is set for siteB.com on your computer.</p>
<p>But siteB.com, <strong>CANNOT</strong> read the data from the cookies set in siteA.com and vice-versa. Thus siteB.com does not know if you ever visited siteA.com or what you did there.</p>
<p><strong>Let&#8217;s introduce the ad-companies</strong></p>
<p>Ad-companies started displaying ads on sites depending on the content of the page. So, if you are a webmaster, you add a line of JavaScript code and a neat looking advertisement is displayed on your site page.</p>
<p><img src="http://anantgarg.com/wp-content/uploads/2012/02/2.jpg" alt="Ads are displayed" title="Ads are displayed" width="224" height="224" class="alignnone size-full wp-image-353" /></p>
<p>But the information on the page is not enough to generate targeted ads. So the ad-companies wanted to know where you&#8217;ve been before.</p>
<p>Now here is where the privacy issue unfolds.</p>
<p><strong>Tracking your moves</strong></p>
<p>Inorder to track you, as you moved from one site to another (both displaying sites ads from the same ad-company), the companies need to store cookies.</p>
<p><img src="http://anantgarg.com/wp-content/uploads/2012/02/3.jpg" alt="Cookies of one site cannot be accessed by another" title="Cookies of one site cannot be accessed by another" width="603" height="231" class="alignnone size-full wp-image-354" /></p>
<p><span style="color:#000">The problem is that cookies set on one site CANNOT be read by another site.</span> In other words, cookies set on siteA.com cannot be read by siteB.com.</p>
<p>So the work-around used is to store a third-party cookie (i.e. a cross-domain cookie). When a user visits siteA.com, a cookie is not only set for siteA.com but also for a <strong>common domain</strong> which is accessed by all sites that use the ad-company&#8217;s code.</p>
<p><img src="http://anantgarg.com/wp-content/uploads/2012/02/4.jpg" alt="siteA.com and siteB.com set cookies for their domain as well as for adsite.com" title="siteA.com and siteB.com set cookies for their domain as well as for adsite.com" width="599" height="298" class="alignnone size-full wp-image-355" /></p>
<p>Thus, siteA.com, siteB.com and all other sites displaying ads from adsite.com, store and access the same third-party cookie.</p>
<p>So when you visit siteA.com, a third-party cookie is created on adsite.com. When you visit siteB.com, the <strong>same</strong> cookie is read.</p>
<p><img src="http://anantgarg.com/wp-content/uploads/2012/02/5.jpg" alt="siteA.com and siteB.com access their own site cookies and the cookies of adsite.com" title="siteA.com and siteB.com access their own site cookies and the cookies of adsite.com" width="522" height="492" class="alignnone size-full wp-image-366" /></p>
<p>Thus adsite.com now knows that you have been to siteA.com before going to siteB.com. On the basis of this knowledge, a targeted ad can be shown. For an ad-company which provides ads to only two sites, this is not very useful. But imagine if you have 70% of the sites using your code.</p>
<p>Then, nearly most of the times, the same cookie can be read and your movements can be tracked as you browse through the internet. So now, the ad-company knows exact what kind of sites you are visiting and serve ads accordingly.</p>
<p><strong>But why does the WSJ article only talk about Safari?</strong></p>
<p>Safari, by default, <strong>does not allow third-party cookies</strong> to be created.</p>
<p><em>However, most major browsers- Firefox, Internet Explorer (using the correct header tags), Chrome etc. do ALLOW setting third-party cookies.</em></p>
<p><strong>Where does your technique come in?</strong></p>
<p>The technique <a href="http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/">I posted</a> two years ago, allows setting third-party cookies for Safari as well. This helps in offering a consistent user experience irrespective of the browser used.</p>
<p>Note that other browsers already allow creation of third-party cookies. So Safari is the only exception.</p>
<p><strong>How was this discovered?</strong></p>
<p>A Stanford researcher, <a href="http://stanford.edu/~jmayer" target="_blank">Jonathan Mayer</a> found that many large companies like Google, MIG, PointRoll etc. used a variation of my technique to circumvent Safari&#8217;s policy.</p>
<p>Infact, Facebook&#8217;s official <a href="https://developers.facebook.com/docs/best-practices/" target="_blank">developer best practices</a> article linked to my post (the page has since been removed).</p>
<p>You must remember that this issue affects Safari users ONLY. </p>
<p>Other browsers already allow third-party cookies and thus they CAN track you <em>as they do NOT have any such policy</em>.</p>
<p><strong>How do I protect myself? Should I disable cookies?</strong></p>
<p>NO. Do not disable cookies altogether. This will cause most of your sites (which have a login system) to stop working. </p>
<p>Third-party cookies also have a number of other uses like logging in users to third-party sites and are NOT always used for tracking. They cannot steal your data (a myth noted by some users in comments).</p>
<p>If you want, you can disable third-party cookies in browsers. Dennis O&#8217;Reilly has written a guide on <a href="http://howto.cnet.com/8301-11310_39-20042703-285/disable-third-party-cookies-in-ie-firefox-and-google-chrome/" target="_blank">how to disable third-party cookies</a> for major browsers.</p>
<p>You may also want to use incognito/private browsing modes when using a public PC.</p>
<p><strong>Questions/Suggestions?</strong></p>
<p>If you have any questions about how this affects you or need further explanation, feel free to post a comment or contact me.</p>
<img src="http://feeds.feedburner.com/~r/anantgarg/~4/0nOULh2Ee0g" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://anantgarg.com/2012/02/18/busting-the-cookies-and-privacy-myth/feed/</wfw:commentRss>
		<feedburner:origLink>http://anantgarg.com/2012/02/18/busting-the-cookies-and-privacy-myth/</feedburner:origLink></item>
		<item>
		<title>Exploit Scanner</title>
		<link>http://feedproxy.google.com/~r/anantgarg/~3/XY54Grbr6xQ/</link>
		<comments>http://anantgarg.com/2010/09/15/exploit-scanner/#comments</comments>
		<pubDate>Wed, 15 Sep 2010 07:26:16 +0000</pubDate>
		<dc:creator>Anant Garg</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[security]]></category>

		<guid isPermaLink="false">http://anantgarg.com/?p=338</guid>
		<description><![CDATA[Exploit Scanner is a PHP script which checks for exploits in software like Joomla, Drupal, WordPress, phpBB, vBulletin etc.]]></description>
			<content:encoded><![CDATA[<p><strong>The Problem</strong></p>
<p>As your site grows, so do your chances of getting hacked. Most of these hacks are due to known exploits in existing open/closed source software. One of the biggest worries is a user getting full access to your site using a simple PHP upload.</p>
<p><strong>So what&#8217;s the solution?</strong></p>
<p>This is no silver bullet but if a user uploads any malicious files to your server, you should be able to quickly check what files have been modified/added. </p>
<p><strong>PHP Exploit Scanner</strong></p>
<p>The Exploit Scanner is a single PHP file which generates MD5 hash for all files of a particular software and then allows you to compare that with software you think has been modified. </p>
<p><strong>Sample Usage</strong></p>
<p>Lets say you want to check for any exploits in your WordPress installation</p>
<p>1. Download a fresh copy of WordPress from the official site (make sure you download correct version)<br />
2. Upload WordPress to your server/localhost<br />
3. Run <em>exploitscanner.php?action=generate</em> in the new WordPress folder (you do not need to install WordPress)<br />
4. Check filehashes.php and verify output is correct (i.e. an array of files with their hashes)<br />
5. Now upload exploitscanner.php and filehashes.php to your live WordPress folder<br />
6. Run <em>exploitscanner.php?action=scan</em> to generate a list of modified/added files<br />
7. Check output of scanresults.txt</p>
<p><strong>Generating Hashes</strong></p>
<p>Upload exploitscanner.php to the folder for which you want to generate file hashes and then point your browser to:</p>
<p>http://www.yoursite.com/untouchedsoftware/exploitscanner.php?action=generate</p>
<p>Only output is filehashes.php</p>
<p>Note: Be sure that the software is not already exploited. Ideally use a fresh copy from the software creators (make sure you check the version)</p>
<p><strong>Scanning For Exploits</strong></p>
<p>Upload exploitscanner.php to the folder for which you want to check file hashes and then point your browser to:</p>
<p>http://www.yoursite.com/hackedsoftware/exploitscanner.php?action=scan</p>
<p>Only output is scanresults.txt<br />
<em>Legend-</em> F: New file | M: Modified file</p>
<p>First search for all .php files to see what is changed. If the file is tagged M, you can use a difference tool like <a href="http://winmerge.org">WinMerge</a> to find out what has changed.</p>
<p><strong>Download</strong></p>
<p>Download <a href="http://www.anantgarg.com/exploit-scanner.zip">PHP Exploit Scanner</a> free</p>
<p><strong>License</strong></p>
<p>Exploit Scanner is licensed under MIT license. Let me know if you make any interesting use of the script.</p>
<p><strong>Comments/Suggestions?</strong></p>
<p>If you would like to assist in creating a community where users can quickly download filehashes.php for their software then feel free to contact me using the form below.</p>
<p>Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p>
<p><strong>Future Updates</strong></p>
<p>1. Improve recursive function to avoid time out on shared servers<br />
2. Create a community where users can upload hashes for known software like phpBB, vBulletin etc.<br />
3. Add GUI<br />
4. Database integrity<br />
5. Malicious code insertion in template files</p>
<p><strong>Spread The Word</strong></p>
<p>If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p>
<img src="http://feeds.feedburner.com/~r/anantgarg/~4/XY54Grbr6xQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://anantgarg.com/2010/09/15/exploit-scanner/feed/</wfw:commentRss>
		<feedburner:origLink>http://anantgarg.com/2010/09/15/exploit-scanner/</feedburner:origLink></item>
		<item>
		<title>Cross-domain cookies/sessions in Safari and all other browsers</title>
		<link>http://feedproxy.google.com/~r/anantgarg/~3/QTb4FOnkAgg/</link>
		<comments>http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/#comments</comments>
		<pubDate>Thu, 18 Feb 2010 12:20:49 +0000</pubDate>
		<dc:creator>Anant Garg</dc:creator>
		
		<category><![CDATA[Javascript]]></category>

		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://anantgarg.com/?p=336</guid>
		<description><![CDATA[Safari does not allow cross-domain cookies. In other words, if on X.com, you load an iFrame with contents of Y.com and set a cookie in the iFrame, Safari will not save the cookie. This post contains a possible solution to this problem.]]></description>
			<content:encoded><![CDATA[<div style="border: 1px solid #E2C822;background:#FFF9D7;padding:10px;">Want to read a simplified version of this article? <a href="http://anantgarg.com/2012/02/18/busting-the-cookies-and-privacy-myth/" style="color:#333;font-weight:bold">Read my follow-up article regarding Google&#8217;s iPhone Tracking</a></div>
<p><strong>The Problem</strong></p>
<p>Safari does not allow cross-domain cookies. In other words, if on X.com, you load an iFrame with contents of Y.com and set a cookie in the iFrame, Safari will not save the cookie. This problem also occurs in IE6/7 but can be resolved by sending a P3P header. </p>
<p><strong>The Solution</strong></p>
<p>I am not entirely sure why this works, but the cookie gets saved if a post is made to the iFrame. This solution relies of jQuery but can be adapted to any other.</p>
<p>Here is the code that goes on the remote domain:</p>
<pre class="brush: php">
&amp;amp;amp;lt;script src=&amp;amp;amp;quot;http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js&amp;amp;amp;quot; type=&amp;amp;amp;quot;text/javascript&amp;amp;amp;quot;&amp;amp;amp;gt;&amp;amp;amp;lt;/script&amp;amp;amp;gt;
&amp;amp;amp;lt;script&amp;amp;amp;gt;
var isSafari = (/Safari/.test(navigator.userAgent));
var firstTimeSession = 0;

function submitSessionForm() {
	if (firstTimeSession == 0) {
		firstTimeSession = 1;
		$(&amp;amp;amp;quot;#sessionform&amp;amp;amp;quot;).submit();
		setTimeout(processApplication(),2000);
  	}
}

if (isSafari) {
	$(&amp;amp;amp;quot;body&amp;amp;amp;quot;).append(&#039;&amp;amp;amp;lt;iframe id=&amp;amp;amp;quot;sessionframe&amp;amp;amp;quot; name=&amp;amp;amp;quot;sessionframe&amp;amp;amp;quot; onload=&amp;amp;amp;quot;submitSessionForm()&amp;amp;amp;quot; src=&amp;amp;amp;quot;http://www.yourdomain.com/blank.php&amp;amp;amp;quot; style=&amp;amp;amp;quot;display:none;&amp;amp;amp;quot;&amp;amp;amp;gt;&amp;amp;amp;lt;/iframe&amp;amp;amp;gt;&amp;amp;amp;lt;form id=&amp;amp;amp;quot;sessionform&amp;amp;amp;quot; enctype=&amp;amp;amp;quot;application/x-www-form-urlencoded&amp;amp;amp;quot; action=&amp;amp;amp;quot;http://www.yourdomain.com/startsession.php&amp;amp;amp;quot; target=&amp;amp;amp;quot;sessionframe&amp;amp;amp;quot; action=&amp;amp;amp;quot;post&amp;amp;amp;quot;&amp;amp;amp;gt;&amp;amp;amp;lt;/form&amp;amp;amp;gt;&#039;);
} else {
	processApplication();
}

function processApplication() {
	alert(&#039;Session has been set. Now you can start your application!&#039;);
}
&amp;amp;amp;lt;/script&amp;amp;amp;gt;
</pre>
<p>The contents for startsession.php would be as simple as:</p>
<pre class="brush: php">
&amp;amp;amp;lt;?php
header(&#039;P3P: CP=&amp;amp;amp;quot;IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA&amp;amp;amp;quot;&#039;);
session_start();
</pre>
<p>To make sure that your site is compatible with IE6/7, always output the following header:</p>
<pre class="brush: php">
&amp;amp;amp;lt;?php
header(&#039;P3P: CP=&amp;amp;amp;quot;IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA&amp;amp;amp;quot;&#039;);
</pre>
<p>Unfortunately, there is no way to find out when the browser has completed submitting the form. Thus, I have placed a delay of 2 seconds. This form needs to be submitted only once per user session. Then you can set your session data in PHP and it will be picked up by your remote domain. You can adapt the same example if you want to set cookies instead of starting a PHP session.</p>
<p><strong>Comments/Suggestions?</strong><br />
Do let me know your suggestions on how we can improve this code.</p>
<p><strong>Spread The Word</strong><br />
If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p>
<img src="http://feeds.feedburner.com/~r/anantgarg/~4/QTb4FOnkAgg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/feed/</wfw:commentRss>
		<feedburner:origLink>http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/</feedburner:origLink></item>
		<item>
		<title>SQLBuddyLite - phpMyAdmin alternative</title>
		<link>http://feedproxy.google.com/~r/anantgarg/~3/PfZUj-eViDs/</link>
		<comments>http://anantgarg.com/2009/12/11/sqlbuddylite-phpmyadmin-alternative/#comments</comments>
		<pubDate>Fri, 11 Dec 2009 09:54:15 +0000</pubDate>
		<dc:creator>Anant Garg</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[mySQL]]></category>

		<guid isPermaLink="false">http://anantgarg.com/?p=325</guid>
		<description><![CDATA[SQLBuddyLite is a lighter version of the excellent SQLBuddy available from http://www.sqlbuddy.com. It combines the entire package into only 4 files.]]></description>
			<content:encoded><![CDATA[<p>SQLBuddyLite is a lighter version of the excellent SQLBuddy available from http://www.sqlbuddy.com</p>
<p>The lite version is an exact copy of the original version but all the files have been combined into only 4 files.</p>
<p><strong>Acknowledgments</strong></p>
<p>SQLBuddyLite is a lighter version of <a href="http://www.sqlbuddy.com">SQLBuddy</a> by Calvin Lough</p>
<p><strong>Why do you need a lite version?</strong></p>
<p>A typical situation is where you have limited access to the user&#8217;s site say only FTP access and you need to verify the database structures. The easiest way to tackle the problem is to upload SQLBuddy and view the data structures.</p>
<p>The lite version makes it quicker to upload files. The lite version does not compromise on the functions; it only reduces the files drastically to only 4 files, thus speeding up remote site management.</p>
<p>Only English language is supported in the latest release.</p>
<p><strong>Demonstration</strong></p>
<p>For security reasons, there is no demonstration version available.</p>
<p><strong>Screenshot</strong></p>
<p><a href="http://anantgarg.com/wp-content/uploads/2009/12/sqlbuddy.jpg" rel="shadowbox[post-325];player=img;"><img src="http://anantgarg.com/wp-content/uploads/2009/12/sqlbuddy-300x156.jpg" alt="sqlbuddy" title="sqlbuddy" width="300" height="156" class="alignnone size-medium wp-image-326" /></a></p>
<p><strong>Requirements</strong></p>
<p>1. PHP<br />
2. mySQL<br />
3. Apache mod-rewrite</p>
<p><strong>Getting Started</strong></p>
<p>Download and upload all the sqlbuddylite folder to your server</p>
<p><strong>Download</strong></p>
<p><a href="http://code.google.com/p/sqlbuddylite/">SQLBuddyLite is hosted on Google Code</a><br />
<a href="http://sqlbuddylite.googlecode.com/files/sqlbuddylite%201.1.zip">Direct download link for SQLBuddyLite</a></p>
<p><strong>License</strong></p>
<p>MIT-style license</p>
<p><strong>Comments/Suggestions?</strong></p>
<p>Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p>
<p><strong>Spread The Word</strong></p>
<p>If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p>
<img src="http://feeds.feedburner.com/~r/anantgarg/~4/PfZUj-eViDs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://anantgarg.com/2009/12/11/sqlbuddylite-phpmyadmin-alternative/feed/</wfw:commentRss>
		<feedburner:origLink>http://anantgarg.com/2009/12/11/sqlbuddylite-phpmyadmin-alternative/</feedburner:origLink></item>
		<item>
		<title>PHP StackOverflow Clone</title>
		<link>http://feedproxy.google.com/~r/anantgarg/~3/wBEs53X3FKA/</link>
		<comments>http://anantgarg.com/2009/12/09/php-stackoverflow-clone/#comments</comments>
		<pubDate>Wed, 09 Dec 2009 09:37:01 +0000</pubDate>
		<dc:creator>Anant Garg</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://anantgarg.com/?p=317</guid>
		<description><![CDATA[A PHP/mySQL based Stack Overflow clone. Features point system, beautiful CSS layout and many unique features.]]></description>
			<content:encoded><![CDATA[<p>Qwench is a PHP/mySQL based StackOverflow clone.  </p>
<p><strong>Features</strong><br />
1. Allow users to post questions and answers<br />
2. Points system similar to StackOverflow<br />
3. Ability to post an article as a knowledge-base (for corporates)<br />
4. Ability to lock site to registered users only<br />
5. Clean CSS layout<br />
6. Works in all major browsers</p>
<p><strong>Demonstration</strong><br />
<a href="/qwench" target="_blank">Launch Qwench - StackOverflow Clone Demo</a></p>
<p><strong>Screenshot</strong></p>
<p><a href="http://anantgarg.com/wp-content/uploads/2009/12/qwench.jpg" rel="shadowbox[post-317];player=img;"><img src="http://anantgarg.com/wp-content/uploads/2009/12/qwench-300x144.jpg" alt="Qwench StackOverflow Clone" title="Qwench StackOverflow Clone" width="300" height="144" class="alignnone size-medium wp-image-318" /></a></p>
<p><strong>Requirements</strong><br />
1. PHP 4+<br />
2. mySQL</p>
<p><strong>Getting Started</strong><br />
Download and follow the instructions in README.txt file</p>
<p><strong>Download</strong><br />
<a href="http://github.com/anantgarg/Qwench">Qwench is hosted at GitHub</a></p>
<p><strong>License</strong><br />
Qwench licence can be found in the LICENSE.txt file.</p>
<p><strong>Comments/Suggestions?</strong><br />
Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p>
<p><strong>Spread The Word</strong><br />
If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p>
<img src="http://feeds.feedburner.com/~r/anantgarg/~4/wBEs53X3FKA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://anantgarg.com/2009/12/09/php-stackoverflow-clone/feed/</wfw:commentRss>
		<feedburner:origLink>http://anantgarg.com/2009/12/09/php-stackoverflow-clone/</feedburner:origLink></item>
		<item>
		<title>jQuery Fancy Gestures</title>
		<link>http://feedproxy.google.com/~r/anantgarg/~3/qYEQ3cq_35c/</link>
		<comments>http://anantgarg.com/2009/05/21/jquery-fancy-gestures/#comments</comments>
		<pubDate>Thu, 21 May 2009 16:26:31 +0000</pubDate>
		<dc:creator>Anant Garg</dc:creator>
		
		<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://anantgarg.com/?p=296</guid>
		<description><![CDATA[Enable complex mouse gestures (letters, alphabets, symbols and your own custom gestures) on your website.]]></description>
			<content:encoded><![CDATA[<p>Enable complex mouse gestures (letters, alphabets, symbols and your own custom gestures) on your website!</p>
<p><strong>Features</strong><br />
1. Create your own custom gestures<br />
2. Create multiple areas where you can accept gestures<br />
3. Visual feedback<br />
4. Works in all major browsers</p>
<p><strong>Acknowledgment</strong><br />
This script is a port of the <a href="http://www.bytearray.org/?p=91">mouse gesture recognition action script</a> by Didier Brun.</p>
<p><strong>Demonstration</strong><br />
<a href="/fancygestures" target="_blank">Launch Fancy Gestures Demo</a></p>
<p><strong>Screenshot</strong></p>
<p><a href="http://anantgarg.com/wp-content/uploads/2009/05/screenshot.jpg" rel="shadowbox[post-296];player=img;"><img style="border:1px solid #666666" src="http://anantgarg.com/wp-content/uploads/2009/05/screenshot-300x165.jpg" alt="jQuery Fancy Gestures Screenshot" title="jQuery Fancy Gestures Screenshot" width="300" height="165" class="alignnone size-medium wp-image-302" /></a></p>
<p><strong>Requirements</strong><br />
1. <a href="http://www.jquery.com">jQuery</a><br />
2. <a href="http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm#download">Walter Zorn&#8217;s VectorGraphics Library</a></p>
<p><strong>Getting Started</strong></p>
<p>Create a new html file with the following code. The function will return data i.e. the recognized character/symbol/name:</p>
<pre class="brush: php">
&lt;div id=&quot;sample&quot; style=&quot;border:1px solid black;position:relative;height:150px;width:150px;&quot;&gt;&lt;/div&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;wz_jsgraphics.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;jquery.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;jquery.fancygestures.js&quot;&gt;&lt;/script&gt;

&lt;script&gt;

$(document).ready(function () {
	$(&#039;#sample&#039;).fancygestures(function (data) {
		alert(data);
	});
});

&lt;/script&gt;
</pre>
<p><strong>Customization</strong></p>
<p>Please use the key below to generate a string of numbers for your gesture: e.g. L will be something like 2 then 0. The following letters are already mapped into the script:</p>
<p><img src="http://anantgarg.com/wp-content/uploads/2009/05/fancygestures.gif" alt="Fancy Gestures Customization" title="Fancy Gestures Customization" width="537" height="152" class="alignnone size-full wp-image-297" /></p>
<p>Suppose you want to add a &#8220;CIRCLE&#8221; symbol with the sequence &#8220;432107654&#8243;, then simply edit jquery.fancygestures.js and add the following after line 7:</p>
<pre class="brush: php">
     gestures[&quot;CIRCLE&quot;] = &quot;432107654&quot;;
</pre>
<p>The above code will return &#8220;CIRCLE&#8221; as data in the function when you make a circle. However, make sure you do not have similar gestures, or you will get unpredictable results e.g. having same gesture for zero and letter O.</p>
<p><strong>Download</strong><br />
<a href="/fancygestures.zip">Download Fancy Gestures</a></p>
<p><strong>License</strong><br />
Fancy Gestures is licensed under MIT license. Let me know if you make any interesting use of the script.</p>
<p><strong>Comments/Suggestions?</strong><br />
Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p>
<p><strong>Spread The Word</strong><br />
If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p>
<img src="http://feeds.feedburner.com/~r/anantgarg/~4/qYEQ3cq_35c" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://anantgarg.com/2009/05/21/jquery-fancy-gestures/feed/</wfw:commentRss>
		<feedburner:origLink>http://anantgarg.com/2009/05/21/jquery-fancy-gestures/</feedburner:origLink></item>
		<item>
		<title>Collabtweet - Multiple Users, Single Twitter Account</title>
		<link>http://feedproxy.google.com/~r/anantgarg/~3/1R99mgS-IM0/</link>
		<comments>http://anantgarg.com/2009/05/20/collabtweet-multiple-users-single-twitter-account/#comments</comments>
		<pubDate>Wed, 20 May 2009 14:18:53 +0000</pubDate>
		<dc:creator>Anant Garg</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://anantgarg.com/?p=273</guid>
		<description><![CDATA[Enable multiple users to post to a single collaborative twitter account.]]></description>
			<content:encoded><![CDATA[<p>Enable multiple users to post to a single collaborative twitter account. </p>
<p><b>Why would you need such a program?</b><br />
1. You run a small company/group which you would like twitter users to follow<br />
2. You would like twitter users to follow a single brand (twitter account) and not separate multiple users<br />
3. You would like a simple <em>slick</em> interface to enable your users to post to your collaborative twitter account<br />
4. You would like to hide your real twitter account password from your users<br />
5. You would like to automatically add &#8220;-username&#8221; to all tweets by a user<br />
6. You would like to host this service on your own server</p>
<p><b>Demonstration</b><br />
Please login with one of the following username/password combination:<br />
johndoe/johndoe<br />
janedoe/janedoe<br />
babydoe/babydoe</p>
<p><a href="/collabtweet/collabtweet.php" target="_blank">Click here to launch Collabtweet demo</a></p>
<p>For demonstration purposes, I am using a dummy twitter account- <a href="http://www.twitter.com/collabtweet" target="_blank">http://www.twitter.com/collabtweet</a></p>
<p><b>Screenshots</b></p>
<table style="border:0;margin:0;background-color:white" border=0 bgcolor="white" >
<tr>
<td style="background-color:white"><a href="http://anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-1.jpg" rel="shadowbox[post-273];player=img;"><img style="border:1px solid black" src="http://anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-1-300x218.jpg" alt="Collabtweet Login Screen" title="Collabtweet Login Screen" width="300" height="218" class="alignnone size-medium wp-image-274" /></a></td>
<td style="background-color:white">
<a href="http://anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-2.jpg" rel="shadowbox[post-273];player=img;"><img style="border:1px solid black" src="http://anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-2-300x215.jpg" alt="Collabtweet Tweet Screen" title="Collabtweet Tweet Screen" width="300" height="215" class="alignnone size-medium wp-image-275" /></a>
</td>
</tr>
<tr>
<td style="background-color:white">
<a href="http://anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-3.jpg" rel="shadowbox[post-273];player=img;"><img style="border:1px solid black" src="http://anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-3-300x168.jpg" alt="Collabtweet Tweeted!" title="Collabtweet Tweeted!" width="300" height="168" class="alignnone size-medium wp-image-276" /></a>
</td>
<td style="background-color:white">
<a href="http://anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-4.jpg" rel="shadowbox[post-273];player=img;"><img style="border:1px solid black" src="http://anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-4-300x181.jpg" alt="Twitter Outcome!" title="Twitter Outcome!" width="300" height="181" class="alignnone size-medium wp-image-277" /></a>
</td>
</tr>
</table>
<p><b>Download</b><br />
<a href="/collabtweet.zip">Collabtweet - Multiple Users, Single Twitter Account</a></p>
<p><b>Requirements</b><br />
Web server capable of running PHP</p>
<p><b>Configuration</b><br />
Simple edit config.php and modify the users which can access your account and your twitter username and password.</p>
<pre class="brush: php">
&lt;?php

// Username =&gt; Password
// Username will be used as nickname
$users = array(
&quot;USER1&quot; =&gt; &quot;PASS1&quot;,
&quot;USER2&quot; =&gt; &quot;PASS2&quot;,
&quot;USER3&quot; =&gt; &quot;PASS3&quot;
);

$twitterUser = &quot;YOURTWITTERUSERNAME&quot;;
$twitterPass = &quot;YOURTWITTERPASSWORD&quot;;
</pre>
<p><b>Future Updates</b><br />
1. Ability to manage users using a web interface</p>
<p><b>Comments/Suggestions?</b><br />
Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p>
<p><b>Spread The Word</b><br />
If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p>
<img src="http://feeds.feedburner.com/~r/anantgarg/~4/1R99mgS-IM0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://anantgarg.com/2009/05/20/collabtweet-multiple-users-single-twitter-account/feed/</wfw:commentRss>
		<feedburner:origLink>http://anantgarg.com/2009/05/20/collabtweet-multiple-users-single-twitter-account/</feedburner:origLink></item>
		<item>
		<title>Gmail/Facebook Style jQuery Chat</title>
		<link>http://feedproxy.google.com/~r/anantgarg/~3/W-PpfT0sDq0/</link>
		<comments>http://anantgarg.com/2009/05/13/gmail-facebook-style-jquery-chat/#comments</comments>
		<pubDate>Wed, 13 May 2009 17:18:23 +0000</pubDate>
		<dc:creator>Anant Garg</dc:creator>
		
		<category><![CDATA[Javascript]]></category>

		<category><![CDATA[PHP]]></category>

		<category><![CDATA[jquery]]></category>

		<category><![CDATA[plugin]]></category>

		<guid isPermaLink="false">http://anantgarg.com/?p=255</guid>
		<description><![CDATA[This jQuery chat module enables you to seamlessly integrate Gmail/Facebook style chat into your existing website.]]></description>
			<content:encoded><![CDATA[<p><strong>Introduction</strong><br />
Everyone loves the <a href="http://mail.google.com/mail/help/chat.html">gmail</a> and <a href="http://blog.facebook.com/blog.php?post=12811122130">facebook</a> inline chat modules. This jQuery chat module enables you to seamlessly integrate Gmail/Facebook style chat into your existing website.</p>
<p><img src="http://anantgarg.com/wp-content/uploads/2009/05/jquery-chat-module.jpg" alt="jQuery Chat" title="jQuery Chat" width="234" height="317" class="alignnone size-full wp-image-256" /></p>
<p><strong>Features</strong><br />
1. Gmail style bottom right display of chat boxes<br />
2. Keeps chat boxes open and stores state (data) even when pages are browsed/refreshed similar to Facebook<br />
3. Displays &#8220;Sent at&#8230;&#8221; after 3 minutes of inactivity<br />
4. Displays &#8220;X says&#8230;&#8221; &#038; blinks chat boxes when window is not in focus<br />
5. Minimize and close chat boxes<br />
6. Auto-resize of text input box<br />
7. Auto-scrolling of chat text<br />
8. Auto-back-off polling policy (hits the server less-often when chat activity is low)<br />
9. Extremely simple to integrate into existing site</p>
<p><strong>Demo</strong><br />
Please load the following links in different browsers otherwise it wont work:<br />
(Note that due to users trying out the chat links below at the same time, it might work slightly erratically because the username is actually not supposed to be used by multiple users at the same time)</p>
<p><a href="/chat/samplea.php">Sample Chat User One</a><br />
<a href="/chat/sampleb.php">Sample Chat User Two</a><br />
<a href="/chat/samplec.php">Sample Chat User Three</a></p>
<p><strong>Getting Started</strong><br />
First download the module (link below)</p>
<p>The script does not manage rosters (i.e. which users are online etc.), it only enables users to chat with each other. I assume your current application already provides that functionality. You must also make sure that $_SESSION['username'] is being set when your website session begins. You will understand the logic better after you try the sample files that I have provided.</p>
<p>You must first create a mySQL table as below (or import db.txt)</p>
<pre class="brush: php">
CREATE TABLE `chat` (
  `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  `from` VARCHAR(255) NOT NULL DEFAULT &#039;&#039;,
  `to` VARCHAR(255) NOT NULL DEFAULT &#039;&#039;,
  `message` TEXT NOT NULL,
  `sent` DATETIME NOT NULL DEFAULT &#039;0000-00-00 00:00:00&#039;,
  `recd` INTEGER UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
 INDEX `to` (`to`),
 INDEX `from` (`from`)
)
ENGINE = InnoDB;
</pre>
<p>Add the following scripts to your page template</p>
<p><code>
<pre class="brush: php">
&lt;script type=&quot;text/javascript&quot; src=&quot;js/jquery.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;js/chat.js&quot;&gt;&lt;/script&gt;
</pre>
<p></code></p>
<p>Add the following CSS to your page template<br />
<code></p>
<pre class="brush: php">
&lt;link type=&quot;text/css&quot; rel=&quot;stylesheet&quot; media=&quot;all&quot; href=&quot;css/chat.css&quot; /&gt;
&lt;link type=&quot;text/css&quot; rel=&quot;stylesheet&quot; media=&quot;all&quot; href=&quot;css/screen.css&quot; /&gt;

&lt;!--[if lte IE 7]&gt;
&lt;link type=&quot;text/css&quot; rel=&quot;stylesheet&quot; media=&quot;all&quot; href=&quot;css/screen_ie.css&quot; /&gt;
&lt;![endif]--&gt;
</pre>
<p></code></p>
<p>Now in your list of users online, add &#8220;javascript:chatWith(&#8217;USERNAME&#8217;);&#8221; function where USERNAME is the username for that particular user who he/she wants to chat with.</p>
<p>Once that is done, edit chat.php and set your database parameters and try your website. </p>
<p>For better understanding, load 3 different browsers (internet explorer, firefox, safari) and point them to samplea.php, sampleb.php and samplec.php.</p>
<p>Click on &#8220;chat with john doe&#8221; link and watch the chat functionality come alive!</p>
<p>Inorder to integrate your existing website, you must place all your content between the &#8220;main_container&#8221; div tag.</p>
<p><strong>Browser Compatibility</strong><br />
1. Firefox 2+<br />
2. Internet Explorer 6+<br />
3. Safari 2+<br />
4. Opera 9+</p>
<p><strong>Licensing</strong><br />
This chat script can be used for <strong>free</strong> under GPL-style license for <strong>non-commercial purposes</strong>. For commercial purposes, please purchase a license.</p>
<p><strong>Download</strong></p>
<p><a href='http://anantgarg.com/wp-content/uploads/2009/05/jquerychat.zip'>jQuery Chat Module</a>. If you would like a more <a href='http://www.cometchat.com'>full featured inline chat software</a>, have a look at <b><a href='http://www.cometchat.com'>CometChat</a></b></p>
<p><strong>Purchase</strong></p>
<p>Your purchase includes free updates for life! If you have any queries, please contact me using the form below.</p>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="5458118">
<input type="hidden" name="on0" value="Type">
<select name="os0">
	<option value="Single Domain License">Single Domain License $49.00<br />
	<option value="5 Domains License">5 Domains License $199.00<br />
	<option value="10 Domains License">10 Domains License $349.00<br />
	<option value="Unlimited Domains License">Unlimited Domains License $499.00<br />
</select>
<input type="hidden" name="currency_code" value="USD">
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_buynow_SM.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" style="vertical-align: -5px; padding-left: 5px;">
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1"><br />
</form>
<p><strong>Future Updates</strong><br />
1. Gmail style pop-out functionality<br />
2. Gmail style emoticons<br />
3. Gmail style video chat<br />
4. Comet integration<br />
5. Jabber integration</p>
<p><strong>Comments/Suggestions?</strong><br />
Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p>
<p><strong>Spread The Word</strong><br />
If you like what you are reading, then please help spread the word by re-tweeting, blogging and dzone upvoting or use the ShareThis button below. Thank you.</p>
<img src="http://feeds.feedburner.com/~r/anantgarg/~4/W-PpfT0sDq0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://anantgarg.com/2009/05/13/gmail-facebook-style-jquery-chat/feed/</wfw:commentRss>
		<feedburner:origLink>http://anantgarg.com/2009/05/13/gmail-facebook-style-jquery-chat/</feedburner:origLink></item>
		<item>
		<title>Bulletproof Subversion Web Workflow</title>
		<link>http://feedproxy.google.com/~r/anantgarg/~3/WDbLG1mOkhc/</link>
		<comments>http://anantgarg.com/2009/04/22/bulletproof-subversion-web-workflow/#comments</comments>
		<pubDate>Wed, 22 Apr 2009 16:26:29 +0000</pubDate>
		<dc:creator>Anant Garg</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://anantgarg.com/?p=234</guid>
		<description><![CDATA[This workflow will help you perform systematic version control (including database versioning) on your web development projects. Two methods have been detailed. Depending on how critical your web-application is, you can adopt either the bulletproof or the semi bulletproof method.]]></description>
			<content:encoded><![CDATA[<p><strong>The Problem</strong></p>
<p>If you do not have subversion installed already, you can borrow parts from the <a href="http://anantgarg.com/2009/03/25/subversion-trac-multiple-projects/">subversion + trac installation</a> tutorial. Once you have subversion installed, its important to integrate it in your web development workflow. Simply running <em>SVN commit</em> everytime you complete a feature is not enough. So let us see how we can fully integrate subversion into our web development. This workflow also includes database versioning (using a simple PHP script).</p>
<p><strong>The Solution</strong></p>
<p>Before we begin, scroll down and download the database backup and version control script that I have written in PHP.</p>
<p><strong><em>The Bulletproof Method</em></strong></p>
<p>This process is slightly lengthy and complicated, but once you get the hang of it, your web development process will be greatly streamlined. Also, you will be able to test your deployed versions before making them live. This method allows you to switch between versions really really quickly. It is advised for all heavy traffic websites.</p>
<p><em>Assumption:</em> You have a heavy traffic website. You release version upgrades to your site regularly. Once in a while, you also modify the database schema. You would like to test your builds on the live server before letting your users view it.</p>
<p><strong>Phase 1: Launching of version 1.0</strong></p>
<p>Development Server</p>
<p>1. Design the database schema and program the first version of your script.<br />
2. Before committing the script to the repository perform the following steps:<br />
a. Create a folder called db and assign appropriate permissions<br />
b. Run dbbvc.php in your browser and create a baseline. The baseline can be created with data or without data as per your requirements. This will create db/1.sql file which is basically mysqldump of the current database.<br />
3. Once that is done, you can safely commit your script (to the trunk folder). Be sure to commit the db folder, along with 1.sql.</p>
<p><a href="http://anantgarg.com/wp-content/uploads/2009/04/dbbvc.png" rel="shadowbox[post-234];player=img;"><img src="http://anantgarg.com/wp-content/uploads/2009/04/dbbvc.png" alt="Db Backup &amp; Versioning Script" title="Db Backup &amp; Versioning Script" width="696" height="417" class="alignnone size-full wp-image-236" /></a></p>
<p>Note: When committing to your repository, make sure you do not commit any configuration files. All configuration files should go in as <em>config.default.php</em> etc. (This will help you as your server <em>config.php</em> will not get updated when you run <em>svn update</em> on the server.) As a rule, any file that differs from your development to production server, must be a .default file.</p>
<p>Production Server</p>
<p>Now suppose you want to launch this version you have committed.</p>
<p>4. First copy your trunk folder to a suitable version in your tags directory (e.g. tags/1.0) using the <a href="http://svnbook.red-bean.com/en/1.0/re07.html">svn copy</a>.<br />
5. Now go to your server and use <a href="http://svnbook.red-bean.com/en/1.0/re10.html">svn export</a> to export the contents of tag/1.0 to /yourserver/www/1.0<br />
6. Now create a folder called static in your www. The static folder is used to store those folders which will have user uploaded data. </p>
<p>For example, you may have /yourserver/www/1.0/images/profilepics<br />
a. First create a folder called profilepics in www/static<br />
b. Delete the /yourserver/www/1.0/images/profilepics folder<br />
c. Create a <a href="http://en.wikipedia.org/wiki/Symbolic_link">symbolic link</a> in your images folder using the following command:<br />
<b>sudo ln -s /yourserver/static/profilepics /yourserver/www/1.0</b></p>
<p>7. Once that is done, create your database and modify your config.php as per your server settings. Now run dbbvc.php in your server and select <em>Backup &#038; Version Upgrade</em>. This will create add all the schema/data to your database.<br />
8. Now make sure your server supports <a href="http://httpd.apache.org/docs/1.3/mod/mod_rewrite.html">mod_rewrite</a>  and add the following file to the /yourserver/www folder.</p>
<pre class="brush: php">
&amp;amp;amp;amp;amp;lt;IfModule mod_rewrite.c&amp;amp;amp;amp;amp;gt;
RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule    ^$  VERSION/    [L]
RewriteRule    (.*) VERSION/$1    [L]

&amp;amp;amp;amp;amp;lt;/IfModule&amp;amp;amp;amp;amp;gt;
&amp;amp;amp;amp;amp;lt;IfModule !mod_rewrite.c&amp;amp;amp;amp;amp;gt;
	ErrorDocument 404 index.php
&amp;amp;amp;amp;amp;lt;/IfModule&amp;amp;amp;amp;amp;gt;
</pre>
<p>In our example we replace VERSION with 1.0 in the above code.</p>
<p>9. Now create a .htaccess file in the /yourserver/www/1.0 folder with the following contents. (just to be safe, not necessary)</p>
<pre class="brush: php">
&amp;amp;amp;amp;amp;lt;IfModule mod_rewrite.c&amp;amp;amp;amp;amp;gt;
RewriteEngine Off
&amp;amp;amp;amp;amp;lt;/IfModule&amp;amp;amp;amp;amp;gt;
</pre>
<p>10. Once that is done, you can point your browser to your server url and it should work! If you encounter any errors, then feel free to post a comment.</p>
<p><strong>Phase 2: Launching of subsequent versions</strong></p>
<p>Development Server</p>
<p>11. Now let us say you are ready to roll out a couple of changes on the server. Also, let us assume you have made modifications to the database schema. Now everytime you make changes to the schema, you must create a sequential file (2.sql,3.sql&#8230;) in the db folder. If you forget to create this file and have already modified the database schema, you can use <a href="http://dev.mysql.com/workbench/">MySQL Workbench</a> to <a href="http://simpcode.blogspot.com/2008/06/using-mysql-workbench-to-generate.html">reverse engineer and find out the changes.</a><br />
12. Now you can safely commit this version to trunk. Be sure to include your 2.sql&#8230; files also. You may use a single .sql file containing all the db changes for that revision.<br />
13. Once that is done, create a tag of your new version and head over to the production server.</p>
<p>Production Server</p>
<p>14. Now svn export the tags folder to /yourserver/www/1.1 and create your symlinks<br />
15. Duplicate your database (create a new database for this tag and import the current database)<br />
16. Point your browser to dbbvc.php, use the Backup &#038; Version Upgrade option. This will upgrade the database to the latest version.<br />
17. Now you can test your script, once your a ready to do the switch, all you have to do is edit /yourserver/www/.htaccess and change the VERSION to your latest version.</p>
<p>Now, the above steps may appear to be an overkill for a small website which can afford a bit of downtime. Also, you may not have permission to create symlinks and thus can&#8217;t use the above method. For that let us have a look at a semi bulletproof svn workflow.</p>
<p><strong><em>The Semi Bulletproof Method</em></strong></p>
<p><strong>Phase 1: Launching of version 1.0</strong></p>
<p>Development Server</p>
<p>Follow the same steps as the bulletproof method (Steps 1,2,3).</p>
<p>Deployment Server</p>
<p>1. Make sure you have created your tag, and use <a href="http://svnbook.red-bean.com/en/1.0/re04.html">svn checkout</a> to checkout the contents of your tags/1.0 folder to /yourserver/www folder<br />
2. Edit your config.php file, create your database and run dbbvc.php to import your database.<br />
3. Create .htaccess file in your /yourserver/www folder with the following contents:</p>
<pre class="brush: php">
&amp;amp;amp;amp;amp;lt;IfModule mod_rewrite.c&amp;amp;amp;amp;amp;gt;
   RewriteEngine On
   RewriteRule ^(.*)(.svn)(.*)$ http://www.yourserver.com [L]
&amp;amp;amp;amp;amp;lt;/IfModule&amp;amp;amp;amp;amp;gt;
</pre>
<p>4. Now you can point your browser to yourserver.com and it should work!</p>
<p><strong>Phase 2: Launching of subsequent versions</strong></p>
<p>Development Server</p>
<p>Follow the same steps as the bulletproof method (Steps 11,12,13).</p>
<p>Production Server</p>
<p>5. Now use the <a href="http://svnbook.red-bean.com/en/1.0/re27.html">svn switch</a> command to update the contents of your /yourserver/www folder.<br />
6. Point your browser to dbbvc.php and update your database (if you have made any changes to schema).</p>
<p>Although the above method is simpler, and will work for most of your projects, there will be a definite downtime during your revision update and db schema update. During this upgrade, you may want to redirect all users to a temporary page using .htaccess.</p>
<p><strong>Download the database versioning script (dbbvc)</strong></p>
<p>Download the <a href='http://anantgarg.com/wp-content/uploads/2009/04/dbbvc.zip'>Database Version Control</a> script. You will need to rename config.default.php to config.php and update the database details or you may edit dbbvc.php and <em>require</em> your own config.php/db.php.</p>
<p><strong>Comments/Suggestions?</strong></p>
<p>Do let me know your suggestions on how we can improve this workflow or anything else you want to add.</p>
<p><strong>Spread The Word</strong></p>
<p>If you like what you are reading, then please help spread the word by <a href="http://twitter.com/home/?status=RT+%40anant_garg+Bulletproof%20Subversion%20Web%20Workflow%20http:%2F%2Ftinyurl.com%2Fdhzxwj">re-tweeting</a>, blogging and dzone upvoting or use the ShareThis button below. Thank you.</p>
<img src="http://feeds.feedburner.com/~r/anantgarg/~4/WDbLG1mOkhc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://anantgarg.com/2009/04/22/bulletproof-subversion-web-workflow/feed/</wfw:commentRss>
		<feedburner:origLink>http://anantgarg.com/2009/04/22/bulletproof-subversion-web-workflow/</feedburner:origLink></item>
		<item>
		<title>Write your own PHP MVC Framework (Part 2)</title>
		<link>http://feedproxy.google.com/~r/anantgarg/~3/8rAT03zQ0BE/</link>
		<comments>http://anantgarg.com/2009/03/30/write-your-own-php-mvc-framework-part-2/#comments</comments>
		<pubDate>Mon, 30 Mar 2009 06:25:20 +0000</pubDate>
		<dc:creator>Anant Garg</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[framework]]></category>

		<category><![CDATA[mvc]]></category>

		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://anantgarg.com/?p=200</guid>
		<description><![CDATA[The much awaited Part 2 of the Write your own PHP MVC Framework tutorial series. This part drastically enhances the feature-set of the framework including object-relational mapping, routing and caching.]]></description>
			<content:encoded><![CDATA[<p>If you haven&#8217;t already, please read <a href="http://anantgarg.com/2009/03/13/write-your-own-php-mvc-framework-part-1/">Part 1</a> of the <a href="http://anantgarg.com/2009/03/13/write-your-own-php-mvc-framework-part-1/">PHP MVC tutorial</a>. A lot of changes have been incorporated in this part. Unlike the last tutorial, I have not pasted the code directly here, and I suggest you <a href='http://anantgarg.com/wp-content/uploads/2009/03/framework-2.zip'>download the framework</a> before reading on. In the example, we have implemented a simple e-commerce website consisting of categories, subcategories, products and tags (for products). The example will help you understand the various relationships between the tables and most of the new functionality provided by this part of the framework.</p>
<p><strong>What&#8217;s New</strong></p>
<p><em>config/inflection.php</em><br />
The inflection configuration file enables us to use irregular words i.e. words which do not have a standard plural name. This file is used in conjunction with <em>library/inflection.class.php</em></p>
<p><em>config/routing.php</em><br />
The routing configuration file enables us to specify default controller and action. We can also specify custom redirects using regular expressions. Currently I have specified only one redirect i.e. http://localhost/framework/admin/categories/view will become http://localhost/framework/admin/categories_view where admin is the controller and categories_view is the action. This will enable us to create an administration centre with pretty URLs. You can specify others as per your requirements. For more information on how to use regular expressions have a look at <a href="http://www.roscripts.com/PHP_regular_expressions_examples-136.html">one</a> <a href="http://weblogtoolscollection.com/regex/regex.php">of</a> <a href="http://www.phpf1.com/tutorial/php-regular-expression.html">these</a> <a href="http://www.webcheatsheet.com/php/regular_expressions.php">tutorials</a>.</p>
<p><em>library/cache.class.php</em><br />
The cache class is in its infancy. Currently there are two simple functions- set and get. The data is stored in a flat text-file in the cache directory. Currently only the <em>describe</em> function of the SQLQuery class uses this cache function.</p>
<p><em>library/html.class.php</em><br />
The HTML class is used to aid the template class. It allows you to use a few standard functions for creating links, adding javascript and css. I have also added a function to convert links to tinyurls. This class can be used only in the views e.g. $html->includeJs(&#8217;generic.js&#8217;);</p>
<p><em>library/inflection.class.php</em><br />
In the previous part, plural of words were created by adding only &#8220;s&#8221; to the word. However, for a more full-fledged version, we now use the inflection class originally created by <a href="http://kuwamoto.org/2007/12/17/improved-pluralizing-in-php-actionscript-and-ror">Sho Kuwamoto</a> with slight modifications. If you have a look at the class, it makes use of simple regular expressions. It would be nice to add a cache function to this class in future.</p>
<p><em>library/template.class.php</em><br />
I have added a new variable called doNotRenderHeader which will enable you to not output headers for a particular action. This can be used in AJAX calls when you do not want to return the headers. It has to be called by the controller e.g. $this->doNotRenderHeader = 1;</p>
<p><em>library/vanillacontroller.class.php &#038; vanillamodel.class.php</em><br />
Pretty much unchanged, but I have added a prefix vanilla to them to emphasize on its simplicity <img src='http://anantgarg.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p><em>library/sqlquery.class.php</em><br />
The SQLQuery class is the heart of this framework. This class will enable you to use your tables as objects.</p>
<p>Let us understand the easy functions first - save() and delete()</p>
<p>The save() function must be used from the controller. The save() function can have two options- if an id is set, then it will update the entry; if it is not set, then it will create a new entry. For example let us consider the categories class i.e. application/controllers/categoriescontroller.php. We can have a function like the one below.</p>
<pre class="brush: php">
function new() {
   $this-&gt;Category-&gt;id = $_POST[&#039;id&#039;];
   $this-&gt;Category-&gt;name = $_POST[&#039;name&#039;];
   $this-&gt;Category-&gt;save();
}
</pre>
<p>If $this->Category->id = null; then it will create a new record in the categories table.</p>
<p>The delete() function enables you to delete a record from the table. A sample code could be as below. Although you should use POST instead of GET queries for deleting records. As a rule idempotent operations should use GET. Idempotent operations are those which do not change the state of the database (i.e. do not update/delete/insert rows or modify the database in anyway)</p>
<pre class="brush: php">
function delete($categoryId) {
   $this-&gt;Category-&gt;id = $categoryId;
   $this-&gt;Category-&gt;delete();
}
</pre>
<p>Now let us look at the search() function. I know it is a bit intimidating at first. But it is pretty straight forward after we break it down. If you are unaware of database table relationships (1:1, 1:Many and Many:Many) have a quick look <a href="http://www.databaseprimer.com/relationship.html">here</a>. During database design, we use the following convention:</p>
<p>1:1 Relationship<br />
For a one is to one relationship, suppose we have two tables students and mentors and each student hasOne mentor, then in the students table we will have a field called &#8216;mentor_id&#8217; which will store the id of the mentor from the mentors table.</p>
<p>1:Many Relationship<br />
For a one is to many relationship, suppose we have two tables parents and children and each parent hasMany children, then in the children table we will have a field called &#8216;parent_id&#8217; which will store the id of the parent from the parents table.</p>
<p>Many:Many Relationship<br />
For a many is to many relationship, suppose we have two tables students and teachers and each student hasManyAndBelongsToMany teachers, then we create a new table called students_teachers with three fields: id, student_id and teacher_id. The naming convention for this table is alphabetical. i.e. if our tables are cars and animals, then the table should be named animals_cars and not cars_animals.</p>
<p>Now once we have created our database as per these conventions, we must tell our framework about their existence. Let us have a look at models/product.php.</p>
<pre class="brush: php">
class Product extends VanillaModel {
	var $hasOne = array(&#039;Category&#039; =&gt; &#039;Category&#039;);
	var $hasManyAndBelongsToMany = array(&#039;Tag&#039; =&gt; &#039;Tag&#039;);
}
</pre>
<p>The first Category is the alias and the second Category is the actual model. In most cases both will be the same. Let us consider the models/category.php where they are not.</p>
<pre class="brush: php">
&lt;?php

class Category extends VanillaModel {
		var $hasMany = array(&#039;Product&#039; =&gt; &#039;Product&#039;);
		var $hasOne = array(&#039;Parent&#039; =&gt; &#039;Category&#039;);

}
</pre>
<p>Here each category has a parent category, thus our alias is <em>Parent</em> while model is <em>Category</em>. Thus we will have a field called parent_id in the categories table. To clearly understand these relationships, I suggest you create a couple of tables and test them out for yourself. In order to see the output, use code similar to the following in your controller.</p>
<pre class="brush: php">
function view() {
   $this-&gt;Category-&gt;id = 1;
   $this-&gt;Category-&gt;showHasOne();
   $this-&gt;Category-&gt;showHasMany();
   $this-&gt;Category-&gt;showHMABTM();
   $data = $this-&gt;Category-&gt;search();
   print_r($data);
}
</pre>
<p>Now let us try and understand the search() function. If there are no relationships, then the function simply does a <em>select * from tableName</em> (tableName is same as controllerName). We can influence this statement, by using the following commands:</p>
<p><em>where(&#8217;fieldName&#8217;,'value&#8217;)</em> => Appends WHERE &#8216;fieldName&#8217; = &#8216;value&#8217;<br />
<em>like(&#8217;fieldName&#8217;,'value&#8217;)</em> => Appends WHERE &#8216;fieldName&#8217; LIKE &#8216;%value%&#8217;<br />
<em>setPage(&#8217;pageNumber&#8217;)</em> => Enables pagination and display only results for the set page number<br />
<em>setLimit(&#8217;fieldName&#8217;,'value&#8217;)</em> => Allows you to modify the number of results per page if pageNumber is set. Its default value is the one set in config.php.<br />
<em>orderBy(&#8217;fieldName&#8217;,'Order&#8217;)</em> => Appends ORDER BY &#8216;fieldName&#8217; ASC/DESC<br />
<em>id = X</em> => Will display only a single result of the row matching the id</p>
<p>Now let us consider when showHasOne() function has be called, then for each hasOne relationship, a LEFT JOIN is done (see line 91-99).</p>
<p>Now if showHasMany() function has been called, then for each result returned by the above query and for each hasMany relationship, it will find all those records in the second table which match the current result&#8217;s id (see line 150). Then it will push all those results in the same array. For example, if teachers hasMany students, then $this->Teacher->showHasMany() will search for <em>teacher_id</em> in the <em>students</em> table.</p>
<p>Finally if showHMABTM() function has been called, then for each result returned by the first query and for each hasManyAndBelongsToMany relationship, it will find all those records which match the current result&#8217;s id (see line 200-201). For example, if teachers hasManyAndBelongsToMany students, then $this->Teacher->showHMABTM() will search for <em>teacher_id</em> in <em>students_teachers</em> table.</p>
<p>On line 236, if <em>id</em> is set, then it will return a single result (and not an array), else it will return an array. The function then calls the clear() function which resets all the variables (line 368-380). </p>
<p>Now let us consider an example to enable pagination on products. The code in the controllers/productscontroller.php should look something similar to the following.</p>
<pre class="brush: php">
function page ($pageNumber = 1) {
  $this-&gt;Product-&gt;setPage($pageNumber);
  $this-&gt;Product-&gt;setLimit(&#039;10&#039;);
  $products = $this-&gt;Product-&gt;search();
  $totalPages = $this-&gt;Product-&gt;totalPages();
  $this-&gt;set(&#039;totalPages&#039;,$totalPages);
  $this-&gt;set(&#039;products&#039;,$products);
  $this-&gt;set(&#039;currentPageNumber&#039;,$pageNumber);
}
</pre>
<p>Now our corresponding view i.e. views/products/page.php will be something like below.</p>
<pre class="brush: php">
&lt;?php foreach ($products as $product):?&gt;
&lt;div&gt;
&lt;?php echo $product[&#039;Product&#039;][&#039;name&#039;]?&gt;
&lt;/div&gt;
&lt;?php endforeach?&gt;

&lt;?php for ($i = 1; $i &lt;= $totalPages; $i++):?&gt;
&lt;div&gt;
&lt;?php if ($i == $currentPageNumber):?&gt;
&lt;?php echo $currentPageNumber?&gt;
&lt;?php else: ?&gt;
&lt;?php echo $html-&gt;link($i,&#039;products/page/&#039;.$i)?&gt;
&lt;?php endif?&gt;
&lt;/div&gt;
&lt;?php endfor?&gt;
</pre>
<p>Thus with a few lines of code we have enabled pagination on our products page with pretty URLs!<br />
(/products/page/1, products/page/2 &#8230;)</p>
<p>If you look at the totalPages() function on line 384-397, you will see that it takes the existing query and strips of the LIMIT condition and returns the count. This count/limit gives us the totalPages.</p>
<p>Now suppose that we are in the categories controller and we want to implement a query on the products table. One way to implement this is using a custom query i.e. $this->Category->custom(&#8217;select * from products where &#8230;.&#8217;);</p>
<p>Alternatively, we can use the performAction function (line 49-57 in shared.php) to call an action of another controller. An example call in categoriescontroller.php would be something like below.</p>
<pre class="brush: php">
function view($categoryId = null, $categoryName = null) {
  $categories = performAction(&#039;products&#039;,&#039;findProducts&#039;,array($categoryId,$categoryName));
}
</pre>
<p>And the corresponding code in productscontroller.php should be as below.</p>
<pre class="brush: php">
function findProducts ($categoryId = null, $categoryName = null) {
  $this-&gt;Product-&gt;where(&#039;category_id&#039;,$categoryId);
  $this-&gt;Product-&gt;orderBy(&#039;name&#039;);
  return $this-&gt;Product-&gt;search();
}
</pre>
<p>The above more or less sums up all the functionality of the SQLQuery class. You can easily extend this as per your requirements e.g. to cache results, add conditions to hasMany, hasOne, hasMABTM queries also etc.</p>
<p>I have also laid the foundation for implementing user registration functionality by specifying a beforeAction and afterAction function which will be executed for each controller action. This will enable us to call a function which checks whether the user cookie is set and is a valid user.</p>
<p>One more feature that I have implemented is to have an administration centre. For this purpose we create a dummy model called models/admin.php and set a variable called $abstract = true. This will tell our VanillaModel to not look for a corresponding table in the database. As stated before I have created a routing for admin/X/Y as admin/X_Y. Thus we can have URLs like admin/categories/delete/15 which will actually call the categories_delete(15) function in the controllers/admincontroller.php file.</p>
<p><strong>Where do we go from here?</strong></p>
<p>I hope this tutorial is not too complicated. I have tried to break it down as much as possible. A couple of functions that we need to implement include -> redirectAction which will be similar to performAction, but instead redirect. performAction can only return results. redirectAction will perform only the redirected action and display that corresponding view only. This will be helpful during user authentication. If the user is not logged in, then we call a function like -> redirectAction(&#8217;user&#8217;,'login&#8217;,array($returnUrl));</p>
<p>Various classes are in their infancy like the cache class, HTML class etc. These classes can be expanded to add more functionality. </p>
<p>More scripts can be added like dbimport, clearcache etc. can be added. Currently there is only one script dbexport.php which dumps the entire database. Classes for managing sessions, cookies etc. also need to be developed.</p>
<p><strong>Download link here</strong></p>
<p><a href='http://anantgarg.com/wp-content/uploads/2009/03/framework-2.zip'>Download Framework (Part 2)</a> (ZIP File)</p>
<p>Make sure you edit config/config.php and add your database details. Point your browser to localhost/FRAMEWORKDIR to see output. Also before executing, import the database that I have created in the db folder. </p>
<p><strong>Suggestions?</strong></p>
<p>Do let me know your suggestions on how we can improve this code/any particular features you would like to see implemented/any other unrelated topic you would like a tutorial on. </p>
<p><strong>Spread The Word</strong></p>
<p>If you like what you are reading, then please help spread the word by <a href="http://twitter.com/home/?status=RT+%40anant_garg+Write%20your%20own%20PHP%20MVC%20Framework%20(Part%202) http%3A%2F%2Ftinyurl.com%2Fca3kls">re-tweeting</a>, blogging and dzone upvoting or use the ShareThis button below. Thank you.</p>
<p><script type="text/javascript">var dzone_url = 'http://anantgarg.com/2009/03/30/write-your-own-php-mvc-framework-part-2/';var dzone_title = 'Write your own PHP MVC Framework (Part 2)';var dzone_blurb = '';var dzone_style = '1';</script><script language="javascript" src="/dzone.js"></script></p>
<img src="http://feeds.feedburner.com/~r/anantgarg/~4/8rAT03zQ0BE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://anantgarg.com/2009/03/30/write-your-own-php-mvc-framework-part-2/feed/</wfw:commentRss>
		<feedburner:origLink>http://anantgarg.com/2009/03/30/write-your-own-php-mvc-framework-part-2/</feedburner:origLink></item>
	</channel>
</rss><!-- Dynamic page generated in 0.336 seconds. --><!-- Cached page generated by WP-Super-Cache on 2013-05-17 01:11:57 --><!-- Compression = gzip -->
