Building a CMS 4 Dummies
Building a CMS 4 Dummies
Building a CMS… For Dummies.
In this article i will explain what a CMS is, and show you how i built mine… This can be found at: http://keiran420.ulmb.com/hbhcms/ Feel free to login as, hbhmember:hbhpassword Thats an editor account, post a message :) It is also 100% open source: http://keiran420.ulmb.com/hbhcms/code.txt
I have used php/MySQL and use a little bit of javascript to make this site. I hope by the end of this article You will understand more what a CMS is and how to make 1.
What is a CMS? In short CMS is a website that allows users to control and add the content.
What is in this CMS? The CMS i will show here simple has three levels of user, (admin, editor, member), and can add/edit/delete users/news/articles. The user control is for admin only, but the news/article control is available to editors, and is very easily manipulated to add more sections to your site.
This CMS uses 15 basic functions… these are…
//login and database functions Connect() - establishes a connection to the database Disconnect() - closes that connection login() - logs you in. Checklogged() - checks if the cookie data is a valid user. logout() - logs user out. UserLevel() - returns the users access level as a number.
//Data manipulation functions AddData() - inserts a new data item into the database DeleteData() - Deletes a data item from the database UpdateData() - allows changing and updating of the data ReturnAll() - returns every result from a specified table Returndata() - returns a single result.
//user data manipulation functions Adduser() - Adds new user account. edituser() - Edit a users name/pass or status DeleteUser() - remove a user ReturnUser() - return data on 1 user. NOTE: we can return all users with ReturnAll('user_db')
//Input encoder encode() - htmlentitie encodes all input.
Getting started
The first thing you need are the tables for your data…
I have: users_db, fields… username - Make the username the key, so no 2 people can have the same username. password - Limit this at 32 chars, we will be saving MD5 hashes ID – Simply admin, editor or member.
The fields on my news_db and articles_db are identical. If you want to add more like say videos_db, keep that the same to… ID – Key, self incrementing number. title – The title of the data. text – The data itself.
remember to limit all field to a reasonable content, you don't want some hacker loading up a 100mb file…
Talking of hackers we want to validate every single last piece of data the user sends us… This means POSTS, GETS, COOKIES and if you use them, REQUESTS.
I have a very simple function that completely translates any harmfully code into htmlentities. Remember to encode both double and single quotes also.
{
$Safe = htmlentities($String, ENT_QUOTES);
return $Safe;
}```
So now for every $_WHATEVER['whatever'] that you use, use Encode($_WHATEVER['whatever']).
Be a little more secure and allows articles and stuff to contain code :)
**Connecting:**
The core of any CMS is how it connects to its database and handles data.
Before any query can be made to the database, we must first open a connection,
And after we are done, we MUST close that connection.
I have made 2 functions called connectdb() and disconnect()
Both of these are called in every function that uses mysql_query();
Their simple enough.
```markup$DB_Host = '***';
$DB_User = '***';
$DB_Pass = '***';
$DB = '***';
function ConnectDB()
{
$Link = mysql_connect($DB_Host, DB_User, $DB_Pass);
mysql_select_db($DB, $Link);
if (!$Link)
return mysql_error();
else
return $Link;
}
function Disconnect($Link)
{
mysql_close($Link);
}```
**Logins and Users:**
Now we are connected, lets have a quick look at how we handle users and logins...
My user and login system is fairly basic.
To register, the only requirement is the username is unique...
All registered members become the lowest level of 'member'
The admin can control users, and add new ones, he is the only
one who can change a users' level.
The login function query's the database for a match, and if found
will save the details to cookies (md5 encrypted)
Each page will check your cookie details against the database, and
proceed as needed.
Because of this, my login function has 3 inputs, the 3rd one is
and optional pre encrypted password for the automatic checks.
My login function goes as follows...
```markupfunction Login($User, $Pass, $MD5Pass)
{
$Link = ConnectDB();
if ($MD5Pass)
$Password = $MD5Pass;
else
$Password = md5($Pass);
$Sql = "SELECT ID, username, password FROM user_db WHERE username='".$User."' AND password='".$Password."'";
$Result = mysql_query($Sql, $Link);
$ID = mysql_fetch_array($Result);
if ($ID){
setcookie(CMSuser, $ID['username'], time()+1800);
setcookie(CMSpass, $ID['password'], time()+1800);
return $ID['ID'];
}
else
return "Error";
Disconnect($Link);
}```
This will check the login details, if found, the cookie is set/refreshed for half an hour.
It will return either the users level, or 'error'.
The checkedlogged function query's this function with cookie data...
It Removes any malicious code the cookies may have been edited with to.
Cant be to careful :)
```markupfunction CheckLogged()
{
$User = Encode($_COOKIE['CMSuser']);
$MD5Pass = Encode($_COOKIE['CMSpass']);
$Result = Login($User, 'null' ,$MD5Pass);
if (!$Result)
return "error";
else
return $Result;
}```
Remember we are using the pre encrypted pass.
And all the logout function does is expire the cookies,
Do this by setting -half hour as the time...
**Handling Data:**
Of course any CMS needs to be able to control and change data...
The Primary one is the adddata function, this can be used by editors to add news, articles,
and any other sections you add.
```markupfunction Add_Data($Table, $Data1, $Data2)
{
$Link = ConnectDB();
$Sql = "INSERT INTO ".$Table." (title, text) VALUES ('".$Data1."','".$Data2."')";
$Result = mysql_query($Sql, $Link);
if (!$Result)
return mysql_error();
else
return "Added";
Disconnect($Link);
}```
All this needs is the table, and the 2 pieces of info (pre sanitized)
If alls good it will return 'Added', else it will tell you what went wrong...
The delete function just needs a table and a unique id, then just removes the match.
```markupfunction Delete_Data($Table, $ID)
{
$Link = ConnectDB();
$Sql = "DELETE FROM ".$Table." WHERE ID=".$ID;
$Result = mysql_query($Sql, $Link);
if (!$Result)
return mysql_error();
else
return "Deleted";
Disconnect($Link);
}```
Where as the edit function wants all the info, table, ID, and the 2 pieces of updated data...
```markupfunction Update_Data($Table, $ID, $Data1, $Data2)
{
$Link = ConnectDB();
$Sql = "UPDATE ".$Table." SET title='".$Data1."', text='".$Data2."' WHERE ID=".$ID;
$Result = mysql_query($Sql, $Link);
if (!$Result)
return mysql_error();
else
return "Edited";
Disconnect($Link);
}```
All good so far?
Lets have a look at how our CMS interface works...
**The G.U.I.**
The index page..
The index page has the login and register buttons on,
As well as search control and a link for editors and admins.
The 1st things this file will do is check to see if a user is logged in,
```markup$Status = CheckLogged();
if ($Status)
$Loggedin = UserLevel($Status);```
The index has 3 buttons for News, Articles and registration.
Heres the code to detect what button is pressed...
The top line is default setting.
```markup$Emlink = 'view.php?V=News';
if (Encode($_POST['Register']))
$Emlink = 'view.php?V=Register';
elseif (Encode($_POST['News']))
$Emlink = 'view.php?V=News';
elseif (Encode($_POST['Articles']))
$Emlink = 'view.php?V=Articles';```
Later on in the page i have a table, where one of the cells is an embedded link to $Emlink.
There are a few ways to display this, but i choose this way.
```markupecho "<iframe height='100%' width='100%' scrolling='auto' src='".$Emlink."'></iframe>";```
The login form is a simple one, it has a condition that will only show it if you are NOT logged in.
Else it will welcome you and offer you a logout option.
Note the register button is also displayed here.
These forms has 2 input values, and 3 submits (login and register. Or logout).
```markupif ($Loggedin == 0)
{
echo "<form method='post' action='index.php'>";
echo "<b>Login:</b><br>";
echo "Username:<input size='10' type='text' name='Username'><br>";
echo "Password:<input size='10' type='password' name='Password'><br>";
echo "<input type='submit' name='Login' value='Login'>";
echo "<input type='submit' name='Register' value='Register'><br>";
echo "</form>";
}
else
{
echo "Welcome ".Encode($_COOKIE['CMSuser'])." your user level is ".$Status."<br>";
echo "<form method='post' action='index.php'>";
echo "<input type='submit' name='Logout' value='Logout'></form>";
}```
The code for the register button was shown above, the login button has a little more to it...
We 1st must sanitize the input, then compare it to the database with our login function.
It will return admin, editor, member or failed.
```markupif(Encode($_POST['Login']))
{
$User = Encode($_POST['Username']);
$Pass = Encode($_POST['Password']);
$Status = Login($User, $Pass);
if ($Status)
{
echo "<script>alert('Attempt login as ".$User;
echo " - Login level ".$Status."');window.location='index.php'</script>";
}
}```
The logout code simple calls the logout function and alerts you.
```markupif (Encode($_POST['Logout'])){
logout();
echo "<script>alert('Logged out');window.location='index.php'</script>";
}```
On the index page you will want some links not viewable by all...
This is easily done, i call a function which returns the users user level, then before showing
the link i check this level.
```markupif ($Loggedin >= 2)
echo "<a href='admin.php'>Admin Control</a><br>";```
Remember we called:
```markup$Status = CheckLogged();
if ($Status)
$Loggedin = UserLevel($Status);```
At the start of the page.
Thats about all the Index Page does....
Lets take a look at view.php, this page handles displaying news, articles, and the
registration.
View.php is an embedded link in the center of index.php or admin.php (tho the admin page has control pages to)
We pass a the page a value in the url, e.g. view.php?V=News
The page will check this at the start, and display the appropriate
response. Example...
```markup$V = Encode($_GET['V']);
if ($V == 'Register')
{
//some code```
To Display Results I grab all the data off a Database, and echo it out in a table.
EDITED:
Here is the code to display bold, underline, italics and a textarea, as you can see its easy to add more...
(i had to split them because hbh use the same ones.
```markup
$tagcount=9;
$tags[0]='[\b]'; $html[0]='<b>';
$tags[1]='[\/b]'; $html[1]='</b>';
$tags[2]='[\u]'; $html[2]='<u>';
$tags[3]='[\/u]'; $html[3]='</u>';
$tags[4]='[\i]'; $html[4]='<i>';
$tags[5]='[\/i]'; $html[5]='</i>';
$tags[6]='[\code]'; $html[6]="<textarea readonly='readonly' cols='60' rows='4'>";
$tags[7]='[\/code]'; $html[7]='</textarea>';
$tags[8]='[br]'; $html[8]='<br>';
To use this just run this code before displaying the text:
$text = $Row['text'];
for ($i=0;$i<$tagcount;$i++)
{
$text1 = str_ireplace($tags[$i],$html[$i], $text);
$text = $text1;
}
Then call $text1 instead of $Row['text']. You now have control over the html used :)
CONTINUED:
{
if ($V == 'News')
$Table = 'news_db';
elseif ($V == 'Articles')
$Table = 'articles_db';
$Result = Return_All($Table);
while ($Row = mysql_fetch_array($Result))
{
echo "<center><table width='95%' border='1'>";
echo "<tr><td>";
echo $Row['title'];
echo "</td></tr><tr><td>";
echo $Row['text'];
echo "</td></tr>";
echo "</table></center><br>";
}
}```
The registration form asks for username and password, it then tries to add the user
as member, it will display if the user is added, or if the username is in use.
```markupelseif ($V == 'Register')
{
if (Encode($_POST['Register']))
{
$Status = 'member';
$User = Encode($_POST['User']);
$Pass = Encode($_POST['Pass']);
$Result = Add_User($Status, $User, $Pass);
if ($Result == 1)
{
echo "<script>alert('Added ".$User."');";
echo "window.location='view.php?V=Register'</script>";
}
else{
echo "<script>alert('UserName unavalible!');";
echo "window.location='view.php?V=Register'</script>";
}
}
echo "<form method='post'>";
echo "Username:<br><input name='User' type='text'><br>";
echo "Password:<br><input name='Pass' type='password'><br>";
echo "<input type='submit' name='Register' value='Register'>";
echo "</form>";
}```
**Admin page:**
The admin page is much like the index page except that:
There is no login or register
It will only allow users with a userlevel of 2 or 3
It displays links to the control pages.
Only the admin can see and use the usercontrol link.
I hope by now your capible of adding in the control forms/buttons/handling to link to:
```markup$Emlink = 'control.php?V=News';
$Emlink = 'control.php?V=Articles';
$Emlink = 'usercontrol.php';```
Lets have a look at the control page...
**Control Page:**
This page will control news, articles and any further tables you may add...
The page is only viewable by editors and admins...
The page then needs to check what 'V' is and display the tables accordingly.
My page Gives the option and space to add a new article at the top,
It the echo's out tables of all the data each with its own EDIT and DELETE button.
If a delete button is clicked, the corresponding data will be permanently removed from the
database, if input is typed and ADD is clicked, a new entry will be added to the database
altogether.
But when the edit button is chosen, we must 1st put the data into an editable form, that
can then be resubmitted...
I will explain only this 1, as it gives more than info for the rest...
Heres the code...
```markup //If an Edit button is clicked....
if (Encode($_POST['Edit']))
{
//The item ID is the buttons value, unique to each button.
$Item = Encode($_POST['Edit']);
//we then query the database to pull this 1 item....
$Result = Select_Data($Item, $Table);
//we store this result to an array, and if alls good we display it.
$Row = mysql_fetch_array($Result);
if ($Row)
{
//this form is basically like the ADDNEW form, but we have prefilled in
//the input values with that of the matching data....
echo "<form method='post'>";
echo "Edit ".$V.": <input type='submit' name='Republish' ";
echo "value='".$Row['ID']."'><br>";
echo "Title:<br>";
echo "<input name='Title' type='text' value='".$Row['title'];
echo "' size='64'><br>Text:<br>";
echo "<textarea name='Text' type='text' cols='74' rows='15'>";
//this searches for any entrys of <br> which are added
//in on line breaks... the line break is then reinserted.
$Text2 = str_ireplace('<br>','
',$Row['text']);
echo $Text2."</textarea>";
echo "</form>";
}
}```
Once the Republish button is clicked, The editdata function is called with the new data.
The usercontrol page works very similar, except that table names differ, and we are dealing with
encrypted passwords, Try and work it out yourself, else refer to my open source code :)
I Hope this article taught you enough to get started on your own CMS :)
Good luck ^^
ghost 16 years ago
I must say… when you mentioned that you were going to write an article like this, I was expecting an insurmountable pile of dogshit. In reality, your article and concepts are very well-structured. You separated out the functionality of your CMS into logical functions, which made the explanation of basic CMS functionality all the easier. The style of writing was somewhat amateurish but, per the focus and title of the article, it fit… perfectly. I could go on and on saying what made this article good, but I'll just say this: I'm genuinely impressed.
ghost 16 years ago
Nice article dude. Really helped me grasp the concept of making a CMS, and it helped me at the moment as I am developing my own one. I have already made most of it, but I did have a few questions and function problems which your article solved. Thanks! and again, Nicely done!
ghost 16 years ago
indeed a very informative article. Very well done!
Wouldn't it be better to use the $_SESSION table to store login information than storing cookies?
K3174N 420 16 years ago
Ahh 3 comments and some nice votes, time to reply :) @ZP - Even i had my doubts when i 1st decided to write this article, i wanted to write it for several reasons, the main one being i knew that if done right, would result in a very useful article that HBH hasn't already got. I took my time, learned about what i was doing as i did it, then wrote it all up In the simplest way i could… I'm happy with the result to, thanks for a great comment. :) @DarkMantis - Fantastic! Thats exactly what i wanted this article to do. :) @Longbow - Yes, session data would defiantly be better, i've already started work on this actually, but until its neat, efficient, and works as it should, i wont be editing the article. Don't want to ruin it. ^^
ghost 16 years ago
Good article. Awesome attempt! It is pretty good but the php could be much shorter and more efficient. It is too much hardcode and no classes, and too much if / else if. Use switches!
Also, you should write a filter that filters all input and not feed all postFields / getData / cookies / sessions through separate functions. Just take all post fields like so (or $_GET, or cookies etc, ) : $postFields = array_keys($_POST); put $postFields into a for loop and filter it from there. Include that file in all others. That should take care of it. Also, try to write more reusable code. Classes, functions, etc.
I can probably find some more small things to nag about, like using javascript and nothing else to inform people about an event. That's wrong, when i turn javascript off, i don't know what happened! That should never happen. Put it like: alert,window_location //endJS echo same_as_alert_text; That way, if javascript fails, people can still read what happened. If you want to write W3 WAI compliant code, you should do that.
But, let's nor forget these are besicly "small" things. Again, awesome attempt. Good article. Therefore i vote very good.
:happy:
ghost 16 years ago
A bit OT… but, since it is helpful, I'll just go ahead and say it: array_map() > array_keys() + for loop.
ghost 16 years ago
Why do you say that? Look at it: $post_keys = array_keys($_POST); for($i=0;$i<count($post_keys);$i++){ $_POST[$post_keys[$i]] = filter($_POST[$post_keys[$i]]); } ;)
ghost 16 years ago
What you said is useless zephyr. I do not mean it offending, but it just is. For this you do not need it.:happy:
ghost 16 years ago
Alright… your reasoning is useless. Your code grabs the keys from the POST array, loops through the array, and applies a single function to each element in the array. array_map() does this in one line, which you would know if you knew what the function did. Think before you speak; if my suggestion of array_map is useless, then your code is potentially life-threatening.
ghost 16 years ago
sure it is lil' fella. I don't discuss with children without experience. I am just elling you kid. I think before i speak, don't try to insul;t me, or i will insult you mama. You are just learning php, and are pretty noob at it. I saw that in you article. Maybe the HBH people don't know it. But that's no wonder either…. I saw that in your last asticle zephyr. You are a beginner. its pathetic to have kiddies walk around as admin omn a "hacker" site. YOU ARE NO HACKERS! lopht, cdc, they are hackers. You are kiddies in the kindergarten whicj accomplished NOTHING at all, still living at home. :happy: Zephyr, your comment i useless. You talk too much, and want to make yourself look good for all the kids in this shithole. What i posted is good. Come try to hack it TalkALot. With your overwhelming experience…lifethreatening would be you having a brain.
ghost 16 years ago
That's nice. Ironic how you defended me in my article but, when it comes to you being wrong here, you turn the other leaf. That's fine, too… I never really did like you. The fact still remains that you have not made an argument for why my solution is "useless" compared to yours. Here, since you're having some trouble proving it to a noob: http://us.php.net/array_map Insult me as much as you like but, until you either admit you were wrong or prove me wrong, you're just being a child.
K3174N 420 16 years ago
FriXioN on December 01 2008 - 23:22:30 No, this article is not good. Bad coding practise written all over it. Care to show a few examples, and highlight where i havegone wrong along with that comment? whats the point of criticism if you dont offer a better alternative…
clone4 16 years ago
I was quite sceptical reading this article at first, but no, it's awesome from me, very nicely explained the basic structures of CMS, useful even in other languages :) thanks K3174N 420, this article does rock! :)