Programming PHP-Nuke

Exclusive offer: get 50% off this eBook here
Building Websites with PHP-Nuke

Building Websites with PHP-Nuke — Save 50%

A practical guide to creating and maintaining your own community website with PHP-Nuke

$20.99    $10.50
by Douglas Paterson | April 2010 | MySQL Content Management Open Source PHP

In the previous article of the series by Douglas Paterson, author of Building Websites with PHP-Nuke, we transformed the look of the Dinosaur Portal with the help of a new PHP-Nuke theme. In this article, which is the concluding article of the article series, we will look at programming PHP-Nuke. Specifically, this means creating new blocks and modules. Before we get stuck into that, we will have a look at what actually happens inside PHP-Nuke when a page is requested by a browser.

After that, we will create a new block, a better version of the Dinosaur of the Day block we created in Article 4. That, if you recall, was a static HTML block, and we had hard-coded the image of the dinosaur and its title into the block. Here we will create a block that takes the image to display and title of the dinosaur from the database. This will introduce us to data access in PHP-Nuke, a topic that you will use a lot as you begin to code more with PHP-Nuke.

After a quick look at the file and folder structure of a module, we then begin creating a new module for PHP-Nuke. This module will allow items of content to be submitted for modules that do not support user-submitted content. In this article, we will code functionality for users to submit encyclopedia entries for administrator approval. However, this will not involve making any modifications to the Encyclopedia module, and can be extended to cover other modules as well.

What Happens When a Page is Requested?

Let's see what happens when a visitor wants to view an article on the site, and the process that PHP-Nuke goes through to construct the page containing this information. Viewing an article means that the visitor will be navigating to a page similar to this:

http://localhost/nuke/modules.php?name=News&file=article&sid=1

The page requested by the visitor is modules.php, so let's have a look at that. Note that although there are many PHP files in the PHP-Nuke installation, there are only four files actually requested in the normal course of interaction with the site:

  • index.php
  • modules.php
  • admin.php
  • backend.php

The other files in the PHP-Nuke installation are used by these files when required.

Where Does PHP-Nuke Get Information From?

PHP-Nuke is able to collect information about what module the visitor wants to see, what operation they want to perform, and details of the user from request variables coming from these places:

  • The query string in the URL: The URL of the page holds information that tells PHP-Nuke which module to select, which part of the module to use, what item of content to show, and so on. The query string information is used to select the page from the system that needs to be shown to the visitor.
  • Posted variables: When a user enters information in a form, and submits this back to the server, this information will be available to PHP-Nuke. This posted information is how PHP-Nuke gets input from the user to create items of content, enter terms to search for, and so on.
  • Cookie variables: There is user account information stored in a cookie (and administrator account information if the user has such an account). This is used to identify the user, so they do not have to keep logging on every time they view a page or come to the site. When the user logs out, this information is deleted from the cookie.

The information that PHP-Nuke gets from these sources has to be treated very carefully within the system. These sources are the only means through which visitors communicate with the site, and are also the channels through which hacks or attacks might be conducted on the site. The patches we applied in Article 2 while installing the system address precisely this issue, and they make sure that the data PHP-Nuke collects from a visitor is in exactly the right form for working with.

Requesting a Page

Once the modules.php page is requested, the first step followed is to include the mainfile.php file. This file does the following things:

  • It checks and processes the request variables (namely the input to the application), to avoid possibly harmful tags, or other indicators of some form of SQL injection attack.
  • It creates global variables for each request variable.
  • It sets up a connection to the database.
  • It gets the site configuration such as the site name, site logo, and so on, from the database.

The mainfile.php file also contains a number of core functions such as checking if the user is logged in or is an administrator, choosing the blocks to display, and filtering text, among others. These will be used at different points in the creation of the page.

After this file has been included, the next thing to happen in modules.php is that PHP-Nuke gets the requested module from the $name global variable, which corresponds to the name query string variable (as in modules.php?name=News), and checks if this module is active. If the module isn't active, and the visitor isn't an administrator, a 'Module Not Active' message is displayed, and the page output is done.

If the module is active, then PHP-Nuke checks if the visitor has rights to access this module. PHP-Nuke checks to see if the access is restricted to a particular user group, and if so, is the user a member of that group? PHP-Nuke also checks if the module is for subscribers only, and if so, is the user a subscriber to the site? If the visitor doesn't have the right permissions to view the module, then a 'No Access' message is displayed, and the page output is done.

If the module selected by the visitor is active, and they do have permission to view it, then PHP-Nuke can get on with passing control to that module. Control is passed to the selected module by attempting to include the index.php file in the folder of the selected module. However, if there is a file variable in the query string, then the file with that name is included instead. If these files can't be found, a 'Module Cannot Be Found' error is displayed to the visitor.

Thus if the user requests a page like modules.php?name=News&file=article&sid=1, the article.php file in the News folder will be included by PHP-Nuke. If the user requests a page like modules.php?name=News&sid=1, then the index.php file in the News folder will be included. Attempting to request a page like modules.php?name=News&file=nosuchfile returns a page with a 'No such page' message, since there is no file called nosuchfile.php in the News folder. The 'No such page' message is generated by PHP-Nuke, since it's in control of the process.

If the user has selected an active module for which they have view permission, and are requesting a page that is part of the module, then control passes to the module, and it's up to that module to do its work and create the page. We'll see how this works later in the article, but for now, our overview of how PHP-Nuke gets the page creation underway is complete.

Seeing how PHP-Nuke works isn't particularly exciting, what is more exciting is seeing how we can extend the power of PHP-Nuke by creating new blocks and modules. Along the way, we'll see most of the components required for 'programming' with PHP-Nuke, and you'll get a good idea of how to go about your own development projects.

Creating a Block

Our development efforts begin with creating a File block. A File block is a PHP script that is stored in the blocks folder. It must have a filename of the form block-NAME.php, where NAME will be used by PHP-Nuke as the title for the block. The filename should not contain any spaces.

The goal of a block is simple. It just has to create one variable, $content, that holds the content of the block. After that, the PHP-Nuke core will bring the theme into play to take care of displaying the block.

The block we will create is a better version of the Dinosaur of the Day static HTML block we created in Article 4. The block will display the name of the Dinosaur of the Day, and a thumbnail image of the lucky lizard. However, on the next day, a different dinosaur will be chosen, and the block display will be updated.

This is how the block works:

  • We will create a database table to hold the date, the title of the dinosaur for that day, and a link to the thumbnail image of that dinosaur.
  • We will create a text data file that will contain the name of a dinosaur and a link to its thumbnail image on each line. The data in this file will be the dinosaur pool from which the dinosaur of the day is chosen at random.
  • When the block code is executed, it will look in the database table to see if there is any information for the current date. If there is, it will retrieve it and build the block output.
  • If there is no information for the current date, the data from the text file will be loaded in. One of the entries in that file will be selected at random, and that data will be inserted into the database. This will become the Dinosaur of the Day. That data will then be used to create the block output.

We will use the text file to hold the 'Dinosaur of the Day' candidates rather than a database table so that we do not have to create a separate administration feature to add these details. To add more dinosaurs to the list, we simply upload a new version of the text file.

Make sure that you copy the dinosaur thumbnails from the code download into the \images\dinosaurs\tnails folder of your PHP-Nuke installation root.

Time For Action—Creating the Database Table

  1. Open up phpMyAdmin in your web browser, and select the nuke database from the drop-down list in the left-hand panel.
  2. Click on the SQL tab, and enter the following code into the textbox, then click on Go.
CREATE TABLE dinop_dinoportal_dotd (
id INT( 10 ) NOT NULL AUTO_INCREMENT ,
day VARCHAR( 16 ) NOT NULL ,
title VARCHAR( 128 ) NOT NULL ,
image VARCHAR( 250 ) NOT NULL ,
PRIMARY KEY ( 'id' )
) TYPE = MYISAM ;

What Just Happened?

We just created our database table. There is only one table needed, with a simple design. There are four fields in the table. The id field will hold an auto-incrementing unique numerical value and the other fields will hold the current date, the title of the dinosaur, and the link to the image of the dinosaur.

Time For Action—Creating the Text File

  1. Open up your text editor, and enter the following:
    Tyrannosaurus Rex,images/dinosaurs/tnails/tyro.gif
    Stegosaurus,images/dinosaurs/tnails/stego.gif
    Triceratops,images/dinosaurs/tnails/triceratops.gif
  2. Save this file as dotd_list.txt in the blocks folder.

What Just Happened?

The dotd_list.txt file will be the data source for choosing a new Dinosaur of the Day image. You will notice that we are storing the data here in the form 'name of the dinosaur', 'path to the image', so it will be easy to extract the information when we need it.

Time For Action—Creating the Block Code

  1. Open up your text editor, and enter the following code into a blank file:
    <?php

    if ( !defined('BLOCK_FILE') )
    {
    Header("Location: ../index.php");
    die();
    }

    global $prefix, $db;

    $today = date('d-m-Y');

    $sql = "SELECT * from ".$prefix."_dinoportal_dotd WHERE day='$today'";

    $result = $db->sql_query($sql);
    $content = "";
    $dino_title = "";
    $image = "";

    $numrows = $db->sql_numrows($result);
    if ($numrows)
    {
    $row = $db->sql_fetchrow($result);
    $dino_title = $row['title'];
    $image = $row['image'];
    }
    else
    {
    $filename = "blocks/dotd_list.txt";
    $possibles =@ file($filename);

    if ($possibles)
    {
    $choice = rand(1, count($possibles));
    $imp = explode("," , $possibles[$choice-1]);
    $dino_title = $imp[0];
    $image = $imp[1];
    $sql = "INSERT INTO ".$prefix."_dinoportal_dotd(day,title,image)
    VALUES ('$today', '$dino_title', '$image')";
    $result = $db->sql_query($sql);
    }
    $choice = rand(1, count($possibles));

    $imp = explode("," , $possibles[$choice-1]);
    $dino_title = $imp[0];
    $image = $imp[1];
    }
    if ($dino_title)
    {
    $content = "Today's dinosaur
    is:<br><center><b>$dino_title</b><center><br>";
    $content .= "<center><img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=\"$image\"
    alt=\"$dino_title\"></center><br>";
    }
    ?>
  2. Save this file as block-DinosaurOfTheDay.php in the blocks folder of your PHP-Nuke installation.

What Just Happened?

We just entered the code for the Dinosaur of the Day block, and we'll step through it now.

This first part of the code stops this file being requested directly by a visitor— the BLOCK_FILE constant is defined in mainfile.php, and without that constant being defined, the visitor would be redirected back to the homepage of the site. Block files are never requested directly by the visitor, they are included by PHP-Nuke. These first few lines of code are found in every block file:

if ( !defined('BLOCK_FILE') )
{
Header("Location: ../index.php");
die();
}

Now we can get started. First, we set ourselves to access some of the global variables of the application, and we will have our first look at the objects to access data from the database. The only global variables we need here are the ones for data access—$prefix, which holds the value of the database tables prefix, and $db, which is used to actually perform database operations.

global $prefix, $db;

Next, we grab today's date, formatted as digits like this 24-05-2005.

$today = date('d-m-Y');

Now we set up the SQL statement to retrieve the information corresponding to this date from the database:

$sql = "SELECT * from ".$prefix."_dinoportal_dotd WHERE day='$today'";

Now we execute the SQL statement:

$result = $db->sql_query($sql);

It is possible that there is no data corresponding to today's date, so we check the number of rows returned from this last query. If there are zero rows, there will be no information.

$numrows = $db->sql_numrows($result);

If there are some rows returned, we can start creating the block output. We use the sql_fetchrow() method to retrieve a row of data from the result of the query. This row is returned as an array, and we set some variables from the array. We'll only grab one row. If for some reason, there is more than one entry for today's date, we simply ignore the others.

if ($numrows)
{
$row = $db->sql_fetchrow($result);
$dino_title = $row['title'];
$image = $row['image'];
}

Now we move on to the situation where there is no information for today's date, and we have to create it. The first thing we do is to read the contents of the dotd_list.txt file into an array—there will be one entry in the array for each line in the text file. However, we have to consider what will happen if there is some kind of problem reading the file.

else
{
$filename = "blocks/dotd_list.txt";

Note that the path to the file for the dodt_list.txt file is \blocks\dotd_list.txt. This may seem strange, since both this file and the block file are in the same blocks folder. However, PHP will be looking for this file from the executing script, which will be one of index.php, modules.php, or admin.php, all of which are outside the blocks folder. Thus we need to add the blocks folder in the path to the dotd_list.txt file.

Now we try to grab the file itself:

$possibles =@ file($filename);

The file function opens the named file, and reads the input into an array called $possibles. The use of the @ character here will suppress any error messages—if there is a problem opening or reading the file, no untidy error messages will be displayed to the user, and execution can continue. Of course, if there is a problem reading the file then there will be a problem with $possibles. So we check this next—if there has been some problem reading the file then $possibles will be false:

if ($possibles)
{

If there is something stored in $possibles, then this check will be passed, and we can proceed to choose one element from it at random. We choose a random number, between 1 and the number of lines in the text file.

$choice = rand(1, count($possibles));

All we have to do now is choose that element from the array (we have to subtract one from the random number because the first element of the array is 0, rather than 1), and then split up that line to get at the title and the path to the image.

$imp = explode("," , $possibles[$choice-1]);
$dino_title = $imp[0];
$image = $imp[1];

We split the line using the explode() function. The explode() function converts a string to an array by splitting it at certain characters. We will split the string at the ',' character, and we get an array with two entries. The first entry is the name of the dinosaur; the second is the path of the image.

Now we have the details of our Dinosaur of the Day, we can add it to the database using an INSERT SQL statement.

$sql = "INSERT INTO ".$prefix."_dinoportal_dotd(day,title,image)
VALUES ('$today', '$dino_title', '$image')";
$result = $db->sql_query($sql);
}
}

At this point, we should have a Dinosaur of the Day, one way or another, and so we can finalize the block output. However, we check the value of the $dino_title variable just in case there has been some problem either retrieving data or creating the new Dinosaur of the Day. If there has been a problem with either of these, there will be no value for the $dino_title variable, and if so, this code will ensure that the block content will remain empty, rather than producing some useless output.

if ($dino_title)
{
$content = "Today's dinosaur is:<br><center><b>$dino_title</b><center><br>";
$content .= "<center><img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=\"$image\"
alt=\"$dino_title\"></center><br>";
}

That's it, our block is complete! The key points of this block were the initial few lines that stopped the file from being requested directly, and this was also our first encounter with the data access code. Another thing to note from this example is the effort we made to ensure that the block output was only created if everything went smoothly. We suppressed errors when trying to read in a file, we checked that the reading of a file had actually given us some data, and then we didn't create any output if there was a problem with dino_title variable, which would be an indicator of some problem.

All this means that if there is a problem, the visitor will not be confronted with lots of error messages, which could disturb the visitor and lead to a poor impression of your site, or even break the layout of the page, or reveal some information about your code that could be used against you.

All that remains now is to set up this File block using the steps we saw in Article 4, and we are away!

Data Access in PHP-Nuke

In the code for creating the block we had a couple of lines with data access functions:

$result = $db->sql_query($sql);
$numrows = $db->sql_numrows($result);
$row = $db->sql_fetchrow($result);

PHP-Nuke uses a 'data abstraction layer', which means that you call functions against an object, which translates them into specific calls against the database of your choice. Generally, MySQL is the database used with PHP-Nuke, but you could choose another database server to power your site. A more pertinent advantage is that you don't need to use database-specific functions to access data in PHP-Nuke; you only need to learn about the functions of the data access object (You will still need to know some SQL to create queries that will be executed on the database).

The code for the data access object is found in the file \db\mysql.php. In fact, the db folder contains code for different types of database server, but this file is the one selected by default by PHP-Nuke for working with the MySQL database server.

The data access object is a genuine object, that is, it's an instance of a class, sql_db in this case. Classes are one of the basics of object-oriented programming, but other than this instance PHP-Nuke does not make much use of object-oriented programming. A discussion of object-oriented programming in PHP is beyond the scope of this article series, and it won't particularly help here since PHP-Nuke makes so little use of it. All that we need to know is how to access the methods (functions) of the data access object.

Object-oriented programming is covered in more detail in any book on PHP programming, and you can read an article about it at http://www.devarticles.com/c/a/PHP/Object-Oriented-Programming-in-PHP/.

The data-access object provides a number of methods that you can use to execute a query, retrieve a row of data, check the number of rows returned by the query, or get the last inserted auto-increment field. Working with the object follows a similar process to the standard way of working with data in PHP using functions like mysql_query() or mysql_fetch_field().

To access data in PHP-Nuke, you will need two global variables, $prefix and $db. The $prefix variable holds the table prefix of the database tables, and this needs to be used in your SQL queries. The $db variable is the data access object itself.

In our block example, we had these lines to create a SQL query and then execute it:

$sql = "SELECT * from ".$prefix."_dinoportal_dotd WHERE day='$today'";
$result = $db->sql_query($sql);

Note the $db->sql_query() syntax. This syntax is used in PHP to call a method on an object, in this case the sql_query() method of the $db object. The sql_query() method executes an SQL query as its name suggests. You provide a string with the query that's to be executed as a parameter.

Following the execution of a query, you can retrieve a row using the sql_fetchrow() method:

$row = $db->sql_fetchrow($result);

This method returns an array, and you can refer to the fields in the data using $row['fieldname'], as we do in the block example to get the title and image fields:

$dino_title = $row['title'];
$image = $row['image'];

If you want to insert or update data, you need to create the relevant SQL query and then use the sql_query() function to do it:

$sql = "INSERT INTO ".$prefix."_dinoportal_dotd(day,title,image)
VALUES ('$today', '$dino_title', '$image')";
$result = $db->sql_query($sql);

This is only a small selection of the methods of the data access object. Another interesting one is the sql_nextid() method, which you can use after an INSERT statement to get the value of the last auto-increment field created. However, these are the methods that you will see the most of as you look around the code in PHP-Nuke.

Module File and Folder Structure

Before we get started creating a new module, let's have a look at the file structure of a typical module. A module is simply a collection of files (usually only PHP files) contained in a folder that goes in the modules folder in the root of the PHP-Nuke installation. The name of the folder is the name that PHP-Nuke will recognize the module by.

However, we can't just place the files into the module folder in any order. There is an organization of files, subfolder names, and filenames that modules need to follow in order to function properly with PHP-Nuke.

The image below shows the contents of the News module folder:

Programming PHP-Nuke

We have already seen how PHP-Nuke switches between files in the module folder based on the value of the file query string variable. If there is no value for this variable, the index.php file of the module is used. Files that sit inside the module folder are the 'front-end' files, which will be used during a standard user's visit to the module.

The code for the administration part of a module resides in the admin folder within the module folder. In earlier versions of PHP-Nuke (before 7.5), the administration code for any module would have to go into the admin folder (the one in the root of the PHP-Nuke installation), and would be mixed with the 'core' administration code. The decision to have a module's administration code contained within the module folder itself means that the module is much more self-contained, keeps people away from the core code itself, and generally makes the module easier to set up, install, and maintain. We'll see more about what is found in the admin folder when we create the administration area of our new module later in this article.

We saw in Article 4 that the Language block allows you to change PHP-Nuke's user interface language. This ability to switch languages is something that has made PHP-Nuke so popular all around the world. This multi-language support is achieved by module developers avoiding coding any 'localizable' text into the module output. Localizable text is text that needs to be translated if a different interface language is selected. Instead of coding the text, PHP constants are used, with the values of the constants defined in a language file. The language files are found in the language folder of the module, and there is a separate language file for each language, each with a filename of the form lang-LANGUAGE.php. All PHP-Nuke needs to do is select the correct file based on the desired language.

Creating a User Submissions Module

Writing a new module allows you to extend PHP-Nuke to get it to do exactly what you want it to do. What we will do here is to create a general-purpose module that will allow users to submit content for modules that do not support user-submitted material. We'll call it UserSubmissions. It will work in the way the Submit News module works for stories:

  • The user will submit the material through a form.
  • The administrator will be notified of the new submission by email.
  • The administrator will be able to see a list of the submitted material in the administration area, and can edit, delete, or approve the material to go into the database.

The point of this module is that it does not touch the modules for which it allows the submission of content; everything will happen in the UserSubmissions module. In this article, we will only code in functionality for users to submit encyclopedia entries. It is straightforward to extend this to allow submissions for the Content module, or questions for the FAQ module.

Conceptually what the module does is to:

  • Present a form to the user similar to the one the administrator would use for entering an encyclopedia entry.
  • Take the user's input and store it in a 'general' way in a single database table.
  • After the administrator checks the input, the data is then stored in the encyclopedia's database tables using the same kind of code that the Encyclopedia module uses.

We will see exactly what the 'general' way we store the data in is later. Basically, the module will take all the parts of the encyclopedia entry—the title, the text, the encyclopedia ID—and put them all together into one bundle, which can then be easily retrieved and broken out to form the individual pieces of data for the administrator to view, approve, or delete.

Rather than presenting the development as a series of steps for you to follow, we will break the code up into various tasks, and then examine each piece of code or activity. You can type in the code as it is presented, although it is probably easiest to grab the example code for this module from the code download, and refer to it as we go.

Module Development Steps

The steps that we will follow to create the module are these:

  • Create the module folder
  • Create the database tables
  • Code the front end (visitor view) of the module
  • Adapt the code for multi-language support
  • Set up module administration
  • Code the administration area

Rather unusually, we're going to start by coding the front end of the site. The reason this is unusual is that modules typically display some kind of data (that is what all the modules we have encountered in the article series do), and you would first need to enter this data into the database. This data is usually entered by the administrator, through the administration interface. With some example data in place, the front end of the site will be able to display it. It will be difficult to test the front end if it is supposed to display data and there is no data to display!

This module does not require any existing data in the database to work. In fact, the data is entered by a standard visitor, and the administrator only has to look at this data, and edit or delete it. There is no facility in the administrator part of the module for the administrator to add new data into the module, which would rather defeat the point of this module! Thus we can start on the front end of the module quite happily.

Let's start off with creating the module folder.

Creating the Module Folder

Create a new folder in the modules folder called UserSubmissions. This will be our module folder. Within this folder, create two new folders called admin and language. The admin folder will contain our administration code, and the language folder will contain the user interface language files. We create another folder, inside the admin folder, also called language. This folder will hold the language files for the module's administration interface.

That's the first step out of the way, so let's move on to the database tables.

Creating the Database Tables

The module has only one database table. The table will be called <prefix>_usersubmissions. You can follow the same steps in phpMyAdmin as we did earlier for creating the block database table to create this table:

CREATE TABLE dinop_usersubmissions (
id int(11) NOT NULL auto_increment,
data text NOT NULL,
parent_id int(11) NOT NULL default '0',
type varchar(36) NOT NULL default '1',
user_id int(11) NOT NULL default '0',
date timestamp NOT NULL default
CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
title varchar(255) NOT NULL default '',
user_name varchar(250) NOT NULL default '',
PRIMARY KEY (id)
) COMMENT='Table for holding user submitted content' ;

Each row in this table will represent a single item of submitted content. The row will be identified by its id field.

With only one table, you may be wondering how this module is going to be able to hold data from different modules. The answer is that the submitted data will be bundled up into a single object, then 'serialized' and stored in the data field. When the data is needed for viewing, it will be unbundled, and 'unserialized' back into a form that can be worked with. The 'title' of the submission will be stored in the title field.

The type of module that we are storing data for will be held in the type field of the row. The details of the submitter will be stored in the user_id and user_name fields. We actually only use the user_name field in this version of the module, but we store both for future use. The date the item is submitted is held in the field called date. This field is a MySQL TIMESTAMP, and whenever a row is inserted, the current date and time will be inserted into the field automatically by the database, so we will not need to record the date and time ourselves.

The final field is parent_id. Recall how an encyclopedia entry belongs to an Encyclopedia; a content page belongs to a Content category; a FAQ question belongs to a particular category, and so on. For each of these types of content, you needed to provide a 'parent' object that the content would belong to. That is where our parent_id field comes in. The ID of the parent object will be stored in this field. For an encyclopedia entry, this will be the ID of the chosen Encyclopedia.

Building Websites with PHP-Nuke A practical guide to creating and maintaining your own community website with PHP-Nuke
Published: November 2005
eBook Price: $20.99
Book Price: $29.99
See more
Select your format and quantity:

The Visitor Code—the index.php File

In this section, we will walk through the code for the visitor part of the module. There is only one file that we will need, index.php.

First we will consider the overall structure of the index.php file, and the parts of the code that every module should contain. Then we will look at the individual parts of code in the index.php file that perform various tasks. The tasks performed in index.php are:

  • Inviting the user to submit an item (an encyclopedia entry in this case)
  • Displaying the form for user input
  • Preparing the user input for storage
  • Storing the user input, and notifying the administrator

Overall Structure of the Module index.php File

Before our detailed exploration of the code, let's have a look at the overall structure of the index.php file. Here is a view of the code listing with the code for each function removed. There are three main parts to the code, with the parts separated by the /* ---- */ lines.

<?php
if (!defined('MODULE_FILE'))
{
die ("You can't access this file directly...");
}

define('INDEX_FILE', true);
$module_name = basename(dirname(__FILE__));
get_lang($module_name);
$pagetitle = "- $module_name";

/* ------------------------------------------------- */

function ShowTypes()
{
...
}

function ShowFormForInput($oid)
{
...
}
function AddEnyclopediaEntry($eid, $title, $text, $language)
{
...
}

function formatEncyclo()
{
...
}

function storeSubmission($type, $storageArray, $parent_id, $title_field)
{
....
}

function get_user_object()
{
....
}
/* ------------------------------------------------- */

switch($op)
{

case "add_encycloentry":
AddEnyclopediaEntry($usr_eid, $usr_title, $usr_text, $usr_language);
break;
case "add":
SubmitContent($oid);
break;
default:
ShowTypes();
break;
}
?>

The first part is standard to every module in PHP-Nuke. The first couple of lines make sure that this file can only be used through the modules.php or index.php file. This check is similar to the one used at the start of the block code we saw earlier. With this check, no one can access the file directly. After that, we define a constant to indicate that we are on the homepage of the module:

if (!defined('MODULE_FILE')) {
die ("You can't access this file directly...");
}

define('INDEX_FILE', true);

The next line gets the module name. Rather than simply typing in the module name, it is taken from the name of the folder containing the current file. The __FILE__ PHP constant contains the full path of the current file, the dirname() function returns the name of that file's parent folder, and the basename() function returns the filename part of this, which in this case is actually the top-most directory.

$module_name = basename(dirname(__FILE__));

Once the module name is defined, it is passed to the get_lang() function, which includes the language file for this module. The module name is also used to set the title of the page:

get_lang($module_name);
$pagetitle = "- $module_name";

Now we will look at the third part of the code in index.php, which contains a construct used in all PHP-Nuke modules.

At the end of the file is a switch-case construct. It checks the value of the $op variable. Depending on the value of this variable, different functions are called within the file. The $op variable holds the value of the op request variable, and determines which operation in the module is to be performed. If the value of $op is add, the SubmitContent() function is called; if the value is add_encycloentry, the AddEnyclopediaEntry() function is called. For any other value of $op (including no value) the ShowTypes() function is called:

switch($op)
{
case "add_encycloentry":
AddEnyclopediaEntry($usr_eid, $usr_title, $usr_text, $usr_elanguage);
break;
case "add":
SubmitContent($oid);
break;
default:
ShowTypes();
break;
}

In this way PHP-Nuke is able to map visitor requests to functions in code. You will notice that some of the functions have parameters passed to them (for example, SubmitContent() has a variable called $oid passed to it). This is a convention followed by modules in PHP-Nuke wherein request variables used in a function are passed to it from the function call in the switch-case construct. If the variables aren't passed to the function, they can still be accessed as global variables from within the function, but we will follow this convention here.

You can read more about the switch-case construct at http://www.php.net/manual/en/control-structures.switch.php.

By convention in PHP-Nuke, this selection process comes toward the end of the module file, since it is easy to locate. You look to the switch-case construct to see what bit of code is executed for a particular module operation.

Tracking Down the Code for a Module Operation

Check the code that was executed when a visitor clicks on a link in the Web Links module. This has a URL of the form:

http://localhost/nuke/modules.php?name=Web_Links&l_op=visit&lid=1

At the bottom of the index.php file in the Web_Links module, you will find the switch-case construct:

switch($l_op) {

Here the variable being checked is $l_op rather than $op. Looking at that switch-case construct, you find the check for visit, and can see what action is taken. In this case, it is a call to a function called visit():

case "visit":
visit($lid);
break;

Now you move up the file to find the visit() function (you can search for function visit( since we are looking for a function definition):

function visit($lid)
{
global $prefix, $db;
$lid = intval($lid);
$db->sql_query("update ".$prefix."_links_links set
hits=hits+1 where lid='$lid'");
update_points(14);
$row = $db->sql_fetchrow($db->sql_query("SELECT url
from ".$prefix."_links_links where lid='$lid'"));
$url = stripslashes($row['url']);
Header("Location: $url");
}

In this way, you are able to see the exact code that is executed when a visitor clicks on a web link. The $lid variable is converted to an integer, the number of clicks stored for that link in the database is increased, the user's point score is updated (with the call to update_points(14)), and then the URL of the web link numbered $lid is retrieved. Finally, the PHP Header() function redirects the visitor's browser to the target of the web link.

The details of the Web Links module aren't important to us now, but the point of this discussion was to illustrate how you can track down the piece of code in a module that is responsible for a particular operation. This is very useful for understanding how modules work, and especially useful if you want to tinker with the existing operation of a module.

Let's continue with our discussion of the functions in the index.php file of the UserSubmissions module.

Inviting the User to Submit an Item

The homepage of the module displays a list of the types of content that the visitor can submit. For this example, visitors can only submit a new encyclopedia entry, although the module can be extended to allow FAQ or Content submissions quite easily. When the visitor visits the homepage of the module (modules.php?name=UserSubmissions), PHP-Nuke will call the ShowTypes() function in index.php.

function ShowTypes ()
{
global $module_name;
include("header.php");
title("Submit a new Item");
OpenTable();
$u = get_user_object();
if (!$u)
{
echo "<center>You need to be a registered user to submit or
edit new items of content<center><br><br>";
CloseTable();
include("footer.php");
exit;
}
echo "Choose the type of content to add<br><br>";

echo "<ul>";
echo "<li><a href=\"modules.php?name=$module_name&amp;op=add&amp;oid=1\">
Submit a new Encyclopedia entry</a></li>";
sssecho "</ul>";
echo "<br>";
CloseTable();
include("footer.php");
}

The function starts with a global statement, so that our function has access to certain variables defined outside our function. Anything defined outside of a function will be defined in the global scope, hence the reason we use the global statement to get at the $module_name variable. This function will offer the visitor a choice of content types to add a new submission for; at the moment, there is only one type, an encyclopedia entry. The ShowTypes() function gets the page output underway:

function ShowTypes()
{
global $module_name;
include("header.php");
title("Submit a new Item");
OpenTable();

The title() function creates a boxed element on the page, displaying a message in a large font. We saw mention of the header.php file and the OpenTable() function in Article 9. The header.php file starts the page output, and renders the page header and the left-hand blocks. The call to OpenTable() defines an enclosing element for our module output.

Before we display a list of possible types of content to offer to the visitor, we need to check if the current user is actually logged in. We will not be allowing anonymous submissions, so the user must be logged in. Of course, this can be achieved by restricting the module to registered visitors only, but if this restriction isn't in place, then the code will break without first checking that the user is indeed a logged in user.

If the user is not logged in, a message will be displayed to them, and the page output will be brought to an end.

$u = get_user_object();
if (!$u)
{
echo "<center>You need to be a registered user to submit or
edit new items of content<center><br><br>";
CloseTable();
include("footer.php");
exit;
}

We have introduced another function here, get_user_object(). Let's talk about that now.

Getting User Information

The information about the current user is got from the get_user_object() function that is defined later in the file. The reason we have put this functionality into its own function is so that we don't have to keep typing the same code over and over again in the file to get user information. The function will return false if there is no user information (when they aren't logged in, even if they have an account), but if the visitor is logged in, it will return an array with some details.

Let's look at the function definition:

function get_user_object()
{
global $user;

The $user global variable holds the user details. This variable is actually stored in a cookie in the visitor's browser, and contains information stored in a form that we have to sort out. First of all, if the $user variable doesn't have a value, we return false for the function, since there is no user information available.

if (!$user) return false;

The next few lines of code in the function are found in many, many places throughout PHP-Nuke and its modules. When the $user variable is taken from the cookie variables, it is a long string, something like this:

Mjp0ZXN0dXNlcjo1ZDljNjhjNmM1MGVkM2QwMmEyZmNmNTRmNjM5OTNiNjoxMDp0
aHJlYWQ6MDowOjA6MDpUaGVEaW5vc2F1clBvcnRhbDozMg

This long string has to be processed and then converted to an array. However, this process is not done only once, for the benefit of the rest of the application. Anytime you want to work with user information you will need to follow this process, which is why we are putting it into its own function. Our function takes care of these details, and we only have to deal with the results.

First, we check if the $user variable is not an array. If it isn't, we make it into one. The string is actually encoded, so it needs to be decoded first. Note that we do not actually change the $user variable itself, we make a copy $user2, and work on that.

if(!is_array($user))
{
$user2 = addslashes($user);
$user2 = base64_decode($user2);

After this decoding, the string will be something like this, and is getting easier to work with:

2:testuser:5d9c68c6c50ed3d02a2fcf54f63993b6:10:thread:0:0:
0:0:TheDinosaurPortal:32

The final step in our function is to split the string at each : character to produce an array.

$user2 = explode(":", $user2);
}

Now we have an array, which we will return from the function:

return $user2;
}

The reason we don't change the $user variable itself into an array is because there are some parts of PHP-Nuke that do the above decoding and splitting process themselves, without checking if $user is already an array. If the variable is already an array, then that process will fail, and the values returned will be unexpected.

The advantage of using our get_user_object() function is that we don't have to worry about any of the details of cleaning and preparing the user information, we just call the function to check if the information is available, and if it is, it will be ready for us as an array.

Returning to our ShowTypes() function, we call the get_user_object() function, and then check the value returned:

if (!$u)
{
...

You should always check that the user information is there before attempting to use it. Once we have the user information as an array, we can get such things as the name of the user, and their user ID. The user ID is the first element of the array, and the user name is the second element.

The remainder of the code in the ShowTypes() function displays a list of the types of content that users can submit, with links to begin the process. Note how the link is built using the $module_name variable rather than typing in the name of the module itself. We are only using one type of content in this article, an encyclopedia entry:

echo "Choose the type of content to add<br><br>";
echo "<ul>";
echo "<li><a href=\"modules.php?name=$module_name&amp;op=add&amp;oid=1\">
Submit a new Encyclopedia entry</a></li>";
echo "</ul>";
echo "<br>";

And finally, the function closes page output, first with the call to CloseTable() and then by including the footer.php file, which takes care of the right-hand blocks and the page footer, in the same way as discussed in Article 9.

Preparing the ShowTypes() Function for Languages

Before we move on to the other functions, we prepare our ShowTypes() function to make use of language constants. You will notice that we used four pieces of text in the ShowTypes() function. We will move these out of the function and into the language file as language constants. We create a file called lang-english.php in the languages subfolder of the UserSubmissions folder and add these lines:

<?php

define("_SUBMITNEWITEM", "Submit a new Item");
define("_CHOOSETYPE", "Choose the type of content to add");
define("_SUBMITNEWENCYCLOENTRY", "Submit a new Encyclopedia entry");
define("_YOUNEEDTOBEAUSER", "You need to be a registered user to submit or
edit new items of content");
?>

This is the English language file, and contains the text that we used in the ShowTypes() function.

We have given the constants names that are very similar to the text that they hold. The final step is to modify the ShowTypes() function to use these constants. The lines with the new constants are shown highlighted:

function ShowTypes()
{
global $module_name;
include("header.php");
title(_SUBMITNEWITEM);
OpenTable();
$u = get_user_object();
if (!$u)
{
echo "<center>"._YOUNEEDTOBEAUSER."<center><br><br>";
CloseTable();
include("footer.php");
exit;
}
echo _CHOOSETYPE."<br><br>";
echo "<ul>";
echo "<li><a href=\"modules.php?name=$module_name&amp;op=add&amp;oid=1\">"
._SUBMITNEWENCYCLOENTRY."</a></li>";
echo "</ul>";
echo "<br>";
CloseTable();
include("footer.php");
}

For the remainder of the code, we will use the language constants from the start, and show the constants afterwards. If you wish, you can begin the process of translating the text into other languages!

This is the result of our module so far. A table that displays the list of content types the user can enter:

Programming PHP-Nuke

There is only one link for the user to click. This will take them to a page of the form:

modules.php?name=UserSubmissions&op=add&oid=1

The value of oid will be used to determine what form they are shown, as we shall now see.

Displaying the Form for User Input

The next function is called ShowFormForInput(). This function shows the form for the user to enter their content, and is executed when the op request variable has the value add. The function checks the value of the $oid variable to see what type of content they want to enter. Before we check the value of $oid, we explicitly convert it to an integer with the intval() function.

We have only one option for the value of $oid:

function ShowFormForInput($oid)
{
global $pagetitle, $module_name;
$oid = intval($oid);
if ($oid==1)
{
$pagetitle = "- $module_name : "._ADDENYCLOENTRY;
include("header.php");
title (_ADDENYCLOENTRY);
OpenTable();

formatEncyclo();

CloseTable();

include("footer.php");
}
else
header("Redirect: modules.php?name=$module_name");

}

Anything other than the value 1 for $oid will have the user redirected to the module homepage. PHP's header() function is used to perform the redirection.

The ShowFormForInput() function doesn't display the form itself, it calls the formatEncyclo() function. This function actually displays a form for the user to enter their new entry.

Here we are following a coding convention whereby a function called from the switch-case construct at the end of the file has the first letter of its name capitalized. A function that is called from within one of these functions will have the first letter of its name in lower case, such as formatEncyclo(). This simple convention makes it easy to distinguish between functions that correspond to user operations of the module (ShowFormForInput()), and the functions that support them (formatEncyclo()).

The ShowFormForInput() function uses a new language constant, _ADDENYCLOENTRY:

define("_ADDENYCLOENTRY", "Add a New Encyclopedia Entry");

The form for entering the encyclopedia entry itself is identical to the form used by the administrator to submit an encyclopedia entry. The first part of the function creates a form with a textbox for the title of the encyclopedia entry, and a text area for the text of the entry. Note the name of the title textbox is usr_title, and the name of the entry text area is usr_text.

function formatEncyclo()
{
global $db, $prefix;
global $module_name;
echo " <form action=\"modules.php?name=$module_name\" method=\"post\">\n
<p><b>Title:</b><br>\n
<input name=\"usr_title\" size=\"50\" type=\"text\" value=\"\">\n<br>
<br>
<b>Term Text:</b><br>
If you want multiple pages you can write <b>&lt;!--pagebreak--&gt;
</b> where you want to cut.<br>
<textarea name=\"usr_text\" cols=\"60\" rows=\"20\"></textarea><br>
<br>
<b>Encyclopedia:</b><br>
<select name=\"usr_eid\">\n";

After the form, we need to display a list of the active encyclopedias to the user so that they can select one to put their entry in. The SQL statement to get the list of encyclopedias is easy to obtain from looking at the code in the Encyclopedia module:

$sql = "SELECT eid, title FROM " . $prefix . "_encyclopedia
WHERE active='1'";

Now we have the SQL statement, we can execute the query against the database, retrieve the rows, and build a drop-down SELECT element.

We loop through all of the returned rows, getting the title of the encyclopedia and its ID, and create the options for the drop-down list. Note that we use the stripslashes() function to strip away slashes from the title, since the code in the encyclopedia administration for adding an encyclopedia uses addslashes() on the title before storing it in the database.

It is always wise to apply addslashes() to any text being stored in the database to avoid problems with quotes in the text.

$result = $db->sql_query($sql);
while ($row = $db->sql_fetchrow($result))
{
$eid = intval($row['eid']);
$title = stripslashes($row['title']);
echo "<option value=\"$eid\">$title</option>\n";
}

We finish the SELECT HTML element:

echo "</select>\n<br><br>";

The next element of the form is a hidden field with the name op. This holds the value add_encycloentry. When the user submits the form, this value of op will be used to determine the next action.

echo " <input name=\"op\" value=\"add_encycloentry\" type=\"hidden\">";

Finally, we add a submit button to the form, and close up the form:

<input value=\"Submit Item\" type=\"submit\"></p>
</form>";
}

After the user clicks on the button to submit their item, the page is posted back to the server. The $op variable in that page will have the value add_enycloentry, thus the AddEncyclopediaEntry() function is called from the switch-case construct at the end of the file.

Preparing to Add the Encyclopedia Entry

There are three parameters passed to the AddEnyclopediaEntry() function. These parameters are the selected encyclopedia ID, the title of the new entry, and the text of the new entry. Here is the function:


function AddEnyclopediaEntry($eid, $title, $text)
{
global $module_name;
$eid = intval($eid);

if ($title=="" || $text=="" || $eid<1)
{
$pagetitle = "- $module_name : "._ADDENYCLOENTRY;
include("header.php");
title (_ADDENYCLOENTRY);
OpenTable();
echo "You need to supply a title, some text, and select an
EncylopediaEncyclopedia!<br><br>";
echo "[ <a href=\"javascript:history.go(-1)\">Go Back</a> ]<br><br>";

CloseTable();
include("footer.php");
exit;
}
$storageArray = array('title'=>$title,
'text'=>$text);
storeSubmission("encyclo", $storageArray, $eid, $title);
$pagetitle = "- $module_name : "._THANKYOUSUBMISSION;
include("header.php");
title (_THANKYOUSUBMISSION);
OpenTable();
echo "<center>"._THANKSTEXT."<br><br>";
echo "[ <a href=\"modules.php?name=$module_name\">"._ADDMOREITEMS."
</a> ]</center><br><br>";

CloseTable();
include("footer.php");
}

First we convert the $eid value, the encyclopedia ID, to an integer. It is supposed to be an integer, so we make sure it is with the intval() function.

function AddEnyclopediaEntry($eid, $title, $text)
{
$eid = intval($eid);

if ($title=="" || $text=="" || $eid<1)
{

If the visitor hasn't submitted a title or some text for the entry, or an encyclopedia ID, we won't accept their submission. We display a message to them that they have missed filling in some details, and allow them to go back to the form and enter the information.

$pagetitle = "- $module_name : "._ADDENYCLOENTRY;
include("header.php");
title (_ADDENYCLOENTRY);
OpenTable();

Now comes our message, and the link to go back a page. Note that the link isn't a URL, but a JavaScript call to step back one page in the browser history, which is the same as the user clicking on the button Back in their browser.

echo "You need to supply a title, some text, and select an
Encyclopedia!<br><br>";
echo "[ <a href=\"javascript:history.go(-1)\">Go Back</a> ]<br><br>";

Now we finish the page output, and end PHP-Nuke:

CloseTable();
include("footer.php");
exit;
}

So we must have a title for the entry, some text, and an encyclopedia ID. All that remains now is to prepare all of this for storage. We create an array with the title and text, and then we call the storeSubmission() function to take care of the storage:

$storageArray = array('title'=>$title,
'text'=>$text);
storeSubmission("encyclo", $storageArray, $eid, $title);

We pass four parameters to the storeSubmission() function. The first parameter is a string indicating the type of content we want to store. The second parameter is an array containing the data that we want to store (the title, text, and language of the entry). The third parameter is the parent ID of the content.

The fourth parameter is the title of the entry. We pass this again so that it can be stored separately from the other data, because that data will be stored in a very general way. By passing the title separately, the title can be easily retrieved to identify the item in a list for the administrator. We'll see that later.

The final part of the function displays a message to the visitor, thanking them for their submission.

$pagetitle = "- $module_name : "._THANKYOUSUBMISSION;
include("header.php");
title (_THANKYOUSUBMISSION);
OpenTable();
echo "<center>"._THANKSTEXT."<br><br>";
echo "[ <a href=\"modules.php?name=$module_name\">"._ADDMOREITEMS.
"</a> ]</center><br><br>";

CloseTable();
include("footer.php");

And here is the list of the new language constants used in this function. These need to be added to the lang-english.php file in the languages folder.

define("_THANKYOUSUBMISSION", "Thank you for your submission.");
define("_THANKSTEXT","We will check your submission in the next few hours,
if it is interesting, and accurate we will publish it soon.");
define("_ADDMOREITEMS", "Add More Items");
Building Websites with PHP-Nuke A practical guide to creating and maintaining your own community website with PHP-Nuke
Published: November 2005
eBook Price: $20.99
Book Price: $29.99
See more
Select your format and quantity:

Storing the Submission

The final function, storeSubmission() is the real guts of this module. It is intended to take data for different types of content, and store it in one form. The storeSubmission() function does not need to know how the data is structured, where it came from, or how to display it. All this function needs to do is store it.

The function requires quite a few global variables. It needs the database access variables, $prefix and $db, and it needs the module name, $module_name. After it has finished storing the data, the function will send a notification email to the administrator, telling them that an item has been submitted, and providing them with a link to view the submission. To do this, we need the $admin_file global variable, which holds the name of the administration file, since the link to view the submission will point to an administration page.

We also need the $nukeurl, $sitename, and $adminmail variables. These variables hold the values of some of the site's configuration settings (the site URL, the site name, and the email address of the administrator). For each site configuration setting, a global variable is created in mainfile.php file. This snippet of code from mainfile.php shows the definition of the first few site configuration variables:

$result = $db->sql_query("SELECT * FROM ".$prefix."_config");
$row = $db->sql_fetchrow($result);
$sitename = $row['sitename'];
$nukeurl = $row['nukeurl'];
$site_logo = $row['site_logo'];
$slogan = $row['slogan'];
$startdate = $row['startdate'];
$adminmail = stripslashes($row['adminmail']);

You can see that all the columns are retrieved from the <prefix>_config table, which holds the site configuration settings. All the site configuration settings are stored in this one table, with each column corresponding to a setting. In mainfile.php, there are a number of lines that go through this and define a variable for each setting. If you want to access one of the site configuration settings in your code, look to this part of the mainfile.php file to find the name of the corresponding variable.

Returning to our storeSubmission() function, it begins in the usual way with global statements to make sure we can access all the variables we need:

function storeSubmission($type, $storageArray, $parent_id, $title_field)
{
global $prefix, $db, $module_name, $admin_file;
global $nukeurl, $sitename, $adminmail;

Recall that only registered users can submit items. We make one last check to make sure that the visitor is indeed a registered user of the site, and is logged in. If they're not, they're redirected to the module homepage.


$u = get_user_object();
if (!$u)
{
Header("Location: modules.php?name=$module_name");
}

If our user is logged in, we can get their ID and username from the result of the get_user_object() function:

$user_id = $u[0];
$user_name = $u[1];

Now comes the bit where we take the submitted data and transform it so that it is ready for storage. Remember we created an array from the submitted data in the AddEnyclopediaEntry() function. Arrays are not particularly easy to store in a MySQL database, so we use the PHP serialize() function to convert the array into a single string. The serialize() function takes an object and returns a simple string representation of it. There is an unserialize() function, that takes that string representation and recreates the object. The idea should be becoming clear now—we serialize the array and store a string representation of it, and when the administrator needs to view the data to approve the submission, the string will be unserialized so that the data can be easily extracted.

$storage = serialize($storageArray);

We use the addslashes() function to prepare the text variables for storing in the database. The addslashes() function escapes ' and " characters in a string by adding a \ character in front of them. Without escaping them, the database will reject any values containing ' or ". We don't escape the $type variable, since we created that ourselves, and we are certain it has no ' or " characters.

$storage = addslashes($storage);
$title_field = addslashes($title_field);

Now we are ready to create the SQL statement to insert the details of the submitted item into our <prefix>_usersubmissions table:

$sql = "INSERT INTO ".$prefix."_usersubmissions(
type, parent_id, data, user_id, user_name, title)
VALUES ('$type', '$parent_id', '$storage', '$user_id',
'$user_name', '$title_field')";

Now we execute the query:

$db->sql_query($sql);

Our work is almost done. The final step is to create the email that is to be sent to the administrator. Creating the text is easy; the important step is to create the URL for the administrator to view the submission. The URL will be of the form: <admin_page>?op=UserSubEdit&sid=XXXX, where XXXX is the ID of the row we just inserted. To get that ID, we use the $sql_nextid() function of the database access object. This function returns the last created auto-increment field on the database connection, which should be the value of the ID/ column in our table. This is precisely the value we need to make the URL.

$lid = $db->sql_nextid();

Now we create the body of the email to send, set a FROM address, the TO address, which is the administrator's email address, the $adminmail variable, and the SUBJECT of the email.


$mailBody = "A new piece of user-submitted content has been added to the
site.<br><br>";

$mailBody .= "Visit this link to check it out.<br><br>";

$mailBody .= "<a
href=\"$nukeurl/".$admin_file.".php?op=UserSubEdit&sid=$lid>Here</a>";

$from = "$sitename <$adminmail>";
$to = $adminmail;

$subject = "A new piece of content has been submitted";

Now we use PHP's mail() function to send the mail, and our function is done.

mail($to, $subject, $mailBody, "From: $from\nX-Mailer: PHP/" .
phpversion());
}

If you do not have access to a mail server on your testing machine, then comment out the last line with the mail() function.

That brings us to the end of the visitor part of the module. The module is ready for testing, although after making your submissions there isn't much else that happens, since only the administrator sees the submissions. That is our next task.

The User Submission Administration Area

Before we get onto writing the administration code, we will set up our module to feature in the Modules Administration menu. After that, we will see how PHP-Nuke selects the correct bit of code for performing the desired administrative operation; it is not as straightforward as the way that the module is selected for the visitor end of the site.

After that, it will be time to complete the administration code for the UserSubmissions module.

Creating the Modules Administration Menu Entry

To create the Modules Administration menu, PHP-Nuke looks for a file with a name of links.php in the admin folder of each module folder that the current administrator user has access to. (Recall that an administrator may have super user power or may have access restricted to certain modules.) If such a file is found, then a menu entry can be added to the Modules Administration menu.

The links.php file provides the following information to PHP-Nuke:

  • A link to the main page for administering that function
  • A title for its Modules Administration menu entry
  • An image for its icon in the Modules Administration menu

Here is the links.php file that we will use for our UserSubmissions module. Any links.php file in an admin folder should resemble this:

<?php
if (!defined('ADMIN_FILE'))
{
die ("Access Denied");
}

global $admin_file;
adminmenu($admin_file.".php?op=UserSubs",
""._USERSUBMISSIONS."",
"usersubs.gif");
?>

The first line checks what page is attempting to make use of this file; if the page is not an administration page, execution is stopped, and the message Access Denied is displayed. (The ADMIN_FILE constant is only defined in the file admin.php.)

The adminmenu() function takes the three parameters that we listed earlier, and creates the menu entry. The first parameter is the link to the main page of the module's administration area. The second parameter is the title of the entry in the menu, _USERSUBMISSIONS. Again PHP-Nuke uses language constants.

Note that we have to define this _USERSUBMISSIONS constant used in a language file in the core \admin\language folder, not the \admin\language folder in the module itself. If the constant is defined only in the module's language file, the text _USERSUBMISSIONS will be displayed in the Modules Administration menu rather than the value of the constant.

The final parameter is the image for the menu entry, usersubs.gif, and it is the name of an image file found in the \images\admin folder. (The usersubs.gif image can be found in the code download for this article.) Although the module code is self-contained, the image file of the menu entry needs to be located in the \images\admin folder.

After working its way through all the modules that the current user has access to, and executing each links.php file, PHP-Nuke is able to produce the MODULES ADMINISTRATION menu.

To add our language constant, we will add it at the end of the \admin\languages\lang-english.php file:

define("_WEBLINKS","Web Links");
define("_IMAGESWFURL","Image or Flash file URL");
define("_USERSUBMISSIONS", "User Submissions");

?>

After adding the highlighted line to that file, save the file.

Selecting the Correct Administration Area

PHP-Nuke uses the value of the op request variable to determine what part of the administration area to present to the user. This variable can be in the URL of the page, as seen here in the URL for the Downloads administration area:

http://localhost/nuke/admin.php?op=downloads

Alternatively, the op variable can be a hidden field in a form when the administrator is entering some information. When the page is submitted, the value of the op field will 'override' any value of the op query string variable, and will be used to determine the action.

Once PHP-Nuke has the value of the op variable, it begins a lengthy process to see which part of the system will 'claim' it.

First of all, PHP-Nuke 'tries' all PHP files with a name like case.something.php in the case folder of the admin folder in the root of the installation. The case.messages.php file looks like this:

switch($op)
{
case "messages":
case "addmsg":
case "editmsg":
case "deletemsg":
case "savemsg":
include("admin/modules/messages.php");
break;
}

The switch-case mechanism is used to match the value of $op against one of the strings. The strings here are a list of all of the 'operations' the messages administration area uses. Any URL of the form admin.php?op=messages, admin.php?op=addmsg, admin.php?op=editmsg/, admin.php?op=deletemsg, or admin.php?op=savemsg will be matched here.

If a match is found, the file \admin\modules\messages.php is included, and execution passes to there. That is the file that will do the work for the messages administration area.

PHP-Nuke loads in all the files in the \admin\case folder and checks to see if there is match of the value of $op. If there is, code for that part of the administration area will be executed. If there is no match found in any of the files in \admin\case, then PHP-Nuke moves on to the module folders as it did with the Module Administration menu above. It looks through all the module folders that the user has access to, and checks for the file \admin\case.php.

Here is the case.php file for the User Submissions module. All the case.php files are similar to this:

<?php

if (!defined('ADMIN_FILE'))
{
die ("Access Denied");
}
$module_name = "UserSubmissions";
include_once("modules/$module_name/admin/language/lang-".
currentlang.".php");

switch($op)
{
case "UserSubDelete":
case "UserSubEdit":
case "UserSubs":
case "UserEncycloAccept":
case "UserSubAccept":
include ("modules/$module_name/admin/index.php");
break;

}

?>

If there is a match here, then the index.php file in the admin folder of that module is included, and code execution continues there. In this way, all the administration functionality of the module can be kept inside the module folder itself. Note that this setup means that there is only one file for the module's administration code, the index.php file in the admin folder. Unlike with the front end of a module where PHP-Nuke selects a different file based on the value of the file query string variable, PHP-Nuke will not choose a different file in the admin folder, unless you add code for that yourself.

PHP-Nuke will look through all the module folders to try to match the value of the op query string variable. If there is no match after all this, a blank page is displayed, and that's the end of that.

We are now ready to code the administration area of the module. If you check out the Modules Administration area in the administration area, you will see the icon for our module. However, clicking on it does not produce anything since there is no code there yet!

Creating the Administration Code

The administration code is contained in a single file, index.php, in the admin folder of the module.

Let's begin with an overview of the structure of the code; again it is broken up into parts by /* ---- */ comment lines:

<?php
if (!defined('ADMIN_FILE'))
{
die ("Access Denied");
}

$module_name = "UserSubmissions";
$aid = substr($aid, 0,25);
$query = $db->sql_query("SELECT title, admins FROM ".$prefix."_modules WHERE title='$module_name'");

$row = $db->sql_fetchrow($query);

$admins = $row['admins'];

$auth_user = 0;

$query2 = $db->sql_query("SELECT name, radminsuper FROM ".$prefix."_authors
WHERE aid='$aid'");

$row2 = $db->sql_fetchrow($query2);
$name = $row2['namer'];
$radminsuper = $row2['radminsuper'];

if ($row2)
{
if (stristr(",".$admins, ",".$name.",") )
$auth_user = 1;
}
if ($radminsuper == 1 || $auth_user == 1)
{
/* ------------------------------------ */
function UserSubs()
{

}
function UserSubDelete($sid)
{

}
function UserSubEdit($sid)
{

}
function editEncycloEntry($sid, $row)
{

}
function removeFromPending($sid)
{

}
function UserEncycloAccept ( $sid, $title, $text, $eid, $user_name, $user_id)
{

}
/* ------------------------------------ */

switch($op)
{
case "UserSubEdit":
UserSubEdit($sid);
break;
case "UserSubDelete":
UserSubDelete($sid);
break;
case "UserEncycloAccept":
UserSubAccept($sid, $usr_title, $usr_text, $usr_eid,
$user_name, $user_id );
break;
case "UserSubs":
UserSubs();
break;
}
}
else
{
include("header.php");
GraphicAdmin();
OpenTable();
echo "<center><b>"._ERROR."</b><br><br>You do not have administration
permission for module \"$module_name\"</center>";
CloseTable();
include("footer.php");
}
?>

The first part is standard to all modules. The first line checks if the ADMIN_FILE constant has been declared. This constant is only defined in the admin.php file, and this check makes sure the file isn't being requested directly or from a non-administration part of the system. If the check fails, the Access Denied message is displayed.

The next line sets the $module_name variable. We can't use the basename(dirname(__FILE__)) setup here that we used in the front end of the module, since that will return admin, rather the name of the module folder. If you really don't want to type in the name of your module, you can use the following instead:

$module_name = basename(dirname(dirname(__FILE__)));

but it is quicker to type in the module name here!

The rest of the lines in this top chunk of code check to see if you have permission to administer this module. Each module has a row in the <prefix>_modules table in the PHP-Nuke database. This row contains information about the module, including a list of the administrators who are permitted to access this module. This list is stored in the admins field of the row. We grab that from the database:

$query = $db->sql_query("SELECT title, admins FROM ".$prefix."_modules WHERE title='$module_name'");

$row = $db->sql_fetchrow($query);

$admins = $row['admins'];

Now we grab the 'credentials' of the current administrator from the database. Administrator details are stored in the <prefix>_authors table, and the author is identified by their aid. We get the name of the administrator, and find out if they are a super user.


$query2 = $db->sql_query("SELECT name, radminsuper FROM ".$prefix."_authors
WHERE aid='$aid'");

$row2 = $db->sql_fetchrow($query2);
$name = $row2['name'];
$radminsuper = $row2['radminsuper'];

If the current administrator is a super user, $radminsuper will have the value 1.

Now all we have to do is check if the administrator's name is in the list of module administrators. This list, held in the $admins variable, consists of the names of the administrators for this module, separated by commas, including a comma at the end of the list. To check if our administrator's name is in that list, we add a comma to the start and end of the name, and use the stristr() function to find an occurrence of that string inside the $admins variable. We add a comma to the start of the $admins variable so that every name in the list is surrounded by commas, and this way there can be a match for the exact name use the stristr() function. If there is a match, $auth_user is set to 1.

if ($row2)
{
if (stristr(",".$admins, ",".$name.",") )
$auth_user = 1;
}

The only way this administrator can access the administration area is if they have super user powers or if $auth_user is 1, which implies that they have administrative rights for this module:

if ($radminsuper == 1 || $auth_user == 1)
{

The second chunk of code in the file is only executed if the administrator passes this test. This is where all the action happens and we will talk about that in a moment.

The bottom chunk of code after the switch-case construct in the file handles the situation where the user does not have the required administrator permissions to access the file. It simply displays the administration area with a message that they have no access to this module's administration. This gives us a good opportunity to see how the administration page is created:

include("header.php");
GraphicAdmin();
OpenTable();
echo "<center><b>"._ERROR."</b><br><br>You do not have administration
permission for module \"$module_name\"</center>";
CloseTable();
include("footer.php");

The first line starts the page output in the same way as the front end of the module, by including the header.php file. Next comes the call to GraphicAdmin(). This displays the administration menus. Whenever you create an administration page, you will need to call this function to display these menus.

After that, page output continues as usual with OpenTable(), the error message, CloseTable() and then including footer.php to wrap things up.

Now that we've seen the overall shape of the code in the file, let's begin to dig into the individual functions in the file.

Displaying the List of Submitted Items

The main part of the administration area will display a list of all the submitted items. The type of content, the title, the user who submitted it, and some buttons to view or delete the item will be displayed. This is similar to the administration functionality we have seen throughout PHP-Nuke.

The main operation is the UserSubs() function, and is the default function called when the page is viewed:

function UserSubs()
{
global $db, $prefix;
global $admin_file;

include("header.php");

GraphicAdmin();

$contenttypes = array('encyclo'=>"Encyclopedia");
title(_USERSUBADMIN);

OpenTable();

echo "<center>";
$sql = "select id, title, type, user_id, user_name, UNIX_TIMESTAMP(date)
AS theDate from ".$prefix."_usersubmissions order by date desc";
$result = $db->sql_query($sql);
echo "<table border=1>";
echo "<tr><td>ID</td><td>Submission Type</td>
<td>Title</td><td>User</td><td>Date</td>
<td colspan=3>Functions</td></tr>";
while($row = $db->sql_fetchrow($result))
{
$type = $row['type'];
$id = $row['id'];
$title = $row['title'];
echo "<tr><td>$id</td>";
echo "<td align=\"center\">".$contenttypes[$type]."</td>";
echo "<td>$title</td>";
echo "<td>".$row[user_name]."</td>";
echo "<td>".date("l dS of F Y h:i:s A", $row['theDate'])."</td>";
echo "<td><a
href=\"".$admin_file.".php?op=UserSubApprove&sid=$id\">
<img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=\"images/unban.gif\" alt=\"Approve\"
title=\"Approve\" border=0></a></td>";
echo "<td><a href=\"".$admin_file.".php?op=UserSubEdit&sid=$id\">
<img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=\"images/edit.gif\" alt=\"Edit\" title=\"Edit\"
border=\"0\"></a></td>";
echo "<td><a
href=\"".$admin_file.".php?op=UserSubDelete&sid=$id\">
<img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=\"images/delete.gif\" alt=\"Delete\"
title=\"Delete\"></a></td>";
echo "</tr>";
}
echo "</table>";
echo "</center>";
CloseTable();
include("footer.php");
}

The function begins with the usual global declarations, $db and $prefix for data access, and $admin_file for the name of the admin file. Next, it starts the page output, and displays the administration menus with a call to GraphicAdmin().

An array called contenttypes is created:

$contenttypes = array('encyclo'=>"Encyclopedia");

This array will hold the module names that correspond to values in the type field of the stored, submitted data.

Now we retrieve the current list of submitted items:

$sql = "select id, title, type, user_id, user_name, UNIX_TIMESTAMP(date) AS
theDate FROM ".$prefix."_usersubmissions ORDER BY date ASC";
$result = $db->sql_query($sql);

Note the UNIX_TIMESTAMP() function. This is a MySQL function, and it returns the TIMESTAMP field, date, as a UNIX timestamp. This is a number counting the number of seconds between the Unix Epoch (January 1 1970 00:00:00 GMT) and the time specified. Retrieving the date in this form means we do not have to do any further formatting with the date to work with it. The 'AS theDate' part creates an alias for this field, so that we can subsequently refer to it as theDate. Note that we order the submissions by the date on which they were submitted.

Now we create a table to display the list of submitted items:

echo "<table border=1>";
echo "<tr><td>ID</td><td>Submission Type</td>
<td>Title</td><td>User</td><td>Date</td>
<td colspan=2>Functions</td></tr>";

We loop through all the retrieved rows and retrieve the type of content, the ID, and the title:

while($row = $db->sql_fetchrow($result))
{
$type = $row['type'];
$id = $row['id'];
$title = $row['title'];

We begin the display of a single row in the table. The first column displays the ID of the submitted item, the second column the type of content it is, the third column shows the title, the fourth column the name of the user who submitted it, and the fifth column the date:

echo "<tr><td>$id</td>";
echo "<td align=\"center\">".$contenttypes[$type]."</td>";
echo "<td>$title</td>";
echo "<td>".$row[user_name]."</td>";
echo "<td>".date("l dS of F Y h:i:s A", $row['theDate'])."</td>";

The final two columns in the table show the icons for editing (and approving) the item, and deleting the item:

echo "<td><a href=\"".$admin_file.".php?op=UserSubEdit&sid=$id\">
<img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=\"images/edit.gif\" alt=\"Edit\" title=\"Edit\"
border=\"0\"></a></td>";
echo "<td><a href=\"".$admin_file.".php?op=UserSubDelete&sid=$id\">
<img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=\"images/delete.gif\" alt=\"Delete\" title=\"Delete\"
border="0"></a></td>";
echo "</tr>";
}

The icon for editing is edit.gif and the icon for deleting is delete.gif. Both these files are found in the images folder of PHP-Nuke and are the same as those used throughout the administration area. The alt attribute is used as substitute text in case there is some problem displaying the image file. We have added the title attribute to give a visual cue about the function of the icon (the text that appears as you hover the cursor over it).

Each of these images is enclosed by a link to the relevant function in our administration area. The ID of the submitted item is held by the $id variable, and will be passed to the pages through the sid query string entry. Note that we use the $admin_file variable in the links rather than typing in admin.php. This ensures that our module supports the renaming of the administration file.

The rest of the function finishes off the page output:

echo "</table>";
echo "</center>";
CloseTable();
include("footer.php");
}

Now when you visit the administration area of the UserSubmissions module, you will see the list of your submitted items.

Programming PHP-Nuke

Let's set about coding the first administrative function, to allow the administrator to view and approve the submitted item.

Editing and Approving the Submitted Item

The UserSubEdit() function allows the administrator to view, edit, and approve or reject the submitted item.

function UserSubEdit($sid)
{
global $admin_file, $db, $prefix;

$sid = intval($sid);

$sql = "select * from ".$prefix."_usersubmissions where id=$sid";
$result = $db->sql_query($sql);
$row = $db->sql_fetchrow($result);
if (!$row)
Header("Location: ".$admin_file.".php?op=UserSubs");

$arry = unserialize($row['data']);
$row['data'] = $arry;
$type = $row['type'];
switch($type)
{
case "encyclo":
$ok = editEncycloEntry($sid, $row);
break;
default:
Header("Location: ".$admin_file.".php?op=UserSubs");
}
}

This function requires the global $admin_file, $db, and $prefix variables, and begins by converting the $sid variable to an integer. The next thing done is to retrieve the submission from the UserSubmissions table:

$sql = "select * from ".$prefix."_usersubmissions where id=$sid";
$result = $db->sql_query($sql);
$row = $db->sql_fetchrow($result);

If there is no row of data returned, we redirect to the main User Submission Administration page:

if (!$row)
Header("Location: ".$admin_file.".php?op=UserSubs");

If the check is passed, it means that we must have some data, so we unserialize the contents of the data column; remember this column contains a serialized array that we created in the storeSubmission() method of the visitor area.

$arry = unserialize($row['data']);

We store the array back in the $row variable, so that all our data is back together again.

$row['data'] = $arry;

Now we need to process the data based on the type of content, stored in the $type field of $row.

$type = $row['type'];
switch($type)
{

For a case of type encyclo, we call the editEnycloEntry() function to display the data to administrator. If the there is any other value for type, we will redirect to the main User Submission Administration page:

case "encyclo":
$ok = editEncycloEntry($sid, $row);
break;
default:
Header("Location: ".$admin_file.".php?op=UserSubs");
}

This UserSubEdit() function is similar to the ShowFormForInput() function of the module front end, in that it will select a different type of form to show to the administrator based on the type of content being looked at.

Displaying the Submitted Item

The editEncycloEntry() function displays the submitted item to the administrator. It contains code similar to the formatEncyclo() function of the front end that displayed the form to the visitor, the difference being that the visitor saw an empty form, but the administrator will be seeing a form populated with the submitted item data.

function editEncycloEntry($sid, $row)
{
global $db, $prefix, $admin_file;

$usr_eid = $row['parent_id'];

$eid = intval($eid);
$data = $row['data'];
$usr_text = $data['text'];
$usr_title = $data['title'];

include("header.php");
GraphicAdmin();

OpenTable();

echo " <form action=\"".$admin_file.".php?op=UserSubs\"method=\"post\">\n
<p><b>Title:</b><br>\n
<input name=\"usr_title\" size=\"50\" type=\"text\"
value=\"$usr_title\">\n<br>
<br>
<b>Term Text:</b><br>
If you want multiple pages you can write <b>&lt;!--pagebreak--
&gt;</b> where you want to cut.<br>
<textarea name=\"usr_text\" cols=\"60\"
rows=\"20\">$usr_text</textarea><br>
<br>
<b>Encyclopedia:</b><br>
<select name=\"usr_eid\">";

$sql = "SELECT eid, title FROM " . $prefix . "_encyclopedia WHERE
active='1'";

$result = $db->sql_query($sql);
while ($row2 = $db->sql_fetchrow($result))
{

$eid = intval($row2['eid']);
$title = stripslashes($row2['title']);
echo "<option value=\"$eid\" ";

if ($eid == $usr_eid)
echo "selected";

echo ">$title</option>\n";
}
echo "</select>\n<br><br>";
echo "<br><br>";

echo "<input name=\"user_name\" value=\"".$row['user_name']."\"
type=\"hidden\">\n";
echo "<input name=\"user_id\" value=\"".$row['user_id']."\"
type=\"hidden\">\n";
echo "<input name=\"sid\" value=\"$sid\" type=\"hidden\">\n";
echo " <input name=\"op\" value=\"UserEncycloAccept\" type=\"hidden\">";
echo "<input value=\"Accept\" type=\"submit\">";
echo "<a
href=\"".$admin_file.".php?op=UserSubDelete&sid=$sid\">Delete</a>";

echo "<a href=\"".$admin_file.".php?op=UserSubs\">Ignore</a>";
echo "</form>";
CloseTable();
include("footer.php");
}

The code begins by picking out and cleaning data from the $row variable:

$usr_eid = $row['parent_id'];

$eid = intval($eid);
$data = $row['data'];
$usr_text = $data['text'];
$usr_title = $data['title'];

After beginning the page output, we create the form for the administrator. This code is almost identical to the code in formatEncyclo(); the difference this time is that we actually fill in the fields in the form (shown highlighted):

echo " <form action=\"".$admin_file.".php?op=UserSubs\" method=\"post\">\n
<p><b>Title:</b><br>\n
<input name=\"usr_title\" size=\"50\" type=\"text\"
value=\"$usr_title\">\n<br>
<br>
<b>Term Text:</b><br>
If you want multiple pages you can write <b>&lt;!--pagebreak--
&gt;</b> where you
want to cut.<br>
<textarea name=\"usr_text\" cols=\"60\"
rows=\"20\">$usr_text</textarea><br>
<br>
<b>Encyclopedia:</b><br>
<select name=\"usr_eid\">";

The next thing is to create the drop-down list of Encyclopedias. Again, this is similar to the code in formatEncyclo(), except this time we have to select the Encyclopedia chosen by the submitter (these lines are highlighted):

$sql = "SELECT eid, title FROM " . $prefix . "_encyclopedia WHERE
active='1'";

$result = $db->sql_query($sql);
while ($row2 = $db->sql_fetchrow($result))
{
$eid = intval($row2['eid']);
$title = stripslashes($row2['title']);
echo "<option value=\"$eid\" ";

if ($eid == $usr_eid)
echo "selected";

echo ">$title</option>\n";
}
echo "</select>\n<br><br>";

We add some hidden fields that contain the details of the user who submitted the item, the ID of the item itself, and a value for op to determine the next action when the page is submitted:

echo "<input name=\"user_name\" value=\"".$row['user_name']."\"
type=\"hidden\">\n";
echo "<input name=\"user_id\" value=\"".$row['user_id']."\"
type=\"hidden\">\n";
echo "<input name=\"sid\" value=\"$sid\" type=\"hidden\">\n";
echo " <input name=\"op\" value=\"UserEncycloAccept\" type=\"hidden\">";

Finally, we add a submit button, and some links for the administrator to delete the item outright, or just ignore the item and return to the main User Submission Administration page:

echo "<input value=\"Accept\" type=\"submit\">";
echo "<a href=\"".
$admin_file.".php?op=UserSubDelete&sid=$sid\">Delete</a>";

echo "<a href=\"".$admin_file.".php?op=UserSubs\">Ignore</a>";
echo "</form>";

With this form, the administrator is able to view the submission, and make some modifications if they choose. If they are happy with the submission, they can click on the Accept button and the data will be added to the encyclopedia database. When they click on the Accept button and the page is submitted, the value of op will be UserEncycloAccept, and the process of inserting the submitted item into the encyclopedia begins.

Accepting the Submitted Item

And at last we come to inserting the data into the encyclopedia.

function UserEncycloAccept( $sid, $title, $text, $eid, $user_name, $user_id)
{
global $db, $prefix, $admin_file;

$sid = intval($sid);
$eid = intval($eid);
if ($sid<1 || $eid<1)
Header("Location: ".$admin_file.".php?op=UserSubs");

$text .= "<br><b>Submitted by: ".$user_name."<b><br>";

$text = stripslashes(FixQuotes($text));
$title = stripslashes(FixQuotes($title));

$db->sql_query("insert into ".$prefix."_encyclopedia_text values(NULL,
'$eid', '$title', '$text', '0')");

removeFromPending($sid);

Header("Location: ".$admin_file.".php?op=UserSubs");
}

We begin by preparing, and checking the ID of the submitted item, and the Encyclopedia it is supposed to be going into. If we have no value for either of these, we redirect the administrator to the main administration page.

Before we insert the data, we add a note to the end of the entry text indicating who added the entry:

$text .= "<br><b>Submitted by: ".$row['user_name']."<b><br>";

We have to prepare the text and title before storing it in the same way that the Encyclopedia module does. You can see what needs to be done in the encyclopedia_save() function in the index.php file of the encyclopedia administration code. We do it here too:


$text = stripslashes(FixQuotes($text));
$title = stripslashes(FixQuotes($title));

We insert the data into the encyclopedia table.

$db->sql_query("INSERT INTO ".$prefix."_encyclopedia_text
VALUES(NULL, '$eid', '$title', '$text', '0')");

Now we remove the submitted item from the list with a call to removeFromPending(), and redirect the administrator to the main User Submission Administration page since we are done.

removeFromPending($sid);
Header("Location: ".$admin_file.".php?op=UserSubs");
}

Removing a Submitted Item

The removeFromPending() function is where the deleting action happens. We create a SQL statement to delete the entry from the UserSubmissions table, and then execute that query:


function removeFromPending($sid)
{
global $db, $prefix;
$sql = "DELETE FROM ".$prefix."_usersubmissions WHERE id=$sid";
$db->sql_query($sql);
}

This deleting functionality is kept separate from the function called by the administrator to delete an item because more than one operation will want to delete the item from the list.

Deleting a Submitted Item

The UserSubDelete() function is how the administrator deletes a submitted item. This function doesn't do too much; it calls the removeFromPending() function that actually removes an item from the database. After the entry has been deleted, the administrator is redirected to the User Submission Administration main page:

function UserSubDelete($sid)
{
$sid = intval($sid);
if ($sid<1) return;
removeFromPending($sid);
Header("Location: ".$admin_file.".php?op=UserSubs");
}

And that completes the code for this module.

Extending the Module

To extend this module for other types of content from different modules (including your own), you will need to make changes to both the front end of the module and the administration end.

Extending at the Front End

At the front end, you will need:

  • A form for the visitor to enter their submission—you will need to add a new check against the value of $oid in the ShowFormForInput() function in the front end index.php file, and then create a new function like formatEncyclo() for displaying the form.
  • A function for serializing the submitted data—like the AddEnyclopediaEntry() function. This will require you add a new entry to the switch-case construct at the end of the index.php file.

Extending at the Administration End

At the administration end, you will need to:

  • Create a new entry in the contenttypes array in the UserSubs() function.
  • Add a new entry in the $type switch-case construct in the UserSubEdit() function, and a new function for displaying the submitted item similar to editEncycloEntry(). In that function, you will need to state a new value for the op hidden field, and make sure you add this value both to the switch-case construct near the end of the index.php file, and also in the case.php file, or else that action will never get recognized by PHP-Nuke.
  • Finally, you will need a new function to insert the submission into the relevant database.

Summary

In this article, we have seen a lot of code. We began with a look at how PHP-Nuke handles requests for a page.

After that, we created a new block, a better version of the Dinosaur of the Day block we created in Article 4. We created a block that displayed an image and the title of a dinosaur from the database, adding that information to the database if there was none there. This introduced us to data access in PHP-Nuke, a topic which we used throughout the article after that.

After a quick look at module file and folder structure, we began creating a new module for PHP-Nuke, a UserSubmissions module. The steps that we followed to create the module were:

  • Creating the module folder structure
  • Creating the database table
  • Coding the front end (visitor view) of the module
  • Adapting the code for multi-language support
  • Coding the module administration area, and adding the module to the Modules Administration menu
[ 1 | 2 |3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 ]

If you have read this article you may be interested to view :

About the Author :


Douglas Paterson

Douglas Paterson is a full-time acquisition editor and part-time author for Packt Publishing. He is a doctor of Mathematics and has over five years experience of working on programming books across a number of different subjects. He lives in Birmingham, England, with his wife, and his unusually hairy dog, Zak.

Contact Douglas Paterson

Books From Packt

Expert PHP 5 Tools
Expert PHP 5 Tools

PHP 5 E-commerce   Development
PHP 5 E-commerce Development

MySQL Admin Cookbook
MySQL Admin Cookbook

High Availability MySQL Cookbook
High Availability MySQL Cookbook

MediaWiki 1.1 Beginner's Guide
MediaWiki 1.1 Beginner's Guide

Zabbix 1.8 Network Monitoring
Zabbix 1.8 Network Monitoring

CMS Made Simple 1.6: Beginner's Guide
CMS Made Simple 1.6: Beginner's Guide

Joomla! 1.5: Beginner's Guide
Joomla! 1.5: Beginner's Guide

No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
X
m
R
p
f
y
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software