Home Private Messages Search
CPG Dragonfly™ CMS Dedicated Server & Bandwidth Sponsored by DedicatedNOW
 
Wiki ⇒ v9 Developer's Manual ⇒ Porting Applications to CPG Dragonfly CMS™ ⇒ Part One of Porting Applications

Wikiv9 Developer's ManualPorting Applications to CPG Dragonfly CMS™ ⇒ Part One of Porting Applications  

Glossary
The Project
Install
Upgrading or Switching
Dragonfly admininistration
Dragonfly public view
Dragonfly Themes
Build local server
Running Dragonfly CVS
Tips and Tricks
Rules & Regulations
v9 Developer's Manual
v10 Developer's manual
13.4.1: Part One of Porting Applications Parent

Section 1: Database Queries

Database queries are the heart of just about every application written for a CMS such as CPG Dragonfly CMS™, therefore they come first.

SECURITY:
Some applications will run without converting queries to the abstraction layer, however the abstraction layer adds a level of protection, error reporting, and debugging that is essential to the workings and security of CPG Dragonfly CMS™. Not converting queries is *NOT* an option when porting an app to DragonflyCMS™.

Over the years, methods for accessing data in a database has changed drastically. Therefore, if the module you are porting is not kept current with current coding practices, this is where you will spend most of your time. CPG Dragonfly CMS™ uses the "database abstraction layer" method for querying the database, so every query must be converted to this newer method if it is not already coded that way. Other methods of querying data that you will more than likely see are what I will call the "mysql API call method" and the "DBI sql layer method." Conversions for these will be outlined below:

Converting MySQL API Calls to the Database Abstraction Layer:

Consider the following bit of code:

PHP:
    mysql_connect("localhost", "mysql_user", "mysql_password") or
    die(
"Could not connect: " . mysql_error());
    
mysql_select_db("mydb");
    
$result = mysql_query("SELECT id, name FROM mytable");
    while (
$row = mysql_fetch_array($result, MYSQL_NUM)) {
        
printf("ID: %s  Name: %s", $row[0], $row[1]);
    }
    
mysql_free_result($result);


Now we see it rewritten using the abstraction layer:

PHP:
       global $db;   // $db is the abstraction layer access object.
       
$result = $db->sql_query("SELECT id, name FROM mytable");
       while (
$row = $db->sql_fetchrow($result)) {
           
printf("ID: %s  Name: %s", $row['id'], $row['name']);
       }
       
$db->sql_freeresult($result);


For starters, we removed the mysql_connect() and mysql_select_db() calls as they are not needed when using the abstraction layer. After that, we give our application access to $db, the abstraction layer database access object. Since this object is in the global scope of the program, you will need to declare it global *IF* you are accessing it from within a function.

Next, we converted the query to the $db->sql_query() format. This is fairly straight-forward.

Notice how when we converted mysql_fetch_array() to $db->sql_fetchrow(), we only pass the $result parameter to $db->sql_fetchrow. The abstraction layer handles the rest for us.

Finally, we converted mysql_free_result() to $db->sql_freeresult(). It's good coding practice to free the results once you are done as it returns resources temporarily used by the database query back to the operating system. Many applications you see will *NOT* free results. This is simply poor coding practices and should be corrected while porting apps to CPG Dragonfly CMS™.

Converting DBI SQL Layer calls to the Database Abstraction Layer:

Take a look at this code:

PHP:
    global $dbi;
    
$sql = "select com_id, userid, date, comments, score from ".
           
$prefix."_MReviews_comments where rid='$rid' ORDER BY date DESC";
    
$result = sql_query($sql, $dbi);
    while(list(
$com_id, $uname, $date, $comments, $score) = sql_fetch_row($result, $dbi)) {
       echo
"on $date, $uname left the comment $comments\n";
    }


Now see it rewritten using the abstraction layer and cleaned up a bit:

PHP:
    global $db; // $db is the abstraction layer access object.
    
$sql = "SELECT com_id, userid, date, comments, score FROM ".
           
$prefix."_MReviews_comments WHERE rid='$rid' ORDER BY date DESC";
    
$result = $db->sql_query($sql);
    while (
$row = $db->sql_fetchrow($result)) {
        
$com_id = $row['com_id'];
        
$uname = $row['uname'];
        
$date = $row['date'];
        
$comments = $row['comments'];
        echo
"on $date, $uname left the comment $comments\n";
    }
    
$db->sql_freeresult($result);


To convert this block of code, we started out by removing the global $dbi declaration and replaced it with $db. This gives us access to the $db abstraction layer object.

Next, we cleaned up the query a bit by using upper-case letters for all SQL language directives used in our query. This makes the query easy to read as it is very easy to differentiate between SQL STATEMENTS and variables.

We then converted the sql_query() call to $db->sql_query(). Notice how we removed the $dbi as the second parameter to the query. This is important as DragonflyCMS™ uses additional parameters passed with the query for debugging purposes.

Now is where the fun begins.

The original loop around the call to sql_fetchrow() reads (in english) something like "while you are getting a record from the database, please take the array returned by the sql_query call and use it to define these 4 variables: $com_id, $uname, $date, $comments."

SECURITY:
This works well in practice, however the more secure method of doing this is to always use "$row = $db->sql_fetchrow($result)" as seen in the rewrite.

If, for some odd reason the query is able to be altered and alternate data is retrieved from the database, and attacker could easily use the list($variables) access method to retrieve information from your database and display it on the screen (or in the email, or whatever).

Next, you will see that we add our own variable assignments from the $row[] array that is returned by the call to $db->sql_fetchrow(). Alternately, those lines could be removed and the echo statement could be changed to read:

PHP:
echo "on ".$row['date'].",".$row['uname']." left the comment ".$row['comments']."\n";


Either method is acceptable and is generally left up to the preference of the programmer.

Database Conclusion:
Now that we've seen the two most common methods of database access converted to the abstraction layer, here is a table that shows various commands under the different methods and how they would look using the abstraction layer:

Code:

+----------------------------------------------------------------------------+
| MySQL API Method               | $db abstraction layer method              |
+----------------------------------------------------------------------------+
| mysql_query($sql)              | $db->sql_query($sql)                      |
| mysql_fetch_array($res, TYPE)  | $db->sql_fetchrow($res)                   |
| mysql_fetch_assoc($res)        | $db->sql_fetchrow($res)                   |
| mysql_numrows($res)            | $db->sql_numrows($res)                    |
| mysql_affected_rows($res)      | $db->sql_affectedrows($res)               |
| mysql_num_fields($res)         | $db->sql_numfields($res)                  |
| mysql_field_name($res, $index) | $db->sql_fieldname($index, $res)          |
| mysql_field_type($res, $index) | $db->sql_fieldtype($index, $res)          |
| mysql_fetch_rowset($res)       | $db->sql_fetchrowset($res)                |
| mysql_fetch_field($res, $index)| $db->sql_fetchfield($index, $rownum, $res)|
| mysql_data_seek($res, $rownum) | $db->sql_rowseek($rownum, $res)           |
| mysql_insert_id($res)          | $db->sql_nextid($res)                     |
| mysql_free_result($res)        | $db->sql_freeresult($res)                 |
| mysql_error($res)              | $db->sql_error($res)                      |
| mysql_connect()                | un-needed                                 |
| mysql_select_db()              | un-needed                                 |
+----------------------------------------------------------------------------+

+----------------------------------------------------------------------------+
| DBI Layer Method               | $db abstraction layer method              |
+----------------------------------------------------------------------------+
| sql_query($sql, $dbi)          | $db->sql_fetchrow($sql)                   |
| sql_num_rows($res)             | $db->sql_numrows($res)                    |
| sql_fetch_row($res)            | $db->sql_fetchrow($res)                   |
| sql_fetch_array($res)          | $db->sql_fetchrow($res)                   |
| sql_fetch_object($res)         | $db->sql_fetchrow($res)                   |
| sql_free_result($res)          | $db->sql_freeresult($res)                |
+----------------------------------------------------------------------------+

As you can see, the $db method not only offers better security and debugging features, it is also more flexible than the DBI sql layer.




Section 2: index.php, modules.php and admin.php vs getlink() and adminlink().

In CPG Dragonfly CMS™, the various entry points for your website are configurable via the $adminindex and $mainindex variables in config.php. Additionally, modules.php is no longer used.

DragonflyCMS™ accomplishes this via two functions: adminlink() and getlink().

When porting an application, you should convert all references to "admin.php", "index.php", and "modules.php" to their CPG Dragonfly CMS™ function equivalents.

getlink() is a function that returns the path to the main entry point of the website, usually "index.php". And since all "modules.php" traffic is now routed through "index.php", you use getlink() for that also.

adminlink() returns the path and filename to your administration menu, traditionally "admin.php".

By converting links to their functional counterparts, you are ensuring that your newly ported module will work properly under all configurations.

These two link functions also accept additional arguments to be passed to the URL as a parameter.

Some examples of link conversions follow:

Code:
Before: echo "<a href=\"index.php\">Return</a> to the main page.<br>";
After:  echo "<a href=\"" . getlink() . \">Return</a> to the main page.<br>";

Before: echo "<a href=\"admin.php\">Return</a> to the admin section.<br />";
After:  echo "<a href=\"" . adminlink() . \">Return</a> to the admin section.<br />";

Before: echo "<a href=\"modules.php?name=Your_Account&op=userinfo\">Your Account</a><br />";
After:  echo "<a href=\"".getlink("Your_Account&amp;op=userinfo)."\">Your Account</a><br />";

That last one requires a bit of explaining. As we noticed before, we used getlink() to direct the user at what is traditionally "index.php". Here we used the same function for "modules.php". Why? Because "modules.php" was removed from CPG Dragonfly CMS™ and all access passes though "index.php" now.

Additionally, you'll note that there is no "name=" added to the extra URL parameters. That is correct as getlink() figures out what you are doing and silently prepends the "name=" parameter to the URL.

Lastly, you'll notice that the "&" symbol was replaced with "&a mp;" (spaced so it won't parse). This is to ensure that your application creates HTML 4.01 Transitional compliant output.

How about a more complex example (mixed HTML and PHP code)?

Code:
Before:
<html><head>welcome to <?php echo $sitename; ?> </head></title>
<a href="index.php">click here for them main page</a>

After:
<html><head>welcome to <?php echo $sitename; ?> </head></title>
<a href="<?php echo getlink(); ?>">click here for them main page</a>

While it may not be proper HTML coding, it works. There is no reason not to use getlink() and adminlink() in your code!

Using getlink() and adminlink() instead of their hard-coded counterparts ensures that your port will continue to work as additional changes are made to the CPG Dragonfly CMS™ core system. These functions are located in "linking.php" in the CPG Dragonfly CMS™ /includes/functions/ folder.

Since CPG Dragonfly CMS™ gives you the option to use LEO (Link Engine Optimization), the use of getlink() and adminlink() will also properly convert your links to either method (LEO or non-LEO links), without having to think about whether or not you're using LEO. LEO converts URL's into search engine friendly link, using .html extensions, rather than .php. Examples of LEO and non-LEO links are below.

Code:
Standard link:
yoursite.tld/index.php?name=Forums

LEO link:
yoursite.tld/Forums.html





Section 3: How to Make a CPG Dragonfly CMS™ Auto-Installer

The modules section of the admin panel in CPG-Nuke has the option to automatically set up an installed module. This removes a lot of the frustration involved in installing a module and instead, turns it into a "click to install" process.

To accomplish this feature, CPG Dragonfly CMS™ uses an installation file called "cpg_inst.php" that is located in the modules' main folder. For example: modules/MyApp/cpg_inst.php. A few things are necessary for Dragonfly to "see" this file. 1) an index.php file must also be present in the directory. 2) the directory for "MyApp" must be identical to the class/function in wording (see below for further explanation).

A trimmed down version of a cpg_inst.php file is this:

PHP:
######################################################
# File to install MyApp module
# This file is called automatically by Admin->Modules
######################################################
if (!defined('ADMIN_MOD_INSTALL')) {  exit; }

class
MyApp {
    var
$radmin;
    var
$version;
    var
$modname;
    var
$description;
    var
$author;
    var
$website;
    var
$prefix;
    var
$dbtables;

// class constructor
    
function MyApp() {
        
$this->radmin = true;
        
$this->version = '2.0.0.0';
        
$this->modname = 'MyApp';
        
$this->description = 'Use the MyApp Module (it was ported properly!)';
        
$this->author = 'AUTHOR_NAME';
        
$this->website = 'AUTHOR_SITE';
        
$this->prefix = strtolower(basename(dirname(__FILE__)));
        
$this->dbtables = array('myapp_data');
    }

// module installer
    
function install() {
        global
$installer;

        
$installer->add_query('CREATE', $this->prefix.'_myapp_data', "
            pid int(10) NOT NULL auto_increment,
            title varchar(64) NOT NULL default '0',
            PRIMARY KEY  (pid)"
, '');

    
$installer->add_query('INSERT', $this->prefix.'_myapp_data', '1, "My Title"');

        return
true;
    }

// module uninstaller
    
function uninstall() {
        global
$installer;

        
$installer->add_query('DROP', $this->prefix.'_myapp_data');

}
?>

The cpg_inst.php is actually a class containing three things: A description of the module, an install function, and an uninstall function.

The $prefix is used to make it easier to have multiple instances of one module (if necessary). Your tablenames will have the module folder name (lowercase) as a prefix (e.g. "cms_modulefoldername_table2").

The $description is used to identify your module on the modules administration panel before it is installed. And guess what? The install and uninstall function perform the needed steps to install and uninstall the module.

The $dbtables simply informs the backup system which tables are associated with this module.

One thing is *very* important in this file. You'll notice that there are two statements, "class MyApp{}" and "function MyApp{}". Both of these statements should be changed to reflect your application's name. Also, remember that the directory name for your application should also be identical as well for Dragonfly to "see" the cpg_inst.php file.

Generally, the installation process for a module will include creating some database tables and seeding them with some basic data. Traditionally, this would be handled by an ".sql" file that the user would need to use to manually update their database tables. DragonflyCMS™ allows this to be handled by the install process in the cpg_inst.php file and reduce the number of problems associated with end users not knowing how to manually run a database query.






Section 4: File Locations, File Locations, and File Locations

In CPG Dragonfly CMS™, the directories are protected so that direct access to executable (.php) files is not allowed outside of certain directories. If you're porting a module, you should know that modules/ is one of those directories.

This basically means that at no point are you allowed to point a URL at the modules directory to run an application.

For example, the URL: localhost/modules/MyApp/print.php?id=12 is not allowed. The CPG Dragonfly CMS™ Security Module does not and will not allow for it. It's a security thing and you're just going to need to deal with it as best you can.

This is especially important as a feature of PHP-Nuke (not DragonflyCMS™!) is to include a file called "copyright.php" in each module's directory that will create a link on the modules' page to a copyright notice. Remember that if you are porting a document, you must leave the existing copyrights in place!

In a pinch, you could simply add a link in the app you are porting to the URL listed above (http://.../modules.php?name=copyright) and you should be in good shape.



On to Page Two

 
Updated: Wednesday, September 13, 2006 (12:07:18) by tuta
Created:  Saturday, November 05, 2005 (19:51:45) by tuta

You are seeing squares or questionmarks on this page?

All content of this website is copyrighted by the Creative Commons NC-SA
The logos and trademarks used on this site are the property of their respective owners
We are not responsible for comments posted by our users, as they are the property of the poster.
Our server runs on a P3 1.2GHz with 512MB RAM with no accelerators
Support GoPHP5.org
Interactive software released under GNU GPL, Code Credits, Privacy Policy