Using PHPINCLUDE_START. (Last Lesson - #7 - Added 12-16-4)
by
03 Nov 2004
This is a tutorial on the use of the PHPINCLUDE_START template. In the process of writing an 800+ line monster into the boards I help administrate I learned a few things about this template I'd like to share. In this tutorial you'll be applying several hacks as an excersize, though some you may wish to keep as an enduring part of your boards. This tutorial assumes you know next to nothing about PHP, but it does assume you know a little about XML/HTML. The examples used in this tutorial are based upon a clean install of vbulletin. If you've made changes you'll need to adjust appropriately. Ready to begin? GREAT! Some basics If you've done any editting of your forum's styleset you've seen the PHPINCLUDE_START template and it's brother the PHPINCLUDE_END template (which is touched upon occassionally but not covered exhaustively here). These templates provide a means for you to inject you're own php code into vbulletin WITHOUT voiding your Jelsoft User Support eligability. PHPINCLUDE_START executes before any other templates are parsed, and PHPINCLUDE_END executes after all other templates are parsed. By the time vbulletin reaches the PHPINCLUDE_START template it has done the following
Hence you can at this point reset any and all of this information, but be warned that you can cause interesting things to happen. $phpinclude_output PHPINCLUDE_START is parsed under an object buffering mask. This means that statements that normally print things to the screen of the browser (echo, print statements) don't affect the browser. Instead the results of these statements is put into the $phpinclude_output variable (NOT $_phpinclude_output as listed in the header templates of vb 3.0.3. This is one of the surviving bugs in vb3). By default $phpinclude_output is contained within your header template (Again, under the eronous $_phpinclude_output). But since we're not planning to just include an html header we need to do something else with it. Note that you don't want to get rid of it entirely - if you're script has a non-fatal error the results of the error message go into $phpinclude_output and you'll need that for debugging. So let's move it somewhere useful - the footer. Open your footer template and append the following code to the very end. HTML Code:
<if condition="is_member_of($bbuserinfo, 6)"> $phpinclude_output </if> Why would we want to limit this to admins? Well, for one, do we really want users seeing error statements? Not really. For another it allows us to use echo and print statements to debug our scripts. Let's test this code. Clear out your PHPINCLUDE_START template's entire code (if you're on a fresh install) and type in the following PHP Code:
echo 'Hello World';
PHP Code:
print_r($bbuserinfo);
Code:
Array ( [userid] => 1 [temp] => [field1] => [field2] => [field3] => [field4] => [subfolders] => [pmfolders] => [buddylist] => [ignorelist] => [signature] => [searchprefs] => [usergroupid] => 6 [membergroupids] => [displaygroupid] => 6 [username] => Michael [password] => That concludes today's lesson. Tomorrow I'll append one of my hacks, the System announcement system, and go over it in detail to explain how and why it works. Lesson Two - An Announcment System in templates - Part 1 Today we're going to begin to build an announcment system and explore a couple of the functions unique to vbulletin in the process. The hack by itself is here, Spoiler (click to open)
Ack, missed a parenthesis. Correction
PHP Code:
$globaltemplates = array_merge($globaltemplates,
Close
Before we start playing around with PHPINCLUDE_START template in depth though it's advisable to build a style specifically for your tests with it. This way you can iron the bugs out on your experiments without crashing vital you're boards (which is possible - Fatal errors in PHPINCLUDE_START can bring the whole thing down in a hurry). Now, let's start our announcment system. The first part is defining output that will be displayed in the template. The system does this already for our echo and print statements by plugging them into the $phpinclude_output variable. That isn't the only variable we can use though. We can define any varaible we like and be able to post it into most templates. Templates that are used in functions, such as forumdisplay_nopost_level1, require a little creative cheating which will be discussed another day. To be effective announcements need to be in a prominent place, and few templates are as prominently placed as the header. So at the very end of your header put the following in HTML Code:
$announcement PHP Code:
$announcement = 'Hello World';
Rules for variables PHP, like all other programming languages, uses variables extensively, and PHP also has a particular love of array variables with over 40 functions devoted to their manipulation. Unlike some languages like C++ you don't have to declare your variables at the start of your program, though it's good practice to do this. One reason is security Security Warning: If you don't initialize a variable properly PHP might take it's value off of the browser line. For instance in the URL somewhere.php?file=lazy PHP will start the script with the variable $file already set to 'lazy.' Depending on that variable's role in your script the result of hackers putting variables on the browser line can range from annoying to catastrophic. Vbulletin initializes it's variables to guard against this threat and you should get in the habit of doing it as well in case you ever write PHP code intended to be ran outside of vbulletin. Variables in php always start with $ and contain no spaces. A few variables are reserved - if you use a PHP context editor such as Dreamweaver you'll be alerted to this when the text changes color. More info on variables can be found at http://www.php.net Ok, back to our variable, $announcement. Let's set it to something more interesting than "Hello World" PHP Code:
$announcement = 'Welcome to my forums <br />'
Now while this is all interesting it isn't particularly useful. What can make things interesting is putting a conditional in - if. Two faces of "IF" The if statement is the the most common control structure across all programming languages. Even Commodore BASIC had if statements. These statements allow you to execute code according the a condition. Let's try a very basic conditional for our announcement which limits it to us. PHP Code:
if ($bbuserinfo['userid']=='1')
Now, with a single ! we can turn this whole thing around. Putting ! at the start of a condition is the same as saying "NOT" Try this PHP Code:
if (!$bbuserinfo['userid']=='1')
PHP Code:
<if condition="$bbuserinfo['userid']==1">Hello Me
Well, that's enough for today methinks. Next lesson will begin to cover some serious ground by actually setting announcement to a useful value. Lesson Three - An Announcment System in templates - Part 2 Today we'll finish up our announcement system. With knowledge of conditionals we can build the thing entirely in the PHPINCLUDE_START template, but defining the variable on all those conditions can be painstaking and hard to edit. Wouldn't it be easier to put it all in a custom template? That way we can simply edit each of our announcements individually without worrying about affecting our code. This is also useful if you have a co-admin or a client that knows nothing about PHP and isn't willing to learn, yet they want to be able to type up announcements to be displayed in their header. Well, yes it is easier, and possible, but we gotta go fetch. The fetch_template and eval functions. When you make a custom template you can't simply plug it into other templates. Indeed, the ability to define you're own templates wouldn't be terribly useful if you couldn't call them back. The fetch_template function accomplishes this. The format of this function, by itself, is: PHP Code:
fetch_template('TEMPLATE_NAME')
PHP Code:
$announcement = fetch_template('announcement');
PHP Code:
eval('$announcement = "' . fetch_template('announcement') . '";');
HTML Code:
Hello $bbuserinfo[username] Now, time to build Now that we understand the tools let's go ahead and build our announcement system. First let's pretty up our announcment in the header. HTML Code:
<if condition="!empty($announcement)"> <table class="tborder" cellpadding="$stylevar[cellpadding]" cellspacing="$stylevar[cellspacing]" width="$stylevar[outertablewidth]" align="center"> <tr><td align="center">$announcement</td></tr> </table> </if> Now we'll define six announcements, as follows
The names for these custom templates should be, in the same order:
Now, clear out the PHPINCLUDE_START template and plug in the following: PHP Code:
// #####################################################
PHP Code:
eval('$announcement = "' . fetch_template('announcement_all') . '";');
PHP Code:
{
PHP Code:
else if ($bbuserinfo['posts'] == 0) // User has never posted. Encourage them.
PHP Code:
{
Next lesson we're going to start playing with user profile fields and using them to create user controls on the vbulletin display. Lesson Four - Power to the People #1 - User Selectable Postbit Start a thread on your boards about the layout and you'll a list of things folks don't like. Worse, often the thing one user detests will be something other users enjoy. Wouldn't it be nice if you could please everyone? Well, in vbulletin you can allow them to customize the layout to their tastes. Layout customization requires use of the User profile fields and most of the time it requires one or more template edits. We'll start with a simple one - letting the users choose the postbit layout (Note: This hack was originally discovered by Boofo, one of our moderators). As you know, vbulletin has two layout schemes - postbit (used over at http://www.vbulletin.com) and postbit_legacy (used here). The existing switch for these is in the vbulletin options panel. The setting is stored in $vboptions['legacypostbit']. If it's set to 1 (true) then we use it, otherwise we don't. Since you probably want to keep the announcement system we'll slide our next bit of code in front of it (This is one of the reasons why we used the header to clearly mark the start). We'll give it a header too. But first we need to go to user profile fields and create a new one. Choose to create a Single Selection drop down menu with the options of "Default - Vb3 style" and "Classic - Vb2 style" Put it in the thread viewing options. Note the field # that vbulletin assigns to it and plug it in for X below where you see "fieldX" PHP Code:
// #####################################################
Only then do we check for the field. Note the use of a new inbuilt function - strstr. strstr has the following format: PHP Code:
strstr($haystack, $needle)
Incidently, strstr is case sensitive. It has a brother, stristr, that is case insensitive. This concludes this lesson. Next time we'll deal with some more template editting intensive features. Lesson Five - Power to the People #2 - Bold / Not so Bold; Italics / Rather not By default vbulletin displays New threads since the last visit in bold. Suppose a user doesn't want that. Well, we can do that. Another user would like his subscribed threads to be displayed in italics so they're easier to spot. We can do that too. Open the threadbit template and search for this code: HTML Code:
<a href="showthread.php?$session[sessionurl]t=$thread[threadid]$thread[highlight]"><if condition="$show['gotonewpost']"><strong>$thread[threadtitle]</strong><else />$thread[threadtitle]</if></a> HTML Code:
<if condition="$show['gotonewpost'] AND $show['new_bold'] AND $show['subscribed'] AND $show['subscribed_italics']"> <a href="showthread.php?$session[sessionurl]t=$thread[threadid]$thread[highlight]"><i><b>$thread[threadtitle]</b></i></a> <else /> <if condition="$show['gotonewpost'] AND $show['new_bold']"> <a href="showthread.php?$session[sessionurl]t=$thread[threadid]$thread[highlight]"><b>$thread[threadtitle]</b></a> <else /> <if condition="$show['subscribed'] AND $show['subscribed_italics']"> <a href="showthread.php?$session[sessionurl]t=$thread[threadid]$thread[highlight]"><i>$thread[threadtitle]</i></a> <else /> <a href="showthread.php?$session[sessionurl]t=$thread[threadid]$thread[highlight]">$thread[threadtitle]</a> </if></if></if> Now let's open our PHPINCLUDE_START template. Since we want these items to be displayed by default we need to set their defaults, so at the very top PHP Code:
// #####################################################
PHP Code:
// #### Set New Posts Bold ##########################
Homework!! Now, it's time to apply what you've learned. As you're probably aware, when you move your mouse over a thread's cell you will get a preview. Create a switch like the ones above that turns the thread previews on or off. Hint 1: The thread preview is in the thread bit template. Hint 2: Specifically, it's here (Don't look if you want to hunt for it...) HTML Code:
<td class="alt1Active" id="t$thread[threadid]" title="$thread[preview]">
This concludes the basics of template based switches to control the user layout. In the next lesson we'll move onto something a bit more complex Lesson Six - Power to the People #3 - My Links Dropdown System Look around any boards and you're likely to note that some users use their sigs to display board related links to their favorite threads or outside sites. Some users also use their favorite boards as homepages. Wouldn't it be nice for these users if they could put some of their links in an easy find location Say, the navbar dropdowns is a useful spot. To begin this project create a user profile field that is multiple text lines and has a character limit of at least 2000. The user is then going to put links in this profile field using the standard url related tags - url, post, and thread. Once you do that crack into your navbar and look for this code: HTML Code:
<!-- nav buttons bar --> <div align="center"> <table class="thead" cellpadding="0" cellspacing="0" border="0" width="100%" align="center"> <tr align="center"> <if condition="$show['popups']"> HTML Code:
<if condition="!empty($mylinks)"> <td id="mylinks" class="vbmenu_control"><a href="#mylinks">My Links</a> <script type="text/javascript"> vbmenu_register("mylinks"); </script></td> </if> HTML Code:
<!-- / NAVBAR POPUP MENUS -->
HTML Code:
<if condition="!empty($mylinks)"> <!-- My Links Menu --> <div class="vbmenu_popup" id="mylinks_menu" style="display:none"> <table cellpadding="4" cellspacing="1" border="0"> $mylinks </table> </div> <!-- /My Links --> </if> PHP Code:
if (!empty($bbuserinfo['field14']))
As to what this code does, line by line: PHP Code:
require_once('./includes/functions_bbcodeparse.php');
PHP Code:
$mylinks = parse_bbcode2($bbuserinfo['field14'], 0, 0, 0, 1);
PHP Code:
$mylinks = str_replace('<br />', '', $mylinks);
PHP Code:
$mylinks = str_replace('</a>', '</a></td></tr>', $mylinks);
Lesson Seven - Navbar Games #1: Avatar in the Navbar The navbar is one of the most powerful templates in vbulletin. It is quite literally the heart of you're poster's browsing of your boards. While it has a lot if information in it, you can certainly cram in some more. Let's take a look at this template because the next several lessons are going to involve changes to it and it's useful to have a good understanding on how its built before cutting into it. HTML Code:
<script type="text/javascript"> <!-- function log_out() { ht = document.getElementsByTagName("html"); ht[0].style.filter = "progid:DXImageTransform.Microsoft.BasicImage(grayscale=1)"; if (confirm('$vbphrase[sure_you_want_to_log_out]')) { return true; } else { ht[0].style.filter = ""; return false; } } //--> </script> HTML Code:
<br />
HTML Code:
<!-- breadcrumb, login, pm info --> <table class="tborder" cellpadding="$stylevar[cellpadding]" cellspacing="$stylevar[cellspacing]" border="0" width="100%" align="center"> <tr> HTML Code:
<td class="alt1" width="100%"> <if condition="is_array($navbits)"> <table cellpadding="0" cellspacing="0" border="0"> <tr valign="bottom"> <td><a href="#" onclick="history.back(1)"><img src="$stylevar[imgdir_misc]/navbits_start.gif" alt="$vbphrase[go_back]" border="0" /></a></td> <td> </td> <td width="100%"><span class="navbar"><a href="$vboptions[forumhome].php?$session[sessionurl]" accesskey="1">$vboptions[bbtitle]</a></span> $navbits[breadcrumb]</td> </tr> <tr> <td class="navbar" style="font-size:10pt; padding-top:1px" colspan="3"><a href="$scriptpath"><img class="inlineimg" src="$stylevar[imgdir_misc]/navbits_finallink.gif" alt="$vbphrase[reload_this_page]" border="0" /></a> <strong>$navbits[lastelement]</strong></td> </tr> </table> <else /> <div class="navbar" style="font-size:10pt"><a href="$vboptions[forumhome].php?$session[sessionurl]" accesskey="1"><img class="inlineimg" src="$stylevar[imgdir_misc]/navbits_start.gif" alt="" border="0" /></a> <strong>$vboptions[bbtitle]</strong></div> </if> </td> Note that you can trade all of the above with the table below to switch the order of the user information and the breadcrumbs on your page if you want. HTML Code:
<if condition="$bbuserinfo['userid']">
HTML Code:
<td class="alt2" valign="top" nowrap="nowrap"> <div class="smallfont"> <!--<span style="float:$stylevar[right]">[<a href="login.php?$session[sessionurl]do=logout&u=$bbuserinfo[userid]" onclick="return log_out()">$vbphrase[log_out]</a>]</span>--> <strong><phrase 1="$bbuserinfo[username]">$vbphrase[welcome_x]</phrase></strong><br /> <phrase 1="$pmbox[lastvisitdate]" 2="$pmbox[lastvisittime]">$vbphrase[last_visited_x_at_y]</phrase> <if condition="$show['pmstats']"><br /><phrase 1="$vbphrase[unread_x_nav_compiled]" 2="$vbphrase[total_x_nav_compiled]" 3="$session[sessionurl]">$vbphrase[private_messages_nav]</phrase></if> </div> </td> HTML Code:
<else /> <td class="alt2" nowrap="nowrap" style="padding:0px"> <!-- login form --> <form action="login.php" method="post" onsubmit="md5hash(vb_login_password,vb_login_md5password,vb_login_md5password_utf)"> <script type="text/javascript" src="clientscript/vbulletin_md5.js"></script> <table cellpadding="0" cellspacing="$stylevar[formspacer]" border="0"> <tr> <td class="smallfont">$vbphrase[username]</td> <td><input type="text" class="button" name="vb_login_username" id="navbar_username" size="10" accesskey="u" tabindex="1" value="$vbphrase[username]" onfocus="if (this.value == '$vbphrase[username]') this.value = '';" /></td> <td class="smallfont" colspan="2" nowrap="nowrap"><label for="cb_cookieuser_navbar"><input type="checkbox" name="cookieuser" value="1" tabindex="3" id="cb_cookieuser_navbar" accesskey="c" checked="checked" />$vbphrase[remember_me]</label></td> </tr> <tr> <td class="smallfont">$vbphrase[password]</td> <td><input type="password" class="button" name="vb_login_password" size="10" accesskey="p" tabindex="2" /></td> <td><input type="submit" class="button" value="$vbphrase[log_in]" tabindex="4" title="$vbphrase[enter_username_to_login_or_register]" accesskey="s" /></td> </tr> </table> <input type="hidden" name="s" value="$session[sessionhash]" /> <input type="hidden" name="do" value="login" /> <input type="hidden" name="forceredirect" value="1" /> <input type="hidden" name="vb_login_md5password" /> <input type="hidden" name="vb_login_md5password_utf" /> </form> <!-- / login form --> </td> </if> </tr> </table> <!-- / breadcrumb, login, pm info --> HTML Code:
<!-- nav buttons bar --> <div align="center"> <table class="tborder" cellpadding="$stylevar[cellpadding]" cellspacing="0" border="0" width="100%" align="center" style="border-top-width:0px"> <tr align="center"> <!--<td class="vbmenu_control"><a href="$vboptions[forumhome].php?$session[sessionurl]">Home</a></td>--> <if condition="$show['member']"> <td class="vbmenu_control"><a href="usercp.php?$session[sessionurl]">$vbphrase[user_cp]</a></td> </if> <if condition="$show['registerbutton']"> <td class="vbmenu_control"><a href="register.php?$session[sessionurl]">$vbphrase[register]</a></td> </if> <td class="vbmenu_control"><a href="faq.php?$session[sessionurl]" accesskey="5">$vbphrase[faq]</a></td> <td class="vbmenu_control"><a href="memberlist.php?$session[sessionurl]">$vbphrase[members_list]</a></td> <td class="vbmenu_control"><a href="calendar.php?$session[sessionurl]">$vbphrase[calendar]</a></td> <if condition="$show['popups']"> <if condition="$show['searchbuttons']"> <if condition="$show['member']"> <td class="vbmenu_control"><a href="search.php?$session[sessionurl]do=getnew" accesskey="2">$vbphrase[new_posts_nav]</a></td> <else /> <td class="vbmenu_control"><a href="search.php?$session[sessionurl]do=getdaily" accesskey="2">$vbphrase[todays_posts]</a></td> </if> <td id="navbar_search" class="vbmenu_control"><a href="search.php?$session[sessionurl]" accesskey="4">$vbphrase[search]</a> <script type="text/javascript"> vbmenu_register("navbar_search"); </script></td> </if> <if condition="$bbuserinfo['userid']"> <td id="usercptools" class="vbmenu_control"><a href="#usercptools">$vbphrase[quick_links]</a> <script type="text/javascript"> vbmenu_register("usercptools"); </script></td> </if> <else /> <if condition="$show['searchbuttons']"> <td class="vbmenu_control"><a href="search.php?$session[sessionurl]" accesskey="4">$vbphrase[search]</a></td> <if condition="$show['member']"> <td class="vbmenu_control"><a href="search.php?$session[sessionurl]do=getnew" accesskey="2">$vbphrase[new_posts_nav]</a></td> <else /> <td class="vbmenu_control"><a href="search.php?$session[sessionurl]do=getdaily" accesskey="2">$vbphrase[todays_posts]</a></td> </if> </if> <td class="vbmenu_control"><a href="forumdisplay.php?$session[sessionurl]do=markread">$vbphrase[mark_forums_read]</a></td> <if condition="$bbuserinfo['userid']"> <td class="vbmenu_control"><a href="#" onclick="window.open('misc.php?$session[sessionurl]do=buddylist&focus=1','buddylist','statusbar=no,menubar=no,toolbar=no,scrollbars=yes,resizable=yes,width=250,height=300'); return false;">$vbphrase[open_buddy_list]</a></td> </if> </if> <if condition="$bbuserinfo['userid']"> <td class="vbmenu_control"><a href="login.php?$session[sessionurl]do=logout&u=$bbuserinfo[userid]" onclick="return log_out()">$vbphrase[log_out]</a></td> </if> </tr> </table> </div> <!-- / nav buttons bar --> HTML Code:
<br /> <br /> HTML Code:
<if condition="$show['popups']"> <!-- NAVBAR POPUP MENUS --> <if condition="$show['searchbuttons']"> <!-- header quick search form --> <div class="vbmenu_popup" id="navbar_search_menu" style="display:none"> <table cellpadding="4" cellspacing="1" border="0"> <tr> <td class="thead">$vbphrase[search_forums]</td> </tr> <tr> <td class="vbmenu_option" title="nohilite"> <form action="search.php" method="post"> <input type="hidden" name="do" value="process" /> <input type="hidden" name="showposts" value="0" /> <input type="text" class="bginput" name="query" size="20" />$gobutton<br /> </form> </td> </tr> <tr> <td class="vbmenu_option"><a href="search.php?$session[sessionurl]" accesskey="4">$vbphrase[advanced_search]</a></td> </tr> </table> </div> <!-- / header quick search form --> </if> <if condition="$show['member']"> <!-- user cp tools menu --> <div class="vbmenu_popup" id="usercptools_menu" style="display:none"> <table cellpadding="4" cellspacing="1" border="0"> <tr><td class="thead">$vbphrase[quick_links]</td></tr> <if condition="$vboptions['enablesearches']"><tr><td class="vbmenu_option"><a href="search.php?$session[sessionurl]do=getnew">$vbphrase[new_posts_nav]</a></td></tr></if> <tr><td class="vbmenu_option"><a href="forumdisplay.php?$session[sessionurl]do=markread">$vbphrase[mark_forums_read]</a></td></tr> <tr><td class="vbmenu_option"><a href="#" onclick="window.open('misc.php?$session[sessionurl]do=buddylist&focus=1','buddylist','statusbar=no,menubar=no,toolbar=no,scrollbars=yes,resizable=yes,width=250,height=300'); return false;">$vbphrase[open_buddy_list]</a></td></tr> <tr><td class="thead"><a href="usercp.php?$session[sessionurl]">$vbphrase[user_control_panel]</a></td></tr> <if condition="$show['siglink']"><tr><td class="vbmenu_option"><a href="profile.php?$session[sessionurl]do=editsignature">$vbphrase[edit_signature]</a></td></tr></if> <if condition="$show['avatarlink']"><tr><td class="vbmenu_option"><a href="profile.php?$session[sessionurl]do=editavatar">$vbphrase[edit_avatar]</a></td></tr></if> <tr><td class="vbmenu_option"><a href="profile.php?$session[sessionurl]do=editprofile">$vbphrase[edit_profile]</a></td></tr> <tr><td class="vbmenu_option"><a href="profile.php?$session[sessionurl]do=editoptions">$vbphrase[edit_options]</a></td></tr> <tr><td class="thead">$vbphrase[miscellaneous]</td></tr> <if condition="$show['pmstats']"><tr><td class="vbmenu_option"><a href="private.php?$session[sessionurl]">$vbphrase[private_messages]</a></td></tr></if> <tr><td class="vbmenu_option"><a href="subscription.php?$session[sessionurl]">$vbphrase[subscribed_threads]</a></td></tr> <tr><td class="vbmenu_option"><a href="member.php?$session[sessionurl]u=$bbuserinfo[userid]">$vbphrase[my_profile]</a></td></tr> <if condition="$show['wollink']"><tr><td class="vbmenu_option"><a href="online.php?$session[sessionurl]">$vbphrase[whos_online]</a></td></tr></if> </table> </div> <!-- / user cp tools menu --> </if> <!-- / NAVBAR POPUP MENUS --> </if> Time to get down to business. Ok, so much for the review, let's do something fun - put the logged in user's avatar into the navbar. This is useful on boards where a log of people use multiple user accounts - it lets them know who they are logged in at a glance (assuming the avatars are distinct). This is actually pretty straightforward. First we fetch the user's avatar. In your PHPINCLUDE_START template plug in this code at the end. PHP Code:
// #### Fetch avatar ######################
Now, going back to the navbar let's plug this in like so... Find this code in the navbar HTML Code:
<!-- / login form --> </td> </if> HTML Code:
<if condition="$bbuseravatarurl"><td><a href="profile.php?do=editavatar$sessionurl"><img src="$bbuseravatarurl" title="Click to Edit" border="0"></a></td></if> This concludes lesson 7. Expect to see more of these in the coming few days as I have more spare time now that the Christmas break has began. |