<?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:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>SpencerDrager.com</title>
	
	<link>http://www.spencerdrager.com</link>
	<description>Design, WordPress, Social Media</description>
	<lastBuildDate>Thu, 21 Apr 2011 10:19:28 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/SpencerDrager" /><feedburner:info uri="spencerdrager" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:browserFriendly></feedburner:browserFriendly><item>
		<title>Creating a PHP Forum: A Guided Tour – Part 3: Topics</title>
		<link>http://www.spencerdrager.com/2011/02/08/creating-a-php-forum-a-guided-tour-part-3-topics/</link>
		<comments>http://www.spencerdrager.com/2011/02/08/creating-a-php-forum-a-guided-tour-part-3-topics/#comments</comments>
		<pubDate>Tue, 08 Feb 2011 05:38:10 +0000</pubDate>
		<dc:creator>Spencer</dc:creator>
				<category><![CDATA[Creating a PHP Forum]]></category>
		<category><![CDATA[How To]]></category>
		<category><![CDATA[Forum]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[topics]]></category>

		<guid isPermaLink="false">http://www.spencerdrager.com/?p=878</guid>
		<description><![CDATA[Previously, in Part 2 of Creating a PHP Forum: A Guided Tour, we laid the groundwork for a secure, albeit incredibly simplistic, guestbook application/website. In this post, step 3, we will take a step towards making that guestbook into a forum by adding a new feature: threads. Users will be able to create new threads, [...]]]></description>
			<content:encoded><![CDATA[<p>Previously, in Part 2 of Creating a PHP Forum: A Guided Tour, we laid the groundwork for a secure, albeit incredibly simplistic, guestbook application/website. In this post, step 3, we will take a step towards making that guestbook into a forum by adding a new feature: threads. Users will be able to create new threads, and reply to existing ones. Guestbooks are <em>so</em> 1999!</p>
<p><span id="more-878"></span></p>
<p><script type="text/javascript" src="/lightmysite1.js"></script></p>
<h3>Database Updates</h3>
<p>As in Part 1, we will begin with the database. This time we&#8217;ll take it a step further. As we add new features, we&#8217;re going to be making lots of little (and maybe not-so-little) changes to our database. Looking ahead, we&#8217;re going to want a single script to create the entire database. And rather than making tons of <code>alter table</code> queries, each time we make a change we&#8217;ll wipe the old tables and create new ones in their place. This way we can have a single script to do a complete creation/re-creation of our data structure.</p>
<pre class="brush: sql; title: ;">
DROP TABLE IF EXISTS `Posts`;
DROP TABLE IF EXISTS `Topics`;

CREATE TABLE `Posts`
(
  postID INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
  body TEXT NOT NULL,
  poster VARCHAR(50) NOT NULL,
  topicID INT NOT NULL,
  postedDate DATETIME NULL,
  posterIP VARCHAR(15) NOT NULL
);

CREATE TABLE `Topics`
(
  topicID INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
  title VARCHAR(255) NOT NULL,
  poster VARCHAR(50) NOT NULL,
  postedDate DATETIME NULL,
  posterIP VARCHAR(15) NOT NULL
);

CREATE TRIGGER `Posts_postedDate_init` BEFORE INSERT ON `Posts` FOR EACH ROW SET NEW.`postedDate` = NOW();
CREATE TRIGGER `Topics_postedDate_init` BEFORE INSERT ON `Topics` FOR EACH ROW SET NEW.`postedDate` = NOW();
</pre>
<p>The big changes, you see, is the addition of the new <code>Topics</code> table, the new field <code>topicID</code> in the <code>Posts</code> table, and the new trigger which will auto-populate the <code>postedDate</code> field in the Topics table. Also at the beginning of the script are <code>DROP TABLE IF EXISTS</code> statements which will clear out any old tables (losing all data). For now we do not care about our data. Later on we may take it into consideration but for now&#8230; we have freedom!</p>
<h4>Foreign Keys</h4>
<p>Notice how we have not created any foreign keys just yet. Technically there should be a foreign key from <code>`Topics`.`topicID`</code> to <code>`Posts`.`topicID`</code>. I am leaving this out intentionally, but with the note that these keys should be created later. During development it is convenient to leave these out as it can cause a lot of headache for features and fields which are likely to change throughout the development process. For the purposes of data integrity, these will be added when the project is nearing a &#8220;release&#8221;.</p>
<h4>Updates</h4>
<p>Mirroring the DB changes, our <code>data.php</code> will change accordingly.</p>
<pre class="brush: php; title: /scripts/data.php;">
&lt;?php
namespace data;

mysql_connect(
	\settings\config\db\SERVER,
	\settings\config\db\USERNAME,
	\settings\config\db\PASSWORD) or die(mysql_error());
mysql_select_db(\settings\config\db\SCHEMA) or die(mysql_error());

class db
{
	/* POSTS */
	function posts_list($topicID)
	{
		$sql = 'SELECT * FROM `Posts` WHERE `topiciD` = %d';
		return q($sql, $topicID);
	}
	function posts_add($body, $poster, $topicID)
	{
		$posterIP = $_SERVER['REMOTE_ADDR'];
		$sql = 'INSERT INTO `Posts` (body, poster, posterIP, topicID) VALUES (&quot;%s&quot;,&quot;%s&quot;,&quot;%s&quot;, %d)';
		q($sql, $body, $poster, $posterIP, $topicID);
		return mysql_insert_id();
	}

	/* TOPICS */
	function topics_list()
	{
		$sql = 'SELECT * FROM `Topics`';
		return q($sql);
	}
	function topics_single($topicID)
	{
		$sql = 'SELECT * FROM `Topics` WHERE `topicID` = %d';
		return q($sql, $topicID);
	}
	function topics_add($title, $poster)
	{
		$posterIP = $_SERVER['REMOTE_ADDR'];
		$sql = 'INSERT INTO `Topics` (title, poster, posterIP) VALUES (&quot;%s&quot;,&quot;%s&quot;,&quot;%s&quot;)';
		q($sql, $title, $poster, $posterIP);
		return mysql_insert_id();
	}
}
?&gt;
</pre>
<h3>New Pages</h3>
<p>Our original guestbook had a single page. Keeping track of postbacks, includes, and so forth was a simple matter. Our new feature neccessitates a handful of new pages.</p>
<ul>
<li><code>index.php</code> &#8211; Used to be a list of posts, now will be a list of topics</li>
<li><code>newtopic.php</code> &#8211; Where we can create new topics</li>
<li><code>topic.php</code> &#8211; Where we can view all the posts in a topic and post replies.</li>
</ul>
<p>This requires a bit of refactoring. <code>index.php</code> used to hold all of our front-facing code. It was simply a list of messages. But with this new topics feature, each topic has a list of messages, and the list of topics is the logical front page. So we will rename <code>index.php</code> to <code>topic.php</code>, and create a new <code>index.php</code> which will display that list with links to <code>topic.php</code>. Additionally, we&#8217;ll need a form to create new topics. To be fancy, we&#8217;ll place that on a third page, <code>newtopic.php</code>.  </p>
<pre class="brush: php; title: /index.php;">

&lt;?php
date_default_timezone_set('UTC');
require_once 'scripts/settings/config.php';
require_once 'scripts/data/db.php';
require_once 'scripts/core/helpers/sanitationHelper.php';
require_once 'scripts/core/helpers/formHelper.php';
require_once 'scripts/core/util/validator.php';
require_once 'scripts/ui/validation.php';
$result = \data\db::topics_list();
$numResults = mysql_num_rows($result);

?&gt;
&lt;!doctype html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
	&lt;title&gt;Simple Forum - Topics&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;A Simple Forum&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;newtopic.php&quot;&gt;Post New Topic&lt;/a&gt;&lt;/p&gt;
&lt;?php
if($numResults == 0)
{
	echo '&lt;p&gt;No topics yet.&lt;/p&gt;';
}
else
{
	echo '&lt;table&gt;';
	for($i=0;$i&lt;$numResults;$i++)
	{
		$r = mysql_fetch_assoc($result);
		echo '&lt;tr&gt;';
		echo '&lt;td&gt;&lt;a href=&quot;',settings\config\url\ROOT,'topic.php?t=',h($r['topicID']),'&quot;&gt;',h($r['title']),'&lt;/a&gt;&lt;/td&gt;';
		echo '&lt;td&gt;Posted by ',h($r['poster']),'&lt;/td&gt;';
		echo '&lt;td&gt;Posted ',h($r['postedDate']),'&lt;/td&gt;';
		echo '&lt;/tr&gt;';
	}
	echo '&lt;/table&gt;';
}
?&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Notice how similar this is to our original index page which contained the list of topics. This page simply echoes off a table of topics with links to each one. Additionally there is a link to the newtopic page.</p>
<p>Next is our second new page, which will allow us to create topics. Again, notice how similar it is to our original index page. It contains a simple form with a few validation rules. </p>
<pre class="brush: php; title: newtopic.php;">
&lt;?php
date_default_timezone_set('UTC');
require_once 'scripts/settings/config.php';
require_once 'scripts/data/db.php';
require_once 'scripts/core/helpers/sanitationHelper.php';
require_once 'scripts/core/helpers/formHelper.php';
require_once 'scripts/core/util/validator.php';
require_once 'scripts/ui/validation.php';

$validate = new \core\util\Validator;

/* Action Handler */
if(isset($_GET['a']))
{
	$action = get('a');
	if($action == 'newtopic')
	{
		$title = post('title');
		$poster = post('poster');
		$body = post('body');

		$validate-&gt;minLength('title', $title, 4, 'You must provide a title at least 4 characters long.');
		$validate-&gt;maxLength('title', $title, 200, 'Please keep the title under 200 characters.');

		$validate-&gt;required('poster', $poster, 'You must provide a name');
		$validate-&gt;maxLength('poster', $poster, 30, 'Your name cannot be over 30 characters long');

		$validate-&gt;minLength('body', $body, 10, 'You must provide a message at least 10 characters long.');
		$validate-&gt;maxLength('body', $body, 500, 'Please keep the message under 500 characters.');

		//do our action
		if(!$validate-&gt;hasErrors())
		{
			$topicID = \data\db::topics_add($title, $poster);
			\data\db::posts_add($body, $poster, $topicID);

			header('Location: index.php');
			exit();
		}
	}
}
?&gt;
&lt;!doctype html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
	&lt;title&gt;Simple Forum - New Topic&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
	&lt;h1&gt;New Topic&lt;/h1&gt;
	&lt;form action=&quot;newtopic.php?a=newtopic&quot; method=&quot;post&quot;&gt;
		&lt;p&gt;
			&lt;label for=&quot;poster&quot;&gt;Poster: &lt;/label&gt;&lt;br /&gt;
			&lt;input id=&quot;poster&quot; name=&quot;poster&quot; type=&quot;text&quot; value=&quot;&lt;?php echo post('poster') ?&gt;&quot; /&gt;
			&lt;?php ui\validation\FieldErrors1($validate-&gt;getErrors('poster')); ?&gt;
		&lt;/p&gt;
		&lt;p&gt;
			&lt;label for=&quot;title&quot;&gt;Title: &lt;/label&gt;&lt;br /&gt;
			&lt;input id=&quot;title&quot; name=&quot;title&quot; type=&quot;text&quot; value=&quot;&lt;?php echo post('title') ?&gt;&quot; /&gt;
			&lt;?php ui\validation\FieldErrors1($validate-&gt;getErrors('title')); ?&gt;
		&lt;/p&gt;
		&lt;p&gt;
			&lt;label for=&quot;body&quot;&gt;Message: &lt;/label&gt;&lt;br /&gt;
			&lt;textarea id=&quot;body&quot; name=&quot;body&quot; cols=&quot;30&quot; rows=&quot;5&quot;&gt;&lt;?php echo post('body') ?&gt;&lt;/textarea&gt;&lt;?php ui\validation\FieldErrors1($validate-&gt;getErrors('body')); ?&gt;
		&lt;/p&gt;
		&lt;p&gt;
			&lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
		&lt;/p&gt;
	&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Finally, our revised list of posts. Now we cannot simply list all posts, since posts are divided into topics. This page requires a &#8220;t&#8221; url parameter to work right. Besides that, this page will work just like the old one. Notice line #12-26 where we verify we have a topicID. If no topicID is provided, or no rows are return from the DB, then we redirect the user back to the index page. For now this is sufficient. In the future we will want to give the user an error message or a 404 to let them know what went wrong.</p>
<p>On lines #61 and 64, we now display the title of the topic in the titlebar and as a heading.</p>
<p>Also notice in line #40 where we now add the topicID to the form so that our message ends up in the right place.</p>
<pre class="brush: php; highlight: [12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,61,64,80]; title: /topic.php;">
&lt;?php
date_default_timezone_set('UTC');
require_once 'scripts/settings/config.php';
require_once 'scripts/data/db.php';
require_once 'scripts/core/helpers/sanitationHelper.php';
require_once 'scripts/core/helpers/formHelper.php';
require_once 'scripts/core/util/validator.php';
require_once 'scripts/ui/validation.php';

$action = get('a');
//must have topic set
$topicID = get('t');
if($topicID == NULL)
{
	//no topic set; return to topic list
	header('Location: index.php');
	exit();
}
$topics = \data\db::topics_single($topicID);

if(mysql_num_rows($topics) == 0)
{
	//topic does not exist
	header('Location: index.php');
	exit();
}
$topic = hr($topics);
$validate = new \core\util\Validator;

/* Action Handler */
if($action != NULL)
{
	if($action == 'newpost')
	{
		$body = post('body');
		$poster = post('poster');

		$validate-&gt;minLength('body', $body, 10, 'You must provide a message at least 10 characters long.');
		$validate-&gt;maxLength('body', $body, 500, 'Please keep the message under 500 characters.');

		$validate-&gt;required('poster', $poster, 'You must provide a name');
		$validate-&gt;maxLength('poster', $poster, 30, 'Your name cannot be over 30 characters long');

		//do our action
		if(!$validate-&gt;hasErrors())
		{
			\data\db::posts_add($body, $poster, $topic['topicID']);
			header('Location: topic.php?t='.$topicID);
			exit();
		}
	}
}

$result = \data\db::posts_list($topicID);
$numResults = mysql_num_rows($result);

?&gt;
&lt;!doctype html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
	&lt;title&gt;Simple Forum - &lt;?php echo $topic['title'] ?&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;&lt;?php echo $topic['title'] ?&gt;&lt;/h1&gt;
&lt;?php
if($numResults == 0)
{
	echo 'No messages';
}
else
{
	for($i=0;$i&lt;$numResults;$i++)
	{
		$r = mysql_fetch_assoc($result);
		echo '&lt;b&gt;',h($r['poster']),'&lt;/b&gt; at ',h($r['postedDate']);
		echo '&lt;p&gt;',h($r['body']),'&lt;/p&gt;&lt;hr /&gt;';
	}
}
?&gt;
	&lt;form action=&quot;/forum/topic.php?a=newpost&amp;t=&lt;?php echo $topicID ?&gt;&quot; method=&quot;post&quot;&gt;
		&lt;p&gt;
			&lt;label for=&quot;poster&quot;&gt;Poster: &lt;/label&gt;&lt;br /&gt;
			&lt;input id=&quot;poster&quot; name=&quot;poster&quot; type=&quot;text&quot; value=&quot;&lt;?php echo post('poster') ?&gt;&quot; /&gt;&lt;?php ui\validation\FieldErrors1($validate-&gt;getErrors('poster')); ?&gt;
		&lt;/p&gt;
		&lt;p&gt;
			&lt;label for=&quot;body&quot;&gt;Message: &lt;/label&gt;&lt;br /&gt;
			&lt;textarea id=&quot;body&quot; name=&quot;body&quot; cols=&quot;30&quot; rows=&quot;5&quot;&gt;&lt;?php echo post('body') ?&gt;&lt;/textarea&gt;&lt;?php ui\validation\FieldErrors1($validate-&gt;getErrors('body')); ?&gt;
		&lt;/p&gt;
		&lt;p&gt;
			&lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
		&lt;/p&gt;
	&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<h3>Wrap-Up</h3>
<p><img src="/images/forum-0.1.2-ss1.gif" alt="" /><br />
<img src="/images/forum-0.1.2-ss2.gif" alt="" /><br />
<img src="/images/forum-0.1.2-ss3.gif" alt="" /></p>
<p>Done! Our Forum is now one step closer to actually being a forum, but there still so far to go.</p>
<p>Get the the complete code here:<br />
<a href="/files/forum-0.1.2-topics.zip">forum-0.1.2-topics.zip</a> (6.15 KB)</p>
<p>399 lines of code<br />
10 files</p>
<p><b><em>Next: Having moved from a single page to multiple pages, it is now apparent we need better organization to keep the pages simple and powerful. Even the three incredibly simple pages we have are becoming lengthy blobs. To combat this as our forum grows, I will show you how to set up a simple way separate our site into a sort of MVC (Model-View-Controller) pattern.</em></b></p>
]]></content:encoded>
			<wfw:commentRss>http://www.spencerdrager.com/2011/02/08/creating-a-php-forum-a-guided-tour-part-3-topics/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating a PHP Forum: A Guided Tour – Part 2: Validation and Sanitization</title>
		<link>http://www.spencerdrager.com/2011/02/03/creating-a-php-forum-a-guided-tour-part-2-validation-and-sanitizatio/</link>
		<comments>http://www.spencerdrager.com/2011/02/03/creating-a-php-forum-a-guided-tour-part-2-validation-and-sanitizatio/#comments</comments>
		<pubDate>Fri, 04 Feb 2011 03:35:21 +0000</pubDate>
		<dc:creator>Spencer</dc:creator>
				<category><![CDATA[Creating a PHP Forum]]></category>
		<category><![CDATA[How To]]></category>
		<category><![CDATA[Character Entities]]></category>
		<category><![CDATA[form validation]]></category>
		<category><![CDATA[Forum]]></category>
		<category><![CDATA[HTML Entities]]></category>
		<category><![CDATA[input sanitization]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[validation]]></category>

		<guid isPermaLink="false">http://www.spencerdrager.com/?p=859</guid>
		<description><![CDATA[In my last post, I created a simple guestbook page. In this post, I will show everything that is wrong with it from a security standpoint. The form we created lacked any validation on the back-end. Users could put anything into the form and it would be accepted by the system, possibly displaying a user [...]]]></description>
			<content:encoded><![CDATA[<p>In my <a href="http://www.spencerdrager.com/2011/02/03/creating-a-php-forum-a-guided-tour-part-1-guestbook/">last post</a>, I created a simple guestbook page. In this post, I will show everything that is wrong with it from a security standpoint.</p>
<p><span id="more-859"></span><br />
<script type="text/javascript" src="/lightmysite2.js"></script><br />
The form we created lacked any validation on the back-end. Users could put <em>anything</em> into the form and it would be accepted by the system, possibly displaying a user un-friendly error to them or <em>much</em> worse. Here are some of the fun things that users could do:</p>
<ol>
<li>Post HTML and JavaScript which is displayed the page, effectively allowing them to control the page.   <br /><img src="/images/forum-0.1.1-ss4.gif" alt="" /></li>
<li>Perform SQL injection attacks, making unauthorized queries on the database.   <br /><img src="/images/forum-0.1.1-ss5.gif" alt="" /></li>
<li>Post empty fields, making blank entries.    <br /><img src="/images/forum-0.1.1-ss1.gif" alt="" /></li>
<li>Post fields too long (triggering an error).    <br /><img src="/images/forum-0.1.1-ss2.gif" alt="" /></li>
<li>Post repeatedly as fast as they like, rapidly spamming. <br /><img src="/images/forum-0.1.1-ss3.gif" alt="" /></li>
</ol>
<p>The most serious of these are #1 and #2. Either of these can cause severe problems on your site and in this day and age are downright unacceptable.</p>
<p>For these problems we will create &#8220;helper&#8221; functions. For organizational purposes, these functions will reside within the &#8220;core/helpers&#8221; folder.</p>
<p>We&#8217;ll start with the most important.</p>
<h3>In and Out Sanitation</h3>
<h4>In Sanitation</h4>
<p>To prevent SQL injection, all we have to do is run input parameters through PHP&#8217;s <code>mysql_real_escape_string()</code> function. But that is a mouthful, and we may want to add onto it later. So let&#8217;s create a short-hand alias for this.</p>
<pre class="brush: php; title: /core/helpers/sanitationHelper.php;">
require_once('db.php');
function mres($value)
{
	return mysql_real_escape_string($value);
}
</pre>
<p>Can&#8217;t be simpler.</p>
<p>Now this gets a little complicated. Using this function we can create a single-point replacement for the mysql_query function which will incorporate our <code>mres()</code> function into all queries.</p>
<pre class="brush: php; title: /core/helpers/dbHelper.php;">
require_once('sanitation.php');
function query($query)
{
	$result = mysql_query($query) or die(&quot;&lt;b&gt;A fatal MySQL error occured&lt;/b&gt;.\n&lt;br /&gt;Query: &quot; . $query . &quot;&lt;br /&gt;\nError: (&quot; . mysql_errno() . &quot;) &quot; . mysql_error());
	return $result;
}
function q()
{
    $args = func_get_args();
    $var  = array_shift($args);
	array_walk($args,'mres');
    return query(vsprintf($var, $args));
}
</pre>
<p>The first function, <code>query()</code>, is a replacement for mysql_query which has error-handling built into it.</p>
<p>The second function, <code>q()</code>, was inspired from <a href="http://stackoverflow.com/questions/1606800/passing-variable-argument-list-to-sprintf">this StackOverflow question</a> and it applies the principle &#8220;Write Once, Run Anywhere&#8221;. It allow us to replace the <code>mysql_query()</code> and <code>sprintf</code> functions when dealing with the db. Queries with multiple inputs (updates/inserts) will be sanitized. Ones without will be handled normally. Beautiful and simple. Now a single letter can handle so much.</p>
<p>Our <code>db</code> class now looks like this:</p>
<pre class="brush: php; title: ;">
class db{
	/* POSTS */
	function posts_list()
	{
		$sql = 'SELECT * FROM `Posts`';
		return q($sql);
	}
	function posts_add($body, $poster)
	{
		$posterIP = $_SERVER['REMOTE_ADDR'];
		$sql = 'INSERT INTO `Posts` (body, poster, posterIP) VALUES (&quot;%s&quot;,&quot;%s&quot;,&quot;%s&quot;)';
		return q($sql, $body, $poster, $posterIP);
	}
}
</pre>
<p>It&#8217;s actually shorter while performing more actions!</p>
<h4>Out Sanitation</h4>
<p>In order to deal with #3 all user-facing data from the DB should be run through PHP&#8217;s <a href="http://php.net/manual/en/function.htmlentities.php">htmlentities()</a> function or <a href="http://www.php.net/manual/en/function.htmlspecialchars.php">htmlspecialchars()</a>. It is a matter of preference whether or not to do this before it enters the database or before it is displayed on the page. For this project I will use the before-it-is-displayed-on-the-page route.</p>
<p>It is important to know that running these functions with their default parameters is not always enough.  And so for this we will create an alias of sorts. A function which can be used with the parameters of htmlentities() filled in, and maybe a little shorter.</p>
<p>Some will fight me on this, but for the most commonly used functions in PHP, I like to award single-character function names. In this case we will use h(), (short for htmlentities).</p>
<p>htmlentities has a few parameters we must consider.</p>
<p>$flags &#8211; Defaults to <code>ENT_COMPAT</code> which is <a href="http://www.php.net/manual/en/function.htmlentities.php#99896">vulnerable to single-quote JavaScript insertion</a>. We use <code>ENT_QUOTES</code> instead.</p>
<p>$charset &#8211; Defaults to <code>ISO-8859-1</code> which for our purposes is fine. The default charset for MySQL is Latin-1 (a nicer name for ISO-8859-1), so unless you changed it while creating your table, this will work beautifully. Without getting into a huge discussion about character sets, localization, and language compatibility&#8230; and instead to but it briefly: MySQL&#8217;s standard Latin-1 charset works for <em>most </em>latin (read: western) languages. MySQL support unicode however for simplicity&#8217;s sake, we will use the standard Latin-1. If you plan to take your forum internationally, use utf8_general_ci or utf8_unicode_ci. Wow, that turned out to be not-so-brief.</p>
<p>$double_encode &#8211; Defaults to <code>true</code>. Depending on what you&#8217;re doing, you may want to set this to <code>false</code>. If you are worried about your form being re-submitted with already-encoded data, use false. If not, use true. In the case of true, submitting <code>&amp;</code> once will show as <code>&amp;amp;</code>. Submitting that will return <code>&amp;amp;amp;</code>, and so forth. Whereas if the setting is <code>true</code>, it will only ever return <code>&amp;</code>. <code>&amp;amp;</code> will return simply <code>&amp;</code>. For our sake, we&#8217;ll use the default, <code>true</code>.</p>
<p>For our new outward-facing html sanitation, we&#8217;ll create a new file called &#8220;sanitation.php&#8221; in /scripts/core/helpers/.</p>
<pre class="brush: php; title: /cre/helpers/sanitationHelper.php;">
&lt;?php
function h($value)
{
	$value = htmlentities($value,ENT_QUOTES,'ISO-8859-1', true);
	return str_replace(&quot;:&quot;, &quot;&amp;#58;&quot;, $value);
}
function hr($mysql_row)
{
	return array_walk(r($mysql_row),'h');
}
?&gt;
</pre>
<p>Now <code>h()</code> is our &#8220;out&#8221; sanitization function. It run output through <code>htmlentities()</code> first, then encode colons, which are not included in htmlentities, which could theoretically let some javascript through within attributes. </p>
<p>The second function, <code>hr()</code> will sanitize an entire row of values from the db. Again, now we have a couple letters performing a powerful actions. This is exactly what how we want to design our programs. A single letter performing perhaps dozens of actions for us. The easier we make our life the better.</p>
<h3>Input Validation</h3>
<p>Our last three issues fall under the umbrella of checking input for expected types of values. For example, if are expecting a number from a user, give them a nice friendly error message if they give any letters. If you expect 2-20 letter of input, give them a nice error if they give less than 2 or more than 20 characters. The trick in this is linking problems with friendly error messages.</p>
<p>Now bear with me, as we&#8217;ll be creating a class called <code>validator</code> and several helper functions to accompany it. We&#8217;ll then use these functions to error-check our user-input and then display friendly errors back to our user describing what is wrong.</p>
<pre class="brush: php; title: /core/util/validator.php;">
&lt;?php
namespace core\util;
class Validator
{
	private $errors = array();

	/* VALIDATION CHECKS */
	public function required($field, $value, $errorMessage)
	{
		if(empty($value))
		{
			$this-&gt;errors[$field][] = $errorMessage;
			return false;
		}
		return true;
	}
	function minLength($field, $value, $length, $errorMessage)
	{
		if(strlen($value)&lt;$length)
		{
			$this-&gt;errors[$field][] = sprintf($errorMessage,$length,strlen($field));
			return false;
		}
		return true;
	}
	public function maxLength($field, $value, $length, $errorMessage)
	{
		if(strlen($value)&gt;$length)
		{
			$this-&gt;errors[$field][] = sprintf($errorMessage,$length,strlen($field));
			return false;
		}
		return true;
	}

	/* UTIL */
	public function hasErrors()
	{
		if(func_num_args()==0)
			return (count($this-&gt;errors, COUNT_RECURSIVE) &gt; 0);
		return isset($this-&gt;errors[func_get_arg(0)]);
	}
	public function getErrors()
	{
		if(func_num_args()==0)
			return $this-&gt;errors;
		if(!isset($this-&gt;errors[func_get_arg(0)]))
			return array();
		return $this-&gt;errors[func_get_arg(0)];
	}
	public function numErrors()
	{
		if(func_num_args()==0)
			return count($this-&gt;errors, COUNT_RECURSIVE)-count($this-&gt;errors, COUNT_NORMAL);
		if(!isset($this-&gt;errors[func_get_arg(0)]))
			return 0;
		return count($this-&gt;errors[func_get_arg(0)]);
	}
}

?&gt;
</pre>
<p>This new class contains 3 functions used to validate inputs&#8230; that they must not be empty, that they have at least so many characters, that they must not have too many characters. If it does not pass the test, an error message is stored in an associative array binding it with the field name.</p>
<p>This class has another 3 functions used for checking if any errors have been logged, how many errors, retrieving those errors. I use <code>func_num_args()</code> and <code>func_get_arg()</code> to overload these functions to work when looking for all errors or just errors for a specific field.</p>
<p>Next, knowing that I&#8217;ll be working with $_GET[] and $_POST[] values a lot more in the future, I started a file for helpers for this.</p>
<pre class="brush: php; title: /core/helpers/formHelper.php;">
&lt;?php
function get($name){
	if(isset($_GET[$name]))
		return $_GET[$name];
	return NULL;
}
function post($name){
	if(isset($_POST[$name]))
		return $_POST[$name];
	return NULL;
}
?&gt;
</pre>
<p>Now we&#8217;ll add in some validation rules to start of our index file. We also have a bunch more includes to require. Soon we may break these off into another file, but for now we&#8217;ll deal.</p>
<pre class="brush: php; title: /index.php;">
&lt;?php
date_default_timezone_set('UTC');
require_once 'scripts/settings/config.php';
require_once 'scripts/data/db.php';
require_once 'scripts/core/helpers/sanitationHelper.php';
require_once 'scripts/core/helpers/formHelper.php';
require_once 'scripts/core/util/validator.php';
require_once 'scripts/ui/validation.php';

$validate = new \core\util\Validator;

/* Action Handler */
if(isset($_GET['a']))
{
	$action = $_GET['a'];
	if($action == 'newpost')
	{
		$body = post('body');
		$poster = post('poster');

		$validate-&gt;minLength('body', $body, 10, 'You must provide a message at least 10 characters long.');
		$validate-&gt;maxLength('body', $body, 500, 'Please keep the message under 500 characters.');

		$validate-&gt;required('poster', $poster, 'You must provide a name');
		$validate-&gt;maxLength('poster', $poster, 30, 'Your name cannot be over 30 characters long');

		//do our action
		if(!$validate-&gt;hasErrors())
		{
			echo print_r($validate-&gt;getErrors());
			\data\db::posts_add($body, $poster);
			header('Location: index.php');
			exit();
		}
	}
}
</pre>
<p>Notice how easy it is to add errors now. It&#8217;s basically just a list of definitions.</p>
<p>If you try to submit invald values now, it will not add them to the Guestbook&#8230; but neither will it alert the user. Let&#8217;s do that now.</p>
<p>Again, knowing we&#8217;ll likely be doing this repeatedly now, and using it a great deal more in the future, I&#8217;m going to create a UI file and namespace to help us out. But just a single UI file would get bloated as it is a very broad area. Our needs fall under UI/Validation.</p>
<pre class="brush: php; title: /ui/validation.php;">
namespace ui\validation;
function FieldErrors1($errors)
{
	if(count($errors)&gt;0)
	{
		echo '&lt;ul class=&quot;errors&quot;&gt;';
		foreach($errors as $error)
		{
			echo '&lt;li&gt;',$error,'&lt;/li&gt;';
		}
		echo '&lt;/ul&gt;';
	}
}
</pre>
<p>All the UI namespace and files will do is handle formatting. Every segment of the webpage is repeated and self-contained should be placed there to allow use to sort of &#8220;widgetize&#8221; our site&#8230; make many pieces of it inter-changable and easy usable across multiple pages. Soon we will add more functions to this area, but for now <code>FieldErrors1</code> is all.</p>
<p>Now we can update the <code>index.php</code></p>
<pre class="brush: php; title: /index.php;">
&lt;!doctype html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
	&lt;title&gt;GuestBook Test&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;?php
if($numResults == 0)
{
	echo 'No messages';
}
else
{
	for($i=0;$i&lt;$numResults;$i++)
	{
		$r = mysql_fetch_assoc($result);
		echo '&lt;b&gt;',h($r['poster']),'&lt;/b&gt; at ',h($r['postedDate']);
		echo '&lt;p&gt;',h($r['body']),'&lt;/p&gt;&lt;hr /&gt;';
	}
}
?&gt;
	&lt;form action=&quot;index.php?a=newpost&quot; method=&quot;post&quot;&gt;
		&lt;p&gt;
			&lt;label for=&quot;poster&quot;&gt;Poster: &lt;/label&gt;&lt;br /&gt;
			&lt;input id=&quot;poster&quot; name=&quot;poster&quot; type=&quot;text&quot; value=&quot;&lt;?php echo post('poster') ?&gt;&quot; /&gt;&lt;?php ui\validation\FieldErrors1($validate-&gt;getErrors('poster')); ?&gt;
		&lt;/p&gt;
		&lt;p&gt;
			&lt;label for=&quot;body&quot;&gt;Message: &lt;/label&gt;&lt;br /&gt;
			&lt;textarea id=&quot;body&quot; name=&quot;body&quot; cols=&quot;30&quot; rows=&quot;5&quot;&gt;&lt;?php echo post('body') ?&gt;&lt;/textarea&gt;&lt;?php ui\validation\FieldErrors1($validate-&gt;getErrors('body')); ?&gt;
		&lt;/p&gt;
		&lt;p&gt;
			&lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
		&lt;/p&gt;
	&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Done! Now test it out! See how it gives pretty errors whenever invalid input is given, and submits a message when the input looks good.</p>
<p><img src="/images/forum-0.1.1-ss6.gif" alt="" /></p>
<h3>Wrap-Up</h3>
<p>It doesn&#8217;t look like much. We haven&#8217;t even created a stylesheet yet. But that will come in due time. There is little point in creating one at this stage as we still have tons of features to add which will drastically change the page. So for now we&#8217;ll stick to functionality over style.</p>
<p>You may have noticed we left out problem/exploit #5&#8211;spamming. Don&#8217;t worry, I haven&#8217;t forgotten about it. However, we&#8217;ll leave that for another time as its not really critical just yet.</p>
<p>Get the complete code here:<br />
<a href="/files/forum-0.1.1-sanivali.zip">forum-0.1.1-sanivali.zip</a> (5.48 KB)</p>
<p>243 lines of code<br />
8 files</p>
<p><b><em>Next: In <a href="http://www.spencerdrager.com/2011/02/08/creating-a-php-forum-a-guided-tour-part-3-topics/">Part 3</a> of Creating a PHP Forum: A Guided Tour, we will add in &#8220;threads&#8221;&#8230; separate areas or conversations to post in, adding just a little more complexity to the program/website.</em></b></p>
]]></content:encoded>
			<wfw:commentRss>http://www.spencerdrager.com/2011/02/03/creating-a-php-forum-a-guided-tour-part-2-validation-and-sanitizatio/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creating a PHP Forum: A Guided Tour – Part 1: Guestbook</title>
		<link>http://www.spencerdrager.com/2011/02/03/creating-a-php-forum-a-guided-tour-part-1-guestbook/</link>
		<comments>http://www.spencerdrager.com/2011/02/03/creating-a-php-forum-a-guided-tour-part-1-guestbook/#comments</comments>
		<pubDate>Thu, 03 Feb 2011 11:32:45 +0000</pubDate>
		<dc:creator>Spencer</dc:creator>
				<category><![CDATA[Creating a PHP Forum]]></category>
		<category><![CDATA[How To]]></category>
		<category><![CDATA[Forum]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.spencerdrager.com/?p=805</guid>
		<description><![CDATA[This is the first in a walk through of my trip creating a simple PHP forum from scratch. I&#8217;m creating this &#8220;forum&#8221; as a way to demonstrate how to create an application using a simple an agile methodology, creating simple components which work by themselves, then tying them together to form something better. I&#8217;ll be [...]]]></description>
			<content:encoded><![CDATA[<p>This is the first in a walk through of my trip creating a simple PHP forum from scratch. I&#8217;m creating this &#8220;forum&#8221; as a way to demonstrate how to create an application using a simple an agile methodology, creating simple components which work by themselves, then tying them together to form something better.</p>
<p>I&#8217;ll be guiding you through my process as each post will represent a separate component that builds on those before it. This first post will be a simple guestbook not requiring any authentication or threading. It is the first and most fundamental piece of a forum.</p>
<p><span id="more-805"></span></p>
<p>To get started you will need a PHP and MySQL setup. I am using a <a href="http://en.wikipedia.org/wiki/WAMP">WAMP</a> setup on my home machine, which is ideal for development as it is the shortest path between development and testing, allowing you to test quickly and easily and facilitating debugging. Setting such an environment is beyond the scope of this article, but I trust you know how to use google if you do not have a setup available.</p>
<p><strong>Guestbook</strong></p>
<p>By the time this post is over, we will have a working guestbook, where visitors will be able to leave a post consisting of their name and a message. We will also log their IP address and date of posting to make it easy to see if the same person has left multiple messages.</p>
<p>Since we already have a clear idea of this feature, I will begin with the database back-end. It consists of a single table which will containing a couple fields.</p>
<h3>Setting up the database</h3>
<p>Before we can create the table, we need to set up a database and user for the database. For this I am using MySQL Administrator. Never use the database root/admin user as the user for development/deployment.</p>
<p>1. Create a new Schema</p>
<p>I&#8217;ll name mine &#8220;sd_forum&#8221;.</p>
<p>2. Create a new user.</p>
<p>I&#8217;ll name mine &#8220;sd_forum_user&#8221;.</p>
<p>3. Give the user privileges to the database.</p>
<p>I&#8217;ll give the user full privileges except for &#8220;grant&#8221; for now. If this were to be deployed, I would remove any unnecessary privileges, such as the ability to drop tables.</p>
<p><strong>Create the table</strong></p>
<p>Now I will switch to my new &#8220;sd_forum_user&#8221; user.</p>
<div style="background: #ffc;">
<p>Conventions</p>
<ul>
<li>All fields use camelCase. First letter is lower case, capitalize the first letter of future words.</li>
<li>All tables are plural, and use camel case with a capital starting letter.</li>
<li>All table in singular form, camelcase with lower-case first letter, ID in caps.</li>
</ul>
</div>
<p>&#8220;Users&#8221; -&gt; &#8220;userID&#8221;<br />
&#8220;Topics&#8221; -&gt; &#8220;topicID&#8221;<br />
&#8220;CrazyUkranians&#8221; -&gt; &#8220;crazyUkranianID&#8221;</p>
<p>The table we are creating is named &#8220;Posts&#8221;.</p>
<pre class="brush: sql; title: ;">
CREATE TABLE Posts
(
postID INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
body TEXT NOT NULL,
poster VARCHAR(50) NOT NULL,
postedDate DATETIME NULL,
posterIP VARCHAR(15) NOT NULL
);
</pre>
<p>Now, the reason we allow postedDate to be null is to be fancy, we&#8217;ll make the default value of postedDate to be NOW(). <a href="http://bugs.mysql.com/bug.php?id=27645">An inconvenient limitation of MySQL</a> is its inability to set a default for DateTime fields to NOW(). Instead we&#8217;ll throw in a trigger to do this for us.</p>
<pre class="brush: sql; light: true; title: ;">CREATE TRIGGER Posted_postedDate_init BEFORE INSERT ON Posts FOR EACH ROW SET NEW.PostedDate = NOW();</pre>
<p>Now we can try inserting a value into our brand-spankin&#8217;-new table.</p>
<pre class="brush: sql; light: true; title: ;">INSERT INTO Posts (body, poster, posterIP) VALUES ('Hello world!', 'Myself', '127.0.0.1');</pre>
<p>And then see what we have in our table.</p>
<pre class="brush: sql; light: true; title: ;">SELECT * FROM Posts;</pre>
<p>There&#8217;s our first row, pre-populated with an ID and a postedDate! Well done.</p>
<h3>The PHP</h3>
<p>Now down to Brass Tacks. Lettus (I&#8217;ve always wanted to use that horrible pun&#8230; why is it funny, again?) start with a relatively simple, but well defined structure.</p>
<p>This entire project will be help in a folder called &#8220;forum&#8221;, away from the http-server root. Within that I&#8217;ll create a <code>index.php</code> file, and a <code>/scripts</code> folder. Within that, create a <code>data</code> and a settings folder. The settings folder will hold another php file, <code>config.php</code>. The data folder will hold <code>db.php</code>.</p>
<p>We&#8217;ll begin working on the config file. Here we&#8217;ll store all of our settings in nice, clearly-define <a href="http://www.php.net/manual/en/language.namespaces.php">namespaces</a> (thank you PHP 5.3.5).</p>
<p><strong>config.php</strong></p>
<p>For convention&#8217;s sake, all constants are in all-caps. I use namespaces and classes for purely organizational purposes. You could easily this and almost anything else in PHP without them. PHP is truely a scripting language and as a result OOP principals are in many ways optional, however they are still useful.</p>
<p>For now our config files simply holds values for our MySQL connection.</p>
<pre class="brush: php; title: ;">
&lt;?php
namespace settings\config\db;
const SERVER = 'localhost';
const SCHEMA = 'sd_forum';
const USERNAME = 'sd_forum_user';
const PASSWORD = '**your_password**';
?&gt;
</pre>
<p><strong>db.php</strong></p>
<p>The namespaces, if you haven&#8217;t figured it out, are the same as the folder path of the file.</p>
<p>First we&#8217;ll create a MySQL connection. We are creating a connection regardless of whether or not we actually use it. Every page load will make a connection if this file is included. For our purposes that is fine as pretty much every page will use the DB.</p>
<p>Start with two simple functions; one for insertion and one for retrieval of our posts. There is little data validation or sanitation here outside of using sprintf. That will come in a later module.</p>
<pre class="brush: php; title: ;">
&lt;?php
namespace data;

mysql_connect(
  \settings\config\db\SERVER,
  \settings\config\db\USERNAME,
  \settings\config\db\PASSWORD) or die(mysql_error());
mysql_select_db(\settings\config\db\SCHEMA) or die(mysql_error());

class db{
  /* POSTS */
  function posts_list(){
    $sql = 'SELECT * FROM `Posts`';
    $result = mysql_query($sql) or die(mysql_error() . '&lt;br /&gt;&lt;b&gt;SQL:&lt;/b&gt; ' . $sql);
    return $result;
  }
  function posts_add($body, $poster){
    $posterIP = $_SERVER['REMOTE_ADDR'];
    $sql = 'INSERT INTO `Posts` (body, poster, posterIP) VALUES (&quot;%s&quot;,&quot;%s&quot;,&quot;%s&quot;)';
    $sql = sprintf($sql, $body, $poster, $posterIP);
    mysql_query($sql) or die(mysql_error() . '&lt;br /&gt;&lt;b&gt;SQL:&lt;/b&gt; ' . $sql);
  }
}
?&gt;
</pre>
<p><strong>index.php</strong></p>
<p>Finally, we&#8217;ll set up our index.php to list out all the posts in our database.</p>
<pre class="brush: php; title: ;">
&lt;?php
  date_default_timezone_set('UTC');
  require 'scripts/settings/config.php';
  require 'scripts/data/db.php';
  $result = \data\db::posts_list();
  $numResults = mysql_num_rows($result);
?&gt;
&lt;!doctype html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
	&lt;title&gt;GuestBook Test&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;?php
if($numResults == 0)
{
  echo 'No messages';
}
else
{
  for($i=0;$i&amp;lt;$numResults;$i++)
  {
    $r = mysql_fetch_assoc($result);
    echo '&lt;b&gt;',$r['poster'],'&lt;/b&gt; at ',$r['postedDate'];
    echo '&lt;p&gt;',$r['body'],'&lt;/p&gt;&lt;hr /&gt;';
  }
}
?&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>If you followed the instructions so far, and inserted an entry into the table, you&#8217;ll see that post now. If not, you&#8217;ll see &#8220;No Messages&#8221;. We&#8217;ll always test retrieval first because it&#8217;s almost always easier and it allows it to immediately see the results when we make &#8220;insert&#8221; functionality.</p>
<h4>Creating a Form</h4>
<p>Now to make our own posts on this page, we need two things: a form, for the user, and a script to handle the user&#8217;s input.</p>
<p>Since our table is simple, so is our form.</p>
<pre class="brush: xml; title: ;">
	&lt;form action=&quot;index.php?a=newpost&quot; method=&quot;post&quot;&gt;
		&lt;p&gt;
			&lt;label for=&quot;poster&quot;&gt;Poster: &lt;/label&gt;&lt;br /&gt;
			&lt;input id=&quot;poster&quot; name=&quot;poster&quot; type=&quot;text&quot; /&gt;
		&lt;/p&gt;
		&lt;p&gt;
			&lt;label for=&quot;body&quot;&gt;Message: &lt;/label&gt;&lt;br /&gt;
			&lt;textarea id=&quot;body&quot; name=&quot;body&quot; cols=&quot;30&quot; rows=&quot;5&quot;&gt;&lt;/textarea&gt;
		&lt;/p&gt;
		&lt;p&gt;
			&lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
		&lt;/p&gt;
	&lt;/form&gt;
</pre>
<p>Only two fields and a submit button. You may notice that the form submits back to the same page, with a &#8220;?a=newpost&#8221; parameter. We will be using the get value &#8220;a&#8221; to let our program know what is expected of it. This way any page can have multiple actions without getting confusing.</p>
<p>Next we&#8217;ll write a script to handle the data the user posts to our page. The following script will appear on the index.php after the requires and before it calls posts_list().</p>
<pre class="brush: php; title: ;">
/* Action Handler */
if(isset($_GET['a']))
{
  $action = $_GET['a'];
  if($action == 'newpost')
  {
    $body = $_POST['body'];
    $poster = $_POST['poster'];
    \data\db::posts_add($body, $poster);
    header('Location: index.php');
    exit();
  }
}
</pre>
<p>If action (&#8220;a&#8221;) is not set, our actions are skipped over. When the action is &#8220;newpost&#8221; our insert script is performed and then the page redirects to itself to avoid any messy browser refresh re-posts. There is no validation here, so the user could put almost anything into those fields and they would come visible to everyone. We will fix that in the future, but for now this is plenty.</p>
<p><img src="http://www.spencerdrager.com/images/forum-0.1.0-ss1.gif" alt="" class="right" /></p>
<p>We&#8217;re done! Try it out. Create some posts and see as they are added to the list of posts displayed on the page. Step 1 complete!</p>
<p>Get the completed code here: <a href="/files/forum-0.1.0-guestbook.zip">forum-0.1.0-guestbook.zip</a> (1.77 KB)</p>
<p>92 lines of code<br />
3 files</p>
<p><strong><em>Next: <a href="http://www.spencerdrager.com/2011/02/03/creating-a-php-forum-a-guided-tour-part-2-validation-and-sanitizatio/">Part 2</a> will show you all the security holes our guestbook has and how we can seal and spackle those holes.</em></strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.spencerdrager.com/2011/02/03/creating-a-php-forum-a-guided-tour-part-1-guestbook/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>YetAnotherForm (YAF) Theming and Layout Customization</title>
		<link>http://www.spencerdrager.com/2010/05/13/yetanotherform-yaf-themeing-and-layout-customization/</link>
		<comments>http://www.spencerdrager.com/2010/05/13/yetanotherform-yaf-themeing-and-layout-customization/#comments</comments>
		<pubDate>Fri, 14 May 2010 02:12:51 +0000</pubDate>
		<dc:creator>Spencer</dc:creator>
				<category><![CDATA[How To]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Customization]]></category>
		<category><![CDATA[MSSQL]]></category>
		<category><![CDATA[Skinning]]></category>
		<category><![CDATA[Theming]]></category>
		<category><![CDATA[YAF]]></category>
		<category><![CDATA[YetAnotherForum]]></category>

		<guid isPermaLink="false">http://www.spencerdrager.com/?p=748</guid>
		<description><![CDATA[YetAnotherForum (YAF — pronounce &#8220;laugh&#8221;) is a forum package based on ASP.NET (C#) and Microsoft SQL Server. It has tons of features and functionality, and it use my , but when tasked with customizing it&#8230; well, that can be not quite so easy. Creating a custom Theme/Skin for YAF Skinning YetAnotherForum is kind of self [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.yetanotherforum.net/">YetAnotherForum</a> (YAF — pronounce &#8220;laugh&#8221;) is a forum package based on ASP.NET (C#) and Microsoft SQL Server. It has tons of features and functionality, and it use my , but when tasked with customizing it&#8230; well, that can be not quite so easy.<br />
<span id="more-748"></span></p>
<h2>Creating a custom Theme/Skin for YAF</h2>
<p>Skinning YetAnotherForum is kind of self evident. Just copy an existing theme folder and XML file from the <code>themes</code> folder. All there is to it is a theme.css file in the folder (and a bunch of images) and the settings in the XML file. Done.</p>
<h2>Modifying the Layout of YAF</h2>
<p>For as easy as changing the theme of YAF is, changing the actual layout is a time-consuming and involved process. And there are a few things which are not as evident from quick google searches or examining the code. So take a lesson from me and save yourself hours of time.</p>
<h3>1. You cannot easily remove elements by commenting out the C#</h3>
<p>Almost any main &lt;YAF:Element&gt; you try to comment out will trigger errors when you try to compile due to dependent blocks elsewhere. And the source of the errors may not always be apparent. You may be able to fix those errors, but chances are it will not be worth your effort. This is a pain because depending on what you want to do you may want to eliminate some elements from showing. This may be typical for ASP.NET, but coming from PHP it prevented a learning curve. Many things you may want to remove can be removed by changing BoardSettings. (See next tip)</p>
<h3>2. YafContext.Current.BoardSettings are stored in the database</h3>
<p>Once you start investigating the depths of the page and class files of YAF, you will eventually start noticing how many part of the board are in hide/show if blocks based on a value of YafContext.Current.BoardSettings.[something]. Other parts of the forum look to these settings for things.</p>
<p>I can&#8217;t tell you how many searches I made trying to find what file BoardSettings are stored in. I still don&#8217;t know if there is a file with defaults. I didn&#8217;t find it despite much searching.</p>
<p>These settings can be changed in the database under the <code>Registry</code> table. Easy peasy? I got thrown off because many of the settings are not in the table. If they are not in the table, you will have to insert a row. The &#8220;value&#8221; field is a string, so numbers and boolean values are stored just as their string equivalent. i.e. a true value can be stored as the string &#8220;true&#8221;.</p>
<h3>3. The logical structure of the code is as follows</h3>
<p>This may seem elementary, but I&#8217;m new to ASP.NET and working with modifying larger(ish) packages. It would have been helpful to me to have a diagram or even just a quick explanation of the layout of what calls what. <img class="alignleft size-full wp-image-764" style="margin: 0px; border: 0px;" title="YAF-diagram" src="http://www.spencerdrager.com/wp-content/uploads/2010/05/YAF-diagram2.png" alt="YAF file structure diagram" width="640" height="514" /><br />
As you can see, any single page may reference a dozen or two files. This can be a serious pain, as those .cs files in the YAF.Controls.dll are in my experience, very tedious to edit. I didn&#8217;t realize I neede the SRC version until I realized I could not access TopicLine.cs, which is necessary to change how the single line each topic is displayed as when you look at a single forum.</p>
<p>And to be clear: There are many more pages, controls, and elements within Controls.dll that aren&#8217;t shown here. Also, there may be some exceptions to the hierarchy shown here.</p>
<h3>4. CSS Class names do not always describe what they refer to</h3>
<p>With class names like .header1, .header1Title, and .rightItem, it is not always easy to tell what element these CSS classes are referring to. This can be particularly challenging when you want to edit what they are referred to and you can&#8217;t locate which source file the element it is referring to spawns in. This is more of a heads up than an actual tip.</p>
<h2>Enjoy, and Contribute</h2>
<p>That&#8217;s all the tips I have for you today about YetAnotherForum. In my one-day&#8217;s experience with them, I found it to be a little frustrating, however now that I am more familiar with it I feel more confident in the likelihood I would be able to switch things up more rapidly. I hope these tips help other newbies trying to do what I was doing.</p>
<p>If you have any tips/tricks of your own you&#8217;d like to contribute, please feel free to share and comment.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.spencerdrager.com/2010/05/13/yetanotherform-yaf-themeing-and-layout-customization/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>ASP.NET: Connecting to MSSQL Server using Windows Authentication/Credentials</title>
		<link>http://www.spencerdrager.com/2010/05/12/asp-net-connecting-to-mssql-server-using-windows-authenticationcredentials/</link>
		<comments>http://www.spencerdrager.com/2010/05/12/asp-net-connecting-to-mssql-server-using-windows-authenticationcredentials/#comments</comments>
		<pubDate>Thu, 13 May 2010 00:56:42 +0000</pubDate>
		<dc:creator>Spencer</dc:creator>
				<category><![CDATA[How To]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[ASP]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[Connecting to Database]]></category>
		<category><![CDATA[Microsoft SQL Server]]></category>
		<category><![CDATA[MSSQL]]></category>

		<guid isPermaLink="false">http://www.spencerdrager.com/?p=742</guid>
		<description><![CDATA[I am learning ASP and ASP.NET for my new job, and today I was working on manipulating a Microsoft SQL database (SCUD) when I ran into a roadblock. I knew I had to use my own user credentials to connect to the secret through ASP.NET, something I thought would be a very simple matter. Well [...]]]></description>
			<content:encoded><![CDATA[<p>I am learning ASP and ASP.NET for my new job, and today I was working on manipulating a Microsoft SQL database (SCUD) when I ran into a roadblock. I knew I had to use my own user credentials to connect to the secret through ASP.NET, something I thought would be a very simple matter. Well after fifteen minutes of attempting to work out why I couldn&#8217;t connect with my username and password (trying variations of the domain/myusername) I finally discovered the MSDN page I needed. Yes, it already exists there, but the fact that it took me so long to find what  I needed means it can&#8217;t hurt to refer to it and reinforce it&#8217;s presence on the web.</p>
<p><span id="more-742"></span></p>
<p>The full MSDN article is <a href="http://msdn.microsoft.com/en-us/library/ff647396.aspx">here</a>. I will summarize the multi-page verbose article with the two important points that you need to make the connection.</p>
<h3>1. Modifying your <code>web.config</code></h3>
<p>Replace &lt;connectionStrings /&gt; with one of the following &lt;add /&gt;&#8217;s:</p>
<pre class="brush: php; title: ;">﻿
&lt;connectionStrings&gt;
  &lt;add name=&quot;MyDbConn1&quot;
       connectionString=&quot;Server=MyServer;Database=MyDb;Trusted_Connection=Yes;&quot;/&gt;
  &lt;add name=&quot;MyDbConn2&quot;
      connectionString=&quot;Initial Catalog=MyDb;Data Source=MyServer;Integrated Security=SSPI;&quot;/&gt;
&lt;/connectionStrings&gt;
&lt;/pre&gt;
</pre>
<p>And obviously, change the MyServer to your server&#8217;s name (i.e. <code>localhost</code> or <code>172.0.0.1</code> or whatever address.), change MyDb to the name of the database you want to use in the server. Simple!</p>
<h3>2. Open the connection in your ASP script</h3>
<p>Use the following code wherever in your script.</p>
<p>At the top of your page, you need to make the include to the framework API:</p>
<pre name="code" class="c-sharp">
&lt;%@ Import Namespace="System.Data.SqlClient" %&gt;</pre>
<p>And then you&#8217;re free to use the following to connect when you need to. This is in C#.</p>
<pre name="code" class="c-sharp">
string connStr = ConfigurationManager.ConnectionStrings["MyDbConn1"].ToString();
SqlConnection conn = new SqlConnection(connStr);
</pre>
<p>And walah! You should be connected.</p>
<p>More short articles will follow further explaining performing queries and outputting data.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.spencerdrager.com/2010/05/12/asp-net-connecting-to-mssql-server-using-windows-authenticationcredentials/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Launching of site: Zombie Attack Plans</title>
		<link>http://www.spencerdrager.com/2010/05/12/launching-of-site-zombie-attack-plans/</link>
		<comments>http://www.spencerdrager.com/2010/05/12/launching-of-site-zombie-attack-plans/#comments</comments>
		<pubDate>Wed, 12 May 2010 10:50:10 +0000</pubDate>
		<dc:creator>Spencer</dc:creator>
				<category><![CDATA[My Sites]]></category>
		<category><![CDATA[website launch]]></category>
		<category><![CDATA[ZombieAttackPlans.com]]></category>

		<guid isPermaLink="false">http://www.spencerdrager.com/?p=739</guid>
		<description><![CDATA[A few days ago I launch, for the first time, ZombieAttackPlans.com. The site is for users to submit their plans for surviving the zombie apocalypse. If you have some idea of what you want to do, come post it! If you don&#8217;t, come and read others&#8217; plans. Leave feedback, rate plans, vote in the Poll [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.spencerdrager.com/wp-content/uploads/2010/05/zombie.png" alt="" title="zombie" width="600" height="152" class="alignleft size-full wp-image-769" /></p>
<p>A few days ago I launch, for the first time, <a href="http://www.ZombieAttackPlans.com/">ZombieAttackPlans.com</a>. The site is for users to submit their plans for surviving the zombie apocalypse. If you have some idea of what you want to do, come post it! If you don&#8217;t, come and read others&#8217; plans. Leave feedback, rate plans, vote in the Poll of the Week, and read the Tip of the Day (updated daily, as per the name).</p>
<p> Please check it out and leave me some feedback on the site. I want to know how I can make it better. What do you like? And more importantly, what don&#8217;t you like?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.spencerdrager.com/2010/05/12/launching-of-site-zombie-attack-plans/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Safely Letting Specific HTML Tags Through Sanitization in PHP</title>
		<link>http://www.spencerdrager.com/2010/04/20/safely-letting-specific-html-tags-through-sanitization-in-php/</link>
		<comments>http://www.spencerdrager.com/2010/04/20/safely-letting-specific-html-tags-through-sanitization-in-php/#comments</comments>
		<pubDate>Wed, 21 Apr 2010 03:49:53 +0000</pubDate>
		<dc:creator>Spencer</dc:creator>
				<category><![CDATA[How To]]></category>
		<category><![CDATA[Character Entities]]></category>
		<category><![CDATA[HTML Entities]]></category>
		<category><![CDATA[input sanitization]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[User Input]]></category>

		<guid isPermaLink="false">http://www.spencerdrager.com/?p=720</guid>
		<description><![CDATA[Sometimes you want to let your users express themselves and style their input—whether it be comments, stories, or whatever else—with a few HTML tags. The trick is doing this without letting through all sorts of bad mojo. Now there are many ways to do this, some more complicated than others. I&#8217;ve devised a fool-proof accomplish [...]]]></description>
			<content:encoded><![CDATA[<p>Sometimes you want to let your users express themselves and style their input—whether it be comments, stories, or whatever else—with a few HTML tags. The trick is doing this without letting through all sorts of bad mojo. Now there are many ways to do this, some more complicated than others. I&#8217;ve devised a fool-proof accomplish this. While this can work with any tag, (i.e. making [b] into &lt;b&gt;), in this example I&#8217;ll be selectively letting through actual HTML tags, rather than aliases. I like to think that by letting users use real HTML tags I might one day help a computer semi-literate learn the fundamentals of HTML. Who knows?<br />
<span id="more-720"></span><br />
As with most mini-tutorials, I&#8217;ll start with what <em>doesn&#8217;t</em> work.</p>
<h2>What doesn&#8217;t work</h2>
<h3>1. <a href="http://php.net/manual/en/function.strip-tags.php">strip_tags</a></h3>
<p>Strip tags is bad for a few reasons. First, it ends up <em>removing </em>content, and sometimes more than you would think. That is not the goal of [textfield] sanitation. Sanitation is meant to prevent mischief while preserving the integrity of the user&#8217;s message. Secondly, and more importantly:</p>
<blockquote><p>This function does not modify any attributes on the tags that you allow using <em><tt>allowable_tags</tt></em>, including  the <em>style</em> and <em>onmouseover</em> attributes that a mischievous user may abuse when posting text that will be  shown to other users.</p></blockquote>
<p>That is bad for you, bad for me.</p>
<h3>2. <a href="http://php.net/manual/en/function.preg-replace.php">preg_replace</a> (or any other regular expression function)</h3>
<p>Regular expression is great for some things, but trying to create regex algorithms that will accurately do what you want here is not likely, and even if it does, it will be overly-complicated, time consuming, and not quickly modifiable  if you want to allow more tags. Basically, it&#8217;s overkill.</p>
<p>So what <em>is</em> a good way to let certain HTML tags through?</p>
<h2>The Right Way</h2>
<p>The right way is simple and maintains the integrity of the original input by first making a clean sweep, and then going back and choosing what to let through. Here is the code.</p>
<pre>function sanitize_out($input){
  htmlentities($input);
  $c_p_open=0;
  $c_p_close=0;
  $c_b_open=0;
  $c_b_close=0;
  $input = str_replace('&amp;lt;br&amp;gt;','&lt;br /&gt;',$input);
  $input = str_replace('&amp;lt;hr&amp;gt;','&lt;hr /&gt;',$input);
  $input = str_replace('&amp;lt;p&amp;gt;','&lt;p&gt;',$input,&amp;$c_p_open);
  $input = str_replace('&amp;lt;/p&amp;gt;','&lt;/p&gt;',$input,&amp;$c_p_close);
  $input = str_replace('&amp;lt;b&amp;gt;','&lt;b&gt;',$input,&amp;$c_b_open);
  $input = str_replace('&amp;lt;/b&amp;gt;','&lt;/b&gt;',$input,&amp;$c_b_close);
  while($c_p_open &gt; $c_p_close){
    $input .= '&lt;/p&gt;';
    ++$c_p_close;
  }
  while($c_b_open &gt; $c_b_close){
    $input .= '&lt;/b&gt;';
    ++$c_b_close;
  }
  return $input;
}
</pre>
<p>Now the code here could be cleaner, but it gets the job done.</p>
<h3>What is it doing?</h3>
<p>1. Convert all brackets and other XML-important characters to their corresponding entities with htmlentities().<br />
2. Back-convert specific entities into their HTML counterparts. In this case; &lt;p&gt; &lt;/p&gt; &lt;b&gt; &lt;/b&gt; &lt;br&gt; and &lt;hr&gt;.<br />
3. Closes all hanging tags, so that there will be a &lt;/b&gt; for every &lt;b&gt; and a  &lt;/p&gt; for every &lt;p&gt;.</p>
<p>Next time you want to do this type of input formatting, do it this way, rather than using overly-complicated eregs or some other expression.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.spencerdrager.com/2010/04/20/safely-letting-specific-html-tags-through-sanitization-in-php/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Universal Before and After Input Santization in PHP with mysql_real_escape_string and stripslashes</title>
		<link>http://www.spencerdrager.com/2010/04/18/universal-before-and-after-input-santizatio-with-mysql_real_escape_string-and-stripslashes/</link>
		<comments>http://www.spencerdrager.com/2010/04/18/universal-before-and-after-input-santizatio-with-mysql_real_escape_string-and-stripslashes/#comments</comments>
		<pubDate>Mon, 19 Apr 2010 03:26:28 +0000</pubDate>
		<dc:creator>Spencer</dc:creator>
				<category><![CDATA[How To]]></category>
		<category><![CDATA[input]]></category>
		<category><![CDATA[input sanitization]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[sanitization]]></category>
		<category><![CDATA[SQL injection]]></category>

		<guid isPermaLink="false">http://www.spencerdrager.com/?p=713</guid>
		<description><![CDATA[Input sensitization can be a tricky thing. None (or too little) and you can find yourself a victim of the notorious SQL injection attack. Databases wiped out, system penetrated&#8230; that is not the place you want to see yourself, as you data is highly valuable and any leak can represent a major catastrophy&#8211;and possibly even [...]]]></description>
			<content:encoded><![CDATA[<p>Input sensitization can be a tricky thing. None (or too little) and you can find yourself a victim of the notorious SQL injection attack. Databases wiped out, system penetrated&#8230; that is not the place you want to see yourself, as you data is highly valuable and any leak can represent a major catastrophy&#8211;and possibly even lead to you losing your precious job. Too little and you&#8217;ll be faced with over-slashing, where escaped characters get escaped a second time, along with that escaping slash. It looks messy and poorly-coded.</p>
<p><code>that's cool</code> &#8211; Original input<br />
<code>that\'s cool</code> &#8211; First sensitization (single apostrophe escaped)<br />
<code>that\\\'s cool</code> &#8211; Second sanitization (single apostrophe and backslash both escaped)</p>
<p>It only gets worse from here.</p>
<p>Doing input sensitization on a per-line basis is sloppy and inefficient. It is asking for you to slip up and forget to sanitize. After all, you are only human. So why not save yourself the trouble and do a universal sensitization of all user input at the beginning of your code? Use this follow PHP code at the very beginning of your script to save yourself a lot of trouble.<br />
<span id="more-713"></span></p>
<pre>$_GET = sanitize($_GET);
$_POST = sanitize($_POST);
$_COOKIE = sanitize($_COOKIE);
$_REQUEST = sanitize($_REQUEST);

function sanitize($input){
  if(is_array($input)){
    foreach($input as $key =&gt; $value)
     $input[$key]=sanitize($value);
    return $input;
  }
  else
    return mysql_real_escape_string(stripslashes(trim($input)));
}
</pre>
<p>First, this script automatically <code>trims</code> your input, preventing usernames with spaces before or after, for example &#8216;Admin &#8216;, which to the user would look identical to &#8216;Admin&#8217;.</p>
<p>Second, it strips any backslashes out from previous sanitizations (plus typically you don&#8217;t want backslashes in input)</p>
<p>Third, it escapes any sensative characters (&#8216;, &#8220;, and \n, for example).</p>
<p>Essentially it leaves you with ALL input safely cleared of any possibility of SQL injection.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.spencerdrager.com/2010/04/18/universal-before-and-after-input-santizatio-with-mysql_real_escape_string-and-stripslashes/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Spice Up Your Theme—Break Out Of Your Layout’s Lines</title>
		<link>http://www.spencerdrager.com/2010/02/15/spice-up-your-theme/</link>
		<comments>http://www.spencerdrager.com/2010/02/15/spice-up-your-theme/#comments</comments>
		<pubDate>Tue, 16 Feb 2010 02:38:36 +0000</pubDate>
		<dc:creator>Spencer</dc:creator>
				<category><![CDATA[Design Tips]]></category>
		<category><![CDATA[How To]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Layout]]></category>
		<category><![CDATA[Theme]]></category>
		<category><![CDATA[Tip]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://www.spencerdrager.com/?p=677</guid>
		<description><![CDATA[Have you ever been frustrated with your site's inability to break free of the <em>line</em>? By the very nature of HTML, most themes are prone to being reduced to a series of horizontal and vertical lines with no overlap. It's nice and orderly but boring&#8212;nothing ever stands out. Many rely on large images to break the monotony but it's simple to do it with small PNG images and a bit of CSS using positioning!]]></description>
			<content:encoded><![CDATA[<p>Have you ever been frustrated with your site&#8217;s inability to break free of the <em>line</em>? By the very nature of HTML, most themes are prone to being reduced to a series of horizontal and vertical lines with no overlap. It&#8217;s nice and orderly but boring&mdash;nothing ever stands out. Many rely on large images to break the monotony but it&#8217;s simple to do it with small PNG images and a bit of CSS using positioning!</p>
<p>Learn how to do this in two minutes following my near-light speed tutorial.<span id="more-677"></span></p>
<h3>Breaking Out Your Grid</h3>
<p>To get started, take any image with transparency. GIFs are a poor choice for this, because while they have transparency it is either on or off. <strong>You want to use PNG-24 images with alpha transparency. </strong></p>
<p>Choose an image. In most cases, it&#8217;s not worth it to make your own since what you want has a great chance of already being made and free. Check out some of these posts from Smashing Magazine (I&#8217;m a huge fan) to take an eye to free icon and image sets made with developers like you in mind: <a href="http://www.smashingmagazine.com/2009/02/16/50-beautiful-useful-and-free-icon-sets/">50 Free High-Quality Icon Sets</a> and <a href="http://www.smashingmagazine.com/2008/03/06/35-really-incredible-free-icon-sets/">35 (Really) Incredible Free Icon Sets</a></p>
<p>For this demonstration, I&#8217;ve selected this banana image from <a href="http://www.klukeart.com/">KlukeArt</a>.</p>
<h3>Position Relative</h3>
<p><img src="http://www.spencerdrager.com/wp-content/uploads/2010/02/Banana.png" alt="" title="Banana" width="256" height="256" class="noborder alignright size-full wp-image-678" /></p>
<p>Now, it&#8217;s a pretty spectacular image by itself, but it&#8217;s within the lines of this post and doesn&#8217;t &#8220;pop&#8221; out of the page.</p>
<pre class="brush: css; title: ;">
position: relative;
left: -150px;
</pre>
<p>With these two lines of code, the banana now pops over the border to the left, having moved 150 pixels left from where it was supposed to lay&#8230; unfortunately text does not wrap around it, so it looks sort of rubbish.</p>
<p><img src="http://www.spencerdrager.com/wp-content/uploads/2010/02/Banana.png" alt="" style="position:relative;left:-150px;" title="Banana" width="256" height="256" class="noborder alignright size-full wp-image-678" /></p>
<p>Next let&#8217;s see what happens when you float it to the left&#8230;</p>
<pre class="brush: css; title: ;">
position: relative;
left: -150px;
float: left;
</pre>
<p><img src="http://www.spencerdrager.com/wp-content/uploads/2010/02/Banana.png" alt="" style="position:relative;left:-150px;float:left;" title="Banana" width="256" height="256" class="noborder alignright size-full wp-image-678" /> </p>
<p>This is better&#8230; the text will wrap, but now the issue is that it still pushes the text out to form a margin where the image used to be, not where it is now. We will remedy this by adjusting the margins manually to match the left-shift.</p>
<div class="clear"></div>
<pre class="brush: css; title: ;">
position: relative;
left: -150px;
float: left;
margin-right: -140px;
</pre>
<p><img src="http://www.spencerdrager.com/wp-content/uploads/2010/02/Banana.png" alt="" style="position:relative;left:-150px;float:left;margin-right: -140px" title="Banana" width="256" height="256" class="noborder alignright size-full wp-image-678" /> </p>
<p>Ahh! Much better! Now you have your image shifted over, breaking the linearity of your page and instantly making it more interesting and less boxy! </p>
<h3>Position Absolute</h3>
<p>We still have one more property at our disposal however. Up until now we have been using <code>position: relative;</code>. Another method is using <code>position: absolute;</code>. Absolute position allows you to position your element wherever you want within it&#8217;s parent box. It&#8217;s parent box will be either the element it is in with <code>position:relative</code>, or the entire page window itself.</p>
<div class="clear"></div>
<p>For example:</p>
<pre class="brush: xml; title: ;">
&lt;div class=&quot;relative&quot;&gt;
  &lt;img class=&quot;absolute&quot; /&gt;
&lt;/div&gt;
</pre>
<pre class="brush: css; title: ;">
.relative{
  position: relative;
  background-color: #f99;
  width: 400px;
  height: 300px;
}
.absolute{
 position: absolute;
 bottom: -100px;
 right: 20px;
</pre>
<div style="position: relative; background-color: #f99;width: 400px; height: 300px;">
<img src="http://www.spencerdrager.com/wp-content/uploads/2010/02/Banana.png" alt="" style="position:absolute;bottom: -100px; right;20px;" title="Banana" width="256" height="256" class="noborder alignright size-full wp-image-678" />
</div>
<p>Pretty nifty. <em>Oh no! There is a banana is on top of my text! Oh no! There is a banana is on top of my text! Oh no! There is a banana is on top of my text! Oh no! There is a banana is on top of my text! Oh no! There is a banana is on top of my text! Oh no! There is a banana is on top of my text!</em></p>
<p>The down side to absolute positioning is it is impossible to margin text around it effectively. So ideally you want to use it in areas where you don&#8217;t need to do so.</p>
<h3>Comment!</h3>
<p> I hope you enjoyed this quick tutorial. This technique works in Firefox, Chrome, IE 7+, and Opera&#8230; and probably more.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.spencerdrager.com/2010/02/15/spice-up-your-theme/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>7 Tips For Form Validation: Striking a Balance</title>
		<link>http://www.spencerdrager.com/2010/02/10/7-tips-for-form-validation/</link>
		<comments>http://www.spencerdrager.com/2010/02/10/7-tips-for-form-validation/#comments</comments>
		<pubDate>Wed, 10 Feb 2010 23:02:04 +0000</pubDate>
		<dc:creator>Spencer</dc:creator>
				<category><![CDATA[Design Tips]]></category>
		<category><![CDATA[form validation]]></category>
		<category><![CDATA[registration pages]]></category>
		<category><![CDATA[tips]]></category>
		<category><![CDATA[validation]]></category>

		<guid isPermaLink="false">http://www.spencerdrager.com/?p=624</guid>
		<description><![CDATA[Input validation has long been a highly debated topic. How much should you validate? How much is too much? Fields like username, password, and email address are common amongst thousands of registration pages and yet most of them handle validation in different ways. Read these seven tips for form validation and strike a balance.]]></description>
			<content:encoded><![CDATA[<p>How much is <em>too much</em>?</p>
<div class="right pictureframe" style="width: 193px;">
<img class="noborder nospace" title="Digg's registration page" src="http://www.spencerdrager.com/wp-content/uploads/2010/02/img3.jpg" alt="Digg's registration form screenshot" width="192" height="300" /></div>
<div class="caption" style="width: 193px;">
<p>
Digg uses client-side on-the-fly validation to walk visitors through their registration page.
</p>
</div>
</div>
<p>Input validation has long been a highly debated field. No one disputes some sort of validation is appropriate, but the question <em>how much</em> is subject of great debate. And more: server-side only enough, or are modern websites expected to have client-side too? Google &#8220;email validation,&#8221; or more specifically, &#8220;php email validation&#8221; and you&#8217;ll find hundreds of scripts all of which go about it different ways.</p>
<p>Programmers have spent years of man-hours working on the same sets of problems when it comes to validation. Username, password, email address, name, phone number, and so on. These fields are common in thousands of registration pages. Most have different ways of handling it. I will admit I&#8217;ve reinvented the wheel a few times. Perhaps this is because there is no definitive set of functions cross-language cross-platform that we&#8217;ve all agreed upon as <em>the</em> way to validate.<span id="more-624"></span></p>
<h2>7 Tips for Validation</h2>
<h3>1. Validate to help your users</h3>
<p>On the surface, the reason for validation is to make sure it doesn&#8217;t screw up your database, isn&#8217;t it? Wrong! That should just be a by-product. The real goal is to help your users—That is the entire goal of your site isn&#8217;t it? Users hate being wrong and they hate going back to fix their mistakes. The less frequently you can make them wrong and have your system work the better. The main goal of validation is to make them realize when they accidentally provide wrong information, as in accidental typos, not information your database doesn&#8217;t like.</p>
<h3>2. Avoid over-validation</h3>
<p>Over-validation is when you take over-examine the field you are validating, thinking strict validation will somehow help you or your user. The fact is people are not exact, and won&#8217;t like it if you try to force them to be. Try to compensate by other means (<a href="#4">see tip#4</a>). Furthermore, no amount of validation will prevent people from giving false names and e-mail addresses. If the reason for your rigorous screening for e-mail addresses is that, then you should reconsider.</p>
<blockquote class="right bigquote" style="width:180px;"><p><span class="superbig left">&ldquo;</span>No amount of validation will prevent people from giving false names and e-mail addresses.<span class="superbig right">&rdquo;</span></p></blockquote>
<p><strong><span style="text-decoration: underline;">Example</span></strong></p>
<p>E-mail addresses are notoriously annoying for validation. What is the right way to validate? Read the follow two ways of validation (in plain English) and tell me which you think is better:</p>
<p><strong>Method One:</strong> Two or more characters followed by an @ symbol followed by 2 or more characters followed by a valid extension (.com, .org, .uk, .ca, etc.). The strings before and after the @ symbol can have a-z, A-Z, 0-9, hyphens, underscores, and periods, but it must begin and end with a letter or number, and cannot have two symbols adjacent to each other. The domain name should be a valid domain (this could be checked). The entire address should not be longer than 64 characters.</p>
<p><strong>Method Two:</strong> a-z, A-Z, 0-9, _, -, and ., followed by an @, followed by more of the same, followed by a period followed by a-z or A-Z only. The entire address should not be longer than 64 characters.</p>
<p>The first one obviously is stronger in that it required an e-mail address that is one hundred percent realistic. <em>But</em>, it does more than it needs to and generally will only stop people who are giving a fake e-mail from giving one that is quite so fake. Instead they&#8217;ll give a more realistic fake one. Why bother? Save yourself the time.</p>
</div>
<p><img class="headliner nospace noborder alignnone size-full wp-image-512" title="Tumblr's registration form" src="http://www.spencerdrager.com/wp-content/uploads/2010/02/header2.jpg" alt="Tumblr's registration form" width="687" height="127" /></p>
<div class="caption">
Tumbler has minimal validation and a simple form.
</div>
<div class="storycontent">
<h3>3. Avoid under-validation</h3>
<p>On the other side you have too <em>little </em>validation. Too little validation is when your users can cause database-side errors with lengthy inputs, empty inputs, or some other wacky input. The minimum you must always do is to check length and presence of expected inputs. Enough said.</p>
<h3>4. Format information behind the scenes instead of causing errors</h3>
<p><a name="4"></a><br />
You don&#8217;t always have to flag problems with inconsistent inputs. Instead, as long as the &#8220;meat&#8221; of the input is correct, you can format it however you like without concerning the user. Take this example with phone numbers:</p>
<p><strong><span style="text-decoration: underline;">Example</span></strong></p>
<p><strong>Site One</strong> uses preg_match, regular expression to confirm that the input contains an input in the format (###) ###-####, and rejects anything not in this format.</p>
<p><strong>Site Two</strong> strips non-numbers out with ereg_replace and checks to make sure it is 10 digits long.</p>
<p>Before you try to guess which is better, I&#8217;ll just tell you. Site two is vastly superior. The resulting numbers-only string can be formatted however is wanted and nothing is required from the user to do it. They can enter their phone number however they like.</p>
<h3>5. Give succinct but detailed error messages</h3>
<p>While writing your error strings, <em>never </em>give your users the error <code>Invalid Input</code>. Describe what they did wrong. This make take extra time, but it will make your users happier. <code>Username too short</code> is much more friendly.</p>
<p>You&#8217;d think this tip would go without saying, yet there is still so many sites out there who give ambiguous and downright misleading error messages.</p>
<h3>6. Only ask for information you will use or will help the user</h3>
<p>Do you <em>need</em> to know your users phone number or birth date? No? Then don&#8217;t ask or make it an option to create a profile—AFTER they have already created their account. Studies have shown that users hate filling forms. Thing of it this way: the more fields you have the more users you will lose. So keep it short. Keep it sweet. While this technically isn&#8217;t validation specifically, it all ties together.</p>
<p>By keeping your forms short it makes less work for you, the designer, and happier (not to mention potentially more plentiful) visitors.</p>
<h3>7. Validate client-side too</h3>
<p>Ideally you want to validate client-side too. It doubles the amount of work you have to do, but it lessens the blow to the user when they get something wrong. When a user fills out a form and presses &#8216;submit&#8217;, they want the see success. Validating client-side (preferably on-the-fly) helps lessen the blow when they do things wrong. Rather than having to go back to a field, they are alerted to their mistake immediately. Users love to be babied and having their hand held, not that there is anything wrong with that.</p>
<p>This all goes back to serving the user. Keep focused on what the user desires&mdash;put yourself in their shoes. The end result is a better, more usable website.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.spencerdrager.com/2010/02/10/7-tips-for-form-validation/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

