As we all know, WordPress is so extensive that you can use it for nearly anything. There are even articles on sites with crazy titles such as 101 alternative uses for WordPress. So I thought, hey, why not? I bet a lot of people want to create their own Web Apps, and essentially WordPress can do that for you. In this video tutorial, we're going to make an online Address Book.
*Editor's Note: We have a special treat for you today. This tutorial was originally intended to be part of our NETTUTS+ program. However, the program has taken a big longer to roll out than we originally anticipated. Don't worry, it's still coming! Until then, enjoy this one on us!
This tutorial was a massive undertaking for Harley. So please, if you don't mind, take a moment to Digg it! Thank you!
Read our interview with Harley!



Preface
This Address Book is going to have the works. Ajax loading, conditional admin stuff, the shiz. This, unfortunately means we need to meet a few requirements, a few thing the theme needs. A copy of jQuery to start with would be handy. On top of this, the theme needs a few images! Download this zip, that you'll later expand into your theme directory. Finally, a copy of the Live Search Plugin that I've edited slightly for CSS reasons. Original here
And of course, as always, you'll need a WordPress blog running that you've got access to. There are tutorials on running Wordpress locally here for Windows, and here for OS X. Due to the nature of the post content, I highly reccomend starting a fresh WordPress directory.
So you should have: jQuery, images, live search plugin, and a WordPress directory. Super.
Step 1 - Organizing files.

Lets start out by creating the necessary files. In the wp-content/themes directory of your WordPress blog, create a folder called 'addressBook'. Add the jQuery file and images into this new folder. The 'live-search-popup' goes in the wp-content/plugins folder, for obvious reasons.

Now that the downloaded files are in place, we need to create a few more files. In the theme directory, create: addressBook.js, index.php, single.php and style.css. While we're dealing with these files, we'll create the theme meta. Open style.css and paste + edit this:
1 |
/* |
2 |
Theme Name: AddressBook |
3 |
Theme URI: https://code.tutsplus.com |
4 |
Description: Using WordPress as an Address Book |
5 |
Version: 1.0 |
6 |
Author: Harley Alexander |
7 |
Author URI: http://www.baffleinc.com/ |
8 |
*/ |
Now you've created some basic information for WordPress to recognise, navigate to the Theme selection page (Design>Themes) in wp-admin, and click the new AddressBook block. For now (if you're using a version of WP that shows a preview before activation) It'll be blank. Don't worry though! Activate it!



Step 2 - Post Content
Now all the files are there and organized, we can move on to creating some sample posts for us. The reason this method works is because WordPress allows for custom fields. Custom fields are just that - custom content area to fill in. A post content field is different to the tags field as custom fields are different to the category fields. The reason this is so fantastic is because it means we can attach separate bits of information to a post without having to painfully fish them out of the content. Navigate to the Dashboard, and write a new post.
We'll be using:
- Title as the name of the person
- Post content as the notes for the person
- Categories as group of the person (work/home related)
- Custom fields For the home, work, and mobile phones. Website and email, address and finally an image of them.



Now the custom fields aren't as daunting as they sound you have to give them a name (key) and some content (value). The cool thing about them is, is that once they've been used once, you can re-select them from a drop down menu so you don't confuse names by accident. Also, not every custom field is necessary, so you can leave some blank if you don't have that specific information (later on when we get theming we'll actually create an edit/add link.



After you've added a couple of dummy posts, we can move onto displaying the content via template files.
Step 3 - WordPress Code
This address book (surprise surprise) will be modeled off the Apple Address Book:



Blurred for obvious reasons, that picture also displays which sections are which, so it's easier to visualize whilst coding! We'll start with index.php.
index.php
This page is what everybody will first see when they come to your Address Book, so it's gotta be informational. Like the preview above, all pages will have the same layout - Single.php will have the same look and feel, as to create that never re-loaded effect. This is just for JS-less browsers, so that it degrades 100% gracefully. I mean 100%. The jQuery in this is just to speed things up the loading of the content, and (eventually) the live search function. The Address Book still needs to work without these commodities though!
To start off a WordPress theme, I always have some basic header information.
1 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
2 |
<html xmlns="http://www.w3.org/1999/xhtml"> |
3 |
<head profile="http://gmpg.org/xfn/11"> |
4 |
|
5 |
<title><?php bloginfo('name'); ?><?php wp_title(); ?></title> |
6 |
|
7 |
<meta http-equiv="Content-Type" content="<?php bloginfo('html_type'); ?>; charset=<?php bloginfo('charset'); ?>" /> |
8 |
<meta name="generator" content="WordPress <?php bloginfo('version'); ?>" /> |
9 |
|
10 |
<link rel="stylesheet" href="<?php bloginfo('stylesheet_url'); ?>" type="text/css" media="screen" /> |
11 |
<?php wp_head(); ?> |
12 |
</head> |
13 |
<body> |
14 |
|
15 |
</body> |
16 |
</html> |
Change whats between the title tags to simply 'Address Book'. Underneath the stylesheet link, add the following code, to bring in the eventual JavaScript code:
1 |
<script src="<?php bloginfo('template_directory'); ?>/jquery-1.2.6.min.js" type="text/javascript"></script> |
2 |
<script src="<?php bloginfo('template_directory'); ?>/addressBook.js" type="text/javascript"></script> |
Since all the header information is done, we can move onto coding together a structured layout. In between the body tags, add this:
1 |
<div id="wrapper"> |
2 |
<div id="header"> |
3 |
|
4 |
<!-- Header things, like the searchform etc go here --> |
5 |
|
6 |
</div> |
7 |
<div id="groups"> |
8 |
|
9 |
<!-- Eventually the categories will go here --> |
10 |
|
11 |
</div> |
12 |
<div id="mainSection"> |
13 |
|
14 |
<!-- This is where the list of names sit --> |
15 |
|
16 |
</div> |
17 |
<div id="loadArea"> |
18 |
|
19 |
<!-- This is where the full post contents go --> |
20 |
|
21 |
</div> |
22 |
<div id="footer"> |
23 |
|
24 |
<!-- Some admin stuff and total card count go here --> |
25 |
|
26 |
</div> |
27 |
</div> |
These sections will contain all the post loops. Obviously. Let's start with the header. It consists of a h1 for the title, and the searchform. Replace the comment:
1 |
<h1><span>Address Book</span></h1> |
2 |
<div id="searchform"> |
3 |
<form action="<?php bloginfo('url'); ?>" method="get" > |
4 |
<p><input type="submit" id="searchsubmit" value="" /> |
5 |
<input type="text" name="s" id="s" value="<?php echo wp_specialchars($s, 1); ?>" /></p> |
6 |
</form> |
7 |
</div> |



The search form isn't actually the plugin yet. This is because we only need the results, right? So we use a different function in the mainSection that overlays the original list to appear as though it's filtering through all the data. The span wrapping the h1 text is so we can add a small favicon-esque icon to the left of.
The groups section is even easier and shorter.
1 |
<h3>Groups</h3> |
2 |
<ul> |
3 |
<li><a href="<?php bloginfo('url'); ?>">All</a></li> |
4 |
<?php wp_list_categories('exclude=1&title_li='); ?> |
5 |
</ul> |



The mainSection, or where the name list goes, consists of the loop and the live search results that overlay this list. The exclude parameter means it doesn't display 'uncategorized' because an empty category is annoying!
1 |
<h3>Name</h3> |
2 |
<ul id="posts"> |
3 |
<?php if(have_posts()) : query_posts('showposts=9999'); while(have_posts()) : the_post(); ?> |
4 |
<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li> |
5 |
<?php endwhile; endif; ?> |
6 |
</ul> |
7 |
<?php if(function_exists('livesearchpopup_resultsbox')){livesearchpopup_resultsbox('160px');} ?> |



Noticed I've compacted the loop to a very small amount. Usually this would be bad practice, but for this application of WordPress it's not actually necessary. The query_posts forces the loop to display 9999 posts instead of the Dashboard set default. The function is included in a safe way, only If the plugin is activated. Which we'll do now. Open the plugins section of the Dashboard, and activate the live-search-popup plugin. If it's not there then you haven't downloaded and installed the plugin. Install it now.
The loadArea on the front page is essentially empty, so we just need an explanitory title!
1 |
<h2>Select an entry to view!</h2> |



Finally the footer. This block has the most amount of code, and is the first encounter with conditional logged in status! I'll explain bit by bit.
1 |
<?php if($user_ID) : ?> |
2 |
<a href="<?php bloginfo('url'); ?>/wp-admin/post-new.php" title="Add Entry" class="addEntry"><img src="<?php bloginfo('template_directory'); ?>/images/plus.png" alt="" /></a> |
3 |
<a href="<?php bloginfo('url'); ?>/wp-admin/post.php?action=edit&post=<?php echo $post->ID; ?>" title="Edit this card" class="editButton"><img src="<?php bloginfo('template_directory'); ?>/images/editButton.png" alt="" /></a> |
4 |
<?php endif; ?> |
5 |
|
6 |
<span id="totCards"><?php $totcards = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->posts WHERE post_status = 'publish'"); |
7 |
if (0 < $totcards) $totcards = number_format($totcards); echo $totcards.' cards'; ?></span> |



So the first two anchor tags are conditional on $user_ID. This variable checks if a user is logged in (in this case that has administrating privileges), then displays some add and edit buttons. The weird SQL php at the end gets the total number of Contact Cards, so it displays just like Apple Address Book.
That's it for index! Next up we'll be working on single.php, so open it up and we'll get started!
single.php
index.php should still be open, so because essentially single.php is the same layout, copy ALL the index.php code over to single.php, and save it.
Because it's single.php, by default the loop will only display one post. To counter this, we've added a query_posts function in index.php already.
Single.php differs from the index mainly with the code inbetween the loadArea. Instead of the h2, the loop will fill this in.
1 |
<?php if(have_posts()) : |
2 |
query_posts($query_string); |
3 |
while(have_posts()) : the_post(); ?> |
4 |
<div id="inner-content" style="overflow: auto;"> |
5 |
|
6 |
<-- vast loop goes here. VAST. --> |
7 |
|
8 |
</div> |
9 |
< endwhile; endif; ?> |
The Loop content, from here on consists of blocks of PHP, that are relatively identical (bar the image and the notes). The $query_string is a short and dirty way of reverting the loop's parameters back to displaying one post for single.php. The post starts with an image.
1 |
<?php if((get_post_meta($post->ID,'entryImg')) != ''){ ?> |
2 |
<img src="<?php echo get_post_meta($post->ID, 'entryImg', true); ?>" alt="" id="entryImg" /> |
3 |
<?php } else { ?> |
4 |
<?php if ( $user_ID ) : ?> |
5 |
<a href="<?php bloginfo('url'); ?>/wp-admin/post.php?action=edit&post=<?php echo $post->ID; ?>#meta-62"><img src="<?php bloginfo('template_directory'); ?>/images/emptyEntryImgAdd.png" alt="" id="entryImg" /></a> |
6 |
<?php else : ?> |
7 |
<img src="<?php bloginfo('template_directory'); ?>/images/emptyEntryImg.png" alt="" id="entryImg" /> |
8 |
<?php endif; ?> |
9 |
<?php } ?> |
Crazy code! The first 'if' checks if that Custom Field has any content, by comparing it to the value of nothing. The then nestled 'if' statement checks to see if the admin is logged in, and if so provides a link to edit the custom field. The weird one (...&post=<?php echo $post->ID; ?>#meta-62). By using the post ID we have a dynamically changing URL that stays 100% relevant! The end of the URL will be different for you! I have a DOM selector, but you can use firebug to find out the IDs of the custom field's divs. The div containing the entryImg value for me was #meta-62.
We need to add a small title too!
1 |
<h2><?php the_title(); ?></h2> |
Easy peasy. Next is the first 'block'. All these blocks are relatively the same. In fact, after the first initial email and website, they're just static characters.



"
Email/Website Blocks
1 |
<?php if((get_post_meta($post->ID,'email')) != ''){ ?> |
2 |
<p> |
3 |
<strong>Email:</strong> <a href="mailto:<?php echo get_post_meta($post->ID, 'email', true); ?>"><?php echo get_post_meta($post->ID, 'email', true); ?></a> |
4 |
<?php if ($user_ID) : ?><a class="edit-link" href="<?php bloginfo('url'); ?>/wp-admin/post.php?action=edit&post=<?php echo $post->ID; ?>#meta-56">Remove or Edit</a> |
5 |
<?php else : echo ''; endif; ?> |
6 |
</p> |
7 |
<?php } else { edit_post_link('<span class="add">Email</span>'); } ?> |
Heavy use of custom fields... The if statement checks if the custom field has any content, by checking it against '' or nothing. So if it doesn't equal nothing (therefore has content), display it. Else it doesn't have any content, display an add/edit link.
Right under this, copy that block and change the 'email' in ALL the custom field tags to 'website', and kill the 'mailto:' in the href value. Don't forget to change the #meta-## link! Mine was 57 for the website field. Don't forget the strong tag or the edit post value either!
1 |
<?php if((get_post_meta($post->ID,'website')) != ''){ ?> |
2 |
<p> |
3 |
<strong>Email:</strong> <a href="<?php echo get_post_meta($post->ID, 'website', true); ?>"><?php echo get_post_meta($post->ID, 'website', true); ?></a> |
4 |
<?php if ($user_ID) : ?><a class="edit-link" href="<?php bloginfo('url'); ?>/wp-admin/post.php?action=edit&post=<?php echo $post->ID; ?>#meta-57">Remove or Edit</a> |
5 |
<?php else : echo ''; endif; ?> |
6 |
</p> |
7 |
<?php } else { edit_post_link('<span class="add">Website</span>'); } ?> |



Phones/Address Blocks
All these are now the same. You only need to change four things per block. The custom field names, the strong tag, the edit link's href value and content. Here is the first block:
1 |
<?php if((get_post_meta($post->ID,'phone-home')) != ''){ ?> |
2 |
<p> |
3 |
<strong>Home Phone:</strong> <?php echo get_post_meta($post->ID, 'phone-home', true); ?> |
4 |
<?php if ($user_ID) : ?><a class="edit-link" href="<?php bloginfo('url'); ?>/wp-admin/post.php?action=edit&post=<?php echo $post->ID; ?>#meta-66">Remove or Edit</a> |
5 |
<?php else : echo ''; endif; ?> |
6 |
</p> |
7 |
<?php } else { edit_post_link('<span class="add">Home Phone</span>'); } ?> |
Go ahead and duplicate this four times, once for each custom field. You should have one of these blocks for: Home phone, Work phone, Mobile phone, and address.



Notes Block
Finally, we need to display some notes.
1 |
<p class="notes"><strong>Note:</strong> <?php the_content(); ?></p> |
Awesome! That's all the WordPress code needed! Note how the Work Phone is a link. Later we'll style this in CSS to have a plus icon. Currently it should look like this:



Step 4 - CSS
CSS makes the site look pretty! Let's jump right into it. We need to start off with a dirty reset, body defenition and the background of the wrapper. WrapperBg.png is a screenshot of the actual Apple Address Book cleared out. We add all the stuff we need on top.
1 |
*{ |
2 |
margin: 0; |
3 |
padding: 0; |
4 |
outline: 0; |
5 |
}
|
6 |
|
7 |
body{ |
8 |
font-family: "Lucida Grande", Lucida, Verdana, sans-serif; |
9 |
}
|
10 |
|
11 |
#wrapper{ |
12 |
|
13 |
width: 621px; |
14 |
height: 370px; |
15 |
margin: 50px auto; |
16 |
padding: 24px 40px 55px 40px; |
17 |
background: url(images/wrapperBg.png); |
18 |
}
|
Already, if you look at the preview, the window shape is already there. Next up, we need to figure out the header. Luckily, I've done it for you! Yay!
1 |
#header{ |
2 |
text-align: center; |
3 |
height: 55px; |
4 |
}
|
5 |
|
6 |
#header h1{ |
7 |
font-size: 12px; |
8 |
font-weight: normal; |
9 |
padding: 5px; |
10 |
}
|
11 |
|
12 |
#header h1 span{ |
13 |
background: url(images/h1Spanbg.png) no-repeat left; |
14 |
padding-left: 20px; |
15 |
}
|
16 |
|
17 |
#searchform{ |
18 |
float: right; |
19 |
margin-right: 10px; |
20 |
background: url(images/searchBg.png) no-repeat left 1px; |
21 |
width: 135px; |
22 |
height: 22px; |
23 |
}
|
24 |
|
25 |
#s{ |
26 |
background: 0; |
27 |
border: 0; |
28 |
line-height: 20px; |
29 |
width: 110px; |
30 |
}
|
31 |
|
32 |
#searchsubmit{ |
33 |
background: 0; |
34 |
border: 0; |
35 |
height: 20px; |
36 |
width: 15px; |
37 |
margin: 0; |
38 |
}
|
Fortunately, thats all thats needed to make the header 100% done.



Now we'll style the groups section, which is actually the categories:
1 |
#wrapper h3{ |
2 |
background: url(images/h3bg.png) repeat-x; |
3 |
height: 16px; |
4 |
font-size: 10px; |
5 |
text-align: center; |
6 |
line-height: 16px; |
7 |
font-weight: normal; |
8 |
border-right: 1px solid gray; |
9 |
}
|
10 |
|
11 |
#groups{ |
12 |
width: 161px; |
13 |
float: left; |
14 |
}
|
15 |
|
16 |
#groups li a{ |
17 |
background: url(images/groupsBg.png) no-repeat 10px 4px; |
18 |
}
|
19 |
|
20 |
#groups li a:hover{ |
21 |
background: url(images/groupsBg.png) no-repeat 10px 4px gray; |
22 |
}
|
23 |
|
24 |
#groups li, #mainSection li{ |
25 |
padding: 0; |
26 |
font-size: 12px; |
27 |
overflow: hidden; |
28 |
}
|
29 |
|
30 |
ul li a{ |
31 |
text-decoration: none; |
32 |
color: black; |
33 |
padding: 3px 0 3px 30px; |
34 |
margin: 0; |
35 |
display: block; |
36 |
}
|
And then the main section to slip in beside the group section:
1 |
#mainSection{ |
2 |
width: 160px; |
3 |
float: left; |
4 |
position: relative; |
5 |
}
|
6 |
|
7 |
#mainSection li a{ |
8 |
background: url(images/h1SpanBg.png) no-repeat 10px 5px !IMPORTANT; |
9 |
}
|
10 |
|
11 |
#mainSection li a:hover{ |
12 |
background: url(images/h1SpanBg.png) no-repeat 10px 5px gray !IMPORTANT; |
13 |
}
|
14 |
|
15 |
#livesearchpopup_box{ |
16 |
position: absolute; |
17 |
top: 0; |
18 |
left: 0; |
19 |
width: 158px !IMPORTANT; |
20 |
height: 264px; |
21 |
border: 0; |
22 |
}
|
And with that, everything is pushed into it's respective areas, except the footer. If you search, the overlay will go underneath the posts that are already there. So we need to position them absolutely. The last selector above does this.



The load area still needs to be styled. To see this fully in action, go to a single page by clicking on a contact. Before it'll look like this:



1 |
#loadArea{ |
2 |
position: relative; |
3 |
overflow: auto; |
4 |
height: 280px; |
5 |
float: left; |
6 |
width: 279px; |
7 |
padding: 10px 10px 0 11px; |
8 |
font-size: 10px; |
9 |
}
|
10 |
|
11 |
#loadArea strong{ |
12 |
color: gray; |
13 |
}
|
14 |
|
15 |
#loadArea #entryImg{ |
16 |
float: left; |
17 |
width: 48px; |
18 |
padding-right: 10px; |
19 |
}
|
20 |
|
21 |
#loadArea p{ |
22 |
line-height: 16px; |
23 |
margin-bottom: 1em; |
24 |
clear: both; |
25 |
}
|
26 |
|
27 |
#loadArea a{ |
28 |
text-decoration: none; |
29 |
color: gray; |
30 |
}
|
31 |
|
32 |
#loadArea span.add{ |
33 |
background: url(images/add.png) no-repeat right center; |
34 |
padding: 3px 20px 1em; |
35 |
display: block; |
36 |
text-align: right; |
37 |
margin-top: 1em; |
38 |
}
|
39 |
|
40 |
#loadArea a.edit-link{ |
41 |
display: block; |
42 |
float: right; |
43 |
line-height: 16px; |
44 |
background: url(images/delete.png) no-repeat right 1px; |
45 |
padding: 0 20px; |
46 |
}
|
47 |
|
48 |
#loadArea h2{ |
49 |
font-size: 16px; |
50 |
height: 55px; |
51 |
}
|
52 |
|
53 |
p.notes{ |
54 |
border-top: 1px solid gray; |
55 |
}
|
56 |
|
57 |
#load{ |
58 |
background: url(images/ajaxLoader.gif); |
59 |
width: 32px; |
60 |
height: 32px; |
61 |
display: block; |
62 |
margin: 0 auto; |
63 |
position: absolute; |
64 |
left: 134px; |
65 |
top: 129px; |
66 |
}
|



Log into the wp-admin, and check how it looks on a single page! The #load is for when we get some jQuery happening. But because it's appended within the jQuery, it doesn't do any harm to have it in here.



The last bit off the CSS section is to fix up the footer. Some simple CSS to do this. The only interesting thing here is that you have to float the number of cards to the right, as if you positioned it from the left it'd look weird when you were logged in.
1 |
|
2 |
|
3 |
#footer{ |
4 |
height: 20px; |
5 |
line-height: 18px; |
6 |
font-size: 11px; |
7 |
padding-top: 3px; |
8 |
clear: both; |
9 |
}
|
10 |
|
11 |
#footer #totCards{ |
12 |
float: right; |
13 |
margin-right: 130px; |
14 |
text-shadow: #fff 1px 1px 2px; |
15 |
}
|
16 |
|
17 |
#footer a.addEntry{ |
18 |
margin-left: 165px; |
19 |
}
|
20 |
|
21 |
#footer a.editButton{ |
22 |
margin-left: 145px; |
23 |
}
|
The CSS is now all done! That means, that the site should now be fully functional in a browser without jQuery, essentially finished. But here at NETTUTS, we like to go the extra mile. So we'll work on making the contacts load with AJAX on the home page instead of reloading the page.
Now our challenge poses a few problems. The live-search-popup Plugin uses Prototype, and sadly the developer didn't consider JS library conflict. So this means we have to use a lengthened selector, and define the noConflict setting. Not hard, just a bit different.
Open addressBook.js. At the very top, we define the noConflict.
1 |
jQuery.noConflict(); |
This is jQuery's method of making sure all the jQuery native code is 100% tout seule, unique from all other libaries.
Now for the document ready call. As with any jQuery, you need to wrap the code in this.
1 |
jQuery(document).ready(function() { |
2 |
|
3 |
//jQuery Code here
|
4 |
|
5 |
});
|
This tutorial uses a similar method of loading to the NETTUTS tutorial How to load in and animate content with jQuery. Let's start with the event.
1 |
jQuery('#posts li a').click(function(){ |
2 |
|
3 |
//loading code...
|
4 |
|
5 |
});
|
Now we need to define a variable that contains all the single.php content that differs from index.php. This is all the custom fields.
1 |
var cardContent = jQuery(this).attr('href')+' #inner-content'; |
As with any self respecting AJAX developer, who doesn't want to show off that they can put a loader on their content! The loader itself has nothing to do with the actual loading, it's just shown whilst the loading is in progress!
1 |
jQuery('#load').remove(); |
2 |
jQuery('#loadArea').append('<span id="load"></span>'); |
3 |
jQuery('#load').fadeIn(300); |
Finally, for the actual loading. These functions use the variable, and replace what was in the #loadArea div with the inner-content of single.php.
1 |
jQuery('#loadArea').fadeOut(300, loadTheContent); |
2 |
|
3 |
function loadTheContent(){jQuery('#loadArea').load(cardContent,'',showNewContent());} |
4 |
|
5 |
function showNewContent(){jQuery('#loadArea').fadeIn(600, hideLoader);} |
6 |
|
7 |
function hideLoader(){jQuery('#load').fadeOut('normal');} |
8 |
|
9 |
return false; |
Well Done!
If you navigate to the home page now and click on one of the names, it will load automatically! It even fades in and out! I'd love for this to work on the results of the live-search-popup plugin, however it's loaded via prototype, so any jQuery applied won't work too well. Otherwise, this concludes yet another extensive way WordPress can be used to its fuller potential as... Pretty much anything!