How to create a simple CMS without DB access

A Content Management System (CMS) allows you to update your website easily without touching anything in the backend. If you are a non-IT person, you will almost always want a CMS for your website because it makes you less dependent on your web designers or developers to add, edit or delete contents in your website.

There are many good CMS out there but most of them require some sort of database access in the backend. Many PHP CMS uses mysql or postgres database which resides in the server. Installation and troubleshooting of CMS might require some technical knowledge and server configuration could be daunting for some.

I was creating a website for a client recently when he requested a CMS for him to update the web pages during his free time. I was tempted to install a popular ready made CMS such as joomla but on futher thoughts, joomla on a small business website could be an overkill. The website would most likely not utilise many features of joomla and furthermore, his hosting package does not include a database (amazing). Given a short time frame to come up with a solution, I created a simple PHP script for him to do the job, ie to edit the content of a few web pages only.

In this example, we are going to create a simple CMS in PHP without the use of MYSQL, POSGRESQL or any other databases.

Simple Login Form

In a form, I added 2 fields, the username and password. The form is to post its data back to the same page so that we can verify whether the username and password fits with a preset value. The html code for the form is shown below:

<form method=”post” action=””>
<table width=”400″ border=”0″ align=”center” cellpadding=”2″ cellspacing=”2″>
<tr>
<td>Username: </td>
<td><input type=”text” name=”username”></td>
</tr>
<tr>
<td>Passwd: </td>
<td><input type=”password” name=”passwd”></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type=”submit” name=”Submit” value=”Submit”>&nbsp;&nbsp; <input type=”reset” name=”reset” value=”Reset”>
</td>
</tr>
</table>
</form>

Once the username and password are verified, a username session will be triggered to remember that the administrator has logged into the system. We will prompt the user if they enter the wrong details.

if (isset($_POST[‘Submit’])) {
if (($_POST[‘username’] == ‘admin’) && ($_POST[‘passwd’] == ‘yourpassword’)) {
$_SESSION[‘username’] = ‘login’;
}
else {
echo “<b>You login details is not correct. Pls login again</b>”;
}
}

If everything goes well as expected, a quick menu will be displayed. The menu consists of a few links to the files that are editable. In this case, the links are pointing to the home and contact us page. There is also a link to logout from the system.

if ($_SESSION[‘username’]==’login’) {
echo “<p align=’center’>
<a href=”?file=../index.html”>Home Page</a><br/>
<a href=”?file=../contact_us.html”>Contact Us</a><br/>
<br/>
<em>Click on the links above to edit the files.</em><br/>
<a href=”?logout”>logout</a></p>”;
}

Creating Start and End Points in the HTML Files

Before we can start editing the content, we have to tell the CMS which part of the content is editable. In order to do that, we need to create a unique marking in the HTML file. The HTML code for my home page may look like this:

<html>
<body>
<div class=’header’>
This is my header
</div>
<div class=’main’>
Contents written here can be edited
</div>
</body>
</html>

If I want all the contents within the <div class=’main’> to be editable, I need to add my own unique code just for the sake of marking the editable region. So the new HTML code might look like this:

<html>
<body>
<div class=’header’>
This is my header
</div>
<div class=’main’>
<!– EDITABLE –>
Contents written here can be edited
<!– EDITABLE –>
</div>
</body>
</html>

The Decoding Engine

What we have done in the previous step is really to encode the HTML file with the <!– EDITABLE –> tag. We now need to decode it. Thanks to the PHP explode function, we can now strip out the <– EDITABLE –> tag and get the contents.

if (isset($_REQUEST[‘file’])) {
$fc = file_get_contents($_REQUEST[‘file’]);
$text = explode(“<!– EDITABLE –>”,$fc);
echo “<form method=’post’ action=”><textarea name=’content’>$text[1]</textarea>”;
echo “<p><input type=’hidden’ name=’file’ value='”.$_REQUEST[‘file’].”‘ />
<input name=’submitUpdate’ type=’submit’ value=’Update Page’></form>”;
}

The variable $text is an array. $text[0] and $text[2] corresponds to the content before and after the tag <!– EDITABLE –>. Therefore $text[1] is the editable text we want and we display it in the textarea.

Capturing the Edited Content

The edited content is passed again back to the same page via the $_POST variable. If the submit button has been clicked, we will update the home page content. The way we do it is similar to how we strip the <!– EDITABLE –> tag using the explode function. Because we are actually writing to a file, not to a database, we need not add escape characters to the content. PHP version 4.x has magic_quotes turned on by default which happens to escape all our $_POST variables. So if magic_quotes is turned on, we need to unescape the $_POST variables.

if (isset($_POST[‘submitUpdate’])) {
if (get_magic_quotes_gpc()) {
$_POST = array_map(‘stripslashes’,$_POST);
}

$fc = file_get_contents($_POST[‘file’]);
// truncate file
$fw = fopen($_POST[‘file’], ‘w+’);
$text = explode(“<!– EDITABLE –>”,$fc);

$newText = $text[0].”<!– EDITABLE –>”.htmlentities($_POST[‘content’]).”<!–EDITABLE ->”.$text[2];
if (fwrite($fw, $newText)===FALSE) {
die(“Cannot write to file.”);
}
fclose($fw);
exit(“<div><span class=’redText’>The file has been updated. Click <a href=\”index.php\”>here</a> to go back to admin page.</div>”);
}

In the function “file_get_contents”, we get the contents of the file to be edited and strip them into 3 parts again. Noticed that I need to clear my original file with a fopen($_POST[‘file’], ‘w+’).

Now, we rejoin the 3 parts back again. Our edited content is actually in $_POST[‘content’] but it might consists of some illegal characters which has certain meaning in PHP. So we need to convert them into the HTML equivalent using the htmlentities function. We then close the file.

The codes may seem complicated but the logic should be simple enough. One thing to note though is that the content is displayed in a textarea. This means that the user has to put in their own html tag such as <b>, <p> …etc if they want any text formating. You can also add in a HTML editor to make the text formatting easier. I actually used fckeditor with this content management system in my client’s website.

The full source code at my website – PHP CMS.

Online Calendar Using PHP and AJAX Update 1

A bit of history if you are interested

I can vaguely recalled that a designer friend wanted to add a calendar to a custom made CMS. We searched high and low found a few good ones – some commercial and some open source. He found the installation and configuration daunting though.

As a programmer, I knew that playing with dates is always complicated. That is why, creating a calendar from scratch is not a simple task. At about the same time, I was researching in how to create a calendar in a quick and efficient way. After a few days of hard work, I managed to come up with a quick solution. To make his life easier, I combined the source code and the cofiguration variables into one file so he can just include one php line in his current CMS and get it to work. The quick calendar turned out to be functional and good enough to meet the deadline.

I then decided to share the code in evolt.org and received alot of request to add more features. Well, that was good and bad news for me. Good news because many people like it – felt good; Bad news because my daytime work became hectic and I don’t have the time!

Thanks for the support

I would like to thank everyone at evolt.org for giving ideas and commenting on the quick calendar. I hope many people find it useful. It has been a year++ since I last updated it…  as mentioned, I didn’t have the time to improve on it. The christmas break provided me some time to re-look at the calendar again. I took on the comments that you guys left on the evolt article or emailed me and made some changes. It turned out that the changes I’ve made was quite major. I’ve updated the code. check it out:

quick calendar demo 1 | quick calendar source

The installation instructions is in the source code.

As you can see, my css skills aren’t that great. Well, I am not a designer.

One big change was the addition of more comments in the source code. I hope that will make things easier if you decide to hack it (you might need to if you want drastic change in the layout for now). The structure of the class has been changed as well. Instead of using 2 separate classes and aggregation, I decided to use one base calendar class and inheritance. There were also some tweaks in the logic and bug fixes.

I still think there are room for improvements:

1. I like to separate the presentation from logic. At the moment, the QCalendarStandard class is working like a ‘view’, creating all the html code. Not ideal. Ultimately, the user should be able to design the layout without touching the source code.

2. As the calendar becomes more flexible, there is a need to separate the application into a few files. Perhaps a config file, a css file, a base class file, a few more extension classes, a template file. This also means that the installation becomes slightly complex (something against my wish!!). I was struggling with this for a long time. It was the first time I chunked all the code into one file. The reason was simple – I wanted to make the installation idiot proof, minimise installation procedures and reduce the complication with other running apps.

3. Use PHP 5 Coding standards. Eg using “__construct” and proper variable declaration. I purposely made it in PHP 4 two years ago because I knew alot of apps are still using PHP 4. Its probably time to upgrade.

4. I think the way the ajax function – qCalendarDetails() is being used to display the long description is kinda doggy. I probably need to rethink that part or create a user friendly admin panel to administer the calendar.

5. Yes, I think adding a  calendar admin panel is a good idea.

6. Possible to use css to create the calendar instead of using tables.

I will post the more information on how to use the calendar  in the demo soon.

Zend QuickStart Error

If you have been following the zend quickstart tutorial at Zend site:

http://framework.zend.com/docs/quickstart

and couldn’t work out why it didn’t work… You need to check a few things. A few friends complained that the code aren’t working. How can you expect Zend to release something that is not working??!! Well, I was persuaded to go through the tutorial myself and it took me sometime to work things out. The guys expect you to know your stuff and I have to say the quick guide might not be as thorough.

If things aren’t working because the url parsing is not correct, ie the front controller is not working as expected, check if your mod_rewrite is correct. mod_rewrite works differently if you are using .htaccess, virtualhost, IIS…etc

Check out the URL Rewriter for different config at:

http://framework.zend.com/wiki/display/ZFDEV/Configuring+Your+URL+Rewriter

Then, make sure your directory structure is correct:

It should be something like this:

YourApp
_ application
____ config
____ controllers
____ forms
____ layouts
____ models
____ views
_data
_library
_public
_scripts

Also check your bootstrap.php. It should be something like this:

Command Line Blogging – WordPress

I am using command line most of the time because of the nature of my work. Normally, when I discovered new techniques to do things, solved a problem or had a cool idea, I would write them down on my exercise book. It soon became clear that I needed something more concrete as copying lines of code isn’t fun (and they were very disorganised). I was also thinking of a way to share my ideas with the world…

How about posting them in a blog! Sounds good, But I don’t want to be alt-tabbing, copying and pasting between firefox and the terminal. Most importantly, I don’t like the idea of people seeing me blogging during working hours. Looks like I have to investigate how to do it in command line then.

I know that there are some command line blogging tools out there but what I need is the ability to post articles in WordPress (because I am using it) using the command line. After some googling, I knew that that is possible using command line php and xmlrpc. WordPress supports xmlrpc. Time to rock.

This point onwards is all WordPress related. Anyone interested can download a copy of WordPress from http://www.wordpress.com. What I will be doing here is to come up with a simple php script to post articles in wordpress blogs.

Investigation

In xmlrpc.php, line 133 – 166 tells us what API WordPress supports.

133 // Blogger API
134 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
135 'blogger.getUserInfo' => 'this:blogger_getUserInfo',
136 'blogger.getPost' => 'this:blogger_getPost',
137 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
138 'blogger.getTemplate' => 'this:blogger_getTemplate',
139 'blogger.setTemplate' => 'this:blogger_setTemplate',
140 'blogger.newPost' => 'this:blogger_newPost',
141 'blogger.editPost' => 'this:blogger_editPost',
142 'blogger.deletePost' => 'this:blogger_deletePost',
143
144 // MetaWeblog API (with MT extensions to structs)
145 'metaWeblog.newPost' => 'this:mw_newPost',
146 'metaWeblog.editPost' => 'this:mw_editPost',
147 'metaWeblog.getPost' => 'this:mw_getPost',
148 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',
149 'metaWeblog.getCategories' => 'this:mw_getCategories',
150 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
151
152 // MetaWeblog API aliases for Blogger API
153 // see http://www.xmlrpc.com/stories/storyReader$2460
154 'metaWeblog.deletePost' => 'this:blogger_deletePost',
155 'metaWeblog.getTemplate' => 'this:blogger_getTemplate',
156 'metaWeblog.setTemplate' => 'this:blogger_setTemplate',
157 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
158
159 // MovableType API
160 'mt.getCategoryList' => 'this:mt_getCategoryList',
161 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',
162 'mt.getPostCategories' => 'this:mt_getPostCategories',
163 'mt.setPostCategories' => 'this:mt_setPostCategories',
164 'mt.supportedMethods' => 'this:mt_supportedMethods',
165 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
166 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
167 'mt.publishPost' => 'this:mt_publishPost',

The name of the function tells a lot. I had a quick look at the functions and MetaWeblog API seems to be quite promising as it supports a wide range of options. The part that I am interested is the function mw_newPost.

1365 /* MetaWeblog API functions
1366 * specs on wherever Dave Winer wants them to be
1367 */
1368
1369 /* metaweblog.newPost creates a post */
1370 function mw_newPost($args) {
1371 $this->escape($args);
1372
1373 $blog_ID = (int) $args[0]; // we will support this in the near future
1374 $user_login = $args[1];
1375 $user_pass = $args[2];
1376 $content_struct = $args[3];
1377 $publish = $args[4];
1378
1379 if (!$this->login_pass_ok($user_login, $user_pass)) {
1380 return $this->error;
1381 }
1382 $user = set_current_user(0, $user_login);
1383
1384 do_action('xmlrpc_call', 'metaWeblog.newPost');

So we need to pass an array containing 5 elements to the function, namely $blog_ID (not implemented yet), $user_login, $user_pass, $content_struct and $publish. Looking at the code further, I know that $content_struct contains another array and there are a few elements within $content_struct that I am interested for now: title, categories, mt_keywords. With all these information at hand, I can start building my script.

Check Requirements

I am going to use php built-in xml-rpc function. So my php has to support xml-rpc.


[username@web ~]$ cd /etc/php.d
[username@web php.d]$ ls
dbase.ini  mysqli.ini  pdo.ini        pdo_sqlite.ini
gd.ini     mysql.ini   pdo_mysql.ini  
[username@web php]$ cd /usr/lib/php/modules/
[username@web modules]$ ls
dbase.so  mysqli.so  pdo_mysql.so  pdo_sqlite.so
gd.so     mysql.so   pdo.so        phpcups.so

ok, looks like xmlrpc is not installed. I need to install it.

[username@web modules]$ yum install php-xmlrpc

Actual PHP Code

Now we have what we need. Time to code:

#!/usr/bin/php -q
$v) {
echo "[$k. $v] ";
}
echo "\n\neg. ".basename($_SERVER['argv'][0])." -t \"blog title\" -c 1,3,4 -k \"php,linux\" -p 0\n";
exit(1);
}
$options = getopt("c:k:t:p:");
if (empty($options)) {
displayHelp();
}
// convert categories to array
$cat = explode(',',$options['c']);
$catList = getCategories();
$categories = array();
foreach ($cat as $v) {
$categories[] = $catList[$v];
}
// convert tags to array
$tag = explode(',',$options['k']);
$title = $options['t'];
$publish = ($options['p']) ? $options['p'] : 1;
$stdin = file_get_contents("php://stdin", "r");
// posting blog
$params = array();
$contruct_params = array(
'title'=>$title,
'description'=>"$stdin",
'mt_allow_comments'=>1,
'mt_allow_pings'=>1,
'mt_keywords'=> $tag,
'categories'=> $categories
);
$request_params = array(
'1',
$username,
$passwd,
$contruct_params,
$publish
);
$request = xmlrpc_encode_request("metaWeblog.newPost", $request_params);
$context = stream_context_create(array('http' => array(
'method' => "POST",
'header' => "Content-Type: text/xml",
'content' => $request
)));
$file = file_get_contents($url, false, $context);
$response = xmlrpc_decode($file);
if (xmlrpc_is_fault($response)) {
echo $response['faultString'];
exit(1);
}
else {
echo "article posted successfully";
exit(0);
}
?>

anyone interested in the code can get it here

As you can see, the script is a quick draft. There are a few things that I can improve on it. First of all, I can do more work on validating the input arguments or making getopt work better. Then, instead of hardcoding the categories, I can retreive all categories using the function ‘metaWeblog.getCategories’. I can also ping technorati after every post. There is a good possibility of expanding the script and create a class that make use of other API functions, like ‘metaWeblog.getPost’ and ‘metaWeblog.deletePost’… and lastly, the command line can be more user friendly with more interactive prompts. The possibilities are limitless.

Time to test

I call the script ‘postblog’ and put in under my bin directory, ie ~/bin. Then display the help to see the category options.


[user@web ~]# postblog
Usage: postblog -t [title] -c [categoriesNo] -k [tags] -p [publish]
category choice: [0. Blog] [1. Think Linux] [2. Think Network Monitoring] [3. Think Networking] [4. Think Technology] [5. Think PHP] [6. Think Regular Expression] [7. Think SEO] [8. Think Software] [9. Think SQL] [10. Other Interest]
eg. postblog -t "blog title" -c 1,3,4 -k "php,linux" -p 0

I wrote my blog in vim and save the file as ‘myblog’. To publish the article in wordpress under the ‘Blog ‘, ‘Think Linux’ category with blogging and technology tags,


[user@web ~]# cat myblog | postblog -t "Command Line Blogging" -c 0,1 -k blogging,technology
[user@web ~]# article posted successfully

Yay, it works!

What’s Next

Perhaps a bash version of the script or further improvements on the php current script.

Search Engine Rankings With PEAR SOAP

Google implemented a search API in 2002 for web developers to do Google search within their own website. By using SOAP (Simple Object Access Protocol) procedures, the API allows the search and results retrieval to run in the background.

I have implemented a google search function in this website using PEAR SOAP. The script loops through google searched results to find how well you rank for certain keywords in google.

/google_api_search.php

download the source code – google api search with pear soap

Online Calendar Using PHP and AJAX

This calendar plugs in seamless with any content management system you are using. With Ajax, your screen need not be refreshed. The functionality can be easily extended using inheritance.

The full demo can be seen here – PHP Ajax Calendar and source code is here – Simple PHP Calendar Source.

Web 2.0 online picture framing application

Yourframer is finally launched after putting in alot of hardwork. The program is designed with flexibility and expandability in mind. Anyone can simply transfer an image from anywhere and start framing almost instantaneously by inserting a simple javascript in their site. Say, for example in my flickr album, I can frame any high res image with a click… pretty cool huh?

The program demonstrates strong integration of Javascript, Ajax, Prototype tookit, OO PHP, Smarty and MYSQL. Go over to www.photobox.co.uk and follow the link to try and frame an image online. Special attention is paid to usability and navigation. At the time of writing, the graphics is still doggy at the moment but some designers can probably do the job better. I felt a great sense of achievement and some relief finally… Cheers.

Ajax and search engine optmisation

Ajax is good at displaying different content in the same url. There is no way for search engines to index pages like that. What search engines need are unique urls. No matter how long the url, as long as they are unique, it is all good.

I heard there were some development in making search engines read ajax code… in the mean time, i dont think it will work after doing some experiments in a few websites. The inability for search engines to follow ajax hyperlinks is a major disadvantage to Web 2.0.

The are a few solutions and it seems that the most obvious one is to use utilise a javascript function to return false in the onclick attribute. That way, the page will remain static while search engines will still be happy. Search engines see the “a href”, ignores the “onclick” and follow the links… cool. The ugly side of it: All the hyperlinks look ugly.