7/30/2019 web app dev full
1/125
Creating a Web App from Scratch Part1 of 8: Basic Idea and DesignP U B L I S H E D N O V E M B E R 2 3 , 2 0 0 9 B Y C H R I S C O Y I E R
Today we begin Part 1 of an 8-Part series on building a web application from absolute scratch to acomplete product. I am going to kick things off by introducing the idea, and then I will be handling thedesign, UI, and general front-end stuff. We are going to be going back and forth from here over to my
friend Jason Lengstorf's siteCopter Labs. Jason will be handling the back-end stuff like applicationplanning and database stuff.At the end of the week,we'll unleash the actual working application foryou. Here is the plan:
A L L S E R I E S N A V I G A T I O N
Part 1 - Planning the App: Basic Idea and Design
Part 2 - Planning the App: Database Architecture and Development Approach
Part 3 - Designing the App: Workflow Map and Photoshop Design
Part 4 - Designing the App: HTML and CSS
Part 5 - Developing the App: User Interaction
Part 6 - Developing the App: Adding AJAX Interactivity
Part 7 - Developing the App: List Interaction
Part 8 - Security & The Future
And finally.... the application!Colored Lists
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.
http://www.copterlabs.com/blog/http://www.copterlabs.com/blog/http://www.copterlabs.com/blog/http://css-tricks.com/app-from-scratch-1-designhttp://css-tricks.com/app-from-scratch-1-designhttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+2/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+2/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+3/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+3/http://css-tricks.com/app-from-scratch-4-html-csshttp://css-tricks.com/app-from-scratch-4-html-csshttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+5/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+5/http://css-tricks.com/app-from-scratch-6-ajaxhttp://css-tricks.com/app-from-scratch-6-ajaxhttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+7/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+7/http://css-tricks.com/app-from-scratch-8-finishinghttp://css-tricks.com/app-from-scratch-8-finishinghttp://coloredlists.com/http://coloredlists.com/http://coloredlists.com/http://stats.buysellads.com/click.go?z=1279518&b=2841047&g=&s=&sw=1360&sh=768&br=chrome,28,win&r=0.4000273884739727&link=http://www.squarespace.com/?channel=display&subchannel=csstricks&source=200x200http://coloredlists.com/http://css-tricks.com/app-from-scratch-8-finishinghttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+7/http://css-tricks.com/app-from-scratch-6-ajaxhttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+5/http://css-tricks.com/app-from-scratch-4-html-csshttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+3/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+2/http://css-tricks.com/app-from-scratch-1-designhttp://www.copterlabs.com/blog/7/30/2019 web app dev full
2/125
The Big IdeaThis "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 OutNo 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
7/30/2019 web app dev full
3/125
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 ScreensJust 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
7/30/2019 web app dev full
4/125
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.
Where Were At
Up to this point, weve 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 whats going to happen behind the scenes to allowour app to work the way weve
planned.
O k a y , S o W e K n o w H o w I t L o o k s , b u t H o w D o e s I t W o r k ?
In order to keep a list available after a user logs out of our app, well need to store list
information in a database. And, of course, to access that database were 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.
D a t a S t o r a g eP l a n n i n g a n d D a t a b a s e S t r u c t u r e
Our first step is to decide how we want to organize list data. Since this app is fairly
simple, well 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, well need a database to work with. Foranyone working along at home, well be operating under the assumption that youre
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 were 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
7/30/2019 web app dev full
5/125
in their lists, so its also a good idea to specify the collation and character set of the
database. Well 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 weve got a database, were ready to build our tables.
Table 1: User Information
Using our list app doesnt require a high security clearance; all we need to know is
that youve got an email address and that its real. To determine that an email addressis real, well 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 users email address, and in the interest of
keeping redundant data storage to a minimum, well 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_INCREMENT,
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 inphpMyAdmins SQL
tab:
CREATE TABLE cl_db.lists
(
ListID INT PRIMARY KEY AUTO_INCREMENT,
7/30/2019 web app dev full
6/125
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 orher list item. Also, to support features well be adding later on, we also need tokeep a
record of the items 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 beingpointed out in the comments by FuSi0N.
http://www.copterlabs.com/blog/creating-an-app-from-scratch-part-2/#cmnt395http://www.copterlabs.com/blog/creating-an-app-from-scratch-part-2/#cmnt395http://www.copterlabs.com/blog/creating-an-app-from-scratch-part-2/#cmnt395http://www.copterlabs.com/blog/creating-an-app-from-scratch-part-2/#cmnt3957/30/2019 web app dev full
7/125
The database with our three tables
Now we have our database and the three tables well need to build our app. Next,
wellplan how were going to create and access our database information using PHP.
D a t a H a n d l i n gP l a n n i n g a n d S c r i p t O r g a n i z a t i o n
Before we start coding, its 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, well 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 incrediblypowerful tool that increases portability, readability, and usability of scripts. Our app is
pretty simple, so well 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 well 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 withpretty 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, well also need some support methods, such as one that
will send a verification email. Well define these methods as we build the app in later
installments of this series.
List Actions Class
The list actions class, which well 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
http://www.copterlabs.com/blog/ITT%20%2310%3A%20Understanding%20OOP/http://www.copterlabs.com/blog/ITT%20%2310%3A%20Understanding%20OOP/http://www.copterlabs.com/blog/ITT%20%2310%3A%20Understanding%20OOP/http://www.copterlabs.com/blog/ITT%20%2310%3A%20Understanding%20OOP/7/30/2019 web app dev full
8/125
Update a list item
Delete a list item
Change a list items position
Change a list items color
Action Handling Scripts
Finally, well need a couple action-handling scripts. These will determine what theusers desired action is, create an instance of the proper object, and call the correct
method. As we build our app, well go into more detail on how these scripts will
work.
M o v i n g O n
In our next installment of this series, well create the application workflow. Make sure
youre subscribed toCSS-Tricksso you dont miss out
D e v e l o p i n g a W o r k f l o w
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 were
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 users
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 andRegistration. Here is some flow:
http://css-tricks.com/http://css-tricks.com/http://css-tricks.com/http://css-tricks.com/7/30/2019 web app dev full
9/125
Basic app
workflow
Its not pretty folks, but thats what sketching is. Its fast and its just to help you think
and plan for the things you need.
B r i n g i n g I t t o L i f e P h o t o s h o p
Our developer is already ahead of us, thinking about the data they need and how this
app is going to actually work. So wed better get started actually designing here.
Homepage (Logged In)
This is the meat of our application so lets start here. The list is obviously the most
important thing, so lets 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.
7/30/2019 web app dev full
10/125
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
isnt much to say about a list app, so well keep it simple.
7/30/2019 web app dev full
11/125
When
logged out, well encourage the visitor to sign up
Small Bits
Weve been designing long enough to know we might as well make the little buttons
into a separate file and keep them together as asprite(a sprite is multiple images
combined into one to save HTTP requests, in our case, also the rollover states). So
well do that and throw together a favicon while were at it.
All the list item tabs
Favicon
http://css-tricks.com/css-sprites/http://css-tricks.com/css-sprites/http://css-tricks.com/css-sprites/http://css-tricks.com/css-sprites/7/30/2019 web app dev full
12/125
Registration
Our intention with registration is going to be extremely simple. Were going to ask for
a users email and thats it. They will be sent an email with a link in it to complete
registration. The link in that email will activate theiraccount 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 sites forms.
Account
Well use the same form design as the registration page here.Its not cheating or
being lazy, its good design through consistency!
7/30/2019 web app dev full
13/125
Account
controls
Buttons
Notice the change in buttons. They are now big, orange and rounded. Much more
button-like dont you think? Again for consistency,lets make this the default for all
buttons across the site.
Site buttons, looking
button-like
7/30/2019 web app dev full
14/125
M o v i n g o n
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.
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.
A L L S E R I E S N A V I G A T I O N
Part 1 - Planning the App: Basic Idea and Design
Part 2 - Planning the App: Database Architecture and Development Approach
Part 3 - Designing the App: Workflow Map and Photoshop Design
Part 4 - Designing the App: HTML and CSS
Part 5 - Developing the App: User Interaction Part 6 - Developing the App: Adding AJAX Interactivity
Part 7 - Developing the App: List Interaction
Part 8 - Security & The Future
And finally.... the application!Colored Lists
Web Root OrganizationThis 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.
http://css-tricks.com/app-from-scratch-1-designhttp://css-tricks.com/app-from-scratch-1-designhttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+2/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+2/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+3/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+3/http://css-tricks.com/app-from-scratch-4-html-csshttp://css-tricks.com/app-from-scratch-4-html-csshttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+5/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+5/http://css-tricks.com/app-from-scratch-6-ajaxhttp://css-tricks.com/app-from-scratch-6-ajaxhttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+7/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+7/http://css-tricks.com/app-from-scratch-8-finishinghttp://css-tricks.com/app-from-scratch-8-finishinghttp://coloredlists.com/http://coloredlists.com/http://coloredlists.com/http://coloredlists.com/http://css-tricks.com/app-from-scratch-8-finishinghttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+7/http://css-tricks.com/app-from-scratch-6-ajaxhttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+5/http://css-tricks.com/app-from-scratch-4-html-csshttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+3/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+2/http://css-tricks.com/app-from-scratch-1-design7/30/2019 web app dev full
15/125
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
Colored Lists |
7/30/2019 web app dev full
16/125
Colored Lists
Log outYour Account
Sign upLog in
7/30/2019 web app dev full
17/125
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.
FooterOur 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.
SidebarOur 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.
Reminders
Your list automatically saves
Double-click list items to edit them
7/30/2019 web app dev full
18/125
Main PageNow that we have our "modules" complete, let's dig into a real page. The template for building any page will be like this:
This site just doesn't work, period, withoutJavaScript
Logged in (The List)
Walk the dog
7/30/2019 web app dev full
19/125
Pick up dry cleaning
Milk
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.
7/30/2019 web app dev full
20/125
Then one of our applications features is having sharable public URL's for our lists. Let's put that in here too.
Public list URL: URL GOES HERE
(Nobody but YOU will be able to edit this list)
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 atthe list but not interact with it. (Like if you wanted to send your mom your Christmas list!)
Logged out (Public list)
Walk the dog
Pick up dry cleaning
Milk
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.
7/30/2019 web app dev full
21/125
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.
7/30/2019 web app dev full
22/125
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.
Your Account
Change Email Address
Change Password
New Password
Repeat New Password
7/30/2019 web app dev full
23/125
Other "Form" PagesNow 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 CSSReset/*
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;}
7/30/2019 web app dev full
24/125
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://reader009.{domain}/reader009/html5/0421/5adb2d7e67repeat-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(http://cdn.css-tricks.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
*/
7/30/2019 web app dev full
25/125
#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;}
7/30/2019 web app dev full
26/125
#add-new input[type="text"] {width: 532px;float: left;margin: 0 10px 069px;}
#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 047px;height: 756px;top: -6px;background:url(http://cdn.css-tricks.com/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
7/30/2019 web app dev full
27/125
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.
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.
Well focus this installment of the series on theusers account interactions. These
include:
Creating an Account
Modifying Account Information
Resetting a Lost Password
Deleting an Account
C o n n e c t i n g t o t h e D a t a b a s e
Before our class will be able to do much of anything, we need to connect to our
database. To do this, well need to create a couple of small files.
Defining Site-Wide Constants
Our site wont require manyconstants, but in the interest of keeping them easy to
maintain, well create 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,
youre 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 were starting
out by developing locally, constants.inc.php will look like this:
7/30/2019 web app dev full
28/125
?>
As we develop, well 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 usingPDO (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 alls said and done:
http://php.net/pdohttp://php.net/pdohttp://php.net/pdohttp://php.net/pdohttp://php.net/pdohttp://php.net/pdo7/30/2019 web app dev full
29/125
Because were 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 staylogged 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 statementthis gives us the ability to
useExceptions, which help improve error handling. In this case, if the database
connection fails, were simply going to output the error message.
Why PDO?
The reason were using PDO for this project is because of its support forprepared
statements, which virtually eliminates the risk ofSQL injection. There are otheroptions that allow prepared statements, such as theMySQLi extension.
However, PDO is not database-specific, so migrating the app to Oracle or PostgreSQL
wouldnt require a full rewrite of our code.
Also, having used both MySQLi and PDO in projects, its 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 inPart 2of this series, well be taking theobject-oriented
approachwith this app. All of these actions will be contained within
our ColoredListsUsersclass. Well also need to create several files that will display
information to the user and interact with the class, which well cover as we get to
them.
Bui l d i ng t he C l a s s
To get started, we need to create the file class.users.inc.php to contain the PHP class, which
well place in the inc folder.
With the file created, lets build the skeleton of the class:
7/30/2019 web app dev full
30/125
* 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 MIT 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 classconstructor,
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
*
http://us2.php.net/manual/en/language.oop5.decon.phphttp://us2.php.net/manual/en/language.oop5.decon.phphttp://us2.php.net/manual/en/language.oop5.decon.php7/30/2019 web app dev full
31/125
* @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 ourColoredListsUsers object and use it to
communicate with our database. Next, lets start building user interactions!
C r e a t i n g a n A c c o u n t
First and foremost, a user needs to be able to create an account. This will give them
access to the rest of the sites functionality.
As it stands, when a user visits our app, theyre greeted with our sales page, which
encourages them to click the Sign Up button in the top right of their screen:
7/30/2019 web app dev full
32/125
The home
screen of our app
Clicking that Sign Up button directs the user to /signup.phpour first order of business
should probably be to build that page.
Creating the Sign-Up Form
In our apps root directory, create a file called signup.php and place the following code
inside:
7/30/2019 web app dev full
33/125
Sign up
Email:
To start, we include our common/base.php and common/header.php files. Also, notice that were
declaring a variable called $pageTitle just before we include the header. Remember
inPart 4when we built the header file and left that comment in the title tag?
Colored Lists |
Were going to replace that with a snippet of PHP that reads:
Colored Lists |
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.phpitselfso 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 well write in the next section).
Finally, we close the if-else statement and include the footer. Our sign-up page should
look like this:
http://css-tricks.com/app-from-scratch-4-html-csshttp://css-tricks.com/app-from-scratch-4-html-csshttp://css-tricks.com/app-from-scratch-4-html-csshttp://css-tricks.com/app-from-scratch-4-html-css7/30/2019 web app dev full
34/125
The sign-
up page.
Notice the use of alternative syntax for the if-else statement.Normally, I dont 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.
Savingthe Users 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. Lets 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
7/30/2019 web app dev full
35/125
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 " Error "
. "
Sorry, that email is already in use. "
. "Please try again.
";}
if(!$this->sendVerificationEmail($u, $v)) {
return " Error "
. "
There was an error sending your"
. " verification email. Please "
. "contact "
. "us for support. We apologize for the "
. "inconvenience.
";}
$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();
7/30/2019 web app dev full
36/125
$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 " Error "
. "
Your account was created, but "
. "creating your first list failed.
";} else {
return " Success! "
. "
Your account was successfully "
. "created with the username $u."
. " Check your email!";
}
} else {
return " Error
Couldn't insert the "
. "user information into the database.
";}
}
}
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 isnt already in use; third, it generates and sends a
verification email to the user with instructions on how to verify their account (well
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.
http://us2.php.net/manual/en/reserved.variables.post.phphttp://us2.php.net/manual/en/reserved.variables.post.phphttp://us2.php.net/manual/en/reserved.variables.post.phphttp://us2.php.net/manual/en/reserved.variables.post.phphttp://us2.php.net/manual/en/reserved.variables.post.php7/30/2019 web app dev full
37/125
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 Verif ication Email
When the user creates an account, we need to send them an email with a link that willconfirm 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, well 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 account
*
* @param string $email The user's email address
* @param string $ver The random verification code for the user
* @return boolean TRUE on successful send and FALSE on failure
*/
private function sendVerificationEmail($email, $ver)
{
$e = sha1($email); // For verification purposes
$to = trim($email);
$subject = "[Colored Lists] Please Verify Your Account";
$headers =
7/30/2019 web app dev full
38/125
MESSAGE;
$msg =
7/30/2019 web app dev full
39/125
Verifying the Users 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 users new
password, as well as setting the accounts status to verified.
First, lets create a new file called accountverify.php in the root level of our app. Inside,
place the following code:
7/30/2019 web app dev full
40/125
echo isset($ret[1]) ? $ret[1] : NULL;
if($ret[0]
Choose a Password
Choose a Password:
Re-Type Password:
7/30/2019 web app dev full
41/125
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
7/30/2019 web app dev full
42/125
{
return array(4, "Verification Errorn"
. "
This account has already been verified. "
. "Did you forget "
. "your password?");
}
$stmt->closeCursor();
// No error message is required if verification is successful
return array(0, NULL);
}
else
{
return array(2, "Errorn
Database error.
");}
}
}
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 isreturned, 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 Users Password and Verif ied Status
Once the user has selected a password and submitted the form, the if-elsestatement 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 users hashed passwordin the database. Lets build this method
in ColoredListsUsers:
class ColoredListsUsers
{
// Class properties and other methods omitted to save space
7/30/2019 web app dev full
43/125
/**
* 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;
}
}
7/30/2019 web app dev full
44/125
else
{
return FALSE;
}
}
}
Finally, since verifying an account logs a user in, we need to
update common/header.phpto recognize that a user is logged in and display different
options. InPart 4, common/header.php featured a code snippet that looked like this:
Log out Your Account
Sign up Log in
To make those comments into functional code, we need to modify this snippet with
an if-else block:
Log out Your Account
Sign up Log in
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, lets build the login form and allow our user to log in. To start, lets create a
new file named login.php at the root level of our app. Like our other publicly displayed
http://css-tricks.com/app-from-scratch-4-html-csshttp://css-tricks.com/app-from-scratch-4-html-csshttp://css-tricks.com/app-from-scratch-4-html-csshttp://css-tricks.com/app-from-scratch-4-html-css7/30/2019 web app dev full
45/125
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 andthe 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:
You are currently logged in.
Log out
7/30/2019 web app dev full
46/125
Login FailedTry Again?
Password
Did you forget your password?
Your list awaits...
Password
7/30/2019 web app dev full
47/125
Did you forget your password?
Notice the Did you forget your password? links well 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 users 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()
{
7/30/2019 web app dev full
48/125
$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.
7/30/2019 web app dev full
49/125
Create a new file named logout.php at the root level of the app and place the following
code inside:
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.phpat the root level of the app. Theres a lot going on here
because were 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 shegets 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 users 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:
7/30/2019 web app dev full
50/125
include_once "common/header.php";
include_once 'inc/class.users.inc.php';
$users = new ColoredListsUsers($db);
if(isset($_GET['email']) && $_GET['email']=="changed")
{
echo "Your email address "
. "has been changed.";
}
else if(isset($_GET['email']) && $_GET['email']=="failed")
{
echo "There was an error "
. "changing your email address.";
}
if(isset($_GET['password']) && $_GET['password']=="changed")
{
echo "Your password "
. "has been changed.";
}
elseif(isset($_GET['password']) && $_GET['password']=="nomatch")
{
echo "The two passwords "
. "did not match. Try again!";
}
if(isset($_GET['delete']) && $_GET['delete']=="failed")
{
echo "There was an error "
. "deleting your account.";
7/30/2019 web app dev full
51/125
}
list($userID, $v) = $users->retrieveAccountInfo();
?>
Your Account
7/30/2019 web app dev full
52/125
New Password
Repeat New Password
7/30/2019 web app dev full
53/125
endif;
?>
Creating the Method to Retrieve Account Info
In order to have the users login name and verification code available to our account
option forms, we need to build a new method that will load this information from thedatabase. 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
{
7/30/2019 web app dev full
54/125
$stmt = $this->_db->prepare($sql);
$stmt->bindParam(':user', $_SESSION['Username'], PDO::PARAM_STR);
$stmt->execute();
$row = $stmt->fetch();
$stmt->closeCursor();
return array($row['UserID'], $row['ver_code']);
}
catch(PDOException $e)
{
return FALSE;
}
}
}
Bui l d i ng t he In t e rac t i ons F i l e
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:
7/30/2019 web app dev full
55/125
&& $_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" : "nomatch";
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;
}
7/30/2019 web app dev full
56/125
exit;
}
else
{
header("Location: /");
exit;
}
?>
Upda t i ng t he Ema i l Addre ss
When a user submits a request to change their email address, the method updateEmail() iscalled. 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";
7/30/2019 web app dev full
57/125
try
{
$stmt = $this->_db->prepare($sql);
$stmt->bindParam(':email', $_POST['username'], PDO::PARAM_STR);
$stmt->bindParam(':user', $_POST['userid'], PDO::PARAM_INT);
$stmt->execute();
$stmt->closeCursor();
// Updates the session variable
$_SESSION['Username'] = htmlentities($_POST['username'], ENT_QUOTES);
return TRUE;
}
catch(PDOException $e)
{
return FALSE;
}
}
}
U p d a t i n g t h e P a s s w o r d
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
willcompare the password and the password confirmation to make sure they match
before saving.
class ColoredListsUsers
{
// Class properties and other methods omitted to save space
/**
7/30/2019 web app dev full
58/125
* 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
7/30/2019 web app dev full
59/125
{
return FALSE;
}
}
}
D e l e t i n g t h e A c c o u n t
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 dont 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 users 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 well build in a
minute.
The method, when its 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=(
7/30/2019 web app dev full
60/125
SELECT ListID
FROM lists
WHERE UserID=:user
LIMIT 1
)";
try
{
$stmt = $this->_db->prepare($sql);
$stmt->bindParam(":user", $_POST['user-id'], PDO::PARAM_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::PARAM_INT);
$stmt->execute();
$stmt->closeCursor();
}
catch(PDOException $e)
{
die($e->getMessage());
7/30/2019 web app dev full
61/125
}
// 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::PARAM_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;
}
}
7/30/2019 web app dev full
62/125
}
Rese t t i ng an Account Password
At this point, were 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 ofour app and place the following code inside:
Reset Your Password
Enter the email address you signed up with and we'll send
you a link to reset your password.
Email
7/30/2019 web app dev full
63/125
When a user visits this page, theyll 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 users 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();
7/30/2019 web app dev full
64/125
}
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 users 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:
Password Reset Requested
Check your email to finish the reset process.
7/30/2019 web app dev full
65/125
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.phpwhere theyre 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 =
7/30/2019 web app dev full
66/125
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 rootlevel 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 theyreshown a confirmation message letting them know that their password was changed.
Inside resetpassword.php, add the following code:
7/30/2019 web app dev full
67/125
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]
7/30/2019 web app dev full
68/125
Reset Your Password
Choose a New Password:
Re-Type Password:
7/30/2019 web app dev full
69/125
decided from the get-go that this was going to be an AJAX-y app. We didn't chose AJAX because it's apopular buzzword, we chose it because we know it's the best path toward making a responsive, easy touse, natural feeling application on the web.
A L L S E R I E S N A V I G A T I O N
Part 1 - Planning the App: Basic Idea and Design
Part 2 - Planning the App: Database Architecture and Development Approach Part 3 - Designing the App: Workflow Map and Photoshop Design
Part 4 - Designing the App: HTML and CSS
Part 5 - Developing the App: User Interaction
Part 6 - Developing the App: Adding AJAX Interactivity
Part 7 - Developing the App: List Interaction
Part 8 - Security & The Future
And finally.... the application!Colored Lists
The Big Thing: Saving the ListAJAX 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.
http://css-tricks.com/app-from-scratch-1-designhttp://css-tricks.com/app-from-scratch-1-designhttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+2/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+2/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+3/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+3/http://css-tricks.com/app-from-scratch-4-html-csshttp://css-tricks.com/app-from-scratch-4-html-csshttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+5/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+5/http://css-tricks.com/app-from-scratch-6-ajaxhttp://css-tricks.com/app-from-scratch-6-ajaxhttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+7/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+7/http://css-tricks.com/app-from-scratch-8-finishinghttp://css-tricks.com/app-from-scratch-8-finishinghttp://coloredlists.com/http://coloredlists.com/http://coloredlists.com/http://coloredlists.com/http://css-tricks.com/app-from-scratch-8-finishinghttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+7/http://css-tricks.com/app-from-scratch-6-ajaxhttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+5/http://css-tricks.com/app-from-scratch-4-html-csshttp://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+3/http://copterlabs.com/blog/Creating+an+App+from+Scratch+Part+2/http://css-tricks.com/app-from-scratch-1-design7/30/2019 web app dev full
70/125
Interface JavaScriptAlongside 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 l ist 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 filesThere 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.
initialize();
1. LoadjQuery(from Google for better speed)
2. Load a customizedjQuery UIlibrary (for the draggable stuff)
3. Load thejEditable 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 JavaScriptIn 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(){
http://jquery.com/http://jquery.com/http://jquery.com/http://jqueryui.com/http://jqueryui.com/http://jqueryui.com/http://www.appelsiini.net/projects/jeditablehttp://www.appelsiini.net/projects/jeditablehttp://www.appelsiini.net/projects/jeditablehttp://www.appelsiini.net/projects/jeditablehttp://jqueryui.com/http://jquery.com/7/30/2019 web app dev full
71/125
};
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?
Walk the dog
Much of that is redundant across all the list items. What we want is more like this:
Walk the dog
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")
7/30/2019 web app dev full
72/125
.wrapInner("")
.append("");
};
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 usbecause 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(){
7/30/2019 web app dev full
73/125
// 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 / sortableMega 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 , 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 listsaving!
},
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
7/30/2019 web app dev full
74/125
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("")
.find(".crossout")
.animate({
width:"100%"
})
.end()
.animate({
opacity:"0.5"
},
"slow",
"swing",
function(){
// DEVELOPER, the user has marked this item as done, commencesaving!
})
}
else
{
$(this)
.siblings('span')
.find('img.crossout')
7/30/2019 web app dev full
75/125
.remove()
.end()
.animate({
opacity :1
},
"slow",
"swing",
function(){
// DEVELOPER, the user has UNmarked this item as done,commence saving!
})
}
});
Color CyclingWe'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({
7/30/2019 web app dev full
76/125
// 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");
}elseif(curColor =="colorYellow"){
$(this).removeClass("colorYellow").addClass("colorRed").attr("color","3");
}elseif(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.
7/30/2019 web app dev full
77/125
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 itemsOur 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, commencedeleting!
success:function(r){
thiscache
.parent()
.hide("explode",400,function(){$(this).remove()});
// Make sure to reorder list items after a delete!
}
});
}
else
{
thiscache.animate({
7/30/2019 web app dev full
78/125
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 li ttle 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 itemsIn 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'tuse 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 :'