Page 1 of 1

ScreevoCMS: A Very Simple Content Management System.

Posted: Fri Aug 25, 2006 12:10 am
by screevo
So, I had this domain name lying around. And I wanted to actually use it. So I whipped up a site design, and remembered how I hated having multiple static HTML pages lying around.

So I went to grab the content management system that I used for one of my old sites, that a friend of mine designed. But it was gone.

Well, i figured now is as good a time as any to learn PHP and MySQL, so I whipped this up over the past month. I'm looking for ways to improve it. Things I may have done in 3 lines that can be done in one function, stuff like that. So, here it is. The page it runs is at http://www.screevo.com

A test of the system is visable at http://test.screevo.com and you can access the administration panel at http://test.screevo.com/?id=admin using username "test" and password "test".

Please give me ideas on how I can change this to make it better.

Changes that need to be made:
1. I need to add links back to the front page of the Admin panel from each stage of the admin process.
2. I think the front page of the administration panel needs to be switched to links as opposed to radio switches.
3. The admin panel needs an option to upload files and images, especially images for use in links on the sidebar.

Code: Select all

<?php
/*
ScreevoCMS v1.1
August 23, 2006

Author: Stephen Martin - stephen@screevo.com
---------------------------
A simple content management system for reading, 
writing, and creating content in an SQL database to
be used in an easily managed webpage. 

Pages are stored in a table called 'pages'.
Users who are allowed to edit content are
stored in a table called 'users'.

The authorization functions authenticateUser() and 
checkUser() rely on hooks that are not usable when
running PHP as CGI. Thus, you must be running mod_php under 
Apache in a Unix-based environmentfor this to function properly. 

The header and footers in which the content is wrapped
are set by the constants HEADER and FOOTER at the 
beginning of the file and must be the absolute 
file location, relative to /, and not to your domain name.
Also set will be the SQL Server (SQLSERVER), the SQL user
(SQLUSER), the SQL password (SQLPASSWORD). and the database
in which the information is stored (SQLDB)

The variable 'carat' is set while in the administration
panel to direct the CMS to the right administrative function.

The next release will allow pages to be reordered, and 
previewed before posting.
*/


//====== CONSTANTS ======//
define("HEADER","/location/of/header/head.php");
define("FOOTER","/location/of/footer/foot.php");
define("SQLSERVER","MySQL Server");
define("SQLUSER","Username");
define("SQLPASSWORD","Pass");
define("SQLDB","Database");
//========================//

//====== FUNCTIONS =======//
//Opens Database Connection
function openDatabaseScreevo() {
        mysql_connect(SQLSERVER, SQLUSER, SQLPASSWORD) or die("Can not connect to DB server.");
        mysql_select_db(SQLDB) or die("Can not connect to database.");
}
//End Function


//Prompts User for Credentials
function authenticateUser() {
        header('WWW-Authenticate: Basic realm="Private"');
        header("HTTP/1.0 401 Unauthorized");
        echo 'Invalid username and password.';
        exit;
}
//End Function

//Checks Credentials against Database
function checkUser() {
        if (!isset ($_SERVER['PHP_AUTH_USER'])) {
                authenticateUser();
        } else {
                openDatabaseScreevo();
                $user = mysql_real_escape_string($_SERVER['PHP_AUTH_USER']);
                $pass = md5(mysql_real_escape_string($_SERVER['PHP_AUTH_PW']));
                $result = mysql_query("SELECT username FROM users WHERE username = '$user' AND pswd = '$pass'");
                if (mysql_num_rows($result) == 0) {
                        authenticateUser();
                }
        }
}
//End Function

//Gathers links for use in the includes. Modify the echo statement to reflect how you want the links displayed.
function screevoLinks() {
    openDatabaseScreevo();
    $result = mysql_query("SELECT link, linkimage, title FROM pages WHERE id > 0 ORDER by id asc");
	$num_rows = mysql_num_rows($result);

    while ($row = mysql_fetch_array($result)) {
        $link = $row["link"];
        $linkimage = $row["linkimage"];
        $title = $row["title"];
        print 	'<img src="/siteimages/spacer.gif" alt=" ">
				<a href="'.$link.'"><img src="'.$linkimage.'" alt="'.$title.'" border="0"></a>
				<br>';
	}
}
//End Function

function makeVariables($available, $wanted) {
 foreach($wanted as $wanted) {
  if (array_key_exists($wanted, $available)) {
   ${$wanted} =  mysql_real_escape_string($available[$wanted]);
  }
  }
} 

//Admin Panel Default Page - Carat is blank
function screevoStart() {
        print ' <h2>Content Management System</h2>
				<form action="/?id=admin" method="post">
				<input type="radio" name="carat" value="update">Update Existing Page<br>
				<input type="radio" name="carat" value="new">Create New Page<br>
				<input type="radio" name="carat" value="delete">Delete a Page<br><br>
				<input type="submit" value="Go">
				</form>';
} //End function

//Create New Page - Carat = "new"
function screevoNew() {
    print	'Content Creator<br><br>
			<form action="/?id=admin" method="post">
			<input type="text" name="id" size="4" value="id">
			<input type="text" name="title" size="20" value="title"><br>
			<input type="text" name="link" size="5" value="/?id=">
			<input type="text" name="linkimage" size="30" value="/siteimages/image.jpg"><br>
			<textarea id="content" name="content" rows="50" cols="50">
			The content of your new page goes here. HTML is allowed. PHP is not.
			</textarea>
			<input type="hidden" name="carat" value="preview">
			<input type="submit" value="Preview New Page"> </p> </form> <br>';
} 
// End Function

//Update Existing Page - Carat = "update"
function screevoUpdate() {
       openDatabaseScreevo();
        if (!$_POST["id"]) {
                $result = mysql_query("SELECT id, title FROM pages WHERE id > '0' ORDER by id ASC");

                echo 'Content Updater<br><form action="/?id=admin" method="post">';

                $num_rows = mysql_num_rows($result);

                while ($row = mysql_fetch_array($result)) {
                        $id = $row["id"];
                        $title = $row["title"];
                        echo '<input type="radio" name="id" value="' . $id . '">' . $id . ' - ' . $title . '<br>';
                }

                echo '<input type="hidden" name="carat" value="update"><br>';
                echo '<input type="submit" value="Edit selected page."></p></form>';
        } else {
                if (isset ($_POST["id"])) {
                        $id = intval($_POST["id"]);
                } else {
                        $id = "1";
                }

                $result = mysql_query("SELECT content, title, link, linkimage FROM pages WHERE id = '$id'");

                if (!$result) {
                        echo 'FAILED! <br>';
                        echo mysql_error();
                } else {
                        $num_rows = mysql_num_rows($result);
                        if ($num_rows == 0)
                                echo 'OMG H4XX0RZ. Making up random numbers will get you nowhere. Try again from the menu on the left.';
                        else {
                                while ($row = mysql_fetch_array($result)) {
                                        extract($row);
                                        print	'<h1>Now editing: ' . $title . ' Section number:' . $id . '<br>
									<form action="/?id=admin" method="post">
									<input type="text" name="id" size="4" readonly value="' . $id . '">
									<input type="text" name="title" size="20" value="' . $title . '"><br>
									<input type="text" name="link" size="5" value="' . $link . '">
									<input type="text" name="linkimage" size="20" value="' . $linkimage . '"><br>
									<textarea name="content" rows="30" cols="70">' . $content . '</textarea>
									<input type="hidden" name="carat" value="preview">
									<input type="submit" value="Preview Changes"></p></form>';
                                }
                        }
                }
        }
        mysql_close();
}
// End Function

// Delete Existing Page - Carat = "Delete"
function screevoDelete() {

        openDatabaseScreevo();

        if (!$_POST["id"]) { //select page to be deleted
                $result = mysql_query("SELECT id, title FROM pages WHERE id > 0 ORDER by id ASC");
                $num_rows = mysql_num_rows($result);
                echo 'Delete a page.<br> n <form action="index.php?id=admin" method="post">';
                while ($row = mysql_fetch_array($result)) {
                        $id = $row["id"];
                        $title = $row["title"];
                        echo '<input type="radio" name="id" value="' . $id . '">' . $id . ' - ' . $title . '<br>';
                }
                print 	'<input type="hidden" name="carat" value="delete">
						<input type="submit" value="Delete selected page.">n</p>n</form>';
        }

        elseif (isset ($_POST["id"]) AND !isset ($_POST["confirm"])) { //confirm deletion
                $id = intval($_POST['id']);
                $result = mysql_query("SELECT id, title FROM pages WHERE id = '$id'");
                $num_rows = mysql_num_rows($result);
                while ($row = mysql_fetch_array($result)) {
                        $id = $row["id"];
                        $title = $row["title"];
                }

                print	'Are you absolutely, positively sure you want to delete page ' . $id . ' - ' . $title . '?<br>
						If you do, and you change your mind, you will have to recreate the page from scratch.<br>
						To confirm, click Submit. Or else, <a href="/index.php?id=admin"> click here to go back.</a><br>
						<form action="/?id=admin" method="post">
						<input type="hidden" name="id" value="' . $id . '">
						<input type="hidden" name="title" value="' . $title . '">
						<input type="hidden" name="confirm" value="1"> <input type="hidden" name="carat" value="delete">
						<input type="submit" value="Yes, I am sure I want to delete the page.">';
        }

        elseif (isset ($_POST["id"]) AND $_POST["confirm"] == "1") {
                $id = intval($_POST["id"]);
                $title = $_POST["title"];
                $result = mysql_query("DELETE FROM pages WHERE id = '$id'");

                if (!$result) {
                        echo "FAILED!";
                        echo mysql_error();
                } else {
                        echo 'You have successfully deleted page number ' . $id . ' called ' . $title . '.';
                        mysql_close();
                }
        }

}
// End Function

//Preview Page - Carat = "preview" // This does not work 100% yet. Still bugs out a bit if the HTML contains a form.
function screevoPreview() {
extract($_POST);
print	'Previewing: '.$title.'
		<hr>
		'.$content.'
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<hr>
		If you need to make more changes, please go back. If you are satisfied with how this looks, click submit.<br>
		<form action="/?id=admin" method="post">
		<input type="hidden" name="id" value="'.$id.'">
		<input type="hidden" name="title" value="'.$title.'">
		<input type="hidden" name="content" value="'.htmlentities($content).'">
		<input type="hidden" name="link" value="'.$link.'">
		<input type="hidden" name="linkimage" value="'.$linkimage.'">
		<input type="hidden" name="carat" value="put">
		<input type="submit" value="Submit">';
}
//End Function

// "Put" - Makes changes to the database.
function screevoPut() 
{
	openDatabaseScreevo();

        $post=array_map('mysql_real_escape_string',$_POST);
        extract($post);
           
	$result = mysql_query( "REPLACE INTO pages (id,title,link,linkimage,content) VALUES ('$id','$title','$link','$linkimage','$content')" );
	if (!$result) {
		echo 'Failed: '.mysql_error();
	} else {
		print 'Successfully completed operation on page #'.$id.'called '.$title.'.';
	}
	mysql_close();
}
//End Function

//==========END FUNCTIONS==========//

//========ADMIN PANEL=========//
if ($_GET['id'] == "admin") {
        checkUser(); //Get username and Password
        include (HEADER);
        switch (mysql_real_escape_string($_POST['carat'])) {
                case 'new' :
                        screevoNew();
                        break;
                case 'update' :
                        screevoUpdate();
                        break;
                case 'preview' :
                        screevoPreview();
                        break;
                case 'delete' :
                        screevoDelete();
                        break;
                case 'put' :
                        screevoPut();
                        break;
                default :
                        screevoStart();
                        break;
        }
        include (FOOTER);
}
//=======GENERATE PAGE========//

else { 
    include(HEADER);
    openDatabaseScreevo();    
    if(!isset($_GET['id'])) 
        $id="1";
    else 
        $id=mysql_real_escape_string($_GET['id']);

    $result = mysql_query("SELECT content AS content FROM pages WHERE id = '$id'");

    if(!$result) 
        echo "Things are broken, people are dying, this page isn't working!";

    else {
        $num_rows = mysql_num_rows($result);   
        if($num_rows == 0) 
            echo 'Quoth the database, "404". It appears the page you are trying to reach is not there.'; 
        else {
            while ($row = mysql_fetch_array($result))   {
                                                        $content = $row["content"];
                                                        echo $content;
                                                        }
        }
    mysql_close();
    }
    
    include(FOOTER);
}
?>

Posted: Fri Aug 25, 2006 12:17 am
by RobertGonzalez
Why the exhaustive use of error suppression?

Posted: Fri Aug 25, 2006 12:21 am
by screevo
Everah wrote:Why the exhaustive use of error suppression?
I've tried to make it so if it errors, it will echo an understandable error statement.

Thinking about it, however, allowing the errors would show the line numbers for easier debugging.

Posted: Fri Aug 25, 2006 12:24 am
by feyd
Not to mention the dramatic slowdown using the error suppresors is.

Posted: Fri Aug 25, 2006 12:26 am
by screevo
feyd wrote:Not to mention the dramatic slowdown using the error suppresors is.
Oh really? I've not heard of this. Using supression causes slowdown, even if an error isn't encountered?

Hm. Removing error supression now.

Posted: Fri Aug 25, 2006 12:27 am
by RobertGonzalez
Error handling in favor of error suppression, in my opinion, is the way to go.

Posted: Fri Aug 25, 2006 12:28 am
by feyd
There's a missing double quote at the end of the code where you swapped out your connection details.

edit: out. out, out damn spot.

Posted: Fri Aug 25, 2006 12:30 am
by screevo
Everah wrote:Error handling in favor of error suppression, in my opinion, is the way to go.
Can you show me an example of how this would be done, by way of the openDatabaseScreevo() function?

Posted: Fri Aug 25, 2006 3:46 am
by Verminox
You have repeatedly used "\n" inside a single quoted string.

Code: Select all

echo "\n"; //Newline
echo '\n'; //Will literally print the character(s) \n

Posted: Fri Aug 25, 2006 5:34 am
by Chris Corbyn
Are we open to constructive criticism then? :)

* Your database connection handling is flaky at best. You have no connection ID stored anywhere in the event that you need to hook up to another database. You could consider using a class for the database work...
* Why does authenticateUser() send headers that suggest the user is not authenticated?
* You consistently mix your display logic with business logic. You might want to look at refactoring this, even perhaps using a template system.
* \n in single quotes as mentioned... this must have looked weird :?
* if (!$_POST["id"]) { will return a notice if that key doesn't exists. Use empty().

;)

Posted: Fri Aug 25, 2006 6:15 am
by screevo
d11wtq wrote:Are we open to constructive criticism then? :)

* Your database connection handling is flaky at best. You have no connection ID stored anywhere in the event that you need to hook up to another database. You could consider using a class for the database work...
* Why does authenticateUser() send headers that suggest the user is not authenticated?
* You consistently mix your display logic with business logic. You might want to look at refactoring this, even perhaps using a template system.
* \n in single quotes as mentioned... this must have looked weird :?
* if (!$_POST["id"]) { will return a notice if that key doesn't exists. Use empty().

;)
A post in a critiquing forum begs critique!

1: Well, the idea to this was to have everything in a single database. That's why I eliminated the idea of storing a connection ID. Also, I haven't gotten far enough into my learning to know much about classes. Or anything at all, actually.

2: Authenticate as a verb. It sends the headers which cause the username and password box to pop up. Then, check user runs it against the database. Since the authenticateUser() function has to be put in twice (once at the opening, and once again if validation fails), i made it it's own function.

3: I honestly have no idea what this means, in regards to business logic vs. display logic.

4: Yes, I know. I just got done switching all the echo statements from being enclosed in double quotes to single quotes so I didn't have to escape all the double quotes in the form statements.

5: Duly noted. I'll look up the exact usage of the empty function. I presume it would be called in the manner of if (empty($_POST['id''])) {

Posted: Fri Aug 25, 2006 6:44 am
by Chris Corbyn
:)

1. No worries. I don't think you're not far enough into learning classes (OOP) though. OOP is commonly misconceived as an advanced topic. In reality it's not, and if you're suing functions for the reasons you are you should probably look at them early... you'll do yourself a big favour. Granted... you can learn a lot of advanced stuff but as with everything, start basic.

2. Maybe I don't understand what the headers in that function do... sorry, my bad.

3. Display logic is put simply, code used to generate some output. Business logic is management of database records etc. You should try to keep the two separate.

4. That makes sense... easy to do.

5. You're right, that is how you use empty().

Posted: Fri Aug 25, 2006 7:16 am
by screevo
d11wtq wrote::)

1. No worries. I don't think you're not far enough into learning classes (OOP) though. OOP is commonly misconceived as an advanced topic. In reality it's not, and if you're suing functions for the reasons you are you should probably look at them early... you'll do yourself a big favour. Granted... you can learn a lot of advanced stuff but as with everything, start basic.
All in time, I'm sure.
2. Maybe I don't understand what the headers in that function do... sorry, my bad.
This:

Code: Select all

header('WWW-Authenticate: Basic realm="Private"');
prompts the browser to ask for a user name and password. If it gets one, it passes it to check user. If not, it sends the Unauthorized header, and kills the script.
3. Display logic is put simply, code used to generate some output. Business logic is management of database records etc. You should try to keep the two separate.
It's strange that you mentioned seperating displaying forms from doing database work. while you were typing that post, I was actually adding a new function that controls writing to the database. The delete function is still seperate.
4. That makes sense... easy to do.

5. You're right, that is how you use empty().
And so I am using empty in every instance of ! that I could find.



I also added a preview function. Now the flow is

Code: Select all

Update page 
     | 
     V
Preview Page  ----> Write information to database.
    ^
    |
Create new page

I've updated the original post with the new source.

BTW, what does (OOP) stand for?

Posted: Fri Aug 25, 2006 9:33 am
by klarinetking
Object Oriented Programming. There's lots and lots of topics in the PHP - Theory and Design forum about OOP, and the PHP Manual has a great section on the OOP in PHP 4 and 5.

klarinetking