Before we can start, we will once again need to run a few SQL queries to populate the application’s database with the appropriate information. These queries simply build on top of the database created in part two:

As you can see, we’ve created several additional SQL tables (which will be explained later in the article) as well as an additional user record; nothing too complex. Now we’re ready to start modifying our application.

{mospagebreak title=Advanced Application Configuration}

Begin by opening the ‘application.defaults.php‘ file that we created in part two. Currently this file contains only those configuration variables required by Nennius. Our first step in exploring the advanced configuration options available to Nennius developers will be to modify this file. The text below shows the additional variables we will be adding to our configuration. We will take a closer look at each variable later, but for now just add the following text to your ‘application.defaults.php‘ file:

As you can see above, most of the variables we have added to our configuration file are self-explanatory, but let’s take a short look at each just to avoid any possible confusion.

Our first variable, ‘nennius_branding_logo‘, specifies the relative path to the Web application’s branding image. Branding images should generally be no more than 200 pixels wide and 40 pixels tall. For our example application, we have developed a simple branding image that displays the name of our Web application in graphical format. (Note: if no branding image is provided, the default Nennius logo will be used.)

Next we told Nennius to store file attachments on the file system instead of in our database; we could have just as easily chosen to store files in the database (using ‘DB_BLOB’). Depending on the application, it will often make more sense to use one method of storage over the other. If neither method is specified, Nennius will default to storing files on the file system within a ‘/webapps/application_name/files/‘ folder.

The ‘nennius_default_page‘ is fairly straightforward. If our application contains more than one component, we are able to specify the default component for users who are logging in. Upon a successful login, Nennius will re-direct the user to the component page specified here.

Our last two variables deal with logging information. Nennius offers a variety of logging options. We have chosen the two most common for our application: ‘nennius_debug_mode‘ and ‘nennius_logging_mode‘. By enabling debug mode and specifying a log file (‘nennius_debug_file‘) we are telling Nennius that any time a SQL query fails it should write that query (and the error message returned) to a log file. This is a very handy feature for Web applications, especially during the initial development stages. Logging mode is similar; it records failed login attempts. It is recommended that logging mode be enabled for any Nennius application managing sensitive data.

Note: When creating the ‘application.defaults.php‘ file, make certain that there is no trailing space before or after the PHP brackets. This is a fairly common oversight to make, but it will prevent the authentication class from setting a user’s login cookie.

{mospagebreak title=Expanding Our Application Menu}

In part two of this article we created a simple news management application. That application contained only two menu options: one to direct an authenticated user to a form for managing news releases, and another to direct all users to our imaginary company’s news release page. That was a great introduction to Nennius development, but not very practical. More often than not, when it comes to data management tools, a wide variety of data is being managed. Because of this, most Nennius applications will deal with more than one component. To add additional components to our example application, we will first start by expanding our menu configuration file as follows:

The menu configuration file now specifies two menu categories: ‘Admin Options’ (shown only if a user is logged into the application) and ‘Public Menu’. In addition we have also added a new menu link: ‘Manage User Comments’. (For the purposes of this article, we will assume that our application’s front end allows users to attach comments to a news entry. This new menu option will then allow us to moderate the comments if necessary.) It will also show us another important aspect of Nennius development: how to create meaningful component relationships.

{mospagebreak title=Creating the User Comments Component}

Now that we have finished configuring the application, let’s create our basic Comments component. Just like last time, we’ll start by creating the entry-point file. Name that file ‘comments.php‘ and place it within the ‘/nennius/webapps/news/‘ directory. Then copy the following content into it:

<?php

# define location of component & descriptor files

$component_file = ‘components/comments.php';

$descriptor_file = ‘descriptors/comments.php';

# include parent nennius_component class

include_once “../../nennius.component.php”;

include_once $component_file;

# create a new object to kick things off

new comments( $descriptor_file );

?>

Next let’s move on to the Comments descriptor file (‘/descriptor/comments.php‘):

<?php

# set display name for component

$GLOBALS['g_main_display_name'] = ‘User Comments';

# set primary db table name

$GLOBALS['g_db_primary_table'] = ‘comments';

# set primary key for main db table

$GLOBALS['g_db_primary_table_key'] = ‘id';

# set (optional) primary display field for db table

$GLOBALS['g_db_primary_table_name'] = ‘subject';

# set required min. threshold for overall access

$GLOBALS['g_threshold_overall'] = $GLOBALS['USER'];

?>

So far things should look pretty familiar, which is good. At their most basic level, all Nennius components will look very similar. The components file (‘/components/comments.php‘) is no exception. Let’s create it next:

<?php

# basic comments component

class comments extends nennius_component {

# store class-wide reference to control object

# NOTE: this object is created by the nennius_component class

var $nennius_control = NULL;

#—————————————————————–

# sets up necessary internal vars, creates new control object

# and passes self-reference to it

#—————————————————————–

function comments( $p_component_descriptor_file ) {

# call out to constructor of parent class with descriptor file

$this->nennius_component( $p_component_descriptor_file );

} # END comments

#—————————————————————–

# retrieves and returns assoc. array containing db column info

#—————————————————————–

function get_info_db_array() {

$f_db_info_array = NULL;

# define primary key attribute for comments table

$f_db_info_array['id'] = (

array (

‘db_field_name’ => ‘id’,

‘db_field_type’ => ‘int’,

‘db_field_length’ => 3,

‘db_primary_key’ => TRUE,

# field is hidden from visibility at all times

‘hidden_all’ => TRUE

)

);

# news id – links comment entries to parent news release entry

$f_db_info_array['news_id'] = (

array (

‘db_field_name’ => ‘news_id’,

‘db_field_display’ => ‘News Release’,

‘db_field_type’ => ‘int’,

‘db_field_length’ => 36,

‘db_field_required’ => TRUE,

# foreign key DB information

‘db_foreign_key’ => ‘id’,

‘db_foreign_display’ => ‘title’,

‘db_foreign_table’ => ‘news’

)

);

# date & time when comment was posted

$f_db_info_array['datetime'] = (

array (

‘db_field_name’ => ‘datetime’,

‘db_field_display’ => ‘Date / Time’,

‘db_field_type’ => ‘datetime’,

‘db_field_length’ => 19,

‘db_field_required’ => TRUE,

# set default value to be current date & time

‘update_default’ => date( ‘Y-m-d H:i:s’ ),

# set attribute to be searchable by min & max date range

‘search_date_range’ => TRUE

)

);

# commentor’s name

$f_db_info_array['name'] = (

array (

‘db_field_name’ => ‘name’,

‘db_field_display’ => ‘Name’,

‘db_field_type’ => ‘char’,

‘db_field_length’ => 50,

# set field to be searchable using a partial-match criteria

‘input_search_type’ => ‘TEXT’,

‘search_partial’ => TRUE,

)

);

# comment’s subject

$f_db_info_array['subject'] = (

array (

‘db_field_name’ => ‘subject’,

‘db_field_display’ => ‘Subject’,

‘db_field_type’ => ‘char’,

‘db_field_length’ => 100,

# set field to be searchable using a partial-match criteria

‘input_search_type’ => ‘TEXT’,

‘search_partial’ => TRUE,

)

);

# comment body / text

$f_db_info_array['comment'] = (

array (

‘db_field_name’ => ‘comment’,

‘db_field_display’ => ‘Comment’,

‘db_field_type’ => ‘char’,

# define size of textarea input

‘input_update_type’ => ‘TEXTAREA’,

‘input_update_rows’ => 10,

# hide field from search & view modes

‘hidden_search’ => TRUE,

‘hidden_view’ => TRUE

)

);

# after all attributes have been setup, return array

return $f_db_info_array;

} # END get_info_db_array()

#—————————————————————–

# retrieves and returns assoc. array containing display headers

#—————————————————————–

function get_info_header_array() {

$f_header_info_array = NULL;

# append default header messages

$f_header_info_array['news_id'] = ‘News Information';

$f_header_info_array['name'] = ‘User Information';

$f_header_info_array['subject'] = ‘Comment';

# return header array

return $f_header_info_array;

} # END get_info_header_array()

} # END comments class

?>

Things should still look somewhat familiar. As you can see we’ve simply described the SQL table that stores our User Comments information, and set some basic configuration options about each of the component’s attributes (aka SQL columns). However, a very important attribute was introduced with the ‘news_id‘ column: the foreign key.

As previously stated, most data management systems will not only need to manage various data components but also control those components in relation to one another. Nennius allows for several methods of interaction between components, one of which is the foreign key attribute we have just identified. By specifying a foreign table, key, and display column we are providing Nennius with all of the information it needs to transparently link the Comments component to the News component. The result of this (in this example) is a drop-down menu containing all News entries, listed by title, that a user may pick from when creating or modifying a Comment record. This creates an association between Comments and News records.

We also introduced a new function in this component file: ‘get_info_header_array()‘. This function defines headers that are displayed above certain attributes when a record is being created or modified. This is a very useful tool when creating components with a high number of attributes. By using the ‘get_info_header_array()‘ function a developer is easily able to categorize and organize the various form elements – making the resulting update form more user-friendly.

{mospagebreak title=Creating Component Dependencies}

Now that we have created an association between our Comments component and the previously created News component, a potential problem presents itself. What happens if a user deletes a News entry which contains one or more Comment entries (attached)?

Nennius offers a simple solution for this scenario as well. Simply update the following data found in the DB Field array in the News component file (‘/components/news.php‘):

# define primary key attribute for news table

$f_db_info_array['id'] = (

array (

‘db_field_name’ => ‘id’,

‘db_field_type’ => ‘int’,

‘db_field_length’ => 3,

‘db_primary_key’ => TRUE,

# field is hidden from visibility at all times

‘hidden_all’ => TRUE

)

# setup dependency information to clean all associated comments as well upon deletion

‘db_dependency_tables’ => array(

array ( ‘db_table_name’ => ‘comments’,

‘db_field_name’ => ‘news_id’,

‘db_display_name’ => ‘Comment’ )

),

‘db_dependency_flush’ => TRUE

);

As you can see we’ve added two array keys to the ‘id’ descriptor array: ‘db_dependency_tables‘ and ‘db_depencency_flush‘. The first, ‘db_dependency_tables‘, contains an array of arrays – each array specifying information about a unique record dependency. Since only one dependency exists in our example application, only one array has been specified.

The second variable, ‘db_depencency_flush‘, contains a boolean value that tells Nennius how to act in the event that a News record (with one or more Comments records attached) is deleted. In our case we specified ‘TRUE’, which means that all associated Comments will also be deleted. (Note: If we had specified ‘FALSE’ then Nennius would have refused to delete any News releases with one or more User Comments attached.)

{mospagebreak title=Component Notes}

Our Comments component is now fully functional. However, we have not yet created any method for moderators to communicate regarding User Comments. This could be an unfortunate oversight. If one moderator is unsure of whether a comment should be edited/removed but has no way of asking another moderator for advice, what will happen? In order to avoid this scenario, let’s allow our moderators to attach private messages, or notes, to each User Comment. This can be done simply by adding the following lines to the end of the ‘/descriptors/comments.php‘ file:

# set record notes table info (if notes desired)

$GLOBALS['g_optional_notes_db_table'] = ‘comment_notes';

$GLOBALS['g_optional_notes_db_key'] = ‘id';

$GLOBALS['g_optional_notes_db_index'] = ‘news_id';

$GLOBALS['g_optional_notes_db_user_id'] = ‘user_id';

$GLOBALS['g_optional_notes_db_datetime'] = ‘datetime';

$GLOBALS['g_optional_notes_db_text'] = ‘description';

Now ADMIN users will have the ability to attach private messages to user-submitted comments. These messages may be modified or deleted at a later time.

{mospagebreak title=Help Files and User Documentation}

We may also want to provide our moderators with basic instructions on how to properly create news entries and regulate comments. We may do this by creating basic HTML help files (we’ll call ours ‘news.htm‘ and ‘comments.htm‘) and specifying their location within our descriptor files as well. Let’s start by creating the help files and placing them within a new directory named ‘/help/’. For our ‘news.htm‘ file, insert the following content:

Next we will need to tell Nennius which help file to use. We can do this by appending the following to our ‘/descriptors/news.php‘ file:

# specify location of help & support file for component

$GLOBALS['g_optional_help_file'] = ‘help/news.htm';

Now that we’ve set up our News help file, try creating your own help file for the Comments component and then tie it into the system using that component’s descriptor file.

{mospagebreak title=File Attachments and Component History}

Now that our Comments component is finished, let’s re-visit our News component. Begin by opening the news component’s descriptor file, ‘/descriptors/news.php‘. Once again our file contains only the required information, but by making a few simple modifications our component can gain a great deal of added functionality.

For instance: what if we wanted to add functionality for admin users to attach images to news releases, so that the our front-end system can display a photo gallery? What if we wanted the system to keep records of when each release was posted and modified, and by whom? We could do that very simply by adding a few lines to the end of our existing defaults file:

# set file attachment associations for component

# NOTE: these fields allows for zero to an infinite number of file attachments

# to be associated with component via a seperate indexing table.

$GLOBALS['g_optional_attachments_db_table'] = ‘news_attachments';

$GLOBALS['g_optional_attachments_db_key'] = ‘id';

$GLOBALS['g_optional_attachments_db_index'] = ‘news_id';

$GLOBALS['g_optional_attachments_db_filename'] = ‘filename';

# set record history table info (to record record creation & modification)

$GLOBALS['g_optional_history_db_table'] = ‘news_history';

$GLOBALS['g_optional_history_db_key'] = ‘id';

$GLOBALS['g_optional_history_db_index'] = ‘news_id';

$GLOBALS['g_optional_history_db_user_id'] = ‘user_id';

$GLOBALS['g_optional_history_db_datetime'] = ‘datetime';

$GLOBALS['g_optional_history_db_description'] = ‘description';

Using Hook Functions

At this point our example ‘news‘ application is complete, but our learning exercise is not. We still have not discussed another powerful feature available to Nennius developers: hook functions.

Nennius supports a fairly robust set of p-defined hook functions, allowing developers to enhance or override default behavior without modifying the Nennius engine directly. A hook function isn’t needed for this example application, but we’ll create one anyway in order to familiarize ourselves with how to do so. Our hook function will simply prevent the modification or deletion of any news article older than one week.

Please begin by opening the ‘/components/news.php‘ file and adding the following information:

As you can see, the above function retrieves the current component’s details from the Nennius Control class, runs a SQL query to determine if the record in question is older than one week, and then returns TRUE or FALSE, indicating whether the record is locked for deletion. This is a rather simple example, but it suggests the power available to Nennius developers through the use of hook functions.

Conclusion

Although this article has taken a brief look at several of the advanced features available to Nennius developers, it is in no way a comprehensive list. The main purpose of the example application we have constructed was to demonstrate a variety of common ways Nennius may be configured to handle data. You are encouraged to take the finished product, and modify it to gain a better understanding of the configuration options we have discussed. You may also want to refer to the Nennius developer manual (http://nennius.sourceforge.net/dev_manual.php) for a more detailed list of hook functions and configuration options.