577 Implementation (headline, story_text, page, writer, created, modified) values ('$headline', '$story_text', '$page', '" .$HTTP_SESSION_VARS['auth_user']."', $time, $time)"; } $result = mysql_query($sql, $conn); if (!$result) { print "There was a database error when executing <pre>$sql</pre>"; print mysql_error(); exit; } if ( (isset($HTTP_POST_FILES['picture']['name']) && is_uploaded_file($HTTP_POST_FILES['picture']['tmp_name']))) { if (!isset($story)) $story = mysql_insert_id(); $type = basename($HTTP_POST_FILES['picture']['type']); switch ($type) { case 'jpeg': case 'pjpeg': $filename = "pictures/$story.jpg"; move_uploaded_file($HTTP_POST_FILES['picture']['tmp_name'], $filename); $sql = "update stories set picture = '$filename' where id = $story"; $result = mysql_query($sql, $conn); break; default: print 'Invalid picture format: '. $HTTP_POST_FILES['picture']['type']; } } header('Location: '.$HTTP_POST_VARS['destination']); ?> The delete story link calls delete_story.php, which actions a simple DELETE statement and returns the writer to the calling page.The code for delete_story.php is shown in Listing 26.8. Listing 26.7 Continued 32 525x ch26 1/24/03 3:38 PM Page 577 578 Chapter 26 Building a Content Management System Listing 26.8 delete_story.php—Is Used to Delete a Story from the Database <?php // delete_story.php include('include_fns.php'); $conn = db_connect(); $story = $HTTP_GET_VARS['story']; $sql = "delete from stories where id = $story"; $result = mysql_query($sql, $conn); header('Location: '.$HTTP_SERVER_VARS['HTTP_REFERER']); ?> Searching Clicking the keywords link on the stories list brings up a new form for entering key- words against the story.There is no limit to the number of keywords that can be entered, and each keyword is given a weight value, with a higher value indicating that it is more relevant. Figure 26.7 shows the screen used to set keywords against a particular story. Figure 26.7 Setting keywords for a story. 32 525x ch26 1/24/03 3:38 PM Page 578 579 Implementation The script keywords.php is fairly straightforward, so we won’t look at it in any detail. It is included on the CD-ROM. This script triggers the keyword_add.php and keyword_delete.php scripts.These scripts are also straightforward, and are therefore not included here. The script keyword_add.php uses the following query to add new keywords to the database: insert into keywords (story, keyword, weight) values ($story, '$keyword', $weight) In a similar vein, keyword_delete.php uses the following query to remove a keyword: delete from keywords where story = $story and keyword = '$keyword' What is interesting is the way in which the weight values are used to calculate a percent- age relevance figure when searching. The search form in search_form.php contains a single field for keywords, and sub- mits to search.php, which queries the database of live stories to find matching content. The source for search.php is shown in Listing 26.9. Listing 26.9 search.php—Finds Matching Stories and Calculates a Percentage Match Score <?php include('include_fns.php'); include('header.php'); $conn = db_connect(); if ($HTTP_POST_VARS['keyword']) { $k = split(' ', $HTTP_POST_VARS['keyword']); $num_keywords = count($k); for ($i=0; $i<$num_keywords; $i++) { if ($i) $k_string .= "or k.keyword = '".$k[$i]."' "; else $k_string .= "k.keyword = '".$k[$i]."' "; } $and .= "and ($k_string) "; $sql = "select s.id, s.headline, 10 * sum(k.weight) / $num_keywords as score from stories s, keywords k where s.id = k.story 32 525x ch26 1/24/03 3:38 PM Page 579 580 Chapter 26 Building a Content Management System $and group by s.id, s.headline order by score desc, s.id desc"; $result = mysql_query($sql, $conn); } print '<h2>Search results</h2>'; if ($result && mysql_num_rows($result)) { print '<table>'; while ($qry = mysql_fetch_array($result)) { print '<tr><td>'; print $qry['headline']; print '</td><td>'; print floor($qry['score']).'%'; print '</td></tr>'; } print '</table>'; } else { print 'No matching stories found'; } include('footer.php'); ?> First, the keyword string passed into the script is split up into individual search words. We are not using any advanced search techniques in this example, such as allowing the searcher to use AND or OR keywords or group words together into a phrase. if ($HTTP_POST_VARS['keyword']) { $k = split(' ', $HTTP_POST_VARS['keyword']); $num_keywords = count($k); for ($i=0; $i<$num_keywords; $i++) { if ($i) $k_string .= "or k.keyword = '".$k[$i]."' "; else $k_string .= "k.keyword = '".$k[$i]."' "; } $and .= "and ($k_string) "; This code uses the PHP function split() to create an array containing each word in the keyword string separated by a space character. If only one word is specified, it still returns a single element array and the subsequent loop is executed once. Ultimately the condition stored in $and will look something similar to and (k.keyword = 'keyword1' or k.keyword = 'keyword2' or k.keyword = 'keyword3') Listing 26.9 Continued 32 525x ch26 1/24/03 3:38 PM Page 580 581 Implementation The search query built based on the previous code would be select s.id, s.headline, 10 * sum(k.weight) / $num_keywords as score from stories s, keywords k where s.id = k.story and (k.keyword = 'keyword1' or k.keyword = 'keyword2' or k.keyword = 'keyword3') group by s.id, s.headline order by score desc, s.id desc The calculation for the score is the sum of the weights from all matching keywords divided by the number of keywords searched for and then multiplied by ten.This favors searches in which all the keywords entered match the keywords in the database. Because the weights range from 1 to 10, the maximum value for the score is 100.A search for three keywords would only be a 100% match with a story if all three were found for that story and each had a weight of 10. Editor Screen The only part of the system we haven’t covered is how a story actually gets published after it has been written.The script publish.php makes a story live. It is shown in Listing 26.10. Listing 26.10 publish.php—Lists All Documents So the Editor Can Choose Which Ones Are Shown on the Live Site <?php include('include_fns.php'); $conn = db_connect(); $sql = 'select * from stories order by modified desc'; $result = mysql_query($sql, $conn); print '<h2>Editor admin</h2>'; print '<table>'; print '<tr><th>Headline</th><th>Last modified</th></tr>'; while ($story = mysql_fetch_array($result)) { print '<tr><td>'; print $story['headline']; print '</td><td>'; print date('M d, H:i', $story['modified']); print '</td><td>'; 32 525x ch26 1/24/03 3:38 PM Page 581 . search .php is shown in Listing 26.9. Listing 26.9 search .php Finds Matching Stories and Calculates a Percentage Match Score < ?php include('include_fns .php& apos;); include('header .php& apos;); $conn. used to calculate a percent- age relevance figure when searching. The search form in search_form .php contains a single field for keywords, and sub- mits to search .php, which queries the database. script keywords .php is fairly straightforward, so we won’t look at it in any detail. It is included on the CD-ROM. This script triggers the keyword_add .php and keyword_delete .php scripts.These scripts