PostNuke: A Flexible Open Source Content Management System
home | forum | international support | contact us

Documentation Wiki

Writing Module Initialization Functions


Introduction


When you write a new module, it is your hope that someone will find it useful, download it, and install it. In order to install it, PostNuke requires that you write an initialization process. The goal of this initialization is to bring all of the module variables, database tables, and their contents up to some initial baseline state, so that the module can be used.

When you sit down to write or update your module, keep in mind what the initial state of the module must be for a fresh installation. What should the initial values for your module variables be? What tables need to exist? Should they have any contents pre-loaded? All of this information will go into the initialization functions you write.

The <moduleName>_init() Function


The <moduleName>_init() function (where "<moduleName>" is the prefix you are applying to all of your module functions) is the workhorse of your module's initialization process. This is the function that should control all aspects of brining your module into its baseline state. When an administrator clicks on "Initialize" in his list of modules, this is the function called1 by PostNuke's process, to perform any module-specific actions.

The function does not accept any parameters. It is required to return a boolean value. You should return true if the initialization was successful, and false if it was unsuccessful for any reason.

What goes function's signature and the final return value of the function is really up to you. You should ensure that every thing that needs some sort of initial value or state gets it.

Listing 1. Example initialization function framework.
function mymodule_init()
{
    if (!DBUtil::createTable('my_main_table')) {
        return false;
    }

    if (!DBUtil::createTable('my_other_table')) {
        return false;
    }

    // populate default table contents
    // (insert a bunch of SQL stuff executed by DBUtil here)

    // populate default module variable values
    pnModSetVar('mymodule', 'myModuleVar', 25);

    // Initialisation successful
    return true;
}


This is a fairly basic function, and you can make it as simple as you want (down to simply returning true), or as complex as you want.2 With PostNuke version .8 it is now possible to store structural data dictionary information in the array returned by your module's <myModule>_pntables() function. This, along with the DBUtil class, makes the process of adding database tables and columns a bit easier.

Users of PostNuke .7x write a similar function, however the DBUtil class is not available to you. You have to construct the proper SQL statements, and execute them directly. For example:
    $dbConn =& pnDBGetConn(true);
    $pnTables =& pnDBGetTables();
    $myMainTable = $pnTables['my_main_table'];
    $myMainColumns = $pnTables['my_main_table_column'];

    $sql = "CREATE TABLE ".$myMainTable."...   // the rest of the SQL statement
    $result = $dbConn->Execute($sql);
    if (!$result === false) {
        // ... do error handling stuff
    }
    // ... do more init stuff

 


Interactive Initialization: The <moduleName>_init_interactiveinit() Function


If there is nothing to ask your user during the initialization process, and you (and they) are happy setting defaults for any module variables (or anything else), then all you really need to implement is the <moduleName>_init() function.

If, on the other hand, your initialization process needs direction from the user, or you would like to give them the opportunity to set values for variables other than the default, then you can implement the <moduleName>_init_interactiveinit() function as well. Please note that this function does not replace the <moduleName>_init() function, but augments it.

Because of the way the Modules system module determines whether an interactive initialization should be called or not, getting the flow between your interactive initialization function, your initialization function, and the Modules system module takes a little bit of alchemy. Once you see what is required, however, you'll see that it is quite easy.

The basic flow of events in an interactive initialization are as follows:
  1. The user clicks on "Initialize" in the module list of the Modules system module.
  2. The Modules system module checks to see if a function called <moduleName>_init_interactiveinit() exists--and if it has already been called or not.
    1. If the function does exist and has not yet been called, then it is called.
    2. The user proceeds through the one or more pages it presents.
    3. The interactive function(s) store the gathered responses somewhere that they can be found later (most likely one or more session variables).
    4. The interactive function redirects the user back to the Module system module's initialization function (where it starts again at #1 from this list).
  3. The Modules system module (having determined that either the interactive function does not exist, or that has already been called) calls the <moduleName>_init() function.
    1. This function retrieves the responses gathered from the interactive process. If none are found, then it uses default values.
    2. It performs the necessary initialization steps, and returns true or false.
  4. The Modules system module updates its tables appropriately, and then returns to the list of modules.

The Modules system module, fortunately, can "auto-magically" determine whether the interactive process has already been called or not. It does this by setting a session variable called "interactive_init", and checking its value3 when the initialization function is called.

The simplest interactive initialization is one that presents one page to the user, gathers all of the information from that page, stores the information in one or more session variables, and then calls the initialization function on the Modules system module back. An example might be:

Listing 2. Example interactive initialization function framework.
function mymodule_init_interactiveinit($args) {
        if (!pnSecAuthAction(0, 'mymodule::', 'admin::', ACCESS_ADMIN)) {
                return _MODULENOAUTH;
        }

        // Get the passed parameters (basically just $args['oldversion'])
        extract($args);

        // See if we are here from a call-back from a form submission.  This is
        // for us, and has nothing to do with the Modules system module
        // knowing that the interactive process is complete.
        $interactiveComplete = pnVarCleanFromInput('interactiveComplete');

        if (empty($interactiveComplete)) {
                // Initialize the $initOpts variable with defaults, which will keep
                // track of the selected options for us.
                $initOpts ['anOption'] = "a default value";
                $initOpts ['anotherOption'] = 3; // another default value
                $initOpts ['activate'] = false; // default for whether or not to activate after init

                $pnRender =& new pnRender('mymodule');
                $pnRender->caching = false;
                $pnRender->assign('initOpts ', $initOpts );
                return $pnRender->fetch('mymodule_init_interactiveinit.tpl');
        }
        else {
                // Get any submitted form variable values since we are here from a
                // call-back from a form submission
                $initOpts ['anOption'] = trim(pnVarCleanFromInput('anOption'));
                $initOpts ['anotherOption'] = (int)pnVarCleanFromInput('anotherOption');
                $initOpts ['activate'] = pnVarCleanFromInput('anotherOption');
                $initOpts ['activate'] = (!empty($initOpts['activate']) ? true : false);

                // Save the initialization options to a session variable, so we can
                // retrieve them later in mymodule_init()
                pnSessionSetVar('mymodule_initOpts', serialize($initOpts));

                // Call back the initialization function from the Modules system module,
                // which will automagically know that we have completed the
                // interactive process. We need to set a new authid, since the last
                // one was used and discarded.  Aso, we set the activate parameter
                // to inform the Modules system module of the users choice
                return pnRedirect(pnModURL('Modules', 'admin', 'initialise', array(
                                'authid' => pnSecGenAuthKey('Modules'),
                                'activate' => $initOpts['activate']
                                )));
        }
}


The above example is fairly basic. If you require a more complex interaction with the user, then you might want to split the process over several functions. There are several ways to manage the user through a flow of several pages, and it is beyond the scope of this document to illustrate those methods. It is left as an exercise for the reader to determine his best course of action.

The key concepts here are (a) how to record information for later use in the <moduleName>_init() function, and (b) how to call back to the main initialization process.

The line:
        pnSessionSetVar('mymodule_initOpts', serialize($initOpts));

is where the information gathered from the user is stored for later use. It should be coupled with:
        $initOpts = pnSessionGetVar('mymodule_initOpts');
        $initOpts = deserialize($initOpts);


within the <moduleName>_init() function, to retrieve the information for use.

The line:
        return pnRedirect(pnModURL('Modules', 'admin', 'initialise', array(
                        'authid' => pnSecGenAuthKey('Modules'),
                        'activate' => $initOpts['activate']
                        )));


is where the process is returned to the initialization process managed by the Modules system module. Note that this is a redirect to a UI function, not a pnModAPIFunc call. The UI function is the one that determines whether the interactive process should be called or not. The authid is reset for the "modules" module, because on the first time through, the authid that was available on the modules list was used up and discarded. The "activate" parameter is optional (defaulting to false), but allows you to ask the user whether the module should be automatically activated at the end of a successful initialization or not.

Conclusion


Providing an initialization process for your modules is a relatively easy task, made more complex only by the complexity of what your module requires.

The <moduleName>_init() function is the workhorse of the process, and is where all of your custom module initialization code goes. It doe not accept any parameters, and returns a boolean value based on whether the initialization was successful or not.

The <moduleName>_init_interactiveinit() function allows you to gather information about the initialization process from the user. In the end, should redirect back to the initialization UI function from the Modules system module. Information gathered during the interactive process should be stored somewhere that the <moduleName>_init() function can later retrieve it and make use of it.

Next step: Module Programming Part 6

Footnotes


1 This is the function called, even if there is an interactive initialization function. The difference, when there is an interactive initialization function, is that the interactive process happens first, and then this function is called.

2 Which also implies that if you don't like the way I've structured the example, you are free to implement your own style!

3 The values the Modules system module uses internally for the interactive_init flag are not necessarily important to this discussion, but the curious who dig into the core code will note that the values that are used seem a bit backwards. Think of the flag representing not "should I call the interactive function," but rather "has the interactive function already been called by me." You'll lose less sleep trying to figure it out.
XML Revisions of $tag
Page history :: Last Editor [ espaan ] :: Owner [ rburkhead ] ::
Valid XHTML :: Valid CSS :: Powered by pnWikka 1.0 (A wiki fork from WikkaWiki)
 

Main Menu

Extensions Database

Documentation

Development

Login





 


 Log in Problems?
 New User? Sign Up!

Donate to PostNuke