• No results found

web app dev full

N/A
N/A
Protected

Academic year: 2021

Share "web app dev full"

Copied!
125
0
0

Loading.... (view fulltext now)

Full text

(1)

Creating a Web App from Scratch – Part

1 of 8: Basic Idea and Design

P 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 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 site Copter Labs. 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:

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.

(2)

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.

(3)

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

(4)

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.

W h e r e W e ‟ r e A t

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.

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, 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.

D a t a S t o r a g e — P 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, 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.

Crea ting th e Da tab ase

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.

(5)

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.

Tab le 1: User In forma tion

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_INCREMENT, Username VARCHAR(150) NOT NULL, Password VARCHAR(150), ver_code VARCHAR(150), verified TINYINT DEFAULT 0 )

Tab le 2: List In fo rm a tio n

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 (

(6)

UserID INT NOT NULL, ListURL VARCHAR(150) )

Tab le 3: List I tems

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 tokeep 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

(7)

Th e da tab ase wi th ou r th ree 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.

D a t a H a n d l i n g — P 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, 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.

Plan n in g ou r PH P Cla sses

Object-oriented programming provides an easy way to keep related functions grouped

together. After

learning 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.

U ser Actio ns Cla ss

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:

C rea t e an ac c oun t

Veri fy t h e a cc ount

Upd at e t h e ac c oun t emai l ad d ress

Upd at e t h e ac c oun t p a ssword

R et ri eve a forgott en p a ssword

Delet e t h e a cc ount

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 Actio ns Cla ss

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:

(8)

Upd at e a list it em

Delet e a li st it em

Change a list item‟s position

Change a list item‟s color

Actio n Ha nd ling Scrip ts

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.

M o v i n g O n

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

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 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:

(9)

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.

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 we‟d better get started actually designing here.

H om epag e (Logg ed 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.

(10)

The home

page as it appears when logged in

H om epag e (Logg ed Ou t)

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.

(11)

When

logged out, we‟ll encourage the visitor to sign up

S ma ll 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

(12)

Reg istra tion

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.

Accoun t

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!

(13)

Account

controls

Bu tton s

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

(14)

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 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.

(15)

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"

/>

(16)

<link rel=

"stylesheet"

href=

"style.css"

type=

"text/css"

/>

<link rel=

"shortcut icon"

type=

"image/x-icon"

href=

"http://cdn.css-tricks.com/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 -->

(17)

</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>

(18)

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>

(19)

<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>

(20)

<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.

(21)

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=

"http://cdn.css-tricks.com/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";

?>

(22)

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>

(23)

<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

:

(24)

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(http://cdn.css-tricks.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(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

*/

(25)

#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

;

}

(26)

#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(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.

(27)

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.

W h e r e A r e W e ?

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:

C rea tin g an Ac c oun t

M od i fyi n g Ac c ou nt In forma t i on

R eset tin g a Lost Pa ssword

Delet i n g an Ac c oun t

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, we‟ll need to create a couple of small files.

D efin in g S ite - Wid e Co nstan ts

Our site won‟t require many

constants

, but in the interest of keeping them easy to

maintain, we‟ll 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,

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');

(28)

?>

As we develop, we‟ll add more to this file.

Crea ting a PDO Ob ject

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;

}

(29)

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.

Wh y PD O ?

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.

Fram ing Ou t a U ser I n tera ction s Cla ss

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.

B u i l d i n g t h e C l a s s

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

(30)

* 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 { } ?>

Co nn ectin g th e Cla ss to th e D a tab ase

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

(31)

* @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!

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 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:

(32)

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.

Crea ting th e S ign -U p 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:

(33)

<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" /><br />

<input type="submit" name="register" id="register" value="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

(34)

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.

S aving th e U ser’ s Ema il Add ress

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

(35)

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]">contact "

. "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();

(36)

$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.

(37)

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.

G en era ting and S en d ing a Verifica tio n Ema il

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 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 = <<<MESSAGE

From: Colored Lists <[email protected]>

(38)

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.

(39)

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])):

(40)

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 $_GET['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()

:

(41)

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;

}

(42)

{

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 successful

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

{

References

Related documents

Life Cycle Model of the Classroom Teacher : The level of expertise of the teacher moves from novice, apprentice, professional, expert, distinguished, to emeritus as time in the

Ultimately, as evident from results, the proposed OAS framework based on global majority voting fusion output image similarity comparison score criterion is an effective and

4.2 Payments to the TO Contractor shall be made as outlined Section 2 of the CATS TORFP, but no later than thirty (30) days after the Agency’s receipt of an invoice for

We found that adolescents with high levels of CSB symptoms (classi fi ed as the CSB group), in compari- son to sexual fantasizers and abstaining adolescents, are characterized by

An alternative explanation for BF loans having larger loss percentages than non-BF loans when a loss occurs is that FSA beginning farmer loan losses occur earlier and thereby

2.6 Achieving pedagogical “equality” by instructional differentiation 19 3 Theory: self-regulation of learning in multilevel instructional contexts 23 3.1 Cognitive learning,

In conclusion, the findings from the current study indicate that infants’ body emotion knowledge undergoes a transition between 3.5 months and 5 months, with infants initially

If Versa-Matic determines that a product bearing a Versa-Matic brand has failed under normal use and service due to a defect in material or workmanship within the warranty period