Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 92

CREATING A WEB APP from

SCRATCH
Skullipso
Today we begin Part 1 of an 8-Part series on
building a web application from absolute scratch to
a complete product. I am going to kick things off by
introducing the idea, and then I will be handling the
design, UI, and general front-end stuff. We are
going to be going back and forth from here over to
my friend Jason Lengstorf’s blog Ennui Design.
Jason will be handling the back-end stuff like
application planning and database stuff. At the end
Home of the week, we’ll unleash the actual working
application for you. Here is the plan:
[Type the company address]

[Type the phone number]

[Type the fax number]

[Pick the date]


Contents
It’s Easy, Right?........................................................................................................................................4
The Big Idea.............................................................................................................................................4
Sketch It Out............................................................................................................................................4
Early UI Planning......................................................................................................................................5
The Screens.............................................................................................................................................6
“Features”...............................................................................................................................................7
Moving On...............................................................................................................................................7
Part II...........................................................................................................................................................7
Where We're At.......................................................................................................................................7
Okay, So We Know How It Looks, but How Does It Work?......................................................................7
Data Storage—Planning and Database Structure....................................................................................7
Creating the Database.............................................................................................................................7
Table 1: User Information..........................................................................................................8
Table 2: List Information............................................................................................................8
Table 3: List Items........................................................................................................................8
Data Handling—Planning and Script Organization..................................................................................9
Planning our PHP Classes.......................................................................................................................10
Moving On.............................................................................................................................................10
Part III........................................................................................................................................................11
Developing a Workflow.........................................................................................................................11
Bringing It to Life Photoshop.................................................................................................................12
Moving on..............................................................................................................................................15
Part IV........................................................................................................................................................15
Web Root Organization.........................................................................................................................15
Header...................................................................................................................................................16
Footer....................................................................................................................................................17
Sidebar...................................................................................................................................................17
Main Page..............................................................................................................................................17
Logged in (The List)................................................................................................................................18
Logged out (Public list)..........................................................................................................................19
Logged out (Sales).................................................................................................................................19
Account Page.........................................................................................................................................19
Other “Form” Pages...............................................................................................................................20
The CSS..............................................................................................................................................21
Moving Along........................................................................................................................................23
Part V.........................................................................................................................................................23
Where Are We?.....................................................................................................................................23
Connecting to the Database..................................................................................................................23
Building the Class...................................................................................................................................25
Connecting the Class to the Database...................................................................................................26
Creating an Account..............................................................................................................................27
Creating the Sign-Up Form....................................................................................................................27
Saving the User's Email Address............................................................................................................29
Generating and Sending a Verification Email.........................................................................................31
Verifying the User's Account.................................................................................................................32
Verifying the User's Email and Verification Code...................................................................................33
Updating the User's Password and Verified Status................................................................................35
Logging In..............................................................................................................................................36
Building the Login Method....................................................................................................................38
Logging Out...........................................................................................................................................39
Modifying Account Information............................................................................................................40
Building the Interactions File.................................................................................................................43
Updating the Email Address..................................................................................................................45
Updating the Password..........................................................................................................................46
Deleting the Account.............................................................................................................................47
Resetting an Account Password.............................................................................................................49
Returning the Account to "Unverified" Status.......................................................................................49
Building the Reset Pending Page...........................................................................................................50
Generating a "Reset Password" Email...................................................................................................51
Resetting the Password.........................................................................................................................52
Moving On.............................................................................................................................................53
Part VI........................................................................................................................................................54
The Big Thing: Saving the List................................................................................................................54
Interface JavaScript...............................................................................................................................55
First things first: calling the JavaScript files...........................................................................................55
Cleaning up the Markup with JavaScript...............................................................................................55
Making the list drag / sortable...............................................................................................................57
Marking items as “done”.......................................................................................................................58
Color Cycling..........................................................................................................................................59
Deleting list items..................................................................................................................................60
Click-to-edit list items............................................................................................................................62
Appending new list items......................................................................................................................62
Moving On.............................................................................................................................................64
Part VII.......................................................................................................................................................64
Where We're At.....................................................................................................................................64
Defining the Class..................................................................................................................................64
Displaying List Items..............................................................................................................................65
Saving New List Items............................................................................................................................72
Reordering List Items.............................................................................................................................76
Changing Item Colors.............................................................................................................................79
Editing Item Text....................................................................................................................................80
Marking Items as "Done".......................................................................................................................82
Deleting Items.......................................................................................................................................84
Moving On.............................................................................................................................................86
Part VIII......................................................................................................................................................87
Object-Oriented Programming..............................................................................................................87
Security..................................................................................................................................................87
Security on the Server Side....................................................................................................................87
PDO........................................................................................................................................................87
Data Escaping........................................................................................................................................88
Security in the JavaScript.......................................................................................................................88
POST vs GET...........................................................................................................................................90
2.0 Features...........................................................................................................................................90
Today we begin Part 1 of an 8-Part series on building a web application from absolute
scratch to a complete product. I am going to kick things off by introducing the idea, and
then I will be handling the design, UI, and general front-end stuff. We are going to be going
back and forth from here over to my friend Jason Lengstorf’s blog Ennui Design. Jason will
be handling the back-end stuff like application planning and database stuff. At the end of
the week, we’ll unleash the actual working application for you. Here is the plan:

It’s Easy, Right?


What we’re going to create is a “list app”. The idea being focused on simplicity and
usefulness. Sign up for an account, and get started making a list in just a few seconds.
Sounds easy right? Even the PHP dabblers out there probably could throw something like
this together fairly quickly, right? Well the fact is, no, it’s not that easy.

First of all, it needs to work and it needs to work well. That means good back end code that
does what it’s supposed to do and well. That means a good UI that is intuitive, helpful, and
pleasurable to use. It means keeping the app secure and users data private. None of these
things is trivial.

Through this whole 8-part series, we are going to create an app that hopefully does all
these things pretty well. We aren’t out to tell you this is the greatest app ever made, but
rather, we are going to use this app as a walk-through journey of the app creating process
and hopefully do as many smart things as we can along the way.

The Big Idea


This “list app” is going to be called Colored Lists. Lists (in real life), can be for anything: a
to-do list, a grocery list, things to bring camping list… As you finish things, you cross them
off. Things on a list may be of different relative importance as well. This makes paper lists
potentially messy and inefficient. With a list on a computer, we can make crossing off items
just a click and we can make rearranging them a matter of drag and drop. For dealing with
relative importance, we can use colorization, which could also be used for things like
grouping. Computers, and the web, are a perfect place for lists.

Sketch It Out
No need to get fancy right away. Here is a very rudimentary sketch of what the app might
look like:
Looks like a list to me. Each list item is a long rectangle, because the big idea here is to
colorize each list item, so putting them inside a colored box makes sense. There are some
interactive elements to the left and right of each list item. Those are going to be for
accomplishing the basic things we intent people to be able to do with their colored list. Lets
take a closer look.

Early UI Planning
We don’t necessarily want to be talking about specific technologies at this point, but we
should be thinking about how the UI will operate, so we can make choices about technology
that can accommodate our UI desires.

 Click-to-edit
 Drag and drop
 Two-click delete
 Automatic saving (after any action)
All this stuff basically adds up to a whole bunch of AJAX. We don’t want to load special
screens to do relatively trivial tasks like deleting a list item. That stuff should happen
seamlessly, smoothly and with proper feedback in response to mouse clicks without page
refreshes. In a sense, we are creating a one-page app, where the majority of interaction
with this app happens on a single page. This is certainly by design, and not trying to adhere
to any particular fad. Lists are easy and quick, that’s why are useful. If this app is
complicated, it’s usefulness is diminished and nobody will use it.

The Screens
Just doing some quick brainstorming of the idea so far, we can come up with quite a
number of “screens”, or states the application can be in.

 Homepage
 Logged out = Intro/Sales Page
 Logged in = Your list
 Log in page
 Settings page
 Lost password page
 Account activation page
 Emails
Yep, even emails should be considered a part of the “screens”, as they are a vital part of the
process and interaction with an app.

“Features”
People love “features”. Things that your app has that other apps don’t have, or that yours
does better. This is just as much for marketing as it is for your actual product. All the fancy
AJAX this app will have is certainly a feature, but that stuff these days is getting more and
more expected rather than a feature. The one feature that we will focus on with this app is
“public sharing”. Each list will have a unique URL that can be publicly shared. A visitor
visiting this URL can see the list in it’s exact current state, but not interact with it as far as
editing/adding/deleting.

Moving On
Now that we have the idea in place of what we want to build, in the next part we’ll dive into
looking at what this is going to take in terms of server-side technology.

Part II
Where We're At
Up to this point, we've planned the way our app is going to look, as well as given ourselves a
basic idea of how the app is going to function. The next step is to figure out what's going to
happen behind the scenes to allow our app to work the way we've planned.

Okay, So We Know How It Looks, but How Does It Work?


In order to keep a list available after a user logs out of our app, we'll need to  store list
information in a database.  And, of course, to access that database we're going to need some
kind of server-side scripting language. For this app, we made the choice to go with a
combination of MySQL and PHP to handle all our behind-the-scenes data handling and storage.

Data Storage—Planning and Database Structure


Our first step is to decide how we want to  organize list data. Since this app is fairly simple, we'll
only need three tables in our database. The first table will store user information, and the second
will store list information. The third table will keep track of list items.

Creating the Database


Of course, before we can create our tables, we'll need a database to work with. For anyone
working along at home, we'll be operating under the assumption that you're building and testing
locally (we recommend XAMPP).

Navigate to  http://localhost/phpmyadmin  and open the SQL tab. You can use the
GUI if you want, but we're going to use raw SQL commands for learning purposes. The database
will be named   cl_db , which is all the information that is required to build the database.
However, we want to make sure that our users can use characters from any language in their
lists, so it's also a good idea to specify the collation and character set of the database. We'll be
using the UTF-8 character set with general collation , which supports multilingual characters
and is case-insensitive.

The command to create this database is:

CREATE DATABASE `cl_db` 
DEFAULT CHARACTER SET utf8 
COLLATE utf8_general_ci;
Execute this command from the SQL tab in phpMyAdmin and the new database will become
available. Now that we've got a database, we're ready to build our tables.

Table 1: User Information


Using our list app doesn't require a high security clearance; all we need to know is that you've
got an email address and that it's real.  To determine that an email address is real, we'll be
sending new users a confirmation link in an email,  which they need to follow before using our
app. This means we need to have a unique confirmation link and a place to store whether or not
an account has been verified.

Of course, we also need to  store the user's email address,  and in the interest of keeping
redundant data storage to a minimum, we'll  assign each user a unique numeric identifier.

The MySQL command to build this table will look like this:

CREATE TABLE cl_db.users(    UserID      INT PRIMARY KEY AUTO_INCREME
NT,    Username    VARCHAR(150) NOT NULL,    Password    VARCHAR(150)
,    ver_code    VARCHAR(150),    verified    TINYINT DEFAULT 0)

Table 2: List Information


List information is fairly straightforward.  Each list will have a unique identifier, a unique URL,
and the identifier of the user that owns the list.  This helps us limit the amount of redundant
information that needs to be stored.

To build this table, execute the following MySQL command in phpMyAdmin's SQL tab:

CREATE TABLE cl_db.lists(    ListID      INT PRIMARY KEY AUTO_INCREME
NT,    UserID      INT NOT NULL,    ListURL     VARCHAR(150))

Table 3: List Items


Finally, we need a table that will store our list items.  Each list item needs a unique identifier,
the ID of the list it belongs to, and the information the user enters as his or her list
item. Also, to support features we'll be adding later on, we also need to  keep a record of the
item's position and color. Execute this command in the SQL tab of phpMyAdmin:
CREATE TABLE cl_db.list_items 

    ListItemID       INT PRIMARY KEY AUTO_INCREMENT, 
    ListID           INT NOT NULL, 
    ListText         VARCHAR(150), 
    ListItemDone     INT NOT NULL, 
    ListItemPosition INT NOT NULL, 
    ListItemColor    INT NOT NULL 
)
NOTE: The  ListItemDone   field was omitted in the original post of this article. It was added
here after being  pointed out in the comments by FuSi0N .

The database with our three tables


Now we have our database and the three tables we'll need to build our app. Next, we'll  plan how
we're going to create and access our database information using PHP.

Data Handling—Planning and Script Organization


Before we start coding, it's always a good idea to take a moment and map out everything that
needs to be done. That way, we can group tasks into logical arrangements.

Because great code starts with great organization, we'll be using an object-oriented approach.
Planning our PHP Classes
Object-oriented programming provides an easy way to keep related functions grouped together.
Afterlearning object-oriented programming , it becomes an incredibly powerful tool that  increases
portability, readability, and usability of scripts.  Our app is pretty simple, so we'll only need
two classes. The first class is going to handle user interactions, such as  registering, updating
information, and logging in and out.  The second class will handle  list interactions, such as
adding, deleting, and moving list items.

User Actions Class


Our first class, which we'll name   ColoredListsUsers , needs to handle all the actions our
app will perform that are user account-related.  Again, this is a pretty simple application, so
when we map out everything that users can do with their account, we end up with pretty short
list:

 Create an account
 Verify the account
 Update the account email address
 Update the account password
 Retrieve a forgotten password
 Delete the account

In addition to those methods, we'll also need some support methods,  such as one that will
send a verification email. We'll define these methods as we build the app in later installments of
this series.

List Actions Class


The list actions class, which we'll call   ColoredListsItems , also has a pretty short list of
methods. This class will handle everything else our app does, which is  anything a user can do
with his or her list items.  The list of available actions ends up looking like this:

 Create a list item


 Update a list item
 Delete a list item
 Change a list item's position
 Change a list item's color

Action Handling Scripts


Finally, we'll need a couple action-handling scripts. These will  determine what the user's
desired action is, create an instance of the proper object, and call the correct method.  As
we build our app, we'll go into more detail on how these scripts will work.

Moving On
In our next installment of this series, we'll  create the application workflow.  Make sure you're
subscribed to CSS-Tricks  so you don't miss out!
Part III
Developing a Workflow
We have a great start going on our list application at this point. The "big idea" is in place, we
know how we want the lists to be displayed and interacted with, and we have some back-end
structure in place to deal with users and all the data that goes along with these lists.

It was a good idea to start with the "meat" of the app, but there is a little bit more that goes into
a full application. Because we have users, that means we need a sign up form and a log in area
for returning users. Because users can be forgetful,  we need a 'Lost Password'
feature. Because users should be just as concerned about security as we are,  users need to be
able to change their passwords, change their login, and delete their accounts.  Our one-
page app has just turned into a four or five page app, so we're going to need to think about
some workflow.

There will be two different states for the homepage:  logged in and logged out. While logged
out, people need a way to sign in and to register, and this will be essentially the "sales" page
too, explaining the app. Logged in, the homepage will be the user's list itself. Logged in users
will also need to do some ancillary stuff related to their account, like change their email, change
their password, and delete their account, as well as a way to log out. These ancillary options are
probably best served on an account page. So now we are looking at at least two new pages:
Account Settings and Registration. Here is some flow:

Basic app workflow


It's not pretty folks, but that's what sketching is. It's fast and it's just to help you think and plan
for the things you need.

Bringing It to Life Photoshop


Our developer is already ahead of us, thinking about the data they need and how this app is
going to actually work. So we'd better get started actually designing here.

Homepage (Logged In)

This is the meat of our application so let's start here. The list is obviously the most important
thing, so let's keep the header small and keep the list front and center.  List items are big
colored blocks with buttons for their associated actions nearby.  Below the list a box for
entering new list items.

The home
page as it appears when logged in

Homepage (Logged Out)

When logged out, the homepage is going to act more like a "sales" page. Not that we plan to
charge for it, but just to explain and get people interested in using it. There isn't much to say
about a list app, so we'll keep it simple.
When logged out, we'll encourage the visitor to sign up

Small Bits

We've been designing long enough to know we might as well make the little buttons into a
separate file and keep them together as a  sprite  (a sprite is multiple images combined into one
to save HTTP requests, in our case, also the rollover states). So we'll do that and throw together
a favicon while we're at it.

All the list item tabs Favicon


Registration

Our intention with registration is going to be extremely simple.  We're going to ask for a user's
email and that's it. They will be sent an email with a link in it to complete registration. The link
in that email will "activate" their account and they can choose the password at that time. So, our
registration page can be pretty darn simple.

The registration form

As small as this is, this registration page sets the stage for other forms.  We have a label/input
pair here that can be used for any input/pair in any of our site's forms.

Account

We'll use the same form design as the registration page here.  It's not cheating or being lazy,
it's good design through consistency!

Account controls
Buttons

Notice the change in buttons. They are now big, orange and rounded. Much more button-like
don't you think? Again for consistency,  let's make this the default for all buttons across the
site.

Site buttons, looking button-like

Moving on
The developer now has plenty to go on to start fleshing out the user interactions for the site. And
we have plenty to go on to start getting the HTML and CSS for all this ready, and ultimately to
AJAX this puppy up and get it working.

Part IV
It’s time to get our hands dirty with some markup!

We know we have a couple different pages to deal with here. The main page of course,
which acts as both our list page and sales page depending on login status. But then we have
sign in and sign up pages and account pages. So let’s be smart and work modularity. That
means we’ll make files like “header.php” and “close.php” that we can include on multiple
pages so we don’t have to repeat common code (e.g. the DOCTYPE, analytics code, and
ubiquitous things like that.

Web Root Organization


This is what we have for files at the root of our web directory so far. All the major views
have their own PHP files. We have subdirectories for images and “common” files. and we
have a few loose files like CSS and the favicon.
Our developer will surely be adding more files. He’s going to need PHP files for interacting
with the database and doing all the list interactions.

Header
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<title>Colored Lists | <!-- Do Something Smart Here --></title>

<link rel="stylesheet" href="style.css" type="text/css" />


<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />

<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js?ver=1.3.2'></script>


</head>

<body>

<div id="page-wrap">

<div id="header">

<h1><a href="/">Colored Lists</a></h1>

<div id="control">

<!-- IF LOGGED IN -->


<p><a href="/logout.php" class="button">Log out</a> <a href="/account.php" class="button">Your Account</a></p>
<!-- IF LOGGED OUT -->
<p><a class="button" href="/signup.php">Sign up</a> &nbsp; <a class="button" href="/login.php">Log in</a></p>
<!-- END OF IF STATEMENT -->

</div>

</div>
Right away in the header we’ve run across a few things where we need to be smart and
leave notes for the developer, but give him the things he needs. In the page title, we’ve left
a note to do something smart there. Different pages need differnet page titles, so clearly
something dynamic needs to happen there. Then with our control buttons (e.g. Account /
Logout) those buttons will be different depending on the logged in state of the user. So we’ll
just let the developer jump in there and make those things function correctly.

So at this point we have the top of a page. We are leaving the body, html, and #page-wrap
elements open, as beyond that is the main content of the page. Before we get into that
main content, let’s toss in the sidebar and footer areas so we have a complete skin.

Footer
Our design doesn’t call for much of a footer, so we’ll just close up those open elements and
add a note to put analytics here.

</div>

<!-- Analytics here -->

</body>

</html>

Sidebar
Our design calls for a bit of a sidebar. Right now, all we’ll use it for is a few notes on using
the application. But it’s nice to have some open room for content, as it’s extremely likely
that room will be needed for additional things as the app grows.

<div id="ribbon">

Reminders

<ul>
<li>Your list automatically saves</li>
<li>Double-click list items to edit them</li>
</ul>

</div>

Main Page
Now that we have our “modules” complete, let’s dig into a real page. The template for
building any page will be like this:

<?php include_once "common/header.php"; ?>

<div id="main">

<noscript>This site just doesn't work, period, without JavaScript</noscript>


<!-- IF LOGGED IN -->

<!-- Content here -->

<!-- IF LOGGED OUT -->

<!-- Alternate content here -->

</div>

<?php include_once "common/sidebar.php"; ?>

<?php include_once "common/footer.php"; ?>

Logged in (The List)


<ul id="list">
<li class="colorRed">
<span>Walk the dog</span>
<div class="draggertab tab"></div>
<div class="colortab tab"></div>
<div class="deletetab tab"></div>
<div class="donetab tab"></div>
</li>

<li class="colorBlue">
<span>Pick up dry cleaning</span>
<div class="draggertab tab"></div>
<div class="colortab tab"></div>
<div class="deletetab tab"></div>
<div class="donetab tab"></div>
</li>

<li class="colorGreen">
<span>Milk</span>
<div class="draggertab tab"></div>
<div class="colortab tab"></div>
<div class="deletetab tab"></div>
<div class="donetab tab"></div>
</li>
</ul>
The list itself will just be a regular ol’ unordered list. We’ll use CSS class names for the
colors. But then we need a bunch of controls for the list items. That’s what all those divs are
inside the list items. There are empty divs for dragging, changing color, deleting, and
checking off list items. We need these for the CSS so we can target them and style them.

We’re smart designers though, we know this markup is merely temporary. These lists will
be dynamically generated by the application. Just looking at all those empty control divs;
we know that those are probably automatically generated by the JavaScript. That’s fine, we
need the HTML in there now to set the stage and have everyone on the same page.

Why the spans inside the list items? Just being smart. Because the list items wrap more
than just the text, it’s likely we’ll need some kind of hook to target just the text later on.

Now we need to get an input on this page for adding new list items. Our developer will be
all over this, but we’ll put the basics in so we can style them.
<form action="" id="add-new">

<div>
<input type="text" id="new-list-item-text" name="new-list-item-text" />
<input type="submit" id="add-new-submit" value="Add" class="button" />

</div>

</form>
Then one of our applications features is having sharable public URL’s for our lists. Let’s put
that in here too.

<div id="share-area">
<p>Public list URL: <a href="#">URL GOES HERE</a>
<small>(Nobody but YOU will be able to edit this list)</small></p>
</div>
Ahhh, more work for the developer! But he’s ready for it. This public URL business leads us
into another possible scenario. We need this main page to be capable of displaying a list
without showing the input form or all the list controls. Basically you can just look at the list
but not interact with it. (Like if you wanted to send your mom your Christmas list!)

Logged out (Public list)


<ul id="list">
<li class="colorRed">
<span>Walk the dog</span>
</li>

<li class="colorBlue">
<span>Pick up dry cleaning</span>
</li>

<li class="colorGreen">
<span>Milk</span>
</li>
</ul>
This will be exactly the same as the list above, only no control tabs, no form to add new
items, and no public URL area (hey, they are already here, what do they need the URL for).
We know this this probably will just be a change in how the backend code outputs the list.
But whatever, if we create this, everybody is on the same page.

Logged out (Sales)


We might do something fancy someday for this, but for now, our big idea is just a cool
graphic showing that this area is potentially where your new list will be and a big ol’ arrow
showing people where they can sign up.

<img src="/images/newlist.jpg" alt="Your new list here!" />

Account Page
As a quick reminder, we us this structure for all pages, including this one.

<?php include_once "common/header.php"; ?>

<div id="main">

<!-- IF LOGGED IN -->


<!-- Content here -->

<!-- IF LOGGED OUT -->

<!-- Alternate content here -->

</div>

<?php

include_once "common/sidebar.php";

include_once "common/footer.php";

?>

That’s the beauty of working modularly, all common content is included so updates down
the line are much easier.

The account page is going to have several forms on it: one for updating email, one for
updating password, and a button for users to delete their accounts. Again, our developer
will be all over these forms filling them up with hidden inputs that pass along data and
adding in action URLs and methods and all that. We’ll leave that to him, but this gives us
enough to style.

<h2>Your Account</h2>

<form action="">
<div>
<input type="text" name="username" id="username" />
<label for="username">Change Email Address</label>

<input type="submit" name="change-email-submit" id="change-email-submit" value="Change Email" class="button" />


</div>
</form>

<hr />

<h2>Change Password</h2>

<form action="#">
<div>
<label for="password">New Password</label>
<input type="password" name="r" id="repeat-new-password" />

<label for="password">Repeat New Password</label>


<input type="submit" name="change-password-submit" id="change-password-submit" value="Change Password"
class="button" />
</div>
</form>

<hr />

<form action="" id="delete-account-form">


<div>
<input type="submit" name="delete-account-submit" id="delete-account-submit" value="Delete Account?"
class="button" />
</div>
</form>
Other “Form” Pages
Now that we’ve done the account page, we have pretty much covered all the bases for the
other “form” style pages. Sign up, sign in, forgot your password, they are all just simpler
versions of the account page. Since we’ll have styled the basic label/input format, the
header format, and the “button” format, the developer can easily create these pages himself
copying the basic format and CSS classes from the account page.

The CSS
Reset
/*
RESET
*/
* { margin: 0; padding: 0; }
body { font: 14px/1.1 Helvetica, Sans-Serif; background: url(images/stripe.png) repeat-x; }
.clear { clear: both; }
img, a img { border: none; }
input { outline: none; }
Just getting things cleaned up.

Structure
/*
STRUCTURE
*/
body { font: 14px/1.1 Helvetica, Sans-Serif; background: url(images/stripe.png) repeat-x; }
#page-wrap { width: 960px; margin: 6px auto 50px; position: relative; }
hr { height: 1px; background: #ccc; clear: both; margin: 20px 0; border: none; display: block; }
Not too much complicated formatting for our little one-page app.

Typography
/*
TYPOGRAPHY
*/
a { text-decoration: none; color: #900; border-bottom: 1px dotted #900; outline: none; }
h1 { font: bold 36px Helvetica, Sans-Serif; margin: 0 0 8px 0; }
h2 { margin: 0 0 10px 0; }
p { margin: 0 0 6px 0; }
.button { background: url(/https/www.scribd.com/images/button-bg.png) repeat-x; -moz-border-radius: 5px; padding: 6px 12px; border: none; color:
white; cursor: pointer; text-shadow: 0 1px 1px #666; -webkit-border-radius: 5px; -webkit-box-shadow: 0 1px 3px #999; -moz-
box-shadow: 0 1px 3px #999; font: bold 16px Helvetica; }
.button:hover { background-position: bottom left; }
.red { background: red; color: white; font-size: 12px; padding: 3px; }
This isn’t really a content-based app, so we don’t have a whole heck of a lot of text
formatting. However we do have page headers, links, and buttons, so we’ll set those up
here.
Header
/*
HEADER
*/
#header { height: 68px; position: relative; }
#header h1 { position: absolute; top: 0; left: 0; z-index: 2; text-indent: -9999px; overflow: hidden; }
#header h1 a { display: block; text-indent: -9999px; width: 200px; height: 38px; border: none; background: url(/https/www.scribd.com/images/logo.png)
no-repeat; }
#control { width: 500px; float: right; padding: 10px 237px 0 0; text-align: right; }

Our little stripe header doesn’t take much. Just a little CSS image replacement for the logo
and placement of our control buttons.

Lists
/*
LISTS
*/
#list { list-style: none; }
#list li { position: relative; margin: 0 0 8px 0; padding: 0 0 0 70px; width: 607px; }
#list li span { padding: 8px; -moz-border-radius: 5px; -webkit-border-radius: 5px; width: 589px; display: block; position:
relative; }
.colorBlue span { background: rgb(115, 184, 191); }
.colorYellow span { background: rgb(255, 255, 255); }
.colorRed span { background: rgb(187, 49, 47); color: white; }
.colorGreen span { background: rgb(145, 191, 75); }
.tab { background: url(images/minibuttons.png) no-repeat; height: 21px; top: 4px; }
.draggertab { position: absolute; left: 0px; width: 31px; cursor: move; }
.draggertab:hover { background-position: 0 -21px; }
.colortab { position: absolute; left: 34px; width: 34px; background-position: -31px 0; cursor: pointer; }
.colortab:hover { background-position: -31px -21px; }
.deletetab { position: absolute; right: -35px; width: 15px; background-position: -82px 0; cursor: pointer; }
.deletetab:hover { background-position: -82px -21px; }
.donetab { position: absolute; right: -17px; width: 16px; background-position: -65px 0; cursor: pointer; }
.donetab:hover { background-position: -65px -21px; }
.crossout { position: absolute; top: 50%; left: 0; height: 1px; }

#share-area { margin: 20px 0 0 69px; width: 600px; }

A lot more stuff needed here. Here we’ll set up how the lists look: the colors, the spacing,
the rounded corners, etc. We’ll also position all the little helper controls and give them
appropriate backgrounds. Notice only a single image is used, minibuttons.png. A single CSS
Sprite for mad efficiency!

Forms
/*
FORM STUFF
*/
label { background: #999; color: white; padding: 3px; }
input[type="text"], input[type="password"] { width: 324px; border: 3px solid #999; font-size: 18px; padding: 7px; display:
block; }
#add-new input[type="text"] { width: 532px; float: left; margin: 0 10px 0 69px; }
#add-new input[type="text"]:focus { border-color: #73B8BF; }
#add-new input[type="submit"] { padding: 10px 12px; }
ul#list li span input[style] { width: 90% !important; }
Forms across our whole site will be the same, so we set that up here. The one exception is
the “Add New” area on our lists, which is basically the same as any other input except
bigger and is floated to the left to accommodate the “Add” button. Since we plan to use
click-to-edit, the list items temporarily turn into text inputs when doing that, so we’ll plan
for that by shortening the length of them to accommodate for a “Save” button.

Messaging
/*
MESSAGING
*/
.message { padding: 10px; margin: 0 0 10px 0; width: 607px; }
.good { background: #9ff5b6; }
.bad { color: #ef0040; }
We haven’t talked too much about error messaging, but we can assume that because this is
a web app, there will be some of it (for example, you enter in a wrong password, your
passwords don’t match, you have successfully done something, etc). We’ll set up one class
for messages in general and then classes for good and bad versions.

Sidebar
/*
SIDEBAR
*/
#ribbon { position: absolute; right: 0; width: 125px; padding: 60px 30px 0 47px; height: 756px; top: -6px; background:
url(/images/ribbon-bg.png) no-repeat; }
#ribbon ul { list-style: none; }
#ribbon ul li { background: rgba(0,0,0,0.8); color: white; padding: 5px; margin: 0 0 5px 0; font-size: 12px; }
Just some simple stuff for our list of reminders.

Moving Along
Our developer now has plenty to work with to make this app functional. Once he’s gotten a
good start on that, we’ll tackle AJAX and making all those list interactions happen almost
like a desktop app.

Part V

Where Are We?


Now that we have a workflow put together and the HTML and CSS to make it look good, we can
actually start building the classes  that will run this puppy.

We'll focus this installment of the series on the  user's account interactions.  These include:

 Creating an Account
 Modifying Account Information
 Resetting a Lost Password
 Deleting an Account

Connecting to the Database


Before our class will be able to do much of anything, we  need to connect to our database.  To
do this, we'll need to create a couple of small files.

Defining Site-Wide Constants

Our site won't require many  constants , but in the interest of keeping them easy to maintain,
we'llcreate a separate file to contain any information that is site-wide.  This will be
called constants.inc.php , and it will reside in a new folder called   inc  — this folder will
contain our PHP classes as well.

Creating a constants file is a good idea for pieces of  information that will be used often and in
different scopes throughout a site.  That way, if your database changes, you're able to change
every database connection  simply by swapping out the information in one file.

Inside  constants.inc.php , we want to define our database credentials.  Since we're


starting out by developing locally,   constants.inc.php  will look like this:

<?php 
 
    // Database credentials 
    define('DB_HOST', 'localhost'); 
    define('DB_USER', 'root'); 
    define('DB_PASS', ''); 
    define('DB_NAME', 'cl_db'); 
 
?>
As we develop, we'll add more to this file.

Creating a PDO Object

Next, we want to create a connection so that our application can communicate with our
database.This file will reside in the   common  folder along with the header, footer, and sidebar
files. This file will create a database connection using  PDO (PHP Data Objects) , as well as
setting up a couple other site-wide features:  error reporting and opening a session.

The file will look like this when all's said and done:

<?php 
    // Set the error reporting level 
    error_reporting(E_ALL); 
    ini_set("display_errors", 1); 
 
    // Start a PHP session 
    session_start(); 
 
    // Include site constants 
    include_once "inc/constants.inc.php"; 
 
    // Create a database object 
    try { 
        $dsn = "mysql:host=".DB_HOST.";dbname=".DB_NAME; 
        $db = new PDO($dsn, DB_USER, DB_PASS); 
    } catch (PDOException $e) { 
        echo 'Connection failed: ' . $e->getMessage(); 
        exit; 
    } 
?>
Because we're in the development stage, we want to  see any and every error that occurs on
the site. By setting  error_reporting()  to  E_ALL  and changing
the  display_errors  directive to 1  using  ini_set() , we ensure that even notices will be
displayed, which will keep our code cleaner and more secure.

Next, we use  session_start()  to start a PHP session. This will  allow our users to stay
logged in when we build that functionality later.

Finally, we include  config.inc.php  and create a PDO object using the constants
defined within it. Note the use of the  try-catch  statement—this gives us the ability to
use Exceptions , which help improve error handling. In this case, if the database connection fails,
we're simply going to output the error message.

Why PDO?

The reason we're using PDO for this project is because of its  support for prepared
statements ,which virtually eliminates the risk of  SQL injection . There are other options that
allow prepared statements, such as the  MySQLi extension . However, PDO is not database-
specific, so migrating the app to Oracle or PostgreSQL wouldn't require a full rewrite of our
code.

Also, having used both MySQLi and PDO in projects, it's just my personal preference to use
PDO. Feel free to use whatever method of connecting to the database you prefer, but  keep in
mind that all database interactions in this exercise are assuming the use of PDO,  and as such
will probably require some reworking to accommodate your changes.

Framing Out a User Interactions Class

As we discussed in Part 2  of this series, we'll be taking the object-oriented approach  with


this app.All of these actions will be contained within our   ColoredListsUsers  class. We'll
also need to create several files that will  display information to the user and interact with the
class, which we'll cover as we get to them.

Building the Class


To get started, we need to  create the file  class.users.inc.php  to contain the PHP class,
which we'll place in the   inc  folder.
With the file created, let's build the skeleton of the class:

<?php 
 
/** 
 * Handles user interactions within the app 
 * 
 * PHP version 5 
 * 
 * @author Jason Lengstorf 
 * @author Chris Coyier 
 * @copyright 2009 Chris Coyier and Jason Lengstorf 
 * @license   http://www.opensource.org/licenses/mit-license.html  MI
T License 
 * 
 */ 
class ColoredListsUsers 

 
     
 

 
 
?>

Connecting the Class to the Database


Before our class can do much of anything,  it needs to have access to the database object  we
created in  base.php . Our database connection within the object will be stored in a private
property called  $_db , and this property will be set by the class  constructor , which will accept
the instance of PDO created in   base.php  as an argument. If no instance of PDO is passed,
one will be created by the constructor.

This ends up looking like this:

class ColoredListsUsers 

    /** 
     * The database object 
     * 
     * @var object 
     */ 
    private $_db; 
 
    /** 
     * Checks for a database object and creates one if none is found 
     * 
     * @param object $db 
     * @return void 
     */ 
    public function __construct($db=NULL) 
    { 
        if(is_object($db)) 
        { 
            $this->_db = $db; 
        } 
        else 
        { 
            $dsn = "mysql:host=".DB_HOST.";dbname=".DB_NAME; 
            $this->_db = new PDO($dsn, DB_USER, DB_PASS); 
        } 
    } 
}
Now we are able to create an instance of our   ColoredListsUsers  object and use it to
communicate with our database. Next, let's start building user interactions!

Creating an Account
First and foremost, a user needs to be able to create an account.  This will give them access to
the rest of the site's functionality.

As it stands, when a user visits our app, they're greeted with our "sales" page, which encourages
them to click the "Sign Up" button in the top right of their screen:

The home screen of our app


Clicking that "Sign Up" button directs the user to   /signup.php —our first order of business
should probably be to build that page.

Creating the Sign-Up Form


In our app's root directory, create a file called   signup.php  and place the following code
inside:

<?php 
    include_once "common/base.php"; 
    $pageTitle = "Register"; 
    include_once "common/header.php"; 
 
    if(!empty($_POST['username'])): 
        include_once "inc/class.users.inc.php"; 
        $users = new ColoredListsUsers($db); 
        echo $users->createAccount(); 
    else: 
?> 
 
        <h2>Sign up</h2> 
        <form method="post" action="signup.php" id="registerform"> 
            <div> 
                <label for="username">Email:</label> 
                <input type="text" name="username" id="username" /><b
r /> 
                <input type="submit" name="register" id="register" va
lue="Sign up" /> 
            </div> 
        </form> 
 
<?php 
    endif; 
    include_once 'common/close.php'; 
?>
To start, we include our   common/base.php  and  common/header.php  files. Also, notice
that we're declaring a variable called   $pageTitle  just before we include the header.
Remember in Part 4  when we built the header file and left that comment in the title tag?

<title>Colored Lists | <!-- Do Something Smart Here --></title>
We're going to replace that with a snippet of PHP that reads:

<title>Colored Lists | <?php echo $pageTitle ?></title>
That gives us the opportunity to post a different title for each page of our app.

With the proper files included, we can then  create our sign-up form. The form will submit
to signup.php —itself—so we need to place an   if-else  check to see if the form has been
submitted. If so, we create a new   ColoredListsUsers  object and call
the  createAccount() method (which we'll write in the next section).

Finally, we close the  if-else  statement and include the footer. Our sign-up page should look
like this:

The sign-up page.

Notice the use of alternative syntax for the   if-else  statement. Normally, I don't like to use
this format, but in the case of outputting HTML, I prefer the way it ends with   endif;  instead of
a closing curly brace ( } ), which helps with readability in the script.

Saving the User's Email Address


With our sign-up form ready, we need to write the   createAccount()  method that will be
called when a user submits the form. This method will be public. Let's go back
to inc/class.users.inc.php  and declare this method:

class ColoredListsUsers 

    // Class properties and other methods omitted to save space 
 
    /** 
     * Checks and inserts a new account email into the database 
     * 
     * @return string    a message indicating the action status 
     */ 
    public function createAccount() 
    { 
        $u = trim($_POST['username']); 
        $v = sha1(time()); 
         
        $sql = "SELECT COUNT(Username) AS theCount 
                FROM users 
                WHERE Username=:email"; 
        if($stmt = $this->_db->prepare($sql)) { 
            $stmt->bindParam(":email", $u, PDO::PARAM_STR); 
            $stmt->execute(); 
            $row = $stmt->fetch(); 
            if($row['theCount']!=0) { 
                return "<h2> Error </h2>" 
                    . "<p> Sorry, that email is already in use. " 
                    . "Please try again. </p>"; 
            } 
            if(!$this->sendVerificationEmail($u, $v)) { 
                return "<h2> Error </h2>" 
                    . "<p> There was an error sending your" 
                    . " verification email. Please " 
                    . "<a href=\"mailto:[email protected]\">conta
ct " 
                    . "us</a> for support. We apologize for the " 
                    . "inconvenience. </p>"; 
            } 
            $stmt->closeCursor(); 
        } 
         
        $sql = "INSERT INTO users(Username, ver_code) 
                VALUES(:email, :ver)"; 
        if($stmt = $this->_db->prepare($sql)) { 
            $stmt->bindParam(":email", $u, PDO::PARAM_STR); 
            $stmt->bindParam(":ver", $v, PDO::PARAM_STR); 
            $stmt->execute(); 
            $stmt->closeCursor(); 
 
            $userID = $this->_db->lastInsertId(); 
            $url = dechex($userID); 
 
            /* 
             * If the UserID was successfully 
             * retrieved, create a default list. 
             */ 
            $sql = "INSERT INTO lists (UserID, ListURL) 
                    VALUES ($userID, $url)"; 
            if(!$this->_db->query($sql)) { 
                return "<h2> Error </h2>" 
                    . "<p> Your account was created, but " 
                    . "creating your first list failed. </p>"; 
            } else { 
                return "<h2> Success! </h2>" 
                    . "<p> Your account was successfully " 
                    . "created with the username <strong>$u</strong>.

                    . " Check your email!"; 
            } 
        } else { 
            return "<h2> Error </h2><p> Couldn't insert the " 
                . "user information into the database. </p>"; 
        } 
    } 
}
This method follows several steps to create an account: first, it  retrieves the posted email
address from the form (stored in the  $_POST  superglobal ) and generates a hard-to-guess
verification code (the SHA1 hash of the current timestamp); second, it  makes sure the supplied
email address isn't already in use;  third, it generates and sends a verification email to the
user with instructions on how to verify their account (we'll define the method that does this in the
next section); fourth, it stores the email address and verification code in the database;  and
finally, it creates a list for the user.

Each of these steps is monitored, and if any of them should fail, a specific error message is
generated. Upon success, a message is generated to let the user know they should expect an
email.

Generating and Sending a Verification Email


When the user creates an account, we need to  send them an email with a link that will
confirm their account.  This is a precautionary measure that proves the user provided a real
email address that they have access to and prevents a ton of spam accounts from being created
easily.

To send the email, we'll be using the built-in   mail()  function.


In  inc/class.users.inc.php , create the
private  sendVerificationEmail()  method by inserting the following code:

class ColoredListsUsers 

    // Class properties and other methods omitted to save space 
 
    /** 
     * Sends an email to a user with a link to verify their new accou
nt 
     * 
     * @param string $email    The user's email address 
     * @param string $ver    The random verification code for the use

     * @return boolean        TRUE on successful send and FALSE on fa
ilure 
     */ 
    private function sendVerificationEmail($email, $ver) 
    { 
        $e = sha1($email); // For verification purposes 
        $to = trim($email); 
     
        $subject = "[Colored Lists] Please Verify Your Account"; 
 
        $headers = <<<MESSAGE 
From: Colored Lists <[email protected]
Content-Type: text/plain; 
MESSAGE; 
 
        $msg = <<<EMAIL 
You have a new account at Colored Lists! 
 
To get started, please activate your account and choose a 
password by following the link below. 
 
Your Username: $email 
 
Activate your account: http://coloredlists.com/accountverify.php?
v=$ver&e=$e 
 
If you have any questions, please contact [email protected]
 
-- 
Thanks! 
 
Chris and Jason 
www.ColoredLists.com 
EMAIL; 
 
        return mail($to, $subject, $msg, $headers); 
    } 
}
The most important part of this method is the activation
link, http://coloredlists.com/accountverify.php?v=$ver&e=$e . This
link sends the user to our app's account verification file  (which we'll write in the next
step) and sends the user's hashed email address along with their verification code in the
URI. This will allow us to identify and verify the user when they follow the link.

Verifying the User's Account


After our user follows the verification link in the email, we need to  check that their email and
verification code are valid, and then allow them to choose a password.  After they choose a
password, we need to update the database to reflect the user's new password, as well as
setting the account's status to verified.

First, let's create a new file called   accountverify.php  in the root level of our app. Inside,
place the following code:

<?php 
    include_once "common/base.php"; 
    $pageTitle = "Verify Your Account"; 
    include_once "common/header.php"; 
 
    if(isset($_GET['v']) && isset($_GET['e'])) 
    { 
        include_once "inc/class.users.inc.php"; 
        $users = new ColoredListsUsers($db); 
        $ret = $users->verifyAccount(); 
    } 
    elseif(isset($_POST['v'])) 
    { 
        include_once "inc/class.users.inc.php"; 
        $users = new ColoredListsUsers($db); 
        $ret = $users->updatePassword(); 
    } 
    else 
    { 
        header("Location: /signup.php"); 
        exit; 
    } 
 
    if(isset($ret[0])): 
        echo isset($ret[1]) ? $ret[1] : NULL; 
 
        if($ret[0]<3): 
?> 
 
        <h2>Choose a Password</h2> 
 
        <form method="post" action="accountverify.php"> 
            <div> 
                <label for="p">Choose a Password:</label> 
                <input type="password" name="p" id="p" /><br />       
          
                <label for="r">Re-Type Password:</label> 
                <input type="password" name="r" id="r" /><br /> 
                <input type="hidden" name="v" value="<?php echo $_GE
T['v'] ?>" /> 
                <input type="submit" name="verify" id="verify" value=
"Verify Your Account" /> 
            </div> 
        </form> 
 
<?php 
        endif; 
    else: 
        echo '<meta http-equiv="refresh" content="0;/">'; 
    endif; 
 
    include_once("common/ads.php"); 
    include_once 'common/close.php'; 
?>

Verifying the User's Email and Verification Code


Before we can allow our user to select a password, we need to  make sure that their account
exists, that their email matches their verification code, and that their account is
unverified. To do that, we need a new method
in  inc/class.users.inc.php  called  verifyAccount() :

class ColoredListsUsers 

    // Class properties and other methods omitted to save space 
 
    /** 
     * Checks credentials and verifies a user account 
     * 
     * @return array    an array containing a status code and status 
message 
     */ 
    public function verifyAccount() 
    { 
        $sql = "SELECT Username 
                FROM users 
                WHERE ver_code=:ver 
                AND SHA1(Username)=:user 
                AND verified=0"; 
 
        if($stmt = $this->_db->prepare($sql)) 
        { 
            $stmt->bindParam(':ver', $_GET['v'], PDO::PARAM_STR); 
            $stmt->bindParam(':user', $_GET['e'], PDO::PARAM_STR); 
            $stmt->execute(); 
            $row = $stmt->fetch(); 
            if(isset($row['Username'])) 
            { 
                // Logs the user in if verification is successful 
                $_SESSION['Username'] = $row['Username']; 
                $_SESSION['LoggedIn'] = 1; 
            } 
            else 
            { 
                return array(4, "<h2>Verification Error</h2>\n" 
                    . "<p>This account has already been verified. " 
                    . "Did you <a href=\"/password.php\">forget " 
                    . "your password?</a>"); 
            } 
            $stmt->closeCursor(); 
 
            // No error message is required if verification is succes
sful 
            return array(0, NULL); 
        } 
        else 
        { 
            return array(2, "<h2>Error</h2>\n<p>Database error.</p>")

        } 
    } 
}
This method executes a query that loads the user name stored in the database with the
verification code, hashed user name, and a verified status of   0 . If a user name is returned, login
credentials are stored.  This method returns an array with an error code in the first index,
and a message in the second.  The error code  0  means nothing went wrong.

Updating the User's Password and Verified Status


Once the user has selected a password and submitted the form, the   if-else  statement will
catch the verification code sent using the   POST  method and execute
the  updatePassword()  method. This method needs to set the account status to verified and
save the user's hashed password in the database. Let's build this method
in  ColoredListsUsers :

class ColoredListsUsers 

    // Class properties and other methods omitted to save space 
 
    /** 
     * Changes the user's password 
     * 
     * @return boolean    TRUE on success and FALSE on failure 
     */ 
    public function updatePassword() 
    { 
        if(isset($_POST['p']) 
        && isset($_POST['r']) 
        && $_POST['p']==$_POST['r']) 
        { 
            $sql = "UPDATE users 
                    SET Password=MD5(:pass), verified=1 
                    WHERE ver_code=:ver 
                    LIMIT 1"; 
            try 
            { 
                $stmt = $this->_db->prepare($sql); 
                $stmt->bindParam(":pass", $_POST['p'], PDO::PARAM_STR
); 
                $stmt->bindParam(":ver", $_POST['v'], PDO::PARAM_STR)

                $stmt->execute(); 
                $stmt->closeCursor(); 
 
                return TRUE; 
            } 
            catch(PDOException $e) 
            { 
                return FALSE; 
            } 
        } 
        else 
        { 
            return FALSE; 
        } 
    } 
}
Finally, since verifying an account logs a user in, we need to update   common/header.php  to
recognize that a user is logged in and display different options.  In Part
4 ,  common/header.php featured a code snippet that looked like this:

<!-- IF LOGGED IN --> 
                <p><a href="/logout.php" class="button">Log out</a> <
a href="/account.php" class="button">Your Account</a></p> 
 
<!-- IF LOGGED OUT --> 
                <p><a class="button" href="/signup.php">Sign up</a> &
nbsp; <a class="button" href="/login.php">Log in</a></p> 
<!-- END OF IF STATEMENT -->
To make those comments into functional code, we need to modify this snippet with an   if-else
block:

<?php 
    if(isset($_SESSION['LoggedIn']) && isset($_SESSION['Username']) 
        && $_SESSION['LoggedIn']==1): 
?> 
                <p><a href="/logout.php" class="button">Log out</a> <
a href="/account.php" class="button">Your Account</a></p> 
<?php else: ?> 
                <p><a class="button" href="/signup.php">Sign up</a> &
nbsp; <a class="button" href="/login.php">Log in</a></p> 
<?php endif; ?>
Notice that we store in the session both the user name ( $_SESSION['Username'] ) and a
flag that tells us if the user is logged in ( $_SESSION['LoggedIn'] ).
Logging In
Next, let's build the login form and allow our user to log in.  To start, let's create a new file
named login.php  at the root level of our app. Like our other publicly displayed files, this will
include the base and header files. Then it checks if a user is already logged in, if the login form
was submitted, or if the user needs to log in.

If logged in, the user is notified of this fact and asked if he or she wishes to log out.

If the form has been submitted, a new   ColoredListsUsers  object is created and
the accountLogin()  method is called. If the login succeeds, the user is directed to the home
page, where his or her list will appear; otherwise, the login form is displayed again with an error.

If neither of the previous conditions exists, the login form is displayed.

Finally, the sidebar ads and footer are included to round out the file.

When the file is all put together, it should look like this:

<?php 
    include_once "common/base.php"; 
    $pageTitle = "Home"; 
    include_once "common/header.php"; 
 
    if(!empty($_SESSION['LoggedIn']) && !
empty($_SESSION['Username'])): 
?> 
 
        <p>You are currently <strong>logged in.</strong></p> 
        <p><a href="/logout.php">Log out</a></p> 
<?php 
    elseif(!empty($_POST['username']) && !empty($_POST['password'])): 

        include_once 'inc/class.users.inc.php'; 
        $users = new ColoredListsUsers($db); 
        if($users->accountLogin()===TRUE): 
            echo "<meta http-equiv='refresh' content='0;/'>"; 
            exit; 
        else: 
?> 
                 
        <h2>Login Failed&mdash;Try Again?</h2> 
        <form method="post" action="login.php" name="loginform" id="l
oginform"> 
            <div> 
                <input type="text" name="username" id="username" /> 
                <label for="username">Email</label> 
                <br /><br /> 
                <input type="password" name="password" id="password" 
/> 
                <label for="password">Password</label> 
                <br /><br /> 
                <input type="submit" name="login" id="login" value="L
ogin" class="button" /> 
            </div> 
        </form> 
        <p><a href="/password.php">Did you forget your password?
</a></p> 
<?php 
        endif; 
    else: 
?> 
               
        <h2>Your list awaits...</h2> 
        <form method="post" action="login.php" name="loginform" id="l
oginform"> 
            <div> 
                <input type="text" name="username" id="username" /> 
                <label for="username">Email</label> 
                <br /><br /> 
                <input type="password" name="password" id="password" 
/> 
                <label for="password">Password</label> 
                <br /><br /> 
                <input type="submit" name="login" id="login" value="L
ogin" class="button" /> 
            </div> 
        </form><br /><br /> 
        <p><a href="/password.php">Did you forget your password?
</a></p> 
<?php 
    endif; 
?> 
 
        <div style="clear: both;"></div> 
<?php 
    include_once "common/ads.php"; 
    include_once "common/close.php"; 
?>
Notice the "Did you forget your password?" links — we'll be building this functionality a little later
on in the article.

Building the Login Method


Now we need to build the   accountLogin()  method. This method will  compare the supplied
user name and the MD5 hash of the supplied password to verify that there is a matching
pair in the database.  If a match is found, the user's name and a login flag are stored in the
session and the method returns   TRUE . If no match is found, the method returns   FALSE .

Build this method in  ColoredListsUsers  by inserting the following code:

class ColoredListsUsers 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Checks credentials and logs in the user 
     * 
     * @return boolean    TRUE on success and FALSE on failure 
     */ 
    public function accountLogin() 
    { 
        $sql = "SELECT Username 
                FROM users 
                WHERE Username=:user 
                AND Password=MD5(:pass) 
                LIMIT 1"; 
        try 
        { 
            $stmt = $this->_db->prepare($sql); 
            $stmt->bindParam(':user', $_POST['username'], PDO::PARAM_
STR); 
            $stmt->bindParam(':pass', $_POST['password'], PDO::PARAM_
STR); 
            $stmt->execute(); 
            if($stmt->rowCount()==1) 
            { 
                $_SESSION['Username'] = htmlentities($_POST['username
'], ENT_QUOTES); 
                $_SESSION['LoggedIn'] = 1; 
                return TRUE; 
            } 
            else 
            { 
                return FALSE; 
            } 
        } 
        catch(PDOException $e) 
        { 
            return FALSE; 
        } 
    } 
}
Logging Out
Next, our user needs to be able to log out. This is as easy as destroying the login data stored in
the session and sending the user back to the login page.

Create a new file named   logout.php  at the root level of the app and place the following code
inside:

<?php 
 
    session_start(); 
     
    unset($_SESSION['LoggedIn']); 
    unset($_SESSION['Username']); 
 
?> 
 
<meta http-equiv="refresh" content="0;login.php">

Modifying Account Information


Next, we need to allow our users to modify their account information.  In order to do that, we
need to provide an "Account" page that will give them options to  change their user name or
password, as well as the option to delete their account.

Create a file named  account.php  at the root level of the app. There's a lot going on here
because we're essentially combining three app functions within one file.

First, we include the base file and check that the user is logged in.  If not, he or she gets sent
out to the main page.

If the user is logged in, we  check if any actions have already been attempted  and assemble
the corresponding success or failure messages if any are found.

Then we load the user's ID and verification code  using the


method  retrieveAccountInfo() and build three forms: one to update the user name
(which is an email address, remember), one to change the account password, and one to delete
the account.

Finally, we include the sidebar ads and the footer. Altogether, the file should look like this:

<?php 
    include_once "common/base.php"; 
    if(isset($_SESSION['LoggedIn']) && $_SESSION['LoggedIn']==1): 
        $pageTitle = "Your Account"; 
        include_once "common/header.php"; 
        include_once 'inc/class.users.inc.php'; 
        $users = new ColoredListsUsers($db); 
 
        if(isset($_GET['email']) && $_GET['email']=="changed") 
        { 
            echo "<div class='message good'>Your email address " 
                . "has been changed.</div>"; 
        } 
        else if(isset($_GET['email']) && $_GET['email']=="failed") 
        { 
            echo "<div class='message bad'>There was an error " 
                . "changing your email address.</div>"; 
        } 
 
        if(isset($_GET['password']) && $_GET['password']=="changed") 
        { 
            echo "<div class='message good'>Your password " 
                . "has been changed.</div>"; 
        } 
        elseif(isset($_GET['password']) && $_GET['password']=="nomatc
h") 
        { 
            echo "<div class='message bad'>The two passwords " 
                . "did not match. Try again!</div>"; 
        } 
 
        if(isset($_GET['delete']) && $_GET['delete']=="failed") 
        { 
            echo "<div class='message bad'>There was an error " 
                . "deleting your account.</div>"; 
        } 
 
        list($userID, $v) = $users->retrieveAccountInfo(); 
?> 
 
        <h2>Your Account</h2> 
        <form method="post" action="db-interaction/users.php"> 
            <div> 
                <input type="hidden" name="userid" 
                    value="<?php echo $userID ?>" /> 
                <input type="hidden" name="action" 
                    value="changeemail" /> 
                <input type="text" name="username" id="username" /> 
                <label for="username">Change Email Address</label> 
                <br /><br /> 
                <input type="submit" name="change-email-submit" 
                    id="change-email-submit" value="Change Email" 
                    class="button" /> 
            </div> 
        </form><br /><br /> 
 
        <form method="post" action="db-interaction/users.php" 
            id="change-password-form"> 
            <div> 
                <input type="hidden" name="user-id" 
                    value="<?php echo $userID ?>" /> 
                <input type="hidden" name="v" 
                    value="<?php echo $v ?>" /> 
                <input type="hidden" name="action" 
                    value="changepassword" /> 
                <input type="password" 
                    name="p" id="new-password" /> 
                <label for="password">New Password</label> 
                <br /><br /> 
                <input type="password" name="r" 
                    id="repeat-new-password" /> 
                <label for="password">Repeat New Password</label> 
                <br /><br /> 
                <input type="submit" name="change-password-submit" 
                    id="change-password-submit" value="Change Passwor
d" 
                    class="button" /> 
            </div> 
        </form> 
        <hr /> 
 
        <form method="post" action="deleteaccount.php" 
            id="delete-account-form"> 
            <div> 
                <input type="hidden" name="user-id" 
                    value="<?php echo $userID ?>" /> 
                <input type="submit" 
                    name="delete-account-submit" id="delete-account-
submit" 
                    value="Delete Account?" class="button" /> 
            </div> 
        </form> 
 
<?php 
    else: 
        header("Location: /"); 
        exit; 
    endif; 
?> 
 
<div class="clear"></div> 
 
<?php 
    include_once "common/ads.php"; 
    include_once "common/close.php"; 
?>

Creating the Method to Retrieve Account Info


In order to have the user's login name and verification code available to our account option
forms, we need to build a new method that will load this information from the database.
In inc/class.users.inc.php , create a new method in   ColoredListsUsers  called
retrieveAccountInfo()  and add the following code:

class ColoredListsUsers 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Retrieves the ID and verification code for a user 
     * 
     * @return mixed    an array of info or FALSE on failure 
     */ 
    public function retrieveAccountInfo() 
    { 
        $sql = "SELECT UserID, ver_code 
                FROM users 
                WHERE Username=:user"; 
        try 
        { 
            $stmt = $this->_db->prepare($sql); 
            $stmt->bindParam(':user', $_SESSION['Username'], PDO::PAR
AM_STR); 
            $stmt->execute(); 
            $row = $stmt->fetch(); 
            $stmt->closeCursor(); 
            return array($row['UserID'], $row['ver_code']); 
        } 
        catch(PDOException $e) 
        { 
            return FALSE; 
        } 
    } 
}

Building the Interactions File


In  account.php , all three forms direct to a file called   db-
interaction/users.php  when submitted. This file helps relieve some of the clutter
in  account.php  by determining form actions, creating a   ColoredListsUsers  object, and
calling the appropriate methods to handle the action.

This file will be placed in a new folder called   db-interaction , and it will be
named  users.php . Place the following code in the new file:

<?php 
 
session_start(); 
 
include_once "../inc/constants.inc.php"; 
include_once "../inc/class.users.inc.php"; 
$userObj = new ColoredListsUsers(); 
 
if(!empty($_POST['action']) 
&& isset($_SESSION['LoggedIn']) 
&& $_SESSION['LoggedIn']==1) 
{     
    switch($_POST['action']) 
    { 
        case 'changeemail': 
            $status = $userObj->updateEmail() ? "changed" : "failed"; 

            header("Location: /account.php?email=$status"); 
            break; 
        case 'changepassword': 
            $status = $userObj->updatePassword() ? "changed" : "nomat
ch"; 
            header("Location: /account.php?password=$status"); 
            break; 
        case 'deleteaccount': 
            $userObj->deleteAccount(); 
            break; 
        default: 
            header("Location: /"); 
            break; 
    } 

elseif($_POST['action']=="resetpassword") 

    if($resp=$userObj->resetPassword()===TRUE) 
    { 
        header("Location: /resetpending.php"); 
    } 
    else 
    { 
        echo $resp; 
    } 
    exit; 

else 

    header("Location: /"); 
    exit; 

 
?>

Updating the Email Address


When a user submits a request to change their email address, the method   updateEmail()  is
called. This function simply executes a query that changes the email address associated with an
account. It returns  TRUE  if the email is successfully changed, and   FALSE  otherwise.

class ColoredListsUsers 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Changes a user's email address 
     * 
     * @return boolean    TRUE on success and FALSE on failure 
     */ 
    public function updateEmail() 
    { 
        $sql = "UPDATE users 
                SET Username=:email 
                WHERE UserID=:user 
                LIMIT 1"; 
        try 
        { 
            $stmt = $this->_db->prepare($sql); 
            $stmt->bindParam(':email', $_POST['username'], PDO::PARAM
_STR); 
            $stmt->bindParam(':user', $_POST['userid'], PDO::PARAM_IN
T); 
            $stmt->execute(); 
            $stmt->closeCursor(); 
     
            // Updates the session variable 
            $_SESSION['Username'] = htmlentities($_POST['username'], 
ENT_QUOTES); 
     
            return TRUE; 
        } 
        catch(PDOException $e) 
        { 
            return FALSE; 
        } 
    } 
}

Updating the Password


Quite similarly to  updateEmail() ,  updatePassword()  is called if the user submits a
request to change their password. The only difference in the methods is that this one
will compare the password and the password confirmation to make sure they match before
saving.

class ColoredListsUsers 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Changes the user's password 
     * 
     * @return boolean    TRUE on success and FALSE on failure 
     */ 
    public function updatePassword() 
    { 
        if(isset($_POST['p']) 
        && isset($_POST['r']) 
        && $_POST['p']==$_POST['r']) 
        { 
            $sql = "UPDATE users 
                    SET Password=MD5(:pass), verified=1 
                    WHERE ver_code=:ver 
                    LIMIT 1"; 
            try 
            { 
                $stmt = $this->_db->prepare($sql); 
                $stmt->bindParam(":pass", $_POST['p'], PDO::PARAM_STR
); 
                $stmt->bindParam(":ver", $_POST['v'], PDO::PARAM_STR)

                $stmt->execute(); 
                $stmt->closeCursor(); 
 
                return TRUE; 
            } 
            catch(PDOException $e) 
            { 
                return FALSE; 
            } 
        } 
        else 
        { 
            return FALSE; 
        } 
    } 
}

Deleting the Account


If the user wants to delete their account, we need to go through several steps. First, we need to
double-check that the user is logged in,  because we certainly don't want any accidental
account deletions. If the user is logged in, we then  delete their list items. If the list items are
successfully deleted, we move on to  delete the user's lists.  Finally, if the lists are successfully
deleted, we delete the user from the database, destroy their session information, and send
them to a page called gone.php , which we'll build in a minute.

The method, when it's all written, will look like this:

class ColoredListsUsers 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Deletes an account and all associated lists and items 
     * 
     * @return void 
     */ 
    public function deleteAccount() 
    { 
        if(isset($_SESSION['LoggedIn']) && $_SESSION['LoggedIn']==1) 
        { 
            // Delete list items 
            $sql = "DELETE FROM list_items 
                    WHERE ListID=( 
                        SELECT ListID 
                        FROM lists 
                        WHERE UserID=:user 
                        LIMIT 1 
                    )"; 
            try 
            { 
                $stmt = $this->_db->prepare($sql); 
                $stmt->bindParam(":user", $_POST['user-id'], PDO::PAR
AM_INT); 
                $stmt->execute(); 
                $stmt->closeCursor(); 
            } 
            catch(PDOException $e) 
            { 
                die($e->getMessage()); 
            } 
 
            // Delete the user's list(s) 
            $sql = "DELETE FROM lists 
                    WHERE UserID=:user"; 
            try 
            { 
                $stmt = $this->_db->prepare($sql); 
                $stmt->bindParam(":user", $_POST['user-id'], PDO::PAR
AM_INT); 
                $stmt->execute(); 
                $stmt->closeCursor(); 
            } 
            catch(PDOException $e) 
            { 
                die($e->getMessage()); 
            } 
             
            // Delete the user 
            $sql = "DELETE FROM users 
                    WHERE UserID=:user 
                    AND Username=:email"; 
            try 
            { 
                $stmt = $this->_db->prepare($sql); 
                $stmt->bindParam(":user", $_POST['user-id'], PDO::PAR
AM_INT); 
                $stmt->bindParam(":email", $_SESSION['Username'], PDO
::PARAM_STR); 
                $stmt->execute(); 
                $stmt->closeCursor(); 
            } 
            catch(PDOException $e) 
            { 
                die($e->getMessage()); 
            } 
 
            // Destroy the user's session and send to a confirmation 
page 
            unset($_SESSION['LoggedIn'], $_SESSION['Username']); 
            header("Location: /gone.php"); 
            exit; 
        } 
        else 
        { 
            header("Location: /account.php?delete=failed"); 
            exit; 
        } 
    } 
}

Resetting an Account Password


At this point, we're almost done. The last thing we need to do is  allow a user to reset a
forgotten password.  To do this, we need to create the file   password.php  at the root level of
our app and place the following code inside:

<?php 
    include_once "common/base.php"; 
    $pageTitle = "Reset Your Password"; 
    include_once "common/header.php"; 
?> 
 
        <h2>Reset Your Password</h2> 
        <p>Enter the email address you signed up with and we'll send 
        you a link to reset your password.</p> 
 
        <form action="db-interaction/users.php" method="post"> 
            <div> 
                <input type="hidden" name="action" 
                    value="resetpassword" /> 
                <input type="text" name="username" id="username" /> 
                <label for="username">Email</label><br /><br /> 
                <input type="submit" name="reset" id="reset" 
                    value="Reset Password" class="button" /> 
            </div> 
        </form> 
<?php 
    include_once "common/ads.php"; 
    include_once "common/close.php"; 
?>
When a user visits this page, they'll be able to enter their email address. Submitting the form will
return the account to unverified and send the user an email with a link to reset their
password.

Returning the Account to "Unverified" Status


When the form in  password.php  is submitted, the information is sent to   db-
interaction/users.php  and the  resetPassword()  method is called before sending
the user to  resetpending.php .
The  resetPassword()  method sets the verified field of our user's database entry to   0 , then
calls the  sendResetEmail()  method.

class ColoredListsUsers 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Resets a user's status to unverified and sends them an email 
     * 
     * @return mixed    TRUE on success and a message on failure 
     */ 
    public function resetPassword() 
    { 
        $sql = "UPDATE users 
                SET verified=0 
                WHERE Username=:user 
                LIMIT 1"; 
        try 
        { 
            $stmt = $this->_db->prepare($sql); 
            $stmt->bindParam(":user", $_POST['username'], PDO::PARAM_
STR); 
            $stmt->execute(); 
            $stmt->closeCursor(); 
        } 
        catch(PDOException $e) 
        { 
            return $e->getMessage(); 
        } 
 
        // Send the reset email 
        if(!$this->sendResetEmail($_POST['username'], $v)) 
        { 
            return "Sending the email failed!"; 
        } 
        return TRUE; 
    } 
}

Building the Reset Pending Page


After the user's account is back in an unverified state and the email has been sent with their
password reset link, we send them to   resetpending.php  to let them know what their next
steps are. Create this file at the root level of the app and insert the following:
<?php 
    include_once "common/base.php"; 
    $pageTitle = "Reset Pending"; 
    include_once "common/header.php"; 
?> 
             
        <h2>Password Reset Requested</h2> 
        <p>Check your email to finish the reset process.</p> 
<?php 
    include_once "common/ads.php"; 
    include_once "common/close.php"; 
?>

Generating a "Reset Password" Email


The  sendResetEmail()  method is very similar to
the  sendVerificationEmail()  method. The main difference here is that the link sent to
the user directs them to a page called resetpassword.php  where they're able to choose a
new password.

class ColoredListsUsers 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Sends a link to a user that lets them reset their password 
     * 
     * @param string $email    the user's email address 
     * @param string $ver    the user's verification code 
     * @return boolean        TRUE on success and FALSE on failure 
     */ 
    private function sendResetEmail($email, $ver) 
    { 
        $e = sha1($email); // For verification purposes 
        $to = trim($email); 
     
        $subject = "[Colored Lists] Request to Reset Your Password"; 
 
        $headers = <<<MESSAGE 
From: Colored Lists <[email protected]
Content-Type: text/plain; 
MESSAGE; 
 
        $msg = <<<EMAIL 
We just heard you forgot your password! Bummer! To get going again, 
head over to the link below and choose a new password. 
 
Follow this link to reset your password: 
http://coloredlists.com/resetpassword.php?v=$ver&e=$e 
 
If you have any questions, please contact [email protected]
 
-- 
Thanks! 
 
Chris and Jason 
www.ColoredLists.com 
EMAIL; 
 
        return mail($to, $subject, $msg, $headers); 
    } 
}

Resetting the Password


Our very last step in this part of the app is to create the file   resetpassword.php  in the root
level of the site. This file is very similar to the   accountverify.php  file we created earlier.
After including the base and header files, it checks if the user is just arriving from their reset
email.

If so, we are able to use the   verifyAccount()  method we wrote earlier to ensure that their
credentials are correct. After verifying their credentials, we  display a form that allows them to
choose a password and confirm it.

After submitting the form, our script will fire the   updatePassword()  method we created
earlier to save the new password.  Then we redirect the user to   account.php , where they're
shown a confirmation message letting them know that their password was changed.

Inside  resetpassword.php , add the following code:

<?php 
    include_once "common/base.php"; 
 
    if(isset($_GET['v']) && isset($_GET['e'])) 
    { 
        include_once "inc/class.users.inc.php"; 
        $users = new ColoredListsUsers($db); 
        $ret = $users->verifyAccount(); 
    } 
    elseif(isset($_POST['v'])) 
    { 
        include_once "inc/class.users.inc.php"; 
        $users = new ColoredListsUsers($db); 
        $status = $users->updatePassword() ? "changed" : "failed"; 
        header("Location: /account.php?password=$status"); 
        exit; 
    } 
    else 
    { 
        header("Location: /login.php"); 
        exit; 
    } 
 
    $pageTitle = "Reset Your Password"; 
    include_once "common/header.php"; 
 
    if(isset($ret[0])): 
        echo isset($ret[1]) ? $ret[1] : NULL; 
 
        if($ret[0]<3): 
?> 
 
        <h2>Reset Your Password</h2> 
 
        <form method="post" action="accountverify.php"> 
            <div> 
                <label for="p">Choose a New Password:</label> 
                <input type="password" name="p" id="p" /><br />       
          
                <label for="r">Re-Type Password:</label> 
                <input type="password" name="r" id="r" /><br /> 
                <input type="hidden" name="v" value="<?php echo $_GE
T['v'] ?>" /> 
                <input type="submit" name="verify" id="verify" value=
"Reset Your Password" /> 
            </div> 
        </form> 
 
<?php 
        endif; 
    else: 
        echo '<meta http-equiv="refresh" content="0;/">'; 
    endif; 
 
    include_once("common/ads.php"); 
    include_once 'common/close.php'; 
?>

Moving On...
This article covered a whole lot of ground, and I went over some of it pretty quickly.  Please
don't hesitate to ask for clarification in the comments!
In the next part of this series, our front-end designer will use some dummy lists to
create AJAX effects. After he's finished with the dummy lists, we'll explore how to combine those
AJAX effects with our back-end and build the list interactions class in part 7.

Part VI

Our developer has already done a massive amount of work turning this idea into a real
application. Now let’s make some more for him! The most important part of this app is
creating and managing your list. We decided from the get-go that this was going to be an
AJAX-y app. We didn’t chose AJAX because it’s a popular buzzword, we chose it because we
know it’s the best path toward making a responsive, easy to use, natural feeling application
on the web.

The Big Thing: Saving the List


AJAX allows for us to send requests and get responses from the server without a page
refresh. In our app, that functionality is going to be used primarily for saving. Let’s think
through each of those times something needs to be saved:

 When a new list items is added, it should be saved to the list.


 When a list item is deleted, it should be deleted from the list.
 When an items color is changed, the new color should be saved.
 When a list item is marked as done, that status should be saved.
 When the list is reordered, the new order should be saved.
 When the text of a list item is altered, the new text should be saved.
That’s a lot of saving going on. Our developer has his work cut out for him, because each of
those little events needs to have a PHP file that is ready to receive that request and deal
with it. Fortunately he has some cool object oriented stuff gong on already and can certainly
extend that to deal with this.

Interface JavaScript
Alongside all that AJAX saving is all the stuff that makes the interface do what visually it’s
saying it will do. That little drag tab is saying it can drag list items up and down. We are
saying after that happens we are going to save the list. But how does that actually work?
Don’t worry, we’ll get to it. For now let’s think through all the interface JavaScript things
that we need:

 Click and drag the drag tab, list items can be dragged around and reordered.
 Click the color tab, the list items color is toggled between some predefined choices.
 Click the checkmark, the list item crosses out and and fades down.
 Click the X, a confirmation slides out. Click again, list item is whisked away.
 Double-click the list item, text turns into a text-input for editing.
 Type in the large box below and click add, new list item is appended to bottom of the
list.
And again, we’re going to get to all that. Just a little more setup to do!

First things first: calling the JavaScript files


There is really only one page on our site, the main list page, that needs JavaScript at all. So
we’ll be dropping the script files right into the index.php file. You’ll often see JavaScript file
linked in the header of sites. This isn’t the time for a lengthy discussion about that, but
suffice it to say that that isn’t required as it’s generally considered a performance
enhancement to list them at the end of pages instead. That’s just what we’ll do here. In our
index.php file, after we’ve output the list, we’ll call the JavaScript we need.

<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js?


ver=1.3.2'></script>
<script type="text/javascript" src="js/jquery-ui-1.7.2.custom.min.js"></script>
<script type="text/javascript" src="js/jquery.jeditable.mini.js"></script>
<script type="text/javascript" src="js/lists.js"></script>
<script type="text/javascript">
initialize();
</script>
1. Load jQuery (from Google for better speed)
2. Load a customized jQuery UI library (for the draggable stuff)
3. Load the jEditable plugin (for click-to-edit)
4. Load our custom script
5. Call the initialization function (our own kickstarter, kind of like a DOM ready
statement)

Cleaning up the Markup with JavaScript


In our custom lists.js file, we’ll be creating a number of different functions. The very first
one is the one that gets called directly in the index.php file after the lists have been output:

function initialize() {

};
The first thing we are going to do there is clean up the markup a bit by inserting common
elements with JavaScript rather than have them directly in the markup. Remember what the
markup for the list looked like when we mocked it up?

<ul id="list">
<li class="colorRed">
<span>Walk the dog</span>
<div class="draggertab tab"></div>
<div class="colortab tab"></div>
<div class="deletetab tab"></div>
<div class="donetab tab"></div>
</li>

<!-- more list items -->

</ul>
Much of that is redundant across all the list items. What we want is more like this:

<ul id="list">
<li class="colorRed">
Walk the dog
</li>
<!-- more list items -->

</ul>
All the divs and the span has been removed. The class name on the list item is fine, because
PHP will be spitting that out for us when it reads the database and outputs the list.

How do we append all that extra HTML? Easy with jQuery. Target the list items and wrap
each of the the innards using the wrapInner() function and append the extra divs with the
append() function.

function initialize() {

// WRAP LIST TEXT IN A SPAN, AND APPLY FUNCTIONALITY TABS


$("#list li")
.wrapInner("<span>")
.append("<div class='draggertab tab'></div><div class='colortab tab'></div></div><div
class='deletetab tab'></div><div class='donetab tab'></div>");

};
Bind events to the new functionality tabs, the smart way
Binding an event to an HTML element is pretty easy in JavaScript. It’s like this:
$("li").click(function() {
// do something
});
There is nothing wrong with that, but we are in a bit of a unique situation with our list app
here. When you bind events like that 1) it creates a unique event handler for every single
list item on the page, each one taking up browser memory and 2) it only does it once, for
the current state of the DOM. Don’t worry about all that too much, the point is binding
events this way isn’t ideal for us because we will be inserting new list items dynamically.

When a user inserts a new list item, that gets plugged into the DOM right then and there.
That new list item will not be bound as the others are, meaning all those fancy little tabs
won’t work right. Boo hoo. What can we do to solve that? Well we can create a new function
that will be called when the page loads and when new list items are appended that does all
that event binding work. That will definitely do the trick, but… jQuery is smarter than that.
jQuery provides a function called live() that eliminates this problem entirely.

$("li").live("click", function() {
// do something
});
Binding events with the live() function is fantastic for us because 1) it only creates one
event handler which is far more efficient and 2) new items appended to the page are
automatically bound by the same handler. Killer.

So for our little functionality tabs, we’ll be using them like this:

$(".donetab").live("click", function() {
// do stuff
});
$(".colortab").live("click", function(){
// do stuff
});

$(".deletetab").live("click", function(){
// do stuff
});
The drag tab doesn’t have click event, it’s actually going to use jQuery UI’s draggable
functionality to do it’s thing. Let’s check that out.

Making the list drag / sortable


Mega thanks to jQuery UI for making such a useful set of functions. The draggable module
is exactly perfect for making a list like our sortable. We target the parent <ul>, tell it the
“handle” we wish to use (which part of the list item you can click and drag to move them).
We also use a parameter forcePlaceholderSize for some visual feedback when the list items
are dragged around (a white block space pops in to indicate where the list item would “land”
if released)

$("#list").sortable({
handle : ".draggertab",
update : function(event, ui){

// Developer, this function fires after a list sort, commence list saving!

},
forcePlaceholderSize: true
});

Marking items as “done”


When the user clicks the little checkmark tab, we have already decided to do two things.
Draw a line through the list item and then fade that whole list item out. But then there is
the consideration of what to do if the item is already marked as done and that tab is clicked.
Well, we’ll uncross it out and fade it back up. So when the click happens, we’ll make sure to
check which state we are in first.

$(".donetab").live("click", function() {

if(!$(this).siblings('span').children('img.crossout').length) {
$(this)
.parent()
.find("span")
.append("<img src='/images/crossout.png' class='crossout' />")
.find(".crossout")
.animate({
width: "100%"
})
.end()
.animate({
opacity: "0.5"
},
"slow",
"swing",
function() {

// DEVELOPER, the user has marked this item as done, commence saving!

})
}
else
{
$(this)
.siblings('span')
.find('img.crossout')
.remove()
.end()
.animate({
opacity : 1
},
"slow",
"swing",
function() {

// DEVELOPER, the user has UNmarked this item as done, commence saving!

})

}
});
Color Cycling
We’d better get on this whole “colored” part of Colored Lists eh? CSS will be applying the
actual color, so what we’ll be doing with JavaScript is just cycling the class names applied to
those list items on clicks.

$(".colortab").live("click", function(){

$(this).parent().nextColor();

$.ajax({

// DEVELOPER, the user has toggled the color on this list item, commence saving!

});
});
That nextColor() function isn’t a built-in function, it will be custom written by us. It’s
abstracted away here for code clarity. The way that we’ve used it here (as a part of the
“chain”) is such that we’ll need to make a little jQuery plugin out of it. No problem.

jQuery.fn.nextColor = function() {

var curColor = $(this).attr("class");

if (curColor == "colorBlue") {
$(this).removeClass("colorBlue").addClass("colorYellow").attr("color","2");
} else if (curColor == "colorYellow") {
$(this).removeClass("colorYellow").addClass("colorRed").attr("color","3");
} else if (curColor == "colorRed") {
$(this).removeClass("colorRed").addClass("colorGreen").attr("color","4");
} else {
$(this).removeClass("colorGreen").addClass("colorBlue").attr("color","1");
};

};
Basically this check what color the list item already is and moves it to the next color. Notice
how we are altering an attribute on the list items too. Color isn’t a valid attribute in XHMTL
(it’s fine in HTML5), but oh well. It’s not in the markup so it doesn’t really matter. Why are
we using this? We’ll, it’s because we are about 50% of they way in doing this really
intelligently. When our developer goes to save the color information about this list item to
the database, he needs something to save. Back in Part 2 of this series, we can see that our
developer already anticipated this and created a field for color called listItemColor, which he
made an INT (integer). He figured that would be the smartest way to do it as it’s
lightweight, easy, and abstract. We can decide later what they key is, e.g., 1 = Blue, 2 =
Red, etc. The data itself doesn’t need to know it’s red. So, if we have an integer
representing the color right in the DOM for him, that makes it really easy to snag out and
pass along to save to the database.

Why is this only 50% smart? Well, because we should probably extend that smartness to
the class names themselves. We are using colorYellow for example, when color-1 might
make more sense, if down the line we decide to drop yellow from the lineup and replace it.
Or even perhaps let users declare their own colors.

Deleting list items


Our little “X” tab is in charge of allowing users to delete list items. We want to have a little
extra insurance against accidentally fat-fingerings though. So we are going to require two
clicks to actually delete something. Some applications resort to a nasty “ARE YOU SURE”
modal popup dialog box, we’ll be a little more sly than that. As you click the X, a little notice
will pop out to the right asking about sureness. If they click again then deleting may
commence.

$(".deletetab").live("click", function(){

var thiscache = $(this);

if (thiscache.data("readyToDelete") == "go for it") {


$.ajax({

// DEVELOPER, the user wants to delete this list item, commence deleting!

success: function(r){
thiscache
.parent()
.hide("explode", 400, function(){$(this).remove()});

// Make sure to reorder list items after a delete!

});
}
else
{
thiscache.animate({
width: "44px",
right: "-64px"
}, 200)
.data("readyToDelete", "go for it");
}
});
Because we were smart earlier and our little tab graphic is all a part of one sprite graphic,
all we need to do is expand the width of that tab to display the message. After the first
click, we append a little bit of data (jQuery’s data() function) to that list item saying to “go
for it”. Upon a second click, that test will be TRUE and we know we can commence the
deletion of that list item.

Since we are using jQuery UI, we tossed in a little extra fun flair with the “explode” option
for hiding elements.

Click-to-edit list items


In order to make our list items click-to-edit, we’ll stand on the shoulders of others and use a
jQuery plugin, jEditable. All we need to do with this plugin is target an element and use the
editable() function on it with some parameters. On big caveat though, we can’t use the
live() function with this plugin because it’s not a standard jQuery event.

Back before we had live, we did what we talked briefly earlier. We called a function that did
all our binding. That way we could call it on DOM ready as well as after any AJAX insertions.
We’ll lean on that technique now.

function bindAllTabs(editableTarget) {

$(editableTarget).editable("/path/for/DEVELOPER/to/save.php", {
id : 'listItemID',
indicator : 'Saving...',
tooltip : 'Double-click to edit...',
event : 'dblclick',
submit : 'Save',
submitdata: {action : "update"}
});

}
With those parameters, we’re giving the developer what he needs for a callback file path.
We’re also declaring that we want list items to require a double-click to edit, what the
tooltip will be, and what the text in the saving button will say. Very nicely customizeable!

Remember those spans we inserted around the text of the list items earlier. Now we are
cashing in on that. When we bind this editable function, we’ll bind it to those spans. So right
after DOM ready we’ll call:

bindAllTabs("#list li span");
We’re going to need this function again when appending new list items…

Appending new list items


Core to the functionality of our list app is allowing people to add new list items. We already
have the markup in place for that, so let’s look at the JavaScript that powers it.

$('#add-new').submit(function(){

var $whitelist = '<b><i><strong><em><a>',


forList = $("#current-list").val(),
newListItemText = strip_tags(cleanHREF($("#new-list-item-text").val()), $whitelist),
URLtext = escape(newListItemText),
newListItemRel = $('#list li').size()+1;

if(newListItemText.length > 0) {
$.ajax({

// DEVELOPER, save new list item!

success: function(theResponse){
$("#list").append("<li color='1' class='colorBlue' rel='"+newListItemRel+"' id='" + theResponse +
"'><span id=""+theResponse+"listitem" title='Click to edit...'>" + newListItemText + "</span><div
class='draggertab tab'></div><div class='colortab tab'></div><div class='deletetab tab'></div><div
class='donetab tab'></div></li>");
bindAllTabs("#list li[rel='"+newListItemRel+"'] span");
$("#new-list-item-text").val("");
},
error: function(){
// uh oh, didn't work. Error message?
}
});
} else {
$("#new-list-item-text").val("");
}
return false; // prevent default form submission
});

NOTE: Stay tuned for the final part in the series where will go over some extra security that
needs to happen here. Anytime we accept input from the user, it needs to be scrubbed. This
is no exception.
One last thing we did there was to clear the input field after submission. That makes adding
a bunch of list items in sequence very easy and natural. Also, note that we called that
bindAllTabs function again after the new list item was appended. That’s why we abstracted
that function away to begin with, so we could call it when new list items were appended
AJAX style. That ensures the click-to-edit functionality is working on newly appended list
items even before a page refresh. The rest of the tabs are automatically cool, due to the
live() binding.

Moving On
Up next we’ll pass it back to the developer for filling in those gaps on how those list
interactions work from the PHP / Database side. Then we’ll finish up talking a little security
and wrapping up any loose ends.

Part VII
Where We're At
Now that we've got a set of AJAX controls mostly assembled, we can start building our PHP
class tohandle list interactions.  This class will be called   ColoredListsItems  and it will
reside in the file class.lists.inc.php  in the  inc  folder.

This class will contain methods to  handle all of the actions performed by our app or our
users regarding list items.  Namely, these actions are:

 Displaying list items


 Saving new list items
 Reordering list items
 Changing item colors
 Editing item text
 Marking an item as "done"
 Deleting items

Also, because we need to be able to  load a non-editable version of a list when viewed from
the public URL, we'll need to add that functionality as well.

Defining the Class


Before we can do much of anything, we need to have our   ColoredListsItems  class
defined. Inside  inc/class.lists.inc.php , add the following class declaration and
constructor:

<?php 
 
/** 
 * Handles list interactions within the app 
 * 
 * PHP version 5 
 * 
 * @author Jason Lengstorf 
 * @author Chris Coyier 
 * @copyright 2009 Chris Coyier and Jason Lengstorf 
 * @license   http://www.opensource.org/licenses/mit-license.html  MI
T License 
 * 
 */ 
class ColoredListsItems 

    /** 
     * The database object 
     * 
     * @var object 
     */ 
    private $_db; 
 
    /** 
     * Checks for a database object and creates one if none is found 
     * 
     * @param object $db 
     * @return void 
     */ 
    public function __construct($db=NULL) 
    { 
        if(is_object($db)) 
        { 
            $this->_db = $db; 
        } 
        else 
        { 
            $dsn = "mysql:host=".DB_HOST.";dbname=".DB_NAME; 
            $this->_db = new PDO($dsn, DB_USER, DB_PASS); 
        } 
    } 

 
?>
Notice that the constructor is identical to the one we used in   ColoredListsUsers  (see Part
5—Developing the App: User Interaction ); it checks for a database object and creates one if
none are available.

Displaying List Items


Even though we don't currently have any items saved in our database, we know what they're
going to look like. With that in mind, we can  write our output functions to display our list items
to users, both in a logged in and logged out state.

If the User Is Logged In

When our user is logged in, we'll be loading their list by their user name. This user name is
stored in the  $_SESSION  superglobal .

In  inc/class.lists.inc.php , define the method  loadListItemsByUser()  and


insert the following code:

class ColoredListsItems 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Loads all list items associated with a user ID 
     * 
     * This function both outputs <li> tags with list items and retur
ns an 
     * array with the list ID, list URL, and the order number for a n
ew item. 
     * 
     * @return array    an array containing list ID, list URL, and ne
xt order 
     */ 
    public function loadListItemsByUser() 
    { 
        $sql = "SELECT 
                    list_items.ListID, ListText, ListItemID, ListItem
Color, 
                    ListItemDone, ListURL 
                FROM list_items 
                LEFT JOIN lists 
                USING (ListID) 
                WHERE list_items.ListID=( 
                    SELECT lists.ListID 
                    FROM lists 
                    WHERE lists.UserID=( 
                        SELECT users.UserID 
                        FROM users 
                        WHERE users.Username=:user 
                    ) 
                ) 
                ORDER BY ListItemPosition"; 
        if($stmt = $this->_db->prepare($sql)) 
        { 
            $stmt->bindParam(':user', $_SESSION['Username'], PDO::PAR
AM_STR); 
            $stmt->execute(); 
            $order = 0; 
            while($row = $stmt->fetch()) 
            { 
                $LID = $row['ListID']; 
                $URL = $row['ListURL']; 
                echo $this->formatListItems($row, ++$order); 
            } 
            $stmt->closeCursor(); 
 
            // If there aren't any list items saved, no list ID is re
turned 
            if(!isset($LID)) 
            { 
                $sql = "SELECT ListID, ListURL 
                        FROM lists 
                        WHERE UserID = ( 
                            SELECT UserID 
                            FROM users 
                            WHERE Username=:user 
                        )"; 
                if($stmt = $this->_db->prepare($sql)) 
                { 
                    $stmt->bindParam(':user', $_SESSION['Username'], 
PDO::PARAM_STR); 
                    $stmt->execute(); 
                    $row = $stmt->fetch(); 
                    $LID = $row['ListID']; 
                    $URL = $row['ListURL']; 
                    $stmt->closeCursor(); 
                } 
            } 
        } 
        else 
        { 
            echo "\t\t\t\t<li> Something went wrong. ", $db-
>errorInfo, "</li>\n"; 
        } 
 
        return array($LID, $URL, $order); 
    } 
}
This method starts with a somewhat complex query that  starts
by joining  the  list_items  and lists  tables, then uses a sub-query to filter the list
items by user ID.

If the query returns results, we  loop through them and call the yet-to-be-defined
formatListItems()  method. Each formatted item is output immediately using   echo() ,
and the list ID and URL are saved.

If no results are returned (meaning the user doesn't have any items on their list), the list ID and
URL won't be returned. However, we need this information in order to allow users to submit new
items and share their lists. A check is in place to see if the list ID variable ( $LID ) is set. If not,
an additional query is executed to retrieve the user's list ID and URL.

The list ID and URL are then returned as an array.

If the User Is Not Logged In

If no user is logged in,  we don't have access to a user name with which to load the
items. Therefore,we need to use the list's ID to load items.  We're able to determine the list's
ID using the list's URL, which is the only way a user who isn't logged in will be able to view a list
in the first place. The method looks like this in   inc/class.lists.inc.php :

class ColoredListsItems 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Outputs all list items corresponding to a particular list ID 
     * 
     * @return void 
     */ 
    public function loadListItemsByListId() 
    { 
        $sql = "SELECT ListText, ListItemID, ListItemColor, ListItemD
one 
                FROM list_items 
                WHERE ListID=( 
                    SELECT ListID 
                    FROM lists 
                    WHERE ListURL=:list 
                ) 
                ORDER BY ListItemPosition"; 
        if($stmt = $this->_db->prepare($sql)) { 
            $stmt->bindParam(':list', $_GET['list'], PDO::PARAM_STR); 

            $stmt->execute(); 
            $order = 1; 
            while($row = $stmt->fetch()) 
            { 
                echo $this->formatListItems($row, $order); 
                ++$order; 
            } 
            $stmt->closeCursor(); 
        } else { 
            echo "<li> Something went wrong. ", $db->error, "</li>"; 
        } 
    } 
}

Formatting List Items

To make our previous two methods work properly, we also need to define our
formatListItems()  method. This method is fairly straightforward,  but it also needs a
helper method to determine the CSS class for each item, which we'll call   getColorClass() .
This helper method only exists to simplify our code. Insert both methods
into  inc/class.lists.inc.php as follows:

class ColoredListsItems 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Generates HTML markup for each list item 
     * 
     * @param array $row    an array of the current item's attributes 

     * @param int $order    the position of the current list item 
     * @return string       the formatted HTML string 
     */ 
    private function formatListItems($row, $order) 
    { 
        $c = $this->getColorClass($row['ListItemColor']); 
        if($row['ListItemDone']==1) 
        { 
            $d = '<img class="crossout" src="/images/crossout.png" ' 
                . 'style="width: 100%; display: block;"/>'; 
        } 
        else 
        { 
            $d = NULL; 
        } 
 
        // If not logged in, manually append the <span> tag to each i
tem 
        if(!isset($_SESSION['LoggedIn'])||$_SESSION['LoggedIn']!=1) 
        { 
            $ss = "<span>"; 
            $se = "</span>"; 
        } 
        else 
        { 
            $ss = NULL; 
            $se = NULL; 
        } 
 
        return "\t\t\t\t<li id=\"$row[ListItemID]\" rel=\"$order\" " 
            . "class=\"$c\" color=\"$row[ListItemColor]\">$ss" 
            . htmlentities(strip_tags($row['ListText'])).$d 
            . "$se</li>\n"; 
    } 
 
    /** 
     * Returns the CSS class that determines color for the list item 
     * 
     * @param int $color    the color code of an item 
     * @return string       the corresponding CSS class for the color 
code 
     */ 
    private function getColorClass($color) 
    { 
        switch($color) 
        { 
            case 1: 
                return 'colorBlue'; 
            case 2: 
                return 'colorYellow'; 
            case 3: 
                return 'colorRed'; 
            default: 
                return 'colorGreen'; 
        } 
    } 
}
An array containing each list item's attributes is passed to   formatListItems() ,
and different attributes are created depending on the values that are passed.  If a user isn't
logged in, we manually append a   <span>  to the markup to keep our CSS from breaking, and
then we wrap the whole thing in a   <li>  and return it.

Calling Our New Methods in the Main View

Our main page ( index.php ) currently has notes from the designer that look like this:

<div id="main"> 
 
 
   <noscript>This site just doesn't work, period, without JavaScript<
/noscript> 
 
 
   <!-- IF LOGGED IN --> 
 
          <!-- Content here --> 
 
 
   <!-- IF LOGGED OUT --> 
 
          <!-- Alternate content here --> 
 
 
</div>
In order to make these notes into a functional script, we need to add the following logic
to index.php  to call the proper methods:

        <div id="main"> 
            <noscript>This site just doesn't work, period, without Ja
vaScript</noscript> 
<?php 
if(isset($_SESSION['LoggedIn']) && isset($_SESSION['Username'])):     
             
    echo "\t\t\t<ul id=\"list\">\n"; 
 
    include_once 'inc/class.lists.inc.php'; 
    $lists = new ColoredListsItems($db); 
    list($LID, $URL, $order) = $lists->loadListItemsByUser(); 
 
    echo "\t\t\t</ul>"; 
?> 
 
            <br /> 
 
            <form action="db-interaction/lists.php" id="add-new" meth
od="post"> 
                <input type="text" id="new-list-item-text" name="new-
list-item-text" /> 
 
                <input type="hidden" id="current-list" name="current-
list" value="<?php echo $LID; ?>" /> 
                <input type="hidden" id="new-list-item-position" nam
e="new-list-item-position" value="<?php echo ++$order; ?>" /> 
 
                <input type="submit" id="add-new-submit" value="Add" 
class="button" /> 
            </form> 
 
            <div class="clear"></div> 
 
            <div id="share-area"> 
                <p>Public list URL: <a target="_blank" href="http://c
oloredlists.com/<?php echo $URL ?>.html">http://coloredlists.com/<?
php echo $URL ?>.html</a> 
                &nbsp; <small>(Nobody but YOU will be able to edit th
is list)</small></p> 
            </div> 
 
            <script type="text/javascript" src="js/jquery-ui-
1.7.2.custom.min.js"></script> 
            <script type="text/javascript" src="js/jquery.jeditable.m
ini.js"></script> 
            <script type="text/javascript" src="js/lists.js"></script

            <script type="text/javascript"> 
                initialize(); 
            </script> 
 
<?php 
elseif(isset($_GET['list'])):                  
    echo "\t\t\t<ul id='list'>\n"; 
 
    include_once 'inc/class.lists.inc.php'; 
    $lists = new ColoredListsItems($db); 
    list($LID, $URL) = $lists->loadListItemsByListId(); 
 
    echo "\t\t\t</ul>"; 
else: 
?> 
                     
            <img src="/images/newlist.jpg" alt="Your new list here!" 
/> 
                   
<?php endif; ?> 
                                       
        </div>
This script checks if a user is logged in, then outputs their list and the proper controls if
so. If not, we check if there was a list URL supplied and outputs a non-editable list if one is
found. Otherwise, the "sales" page is displayed, encouraging the viewer to sign up.

Saving New List Items


At this point, our app will function properly for a user that is logged in. Now we just need to plug
in the controls that will allow him or her to interact with the list. First, we need to  allow for new
items to be created. To do this, we need to write a PHP method that will add list items to our
database, and then we need to complete the jQuery started by our designer in  Part 6 .

The PHP

Saving an item is simple enough on the server side. We simply grab all of the new item's
information out of the  $_POST  superglobal, prepare a statement, and save the info in the
database. Note that we're running   strip_tags()  on the list item's text. This is a redundant
check since we're using JavaScript to remove any unwanted tags, but we shouldn't rely on
data that was sanitized client-side.

class ColoredListsItems 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Adds a list item to the database 
     * 
     * @return mixed    ID of the new item on success, error message 
on failure 
     */ 
    public function addListItem() 
    { 
        $list = $_POST['list']; 
        $text = strip_tags(urldecode(trim($_POST['text'])), WHITELIST
); 
        $pos = $_POST['pos']; 
 
        $sql = "INSERT INTO list_items 
                    (ListID, ListText, ListItemPosition, ListItemColo
r) 
                VALUES (:list, :text, :pos, 1)"; 
        try 
        { 
            $stmt = $this->_db->prepare($sql); 
            $stmt->bindParam(':list', $list, PDO::PARAM_INT); 
            $stmt->bindParam(':text', $text, PDO::PARAM_STR); 
            $stmt->bindParam(':pos', $pos, PDO::PARAM_INT); 
            $stmt->execute(); 
            $stmt->closeCursor(); 
 
            return $this->_db->lastInsertId(); 
        } 
        catch(PDOException $e) 
        { 
            return $e->getMessage(); 
        } 
    } 
}
Notice that we used a constant called   WHITELIST  in the  strip_tags()  function. This is a
list of allowed tags that our users have access to.  However, we should assume that we'll want
to change this list at some point in the future, which is why we're saving the list as a constant,
which we'll define in  inc/constants.inc.php :

    // HTML Whitelist 
    define('WHITELIST', '<b><i><strong><em><a>');
Finishing the JavaScript

To complete the jQuery in   js/lists.js , we need to modify the script with the code below:

    // AJAX style adding of list items 
    $('#add-new').submit(function(){ 
        // HTML tag whitelist. All other tags are stripped. 
        var $whitelist = '<b><i><strong><em><a>', 
            forList = $("#current-list").val(), 
            newListItemText = strip_tags(cleanHREF($("#new-list-item-
text").val()), $whitelist), 
            URLtext = escape(newListItemText), 
            newListItemRel = $('#list li').size()+1; 
         
        if(newListItemText.length > 0) { 
            $.ajax({ 
                type: "POST", 
                url: "db-interaction/lists.php", 
                data: "action=add&list=" + forList + "&text=" + URLte
xt + "&pos=" + newListItemRel, 
                success: function(theResponse){ 
                  $("#list").append("<li color='1' class='colorBlue' 
rel='"+newListItemRel+"' id='" + theResponse + "'><span id=\""+theRes
ponse+"listitem\" title='Click to edit...'>" + newListItemText + "</s
pan><div class='draggertab tab'></div><div class='colortab tab'></div
><div class='deletetab tab'></div><div class='donetab tab'></div></li
>"); 
                  bindAllTabs("#list li[rel='"+newListItemRel+"'] spa
n"); 
                  $("#new-list-item-text").val(""); 
                }, 
                error: function(){ 
                    // uh oh, didn't work. Error message? 
                } 
            }); 
        } else { 
            $("#new-list-item-text").val(""); 
        } 
        return false; // prevent default form submission 
    });
We're completing the  $.ajax()  call by submitting the new item via the   POST  method to  db-
interation/lists.php . The successfully added item is then appended to our list, all
without a page refresh.

Handling List Interactions

Our  $.ajax()  call sends to  db-interaction/lists.php , which doesn't exist yet.  This
script acts as a switch that will determine what action is needed and execute the proper
method. All requests are handled the same way, so let's just define the whole file here. Create
new file called lists.php  in the  db-interaction  folder and insert the following code into
it:

<?php 
 
session_start(); 
 
include_once "../inc/constants.inc.php"; 
include_once "../inc/class.lists.inc.php"; 
 
if(!empty($_POST['action']) 
&& isset($_SESSION['LoggedIn']) 
&& $_SESSION['LoggedIn']==1) 

    $listObj = new ColoredListsItems(); 
    switch($_POST['action']) 
    { 
        case 'add': 
            echo $listObj->addListItem(); 
            break; 
        case 'update': 
            $listObj->updateListItem(); 
            break; 
        case 'sort': 
            $listObj->changeListItemPosition(); 
            break; 
        case 'color': 
            echo $listObj->changeListItemColor(); 
            break; 
        case 'done': 
            echo $listObj->toggleListItemDone(); 
            break; 
        case 'delete': 
            echo $listObj->deleteListItem(); 
            break; 
        default: 
            header("Location: /"); 
            break; 
    } 

else 

    header("Location: /"); 
    exit; 

 
?>

Reordering List Items


Next, we need to allow users to save the order of their items  after they've dragged and
dropped them. This is definitely the most complex part of our whole app.

The PHP

Each item is assigned a position when it's read out of the database.  This is the
item's starting position. When it is dragged, it ends up in a new place in the list; we're going to
call this new position it's  current position.
When  changeListItemPosition()  is called, both the item's starting position and current
position are passed, as well as the direction it moved.  This is where it gets tricky.

Depending on the direction the item was moved, we set up one of two conditional queries.
We select all the items in the current list with a position falling between the starting and
current positions , then, using the  CASE  clause , increment or decrement their positions
by  1  unless the item's position plus or minus one falls outside the range we've selected, at
which point we set the item's position to the current position.  In this way, we're able to
avoid firing an individual query for each item, which could potentially cause a performance
bottleneck.

class ColoredListsItems 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Changes the order of a list's items 
     * 
     * @return string    a message indicating the number of affected 
items 
     */ 
    public function changeListItemPosition() 
    { 
        $listid = (int) $_POST['currentListID']; 
        $startPos = (int) $_POST['startPos']; 
        $currentPos = (int) $_POST['currentPos']; 
        $direction = $_POST['direction']; 
 
        if($direction == 'up') 
        { 
            /* 
             * This query modifies all items with a position between 
the item's 
             * original position and the position it was moved to. If 
the 
             * change makes the item's position greater than the item
's 
             * starting position, then the query sets its position to 
the new 
             * position. Otherwise, the position is simply incremente
d. 
             */ 
            $sql = "UPDATE list_items 
                    SET ListItemPosition=( 
                        CASE 
                            WHEN ListItemPosition+1>$startPos THEN $c
urrentPos 
                            ELSE ListItemPosition+1 
                        END) 
                    WHERE ListID=$listid 
                    AND ListItemPosition BETWEEN $currentPos AND $sta
rtPos"; 
        } 
        else 
        { 
            /* 
             * Same as above, except item positions are decremented, 
and if the 
             * item's changed position is less than the starting posi
tion, its 
             * position is set to the new position. 
             */ 
            $sql = "UPDATE list_items 
                    SET ListItemPosition=( 
                        CASE 
                            WHEN ListItemPosition-1<$startPos THEN $c
urrentPos 
                            ELSE ListItemPosition-1 
                        END) 
                    WHERE ListID=$listid 
                    AND ListItemPosition BETWEEN $startPos AND $curre
ntPos"; 
        } 
 
        $rows = $this->_db->exec($sql); 
        echo "Query executed successfully. ", 
            "Affected rows: $rows"; 
    } 
}
Finishing the JavaScript

To call our method, we need to modify   js/lists.js  by adding a new function
called saveListOrder() :

function saveListOrder(itemID, itemREL){ 
    var i = 1, 
        currentListID = $('#current-list').val(); 
    $('#list li').each(function() { 
        if($(this).attr('id') == itemID) { 
            var startPos = itemREL, 
                currentPos = i; 
            if(startPos < currentPos) { 
                var direction = 'down'; 
            } else { 
                var direction = 'up'; 
            } 
            var postURL = "action=sort&currentListID="+currentListID 
                +"&startPos="+startPos 
                +"&currentPos="+currentPos 
                +"&direction="+direction; 
 
            $.ajax({ 
                type: "POST", 
                url: "db-interaction/lists.php", 
                data: postURL, 
                success: function(msg) { 
                    // Resets the rel attribute to reflect current po
sitions 
                    var count=1; 
                    $('#list li').each(function() { 
                        $(this).attr('rel', count); 
                        count++; 
                    }); 
                }, 
                error: function(msg) { 
                    // error handling here 
                } 
            }); 
        } 
        i++; 
    }); 
}
This function accepts the ID and rel attribute of the item that was moved. The rel attribute
contains the original position of the item, which we need as its starting position. Then we  loop
through each list item while incrementing a counter  ( i ). When we find the list item that
matches the moved item's ID, our counter now reflects the item's current position. We can then
determine which direction the item was moved and send the info to   db-
interaction/lists.php  for processing.

This function needs to be called when a sortable item is updated, which we accomplish by
modifying the following in   js/lists.js :

    // MAKE THE LIST SORTABLE VIA JQUERY UI 
    // calls the SaveListOrder function after a change 
    // waits for one second first, for the DOM to set, otherwise it's 
too fast. 
    $("#list").sortable({ 
        handle   : ".draggertab", 
        update   : function(event, ui){ 
            var id = ui.item.attr('id'); 
            var rel = ui.item.attr('rel'); 
            var t = setTimeout("saveListOrder('"+id+"', '"+rel+"')",5
00); 
        }, 
        forcePlaceholderSize: true 
    });

Changing Item Colors


Changing an item's color is fairly simple on both the server- and client-side.

The PHP

To update an item's color, we simply  pass it's ID and the new color code to the method
changeListItemColor()  and create and execute a query.

class ColoredListsItems 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Changes the color code of a list item 
     * 
     * @return mixed    returns TRUE on success, error message on fai
lure 
     */ 
    public function changeListItemColor() 
    { 
        $sql = "UPDATE list_items 
                SET ListItemColor=:color 
                WHERE ListItemID=:item 
                LIMIT 1"; 
        try 
        { 
            $stmt = $this->_db->prepare($sql); 
            $stmt->bindParam(':color', $_POST['color'], PDO::PARAM_IN
T); 
            $stmt->bindParam(':item', $_POST['id'], PDO::PARAM_INT); 
            $stmt->execute(); 
            $stmt->closeCursor(); 
            return TRUE; 
        } catch(PDOException $e) { 
            return $e->getMessage();     
        } 
    } 
}
Finishing the JavaScript

The function that saves new colors is called by submitting the item ID and new color via   POST  in
the $.ajax()  call below in  js/lists.js :
    // COLOR CYCLING 
    // Does AJAX save, but no visual feedback 
    $(".colortab").live("click", function(){ 
        $(this).parent().nextColor(); 
         
        var id = $(this).parent().attr("id"), 
            color = $(this).parent().attr("color"); 
 
        $.ajax({ 
            type: "POST", 
            url: "db-interaction/lists.php", 
            data: "action=color&id=" + id + "&color=" + color, 
            success: function(msg) { 
                // error message 
            } 
        }); 
    });

Editing Item Text


Next, let's make sure edited items are updated in the database.

The PHP

To save updated items in the database, we need to create a method


called  updateListItem() . This method will extract the ID of the modified item and the
new text from the  $_POST superglobal, double-check the item text for disallowed tags,
and prepare and execute a query to update the item in the database.  Add the following
method in  inc/class.lists.inc.php :

class ColoredListsItems 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Updates the text for a list item 
     * 
     * @return string    Sanitized saved text on success, error messa
ge on fail 
     */ 
    public function updateListItem() 
    { 
        $listItemID = $_POST["listItemID"]; 
        $newValue = strip_tags(urldecode(trim($_POST["value"])), WHIT
ELIST); 
     
        $sql = "UPDATE list_items 
                SET ListText=:text 
                WHERE ListItemID=:id 
                LIMIT 1"; 
        if($stmt = $this->_db->prepare($sql)) { 
            $stmt->bindParam(':text', $newValue, PDO::PARAM_STR); 
            $stmt->bindParam(':id', $listItemID, PDO::PARAM_INT); 
            $stmt->execute(); 
            $stmt->closeCursor(); 
     
            echo $newValue; 
        } else { 
            echo "Error saving, sorry about that!";     
        } 
    } 
}
Finishing the JavaScript

Activate this method by modifying the path in   bindAllTabs()  in  js/lists.js :

// This is seperated to a function so that it can be called at page l
oad 
// as well as when new list items are appended via AJAX 
function bindAllTabs(editableTarget) { 
     
    // CLICK-TO-EDIT on list items 
    $(editableTarget).editable("db-interaction/lists.php", { 
        id        : 'listItemID', 
        indicator : 'Saving...', 
        tooltip   : 'Double-click to edit...', 
        event     : 'dblclick', 
        submit    : 'Save', 
        submitdata: {action : "update"} 
    }); 
     
}

Marking Items as "Done"


To mark an item as done, the user needs to be able to save a flag in the database that will
indicate the item's "done" status.

The PHP

The  toggleListItemDone()  method retrieves the item's ID and "done" status from the
$_POST  superglobal and uses them to update the item in the database:

class ColoredListsItems 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Changes the ListItemDone state of an item 
     * 
     * @return mixed    returns TRUE on success, error message on fai
lure 
     */ 
    public function toggleListItemDone() 
    { 
        $sql = "UPDATE list_items 
                SET ListItemDone=:done 
                WHERE ListItemID=:item 
                LIMIT 1"; 
        try 
        { 
            $stmt = $this->_db->prepare($sql); 
            $stmt->bindParam(':done', $_POST['done'], PDO::PARAM_INT)

            $stmt->bindParam(':item', $_POST['id'], PDO::PARAM_INT); 
            $stmt->execute(); 
            $stmt->closeCursor(); 
            return TRUE; 
        } catch(PDOException $e) { 
            return $e->getMessage();     
        } 
    } 
}
Finishing the JavaScript

To call our method, we write a function called   toggleDone()  in  js/lists.js . This
function simply executes a call to the   $.ajax()  function and sends the item ID and "done"
status to our list handler.

    function toggleDone(id, isDone) 
    { 
        $.ajax({ 
            type: "POST", 
            url: "db-interaction/lists.php", 
            data: "action=done&id="+id+"&done="+isDone 
        }) 
    }
Next, we assign   toggleDone()  as the callback function for the   animate()  even that
happens when our user clicks the done tab:
    $(".donetab").live("click", function() { 
        var id = $(this).parent().attr('id'); 
        if(!$(this).siblings('span').children('img.crossout').length) 

        { 
            $(this) 
                .parent() 
                    .find("span") 
                    .append("<img src='/images/crossout.png' class='c
rossout' />") 
                    .find(".crossout") 
                    .animate({ 
                        width: "100%" 
                    }) 
                    .end() 
                .animate({ 
                    opacity: "0.5" 
                }, 
                "slow", 
                "swing", 
                toggleDone(id, 1)); 
        } 
        else 
        { 
            $(this) 
                .siblings('span') 
                    .find('img.crossout') 
                        .remove() 
                        .end() 
                    .animate({ 
                        opacity : 1 
                    }, 
                    "slow", 
                    "swing", 
                    toggleDone(id, 0)); 
                 
        } 
    });

Deleting Items
Finally, we need to allow our users to delete items that they no longer want on their list.

The PHP

To delete an item, we need to create a method called   deleteListItem()  in


inc/class.lists.inc.php . This method will retrieve the item and list IDs from
the  $_POST superglobal, then remove the item from the list.  Then, to preserve proper order
in the list, all items in the list with a position higher than that of the item that was deleted
need to be decremented by 1.

class ColoredListsItems 

    // Class properties and other methods omitted to save space 
 
 
    /** 
     * Removes a list item from the database 
     * 
     * @return string    message indicating success or failure 
     */ 
    public function deleteListItem() 
    { 
        $list = $_POST['list']; 
        $item = $_POST['id']; 
 
        $sql = "DELETE FROM list_items 
                WHERE ListItemID=:item 
                AND ListID=:list 
                LIMIT 1"; 
        try 
        { 
            $stmt = $this->_db->prepare($sql); 
            $stmt->bindParam(':item', $item, PDO::PARAM_INT); 
            $stmt->bindParam(':list', $list, PDO::PARAM_INT); 
            $stmt->execute(); 
            $stmt->closeCursor(); 
 
            $sql = "UPDATE list_items 
                    SET ListItemPosition=ListItemPosition-1 
                    WHERE ListID=:list 
                    AND ListItemPosition>:pos"; 
            try 
            { 
                $stmt = $this->_db->prepare($sql); 
                $stmt->bindParam(':list', $list, PDO::PARAM_INT); 
                $stmt->bindParam(':pos', $_POST['pos'], PDO::PARAM_IN
T); 
                $stmt->execute(); 
                $stmt->closeCursor(); 
                return "Success!"; 
            } 
            catch(PDOException $e) 
            { 
                return $e->getMessage(); 
            } 
        } 
        catch(Exception $e) 
        { 
            return $e->getMessage(); 
        } 
    } 
}
Finishing the JavaScript

To activate this method, we need to modify our jQuery by updating the section
in  js/lists.js  that deals with item deletion:

    // AJAX style deletion of list items 
    $(".deletetab").live("click", function(){ 
        var thiscache = $(this), 
            list = $('#current-list').val(), 
            id = thiscache.parent().attr("id"), 
            pos = thiscache.parents('li').attr('rel'); 
                 
        if (thiscache.data("readyToDelete") == "go for it") { 
            $.ajax({ 
                type: "POST", 
                url: "db-interaction/lists.php", 
                data: { 
                        "list":list, 
                        "id":id, 
                        "action":"delete", 
                        "pos":pos 
                    }, 
                success: function(r){ 
                        var $li = $('#list').children('li'), 
                            position = 0; 
                        thiscache 
                            .parent() 
                                .hide("explode", 400, function(){$
(this).remove()}); 
                        $('#list') 
                            .children('li') 
                                .not(thiscache.parent()) 
                                .each(function(){ 
                                        $(this).attr('rel', +
+position); 
                                    }); 
                    }, 
                error: function() { 
                    $("#main").prepend("Deleting the item failed...
"); 
                } 
            }); 
        } 
        else 
        { 
            thiscache.animate({ 
                width: "44px", 
                right: "-64px" 
            }, 200) 
            .data("readyToDelete", "go for it"); 
        } 
    });

Moving On
We have now succeeded in building all of the AJAX functionality for our app! There was a ton of
information in this article, and we went through it rather quickly, so  please post any questions
you have in the comments!

In the final installment of this series, we'll go over the  security measures and other finishing
touches this app needs to be ready for public use. We'll also go over some of the features we
hope to add in the future.

Part VIII
Hooray we made it! First of all, thanks for following along this whole journey. You can go
check out the real live app for yourselves:

http://coloredlists.com
Below we’re going to wrap up a few things by talking about some of the choices we made,
security precautions, and ideas we (and you) have for a version 2.0 of this app.

Object-Oriented Programming
Because we should always aim to be efficient when programming, we built this app with the
concept of DRY programming in mind. DRY stands for “Don’t Repeat Yourself” and should lie
somewhere near the core of our programming philosophy.

In our opinion, taking the object-oriented programming (OOP) approach was the best way
to keep this app DRY. OOP allows us to group common methods together and separate
tasks out without needing to pass parameters from function to function. For a little more
information on OOP and why it’s beneficial, read Jason’s introduction to OOP.

Security
Security is incredibly important in any application. We have users with accounts who are
storing data with us. Those users are putting their trust in us to make sure their data is
safe, which includes their password and all the information they’ve entered into the lists.
This app is already pretty darn secure. Passwords are stored in encrypted formats and
never sent in the clear via Email. All the interaction that happens with the database is
secure. Only users who are logged in can issue commands which result in database
changes, and those users are only able to issue commands that affect their own data.

But because there is a variety of AJAX stuff going on in this app, our security needs to take
into account a few more scenarios. First, our JavaScript (like all JavaScript) is publically
viewable. This JavaScript contains the code for making AJAX calls, meaning the URL we are
sending to and what data that URL is expecting. This tells potential attackers a good bit of
information regarding how they might send malicious requests. Because of this, we need to
be very careful and ensure that all incoming data is escaped properly.

Security on the Server Side


Avoiding attacks on the server side involves two major risk factors: first, the potential for
database attacks; and second, the potential that a malicious user could submit dangerous
data that hurts our app or users in some way when read out of the database and displayed.
Fortunately, PHP provides us with several methods to combat these risks.

PDO
Database attacks, called SQL injection, are a particularly nasty form of attack. A vulnerable
database can be read, manipulated, or deleted entirely by a malicious user. This means that
it is really important that we keep any kind of SQL injection from happening.

Lucky for us, PHP Data Objects (PDO) virtually eliminates the risk for SQL injection through
the use of prepared statements, which are like query templates that we can customize with
parameters. All the escaping is done for us when the parameters are inserted into the
query, so it’s virtually impossible for SQL injection to occur while using prepared
statements.

It was because of this powerful security advantage that we chose PDO for this app. (Keep in
mind that prepared statements are not exclusive to PDO; other database extensions, such
as MySQLi, also support them.)

Data Escaping
While PDO is powerful against SQL injection, it doesn’t help us when we’ve read the
information out of the database. If a malicious user injects dangerous tags into our
database, they’ll still be dangerous when they’re retrieved unless we take further measures
to sanitize user data.

Fortunately, PHP has built-in functions that will allow us to perform basic sanitization of user
input. We’re namely using strip_tags() with a whitelist to make sure no <script> tags or
other potentially dangerous tags make it into the database. Also, because we never want
that sort of thing to be allowed, we’re performing this escaping before the data is inserted
into the database.

Security in the JavaScript


First, a good measure is to “Pack” the javascript so it isn’t so easily readable, as well as
downloads faster. There are a lot of tools available to do this, including this oneby Dean
Edwards.

Client Side Sanitization

Secondly, because we are inputting data and turn it around to display immediately on the
screen, it’s best to do some of that input scrubbing directly in the JavaScript. When a user
enters a new list item, we’ll take two steps to scrub it. First we’ll ensure they aren’t
naughtily trying to insert immediately executable JavaScript into links:

// Check for JS in the href attribute


function cleanHREF(str) {
return str.replace(/\<a(.*?)href=['"](javascript:)(.+?)<\/a>/gi, "Naughty!");
}
Then we’ll also scrub that input text for any other HTML. Some HTML we will allow, in case
users want to format their lists a bit like with <strong> tags and the like. With the function
below, we’ll strip away all tags except those set up in a whitelist.

NOTE: The strip_tags() function used below is part of the php.js project, which has ported a
number of useful PHP functions to JavaScript.
var $whitelist = '<b><i><strong><em><a>',

// Strip HTML tags with a whitelist


function strip_tags(str, allowed_tags) {

var key = '', allowed = false;


var matches = [];
var allowed_array = [];
var allowed_tag = '';
var i = 0;
var k = '';
var html = '';

var replacer = function(search, replace, str) {


return str.split(search).join(replace);
};

// Build allowes tags associative array


if (allowed_tags) {
allowed_array = allowed_tags.match(/([a-zA-Z]+)/gi);
}

str += '';

// Match tags
matches = str.match(/(<\/?[\S][^>]*>)/gi);

// Go through all HTML tags


for (key in matches) {
if (isNaN(key)) {
// IE7 Hack
continue;
}

// Save HTML tag


html = matches[key].toString();

// Is tag not in allowed list? Remove from str!


allowed = false;

// Go through all allowed tags


for (k in allowed_array) {
// Init
allowed_tag = allowed_array[k];
i = -1;

if (i != 0) { i = html.toLowerCase().indexOf('<'+allowed_tag+'>');}
if (i != 0) { i = html.toLowerCase().indexOf('<'+allowed_tag+' ');}
if (i != 0) { i = html.toLowerCase().indexOf('</'+allowed_tag) ;}

// Determine
if (i == 0) {
allowed = true;
break;
}
}

if (!allowed) {
str = replacer(html, "", str); // Custom replace. No regexing
}
}

return str;
}
These functions are implemented in js/lists.js before sending off the AJAX request that adds
a new list item…

...
// AJAX style adding of list items
$('#add-new').submit(function(){
// HTML tag whitelist. All other tags are stripped.
var $whitelist = '<b><i><strong><em><a>',
forList = $("#current-list").val();
newListItemText = strip_tags(cleanHREF($("#new-list-item-text").val()), $whitelist),
...

POST vs GET
One last small measure we’ve taken to secure our app is to use POST over GET for all of our
AJAX calls. This is done because the GET method should only be used for retrieval, and not
for any action that will modify data in any way.

The primary reason not to use GET for modifying data is that a request made using GET is
sent in the URL (i.e. http://example.com?get=request&is=this&part=here). There’s an
inherent danger in modifying data based on the information passed in the URL in that a user
can cause duplicate processing by accidentally refreshing his or her browser.
A secondary, less important reason to use POST is that it’s a little harder to send a bogus
request using POST, which provides a (minor) deterrent to malicious users.

2.0 Features
Of course our work as designers and developers is never done. This is a great start on a
simple and usable list application, but right away new features jump to mind. Here are some
ideas of ways to expand functionality. Perhaps they slightly complicate things, but are all
probably great ideas assuming they are implemented well.

 List sharing
Enter an email address for someone to share the list with. Sharing meaning literally
collaborative editing. The user would need an account, so if they already have one they
would just be emailed and asked to join the list (they can accept or not accept). If that
email address did not have an account, they would be promoted to join first.
 Multiple lists
Right now a user can have only one list. It would probably be useful for users to keep
multiple lists. Perhaps a dropdown menu for toggling between lists and a simple button
for adding new ones. Plenty of interface to think about here, including figuring out how
to delete lists.
 RSS
Each list could have it’s own RSS feed. Options would probably be necessary, like what
the RSS feed would contain (e.g. Do you wish to see entries for when list items are
completed or not?). Feed URLs could be long gibberish URL’s, so they are essentially
completely private unless specifically shared.
 iPhone interface
Logging in via iPhone or other mobile device would have a better more optimized
experience.

You might also like