You should see some links somewhere that will take you to more pages of search results, with the option of clicking next, previous, or a specific numbered page.. You also need to know ho
Trang 1return $pagelinks;
} function bbcode($db, $data) { $sql = ‘SELECT
template, replacement FROM
frm_bbcode’;
$result = mysql_query($sql, $db) or die(mysql_error($db));
if (mysql_num_rows($result) > 0) { while($row = mysql_fetch_array($result)) { $bbcode[‘tpl’][] = ‘/’
html_entity_decode($row[‘template’], ENT_QUOTES) ‘/i’;
$bbcode[‘rep’][] = html_entity_decode($row[‘replacement’], ENT_QUOTES);
} $data1 = preg_replace($bbcode[‘tpl’], $bbcode[‘rep’], $data);
$sql = ‘SELECT * FROM frm_admin’;
$result = mysql_query($sql, $db) or die(mysql_error($db));
while ($row = mysql_fetch_array($result)) { $admin[$row[‘constant’]][‘title’] = $row[‘title’];
$admin[$row[‘constant’]][‘value’] = $row[‘value’];
}mysql_free_result($result);
$sql = ‘SELECT * FROM frm_bbcode’;
$result = mysql_query($sql, $db) or die(mysql_error($db));
while ($row = mysql_fetch_array($result)) { $bbcode[$row[‘id’]][‘template’] = $row[‘template’];
$bbcode[$row[‘id’]][‘replacement’] = $row[‘replacement’];
}mysql_free_result($result);
define(‘NEWPOST’, ‘ & raquo;’);
define(‘POSTLINK’, ‘ & diams;’);
?
Trang 23 Create frm_header.inc.php This goes at the top of each page that gets displayed
$db = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD) or
die (‘Unable to connect Check your connection parameters.’);
Trang 3? < form method=”get” action=”frm_search.php” >
< div >
< input type=”text” name=”keywords”
< ?php
if (isset($_GET[‘keywords’])) { echo ‘value=”’ htmlspecialchars($_GET[‘keywords’]) ‘” ‘;
}
? / < input type=”submit” value=”Search”/ >
< /div >
< /form >
< ?phpecho ‘ < > < a href=”frm_index.php” > Home < /a >
if (!isset($_SESSION[‘user_id’])) { echo ‘ | < a href=”frm_login.php” > Log In < /a >
echo ‘ | < a href=”frm_useraccount.php” > Register < /a >
} else { echo ‘ | < a href=”frm_transact_user.php?action=Logout” >
echo “Log out “ $_SESSION[‘name’] “ < /a >
if ($_SESSION[‘access_lvl’] > 2) { echo ‘ | < a href=”frm_admin.php” > Admin < /a >
} echo ‘ | < a href=”frm_useraccount.php” > Profile < /a >
}echo ‘ < /p >
functions.inc.php gives you
Pagination
If you are not familiar with pagination, then we suggest you do a quick search — for anything — on your favorite search engine No matter what you search for, most likely you ’ ll have a large number of links returned in response to your query You should see some links somewhere that will take you to more pages of search results, with the option of clicking next, previous, or a specific numbered page
That, my friend, is pagination, and we are going to teach you how to do it for your own pages
Trang 4When paginating your data, there are a few things you should have The first, of course, is a large set of
data that you can ’ t display on one page You also need to know how many rows of data you will display
per page, and how many total records you have in your result set You also need to know how many
pages you will have access to at one time For example, if you had 40 pages of data to display, you might
want to show links only for pages 1 through 10, or 12 through 21, and so forth This is called the range
Take a look at show_topic() in frm_output_functions.inc.php It ’ s quite lengthy, so we ’ ll
highlight for you the relevant code lines that affect pagination
function show_topic($db, $topic_id, $user_id, $limit = 25) {
$start = ($page - 1) * $limit;
In a calling page, you pass in a number equaling the maximum number of records per page you want to
display If you don ’ t pass a page parameter in the URL to the web page, you assume you are on page 1
Otherwise, you will be setting page to the value passed to you in the URL By knowing the page and the
limit (number of posts per page), you can calculate your $start value (which will be used by the LIMIT
statement in the SQL statement used to retrieve rows) For example, if you are on page 3, and your limit
is 25 posts per page, then the third page will display rows 51 through 75
Here is the SQL statement for returning posts It may be long, but thankfully it is not overly complex It
is simply four tables joined by the JOIN statement Please note the first line and the last line of the SQL
statement:
$sql = ‘SELECT SQL_CALC_FOUND_ROWS
p.id, p.subject, p.body, p.date_posted, p.date_updated,
u.name as author, u.id as author_id, u.signature as sig,
c.post_count as postcount, p.forum_id as forum_id,
f.forum_moderator as moderator, p.update_id, u2.name as updated_by
FROM
frm_forum f JOIN frm_posts p ON f.id = p.forum_id
JOIN frm_users u ON u.id = p.author_id
LEFT JOIN frm_users u2 ON u2.id = p.update_id
LEFT JOIN frm_post_count c ON u.id = c.user_id
LIMIT ‘ $start ‘, ‘ $limit;
$result = mysql_query($sql, $db) or die(mysql_error($db));
$page_links = paginate($db, $limit);
This query will return a maximum of the number of rows in $limit The problem is, you need to know
how many rows would have been returned if LIMIT had not been used You could execute the query again
Trang 5without the LIMIT clause and retrieve the number of rows returned, but it turns out that isn ’ t necessary MySQL provides the SQL_CALC_FOUND_ROWS command as a means for you to find out In the first line, immediately following SELECT , you use the SQL command SQL_CALC_FOUND_ROWS This doesn ’ t do anything to the query directly, but does allow you to subsequently run the SQL command:
$sql = “SELECT FOUND_ROWS();”;
The MySQL function FOUND_ROWS() returns the number of rows that SQL_CALC_FOUND_ROWS found
SQL_CALC_FOUND_ROWS makes the SELECT query take slightly longer to execute, but it is still more efficient than running the query a second time to find out how many rows would have been returned if you had not used a LIMIT clause
After you have your numbers, it ’ s time to create the page links Take a look at the paginate() function
in the same file:
function paginate($db, $limit = 10) { global $admin;
$sql = ‘SELECT FOUND_ROWS();’;
$result = mysql_query($sql, $db) or die(mysql_error($db));
$row = mysql_fetch_array($result);
$numrows = $row[0];
$pagelinks = ‘ < >
if ($numrows > $limit) { if(isset($_GET[‘page’])){
$page = $_GET[‘page’];
} else { $page = 1;
}
} else { $pagelinks = ‘ & lt; PREV & nbsp; & nbsp;NEXT & gt; & nbsp; & nbsp;’;
} $pagelinks = ‘ < /p >
return $pagelinks;
}
The paginate function takes a $limit parameter, which, if it is not passed in to the function, you set to a default value of 10 In order for the code to access the forum configuration variables, such as range and limit, $admin must be declared global because the scope of PHP ’ s execution is now in the function
Otherwise, you would not be able to access the configuration
As you can see, because you used SELECT FOUND_ROWS() , $numrows contains the number of rows your query returns As long as the number of rows is larger than your limit, you ’ ll generate the pagination links Otherwise, you ’ ll just display inactive links
Next, you grab the page variable, if it is set If not, then you set $page to 1 Then you determine whether the < PREV link should be active or not Obviously if you are on page 1, there is no previous
Trang 6page, and the link should not be active Otherwise, the previous page is one less than the number of the
The next chunk of code does a little bit of math The number of pages is determined by dividing the total
number of rows returned by your previous SELECT FOUND_ROWS() query by the number of posts per
page ( $numrows divided by $limit ) and rounding up
The range is grabbed from $admin[‘pagerange’][‘value’] and stored in $range If it ’ s not
available, then $range defaults to 7 This value determines how many pages are accessible via a link at
the bottom of the page For example, if the range is 5, there are 13 pages, and you are currently viewing
page 6, you will have access to pages 4, 5, 6, 7, and 8:
< PREV [4] [5] 6 [7] [8] NEXT >
The “ ” shows you that there are more pages in that direction (either before or after)
$numofpages = ceil($numrows / $limit);
$range = $admin[‘pageRange’][‘value’];
if ($range == ‘’ or $range == 0) {
$range = 7;
}
The next few lines determine what range of pages to show you In the previous example, if the $range is
5, but you are viewing page 2 out of 13 pages, the code should be smart enough to allow you access to
pages 1 through 5:
< PREV [1] 2 [3] [4] [5] NEXT >
As you can see, you are viewing page 2, you can get to pages 1 through 5 directly, and there are more
pages past 5 The piece of logic that determines which pages are available is the following:
$lrange = max(1, $page - (($range - 1) / 2));
$rrange = min($numofpages, $page + (($range - 1) / 2));
if (($rrange - $lrange) < ($range - 1)) {
Then, the next part of the code renders the space between PREV and NEXT If the lower range is higher
than 1, you put in to show that more pages can be accessed by clicking < PREV Then, use the $lrange
and values to build the page number links If the link corresponds to the current page, don ’ t
Trang 7make it a link Next, if the high end of the range of pages is lower than the total number of pages available, you put in the to show that more pages can be accessed by clicking NEXT >
if ($lrange > 1) { $pagelinks = ‘ ’;
} else { $pagelinks = ‘ & nbsp; & nbsp;’;
}for($i = 1; $i < = $numofpages; $i++) {
if ($i == $page) { $pagelinks = $i;
} else {
if ($lrange < = $i and $i < = $rrange) { $pagelinks = ‘ < a href=”’ $currpage ‘ & page=’ $i ‘” > ’ $i ‘ < /a >
} }}
if ($rrange < $numofpages) { $pagelinks = ‘ ’;
} else { $pagelinks = ‘ & nbsp; & nbsp;’;
}} else { $pagelinks = ‘ & lt; PREV & nbsp; & nbsp;NEXT & gt; & nbsp; & nbsp;’;
}
Voil à ! You have a terrific, customizable, dynamically built pagination function Your code generates
simple text links for the pages However, you can easily take the logic presented here and modify the code to implement CSS styles, images, or whatever else tickles your creative fancy
Breadcrumbs
Once upon a time, there were two little children named Hansel and Gretel They didn ’ t want to get lost
in the forest, so the story goes So Hansel got the bright idea of dropping crumbs of bread along the path
so that they could find their way back Birds ate the bread, the kids got lost, and then they stumbled upon a house made out of gingerbread and candy (yum!) The little old lady who owned the house wasn ’ t too keen on the idea of children eating holes through her walls, and so she enslaved them Hansel got fat eating German chocolates and candies while sitting in a cage, and Gretel was forced to do chores Then one day they stuffed the little old lady in an oven and ran home The end
Trang 8Exactly how Hansel and Gretel found their way home remains a mystery to us, since the birds ate the
breadcrumbs marking the trail, and that ’ s how they got lost in the first place! But aside from that, Hansel
had the right idea By leaving some sort of trail behind them, they should have been able to navigate out
of any dark forest
Some time ago, search engines came along, and some of them gave us the ability to find web sites based
on categories Because there are so many sites out there that are very specialized, some of them might be
in a sub - sub - sub - subcategory For example, say you wanted to view some sites in the Yahoo! directory
about PHP You click the Computers and Internet category Hmmm Next, click Software, then Internet,
World Wide Web, Servers (ah, we ’ re getting close), Server Side Scripting, and (yes, finally!) PHP Now
that you have reached this page, wouldn ’ t it be nice to remember how you got here? If you look near the
top of the screen, you should see something that looks like this:
Directory > Computers and Internet > Software > Internet >
World Wide Web > Servers > Server Side Scripting > PHP
It is a map of categories and subcategories telling you exactly how to get to the category you are looking
at Someone (probably a fan of gingerbread houses, but don ’ t quote us on that) saw this “ map ” and
decided to call it a breadcrumb list The name has stuck
The truth is, breadcrumbs are very helpful, and they make a lot of sense for a bulletin board forum They
can give you a map from the post you are reading to the thread it was in, to the forum the thread was in,
to the category the forum was in, and to the home page By clicking on any part of the breadcrumb trail,
you can easily navigate to another part of the site Perhaps one would look like this:
Home > Comic Book Movies > Spider-Man > This movie rocked! > I agree
You have implemented breadcrumbs for this application, and we will explain to you how it was done
You could implement a breadcrumb system in many different ways (such as by folder structure) This is
just one way, and it is relatively simple
The function itself takes two arguments, $id and $getfrom The argument $getfrom will either be F for
forum or P for post There is no one standard separator for crumbs Some people use > , but we like to use
a bullet or dot You can use whichever HTML entity you like:
function breadcrumb($db, $id, $get_from = ‘F’) {
$separator = ‘ & middot; ‘;
If you are in a post, then you want your breadcrumb to include a link to the forum, along with a
nonlinked indication of what thread you are in You pass in the topic_id to retrieve the right topic and
get the forum_id from that topic and put it into the $id field You also extract the name of the topic
if ($get_from == ‘P’) {
$sql = ‘SELECT forum_id, subject FROM frm_posts WHERE id = ‘ $id;
$result = mysql_query($sql, $db) or die(mysql_error($db));
Trang 9Next, you call get_forum() with the $id that is now a forum_id It returns a row that contains the name and description of the forum You don ’ t currently use the description, but you could use it as alt
or title attributes for the breadcrumb, if you wanted to
$row = get_forum($db, $id);
At this point, you begin building the breadcrumb in the variable $bcrumb Home is always first, and then the separator Next is either a link to the forum (if looking at a post), or simply the forum listed without a link Next comes the thread title for the post you are looking at
$bcrumb = ‘ < a href=”frm_index.php” > Home < /a > ’ $separator;
switch ($get_from) { case ‘P’:
$bcrumb = ‘ < a href=”frm_view_forum.php?f=’ $id ‘” > ’ $row[‘name’]
‘ < /a > ’ $separator $topic;
break;
case ‘F’:
$bcrumb = $row[‘name’];
break;
} return ‘ < h2 > ’ $bcrumb ‘ < /h2 >
}
As we said before, this breadcrumb is not that difficult or complex, but we are sure that, armed with all
of the PHP knowledge you now have from reading this book, you could easily come up with a very impressive breadcrumb function!
Next, take a look at your frm_header.inc.php file There isn ’ t much new to see here, but it gives us a chance to discuss authentication with you for a moment
A Last Look at User Authentication
The Comic Book Appreciation board uses user authentication, but it is by no means totally secure For a board application, it is probably secure enough If this were human resources data containing sensitive information, you might want to make it a bit more secure This book does not attempt to help you create
a virtual Fort Knox If you have such a need, we strongly suggest you look for a good book on security, and perhaps look at a few online resources A good start is www.w3.org/Security/Faq/
Take a look at your security model, and see where there might be some places to improve it a bit If you look at most of the PHP pages that make up the application, you see that you check for a user ’ s access level before displaying certain items For example, examine frm_header.inc.php
Because frm_header.inc.php is included at the top of almost every web page, you do most of your user authentication there By checking for the existence of the user_id session variable, you know the user is logged in By checking if access_lvl is greater than 2, you know whether the user has administrator access This allows you to customize the main menu according to the user ’ s login status and his or her access level It also allows you to address the user by name
Trang 10echo ‘ | < a href=”frm_login.php” > Log In < /a >
echo ‘ | < a href=”frm_useraccount.php” > Register < /a >
If users are not logged in, you give them links to log in or register as a new user If they are logged in,
they can log out or view their profile If they are administrators, they will have access to the admin
functions
Transaction Pages
The next group of files you ’ re going to create is the transaction pages Like the reusable scripts just
covered, they don ’ t have anything pretty to show the end user, but they drive a large portion of the
behind - the - scenes board operations
Try It Out Admin Transactions
The first file is responsible for all transactions related to the general administration of the board —
things like creating new forums, changing the board options, text substitutions, and so on
1 Create frm_transact_admin.php , the first of four transaction pages Admin forms post to
this page, which manipulates the data and then redirects the user to another page Transaction
pages do not send any data to the client unless there is an error
< ?php
require ‘db.inc.php’;
require ‘frm_output_functions.inc.php’;
$db = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD) or
die (‘Unable to connect Check your connection parameters.’);
Trang 11case ‘Add Forum’:
if (isset($_POST[‘forumname’]) & & $_POST[‘forumname’] != ‘’ & &
isset($_POST[‘forumdesc’]) & & $_POST[‘forumdesc’] != ‘’) { $sql = ‘INSERT IGNORE INTO frm_forum
(id, forum_name, forum_desc, forum_moderator) VALUES
(NULL, “’ htmlspecialchars($_POST[‘forumname’], ENT_QUOTES)
‘”, “’ htmlspecialchars($_POST[‘forumdesc’], ENT_QUOTES)
‘”, ‘ $_POST[‘forummod’][0] ‘)’;
mysql_query($sql, $db) or die(mysql_error($db));
} header(‘Location: frm_admin.php?option=forums’);
exit();
break;
case ‘Edit Forum’:
if (isset($_POST[‘forumname’]) & & $_POST[‘forumname’] != ‘’ & &
isset($_POST[‘forumdesc’]) & & $_POST[‘forumdesc’] != ‘’) { $sql = ‘UPDATE frm_forum SET
forum_name = “’ $_POST[‘forumname’] ‘”, forum_desc = “’ $_POST[‘forumdesc’] ‘”
forum_moderator = ‘ $_POST[‘forummod’][0] ‘ WHERE
id = ‘ $_POST[‘forum_id’];
mysql_query($sql, $db) or die(mysql_error($db));
} header(‘Location: frm_admin.php?option=forums’);
exit();
break;
case ‘Modify User’:
foreach ($_POST as $key = > $value) {
if ($key != ‘action’) { $sql = ‘UPDATE frm_admin SET value=”’ $value ‘”
WHERE constant = “’ $key ‘”’;
mysql_query($sql, $db) or die(mysql_error($db));
} } header(‘Location: frm_admin.php’);
exit();
break;
Trang 12
case ‘Add New’:
$sql = ‘INSERT INTO frm_bbcode
(id, template, replacement)
case ‘Update BBCodes’:
foreach($_POST as $key = > $value) {
$sql = ‘UPDATE frm_bbcode SET ‘
$col ‘ = “’ htmlentities($value, ENT_QUOTES) ‘”
Trang 13exit();
break;
default:
header(‘Location: frm_index.php’);
}} else { header(‘Location: frm_index.php’);
One of the more important things to remember from this page is the actions it handles, as shown here:
switch ($_REQUEST[‘action’]) {case ‘Add Forum’:
case ‘Edit Forum’:
case ‘Modify User’:
case ‘Update’:
case ‘deleteForum’:
case ‘Add New’:
case ‘deleteBBCode’:
case ‘Update BBCodes’:
default:
}
You probably already understand how the switch statement works, so the key thing to keep in mind
is the different cases this specific switch processes Remembering where a certain action takes place can help you more quickly find and diagnose problems when they occur
Trang 14Try It Out Post Transactions
The next transaction file controls all transactions related to forum posts — creating, editing, replying,
$db = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD) or
die (‘Unable to connect Check your connection parameters.’);
case ‘SUBMIT NEW POST’:
if (isset($_POST[‘subject’]) & & isset($_POST[‘body’]) & &
isset($_SESSION[‘user_id’])) {
$sql = ‘INSERT INTO frm_posts
(id, topic_id, forum_id, author_id, update_id,
$topicid = ($_POST[‘topic_id’] == 0) ? $postid : $_POST[‘topic_id’];
header(‘Location: frm_view_topic.php?t=’ $topicid ‘#post’
$postid);
exit();
break;
Trang 15
case ‘NEW TOPIC’:
header(‘Location: frm_compose.php?f=’ $_POST[‘forum_id’]);
exit();
break;
case ‘EDIT’:
header(‘Location: frm_compose.php?a=edit & post=’ $_POST[‘topic_id’]);
exit();
break;
case ‘SAVE CHANGES’:
if (isset($_POST[‘subject’]) & & isset($_POST[‘body’])) { $sql = ‘UPDATE frm_posts SET
subject = “’ $_POST[‘subject’] ‘”, update_id = ‘ $_SESSION[‘user_id’] ‘, body = “’ $_POST[‘body’] ‘”,
date_updated = “’ date(‘Y-m-d H:i:s’) ‘”
WHERE
id = ‘ $_POST[‘post’];
if (isset($_POST[‘author_id’])) { $sql = ‘ AND author_id = ‘ $_POST[‘author_id’];
} mysql_query($sql, $db) or die(mysql_error($db));
} $redirID = ($_POST[‘topic_id’] == 0) ? $_POST[‘post’] :
if ($_REQUEST[‘post’]) { $sql = ‘DELETE FROM frm_posts WHERE id = ‘ $_REQUEST[‘post’];
mysql_query($sql, $db) or die(mysql_error($db));
} header(‘Location: ‘ $_REQUEST[‘r’]);
exit();
break;
}} else { header(‘Location: frm_index.php’);