web app dev full

download web app dev full

of 125

Transcript of web app dev full

  • 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/#cmnt395
  • 7/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-design
  • 7/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/pdo
  • 7/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.php
  • 7/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-css
  • 7/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.php
  • 7/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-css
  • 7/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?

    Email



    Password



    Did you forget your password?

    Your list awaits...

    Email



    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-design
  • 7/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 :'