Content Management Systems—Article Indexing
Article Content Management System
Article Indexing
Okay, here is the entire article indexing PHP script. This script gets all the titles in the "articles" MySQL table and displays them in a sidebar. Then it turns these titles into links. The links simply reload the page—they're links to cms-index-articles.php. However, the links contain a number at the end which identifies which record we're wanting. The number at the end of the link corresponds to a number in a field of the records which identifies them like an ID would. This field, N, was included in the articles table as an alternative to the primary field: id. The reason that we use this N field rather than the id field is that we needed sorted titles so we needed to do a multisort operation from parallel arrays that we pulled from the articles table, and the id field has the unpleasant habit of forcing itself to be sorted as it's being pulled into arrays, a habit which no other field has, and which creates unparallel arrays and that sucks. More info on unwanted primary field auto-sorting. If any of you geniuses out there know a way to get the primary field into an array unsorted, we're all ears. Please don't send untested theory. I'm from Missouri—SHOW me. We already realize we can display the titles sorted using the SQL ORDER BY keyword, and this will correctly sort the other fields as well so we can correctly display them. But when we array_push these values into arrays, they're not in the displayed order. This is not really a problem as long as we avoid using the primary field (id in this case) in these arrays and avoid trying to get the primary field numbers to identify records, which it only does nicely in displays where we SELECT and then ECHO the results, but does badly when we mysql_fetch_row the table rows and array_push these rows into arrays. Either MySQL isn't set up to support having its primary field used in this way in arrays, which is what we suspect is true, or we missed something somewhere and need a MySQL guru to enlighten us. Like we say—it's not a problem. We'd just like to know WHY it's impossible. Just curious, folks.
Anyway, the reason the page reloads is to give the GET a chance to grab this number and use it to determine which of the articles to load into the display box. When the reloaded page finds no number at the end of the URL, it displays the article titles only. But when it GETs the N value and finds N has been set, it also loads the display box with the article title and contents. So that's article indexing in a nutshell, so let's get to the details:
The reason for the browser sniff about Opera is that it botches the print routine that easily prints the display box DIV only for all other browsers using standard window commands and innerHTML to write the div into the window prior to printing, but with Opera, we had to print the whole page, index and all, just to get anything. (Come on Opera guys—how about a little Beta testing?)
The JavaScript function relates to formating the article display. You get to choose from 3 font sizes, 3 font families, and 3 text indentation styles for paragraphs. We'll discuss the radio buttons that you click to
get this instantaneous formatting at the end of this page. The indenting script is especially interesting since in writing articles with our system, you're allowed to hit Return/Enter for new paragraphs, and when the PHP script below finds a Return we use the nl2br() function to turn it into <BR />, and then a preg_replace() function to turn this into </p><p> (there was already a <p> echoed in at the start of the display script). We simply have to use CSS scripting (via a DHTML JavaScript style changing function) on the paragraph tag to get this instant format trick accomplished. We use getElementsByTagName("p") to get the web page's paragraphs set up to style with CSS, and since that function gets the paragraph tags as an array, we loop through it styling these tags like there's no tomorrow. The CSS style properties textIndent and marginBottom were used for the indenting options, while the fontSize and fontFamily properties were used for the six font-related options.
Download the files: cms-articles.zip
SAVE THIS PAGE AS: cms-index-articles.php
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<TITLE>Index Articles—Content Management System (CMS)</TITLE>
<meta name="description" content="Index Articles—Content Management System (CMS)">
<meta name="keywords" content="Index Articles,Content Management System,Content Management System Articles,php,CMS,javascript, dhtml, DHTML">
<script language="javascript">
is_opera = 0; if(window.opera){is_opera=1}
function printem(){if(is_opera){window.print()}else{wi = window.open('', 'p');wi.document.open();element=document.getElementById("dv3"); wi.document.write(element.innerHTML);wi.print();wi.document.close();wi.close()}}
var fonts="";var formats="";var familys="";
function fixfont(){
var e=document.getElementsByTagName("p");
if (fonts == 'large') {for(var i=0;i<e.length;i++){e[i].style.fontSize="15px";}}
if (fonts == 'medium') {for(var i=0;i<e.length;i++){e[i].style.fontSize="13px";}}
if (fonts == 'small') {for(var i=0;i<e.length;i++){e[i].style.fontSize="11px";}}
if (familys == 'Times') {for(var i=0;i<e.length;i++){e[i].style.fontFamily="'Times New Roman',serif";}}
if (familys == 'Verdana') {for(var i=0;i<e.length;i++){e[i].style.fontFamily="Verdana,sans-serif";}}
if (familys == 'Arial') {for(var i=0;i<e.length;i++){e[i].style.fontFamily="Arial,sans-serif";}}
if (formats == 'indented') {for(var i=0;i<e.length;i++){e[i].style.marginBottom="-1em";e[i].style.textIndent="2em";};formats ="";}
if (formats == 'spaced paragraphs') {for(var i=0;i<e.length;i++){e[i].style.marginBottom="0em";e[i].style.textIndent="0em";};formats ="";}
if (formats == 'both') {for(var i=0;i<e.length;i++){e[i].style.marginBottom="0em";e[i].style.textIndent="2em";};formats ="";}
}
</script>
<style type="text/css">
BODY {margin-left:0; margin-right:0; margin-top:0;text-align:left;background-color:#ddd}
p, li {font:13px Verdana; color:black;text-align:left;text-indent:2em;margin-bottom:-1em}
h1 {font:bold 28px Verdana; color:black;text-align:center}
h2 {font:bold 24px Verdana;text-align:center}
h3 {font:bold 15px Verdana;}
th {font:bold 24px Verdana;text-align:center}
.title {position:absolute;top:10px;left:10px;width:998px}
.form {position:absolute;top:90px;left:100px;width:600px}
.box {position:absolute;top:43px;left:190px;width:500px;}
.side {position:absolute;top:90px;left:792px;width:200px;padding:6px;background-color:#fff;border:1px solid blue}
.choose {position:absolute;top:238px;left:2px;width:160px;background-color:#bbb;border:1px solid blue;padding:5px}
</style>
</head>
<body>
After some CSS styling, we get to some cool PHP that's even cooler than the cool Javascript above. First we slap in a couple of includes. The first does nothing but put the HTML navigation links into the page, and the second gets in the configuration variables so the interfacing with the MySQL database and its table would actually work. The code in it is here in the config.php file discussion: Content-Management-Systems.html.
Next we remind PHP that we are here with: $here = "cms-index-articles.php". Now we read the title column and the N column in the articles table, using array_push to fill the $titles and $N arrays. We could have used the N field as primary and the id field as the column to identify the records/articles with but why bother? If you're not familiar with multisort, then array_multisort($titles,$N) will be a bit counterintuitive, since it seems to be sorting 2 arrays and that's it. That is NOT it. It's sorting the first array (ascending order is the default so we didn't bother to specify it), but as it does so it sorts the second array and all subsequent arrays we name in parallel with how it sorts the first array. That's not only cool stuff—it's also useful for when you get arrays out of MySQL tables and wish to keep them as parallel arrays, with each element number reflecting a record, even after sorting on one of these arrays.
Next comes the link creation. The link is going to reload the current page so we use the $here variable followed by the ? and the rest of the query string which is the identifying number of that record, which is N and in the $N array. Then comes the link text which is the $titles array element parallel to the $N array element in the URL link: echo "<a href='$here?N=$N[$i]'>$titles[$i]</a><br>";. The query string is used to send a value that GET can find after the reload.
Then comes the checking of the GET value N. If it's set, it's time to use the SQL SELECT command to find the article to display, title first, that has the N field value that was in the query string of our link. We fetch the result into an array with the title and content in the needed row. First we display the title: echo htmlentities($row['title'], ENT_QUOTES); using the safest PHP function known for display: htmlentities(). The optional parameter is to make sure single and double quotes are both escaped, for additional safety.
Now we use several more safety functions, only this time on the content, not the title. They are $content=strip_tags($row['content']) to dump all tags since they can contain hacker mischief, then $content=stripslashes($content) to unescape quotes and backslashes so they're normal for htmlentities()—they got escaped by mysql_real_escape_string() in our cms write application wherein articles are written. Next, $content=htmlentities($content, ENT_QUOTES) is used to turn quotes and other stuff too to html entities since we need quotes in articles but require them to be relatively secure, which this will do. We've already discussed the nl2br() function $content=nl2br($content) that turns Returns to break tags: <BR />. But let's look at the preg_replace function that turns these tags, in turn, to </p><p> tags: $pattern = '/(<BR\s\/>)+/i' sets up the regular expression replacement pattern for the function, and the i at the end is to make it case insensitive. $replacement = '</p><p>' sets up the replacement text. Finally, the function itself: $content=preg_replace($pattern, $replacement, $content).
Next comes dealing with all the custom tags we invented for various HTML code creation purposes. You can learn about the custom codes for italics, bold, underline, links, email links, pictures, videos, and sounds, if you wish, in order to understand what's going on below. The purpose of these custom tags is to be safer than regular HTML tags.
For italics, the tag we get writers to use is (i-) and (ii-) for start and end tag, which our PHP script turns into <i> and </i>, using '/\(i-\)/i' and '/\(ii-\)/i' as regular expression replacement patterns and '<i>' and '</i>'as replacement text. The same is done for underline and bold. And (p-) and (pp-) are the picture tags, with '<center><br><IMG SRC="' replacing the first and
'" BORDER=0><br><br></center>' replacing the second, and the image name like pic.png in between. A special replacement happens if the writer puts 2 dots in front of the file name: ../ is added to the start of the image source so a picture can be in a higher level folder or no folder (except public_html).
With links, (l-) turns into '<a href="http://', (ll-) turns into "> after the domain, and (lll-) turns into </a> after the link text. With video, (v-) turns into '<div style="width:580px"><br><center><object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/', (vv-) turns into '&hl=en_US&fs=1&"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/' after the special letter codes in their video's YouTube embed code, and (vvv-) turns into '&hl=en_US&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object><br><br></center></div>' after they repeat their letter code. Nutty? It works! The special letter codes are simply the ones between http://www.youtube.com/v/ and &hl=en_US&fs=1& in their embed code, which YouTube happily shares with video uploading people and visitors alike.
For audio, they need to load a sound.js file into their web page's folder which has only this in it: function sound(s,q) {document.getElementById(q).innerHTML="<embed src='"+s+"' hidden=true autostart=true loop=false>"} to empower the audio. Then (a-) is turned into '<div style="width:580px"><br><center><script src="sound.js"></script><span id="a1"></span><form><input type="button" value="', (aa-) is turned into '" onClick="sound(\'' after the sound's name, and (aaa-) is turned into '\',\'a1\')"></form><br><br></center></div>' after the sound file name such as siren.mp3. Note that the backslashes are all escaping the quotes; also, a button to press to get sound will show up onscreen to give readers of the article something to press to get the sound.
Email links get (e-) turned into '<a href="mailto:', (ee-) turned into '@' after the 1st section of the email before the @ and before the second, (eee-) turned into '?subject=' before the email's subject text, (eeee-) turned into '">' after the email subject, and (eeeee-) turned into '</a>' after the link text.
<?php
include("navigation.html");
include_once"config.php";
$here = "cms-index-articles.php";
$titles=array();
$res = mysql_query("SELECT title FROM articles") or die(mysql_error());
while ($row = mysql_fetch_row($res)) {
array_push ($titles, $row[0]);}
$number=mysql_num_rows($res);
$N=array();
$res = mysql_query("SELECT N FROM articles") or die(mysql_error());
while ($row = mysql_fetch_row($res)) {
array_push ($N, $row[0]);}
array_multisort($titles,$N);
echo "<div class='side'>";
for ($i=0;$i<$number;$i++){
echo "<a href='$here?N=$N[$i]'>$titles[$i]</a><br>";
}
echo "</div><div id='dv3' class='box'>";
if(isset($_GET['N'])){
$r = mysql_query("SELECT title, content FROM articles WHERE N=".$_GET['N']) or die(mysql_error());
$row = mysql_fetch_array($r);
echo "<table border='0'><tr><th align='center'><b>";
echo htmlentities($row['title'], ENT_QUOTES);
echo "</b></th></tr><br><br><tr><td><p>";
$content=strip_tags($row['content']); //dump all tags
$content=stripslashes($content); //unescape quotes and backslashes so they're normal for htmlentities()
$content=htmlentities($content, ENT_QUOTES); //turn quotes and other stuff too to html entities
$content=nl2br($content); //Enter turns into <BR />
$pattern = '/(<BR\s\/>)+/i';
$replacement = '</p><p>'; //turn any <BR />s into </p><p> to allow indent since <p>s are css styled to indent!
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(i-\)/i';
$replacement = '<i>'; //turn any (i-)s into <i>s to allow italics
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(ii-\)/i';
$replacement = '</i>'; //turn any (ii-)s into </i>s to allow italics
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(b-\)/i';
$replacement = '<b>'; //turn any (b-)s into <b>s to allow bold
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(bb-\)/i';
$replacement = '</b>'; //turn any (bb-)s into </b>s to allow bold
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(u-\)/i';
$replacement = '<u>'; //turn any (u-)s into <u>s to allow underline
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(uu-\)/i';
$replacement = '</u>'; //turn any (uu-)s into </u>s to allow underline
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(p-\)\.\./i';
$replacement = '<center><br><IMG SRC="../'; //turn any (p-)s into start of image tag to allow image
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(p-\)/i';
$replacement = '<center><br><IMG SRC="'; //turn any (p-)s into start of image tag to allow image
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(pp-\)/i';
$replacement = '" BORDER=0><br><br></center>'; //turn any (pp-)s into end of image tag to allow image
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(l-\)/i';
$replacement = '<a href="http://'; //turn any (l-)s into <http:// to allow link protocol
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(ll-\)/i';
$replacement = '">'; //turn any (ll-)s into "> to allow url
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(lll-\)/i';
$replacement = '</a>'; //turn any (lll-)s into </a> to link text
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(v-\)/i';
$replacement = '<div style="width:580px"><br><center><object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/'; //turn any (v-)s into start of video tag
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(vv-\)/i';
$replacement = '&hl=en_US&fs=1&"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/'; //middle of video tag
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(vvv-\)/i';
$replacement = '&hl=en_US&fs=1&" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object><br><br></center></div>'; //end of video tag
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(a-\)/i';
$replacement = '<div style="width:580px"><br><center><script src="sound.js"></script><span id="a1"></span><form><input type="button" value="'; //turn any (a-)s into start of audio tag
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(aa-\)/i';
$replacement = '" onClick="sound(\''; //middle of audio tag
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(aaa-\)/i';
$replacement = '\',\'a1\')"></form><br><br></center></div>'; //end of audio tag
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(e-\)/i';
$replacement = '<a href="mailto:'; //turn any (e-)s into <a href="mailto: to allow link protocol
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(ee-\)/i';
$replacement = '@'; //turn any (ee-)s into @ to allow email @ sign
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(eee-\)/i';
$replacement = '?subject='; //turn any (eee-)s into ?subject= to allow email subject
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(eeee-\)/i';
$replacement = '">'; //turn any (eeee-)s into "> to allow email address
$content=preg_replace($pattern, $replacement, $content);
$pattern = '/\(eeeee-\)/i';
$replacement = '</a>'; //turn any (eeeee-)s into </a> to allow link text
$content=preg_replace($pattern, $replacement, $content);
echo $content."<br><br><br>";
echo "</p></td></tr></table><br>";
}
echo "</div>";
mysql_close();
?>
</div>
Finally, we come to the form that gives users article formatting options. Radio buttons are appropriate since for each of the three categories—font size, font family, and paragraph formatting—only one style can be selected. When you click any radio button, the response is immediate. This is due to how it's set up. The onClick event when a button is clicked is detected with such scripts as: onclick="fonts='large';fixfont()"> large<br>. This changes the value of the variable fonts, then runs the JavaScript function fixfont(). You may recall that earlier on this web page we had a discussion of this function.
<div class='title'>
<h1>Index Articles—Content Management System (CMS)</h1>
</div>
<div class='choose'>
<form method=" " name='formfont'>
<b>FONT SIZE</b><br>
<input type="radio" name="font" value="large" onclick="fonts='large';fixfont()"> large<br>
<input type="radio" name="font" value="medium" checked onclick="fonts='medium';fixfont()"> medium<br>
<input type="radio" name="font" value="small" onclick="fonts='small';fixfont()"> small
<hr>
<b>FONT FAMILY</b><br>
<input type="radio" name="family" value="Times" onclick="familys='Times';fixfont()"> Times New Roman<br>
<input type="radio" name="family" value="Verdana" checked onclick="familys='Verdana';fixfont()"> Verdana<br>
<input type="radio" name="family" value="Arial" onclick="familys='Arial';fixfont()"> Arial
<hr>
<b>FORMAT</b><br>
<input type="radio" name="format" value="indented" checked onclick="formats='indented';fixfont()"> indented<br>
<input type="radio" name="format" value="spaced paragraphs" onclick="formats='spaced paragraphs';fixfont()"> spaced paragraphs<br>
<input type="radio" name="format" value="both" onclick="formats='both';fixfont()"> both
</form>
<hr>
<a href='#' onClick="printem();">Print content</a>
</div>
</body>
</html>