Introduction to LogiCreate

Welcome to LogiCreate! This document contains all you'll need to get started developing advanced web applications, including intranet and extranet systems, content publishing systems and other customized systems. We hope you find LogiCreate both easy to use and powerful.

Purpose of LogiCreate

LogiCreate is a PHP-based application server which was developed to provide both a centralized, modular architecture and a strong security model. Many development toolkits fall short in one respect or the other, or fail to provide a large base of actual working code. LogiCreate addresses both the security and modularity issues, as well as provides a growing base of sample applications ready for customization by developers.

What this document will cover

This document will cover

  • installation of the LogiCreate package

  • the basic operating theory behind how LogiCreate works

  • creating a simple LogiCreate module

  • the basics of the included library modules

Assumptions

This document assumes you are familiar with Unix command-line administration, basic SQL commands, and basic PHP programming concepts and syntax(variables and objects, for example). If you are not familiar with these topics, you may wish to review these ideas and techniques before proceeding.

Installation and setup

The LogiCreate package comes as a single .tar.gz file and requires just a few steps to get it ready for use.

Unpacking

Copy the .tar.gz file to the directory above the web server's "document root" directory. For example, if the document root of the web server is /var/www/html, copy the .tar file to /var/www

Next, run the command:

tar -xvfz logicreate.tar.gz

The contents of the file are two directories - logicreate/ and public_html/. The contents of the 'public_html' folder will need to be copied to the installation's document root.

Run the mysql setup file.

It is recommended that you create a brand new database for your new logicreate installation. Once you have created a new database, you are now ready to import the standard LogiCreate SQL tables. There is a setup.sql file for mysql in the LogiCreate directory. Feed this file to mysql with the following command:

mysql -u username -p --database=databasename < ./logicreate/setup.sql

Replace "databasename" with your database name and the "username" with your database username. You should be prompted for the password. These SQL statements may work with other databases but they only supported on MySQL.

Edit the database file

The primary database connection information is stored in ./logicreate/lib/LC_db.php. At the bottom of that file you will find the appropriate spaces to insert the database hostname (usually 'localhost') the database username, password and the name of the default database to use. These will need to be set up prior to using LogiCreate.

Permissions

You may need to modify the permissions of the files and directories to allow the web server to properly access them. The following steps are the easiest, although the final issue of file permissions should be left up to your server administrator.

  • chmod 755 ./logicreate -R
  • chmod 755 ./public_html -R
  • chmod 777 ./logicreate/content -R
  • chmod 777 ./logicreate/lcFiles -R

Note: Optionally if you wish to use the image upload funcationality in Hercules Control Center you will need to "chmod 777 ./logicreate/public_html/images -R". These permissions are being set world readable so the web server can write to these directories. Depending on your setup you may wish to contact your system administrator for the proper way to setup these directories.

Finished

That should be it. If your "document root" directory is not "public_html", you'll need to copy the files from it to the appropriate "document root" directory. You should now be able to view http://www.yourdomain.com/index.php

Protecting the Hercules Control Center

The Hercules Control Center is the administrative tool which you use to control your LogiCreate powered site with. The files for the control center are stored in the public_html/herc/ folder by default. Since the control center must be accessible from the web the directory "herc" needs to be password protected. We recommend taking a two step process to protect your Hercules Control Center.

      Add a .htaccess file to the herc/ directory
        Edit the auth.php file to include your desired username and password.

Creating a .htaccess file
The following is instruction for the apache web server on most unix systems.

Create a file in the herc/ directory called .htaccess. Add the following lines to this file:
AuthUserFile /path/to/htpasswd/file
AuthName "HCC"
AuthType Basic
require valid-user
Next, follow the syntax below to create a user account:

htpasswd -c /path/to/htpasswd/file/NameOfFile username

Example: htpasswd -c /www/.htpasswd hccadmin

You will be prompted to enter a password. After the password is entered be sure to test your new account. If you run into problems consult the http://www.apache.org web site for more information.

Modifying auth.php

Additionally, there is a username/password combination which must be entered to use the Hercules Control Center. This must first be set in the public_html/herc/auth.php file before Hercules will function. Edit this file, inserting an appropriate username and password where required. Accessing Hercules will now require the user to input that same username/password combination. If you set the same username/password combination in the auth.php and the .htaccess file then you will be prompted only for one password and username.

ONLY SITE ADMINISTRATORS SHOULD BE ABLE TO ACCESS THE HERCULES CONTROL CENTER.

System Architecture

The LogiCreate system is organized into a logical structure which enforces separation not only between presenation and logic code, but between individual modules themselves, as well as between system-level libraries and user-defined libraries.

Overview of file/directory structure

After installation you will have some top level folders that should be parallel with your webserver's "document root". For example, if your webserver will serve www.yourdomain.com files from /home/webfiles/html, then the LogiCreate installation should look like this:

/home/webfiles/logicreate
/home/webfiles/html
/home/webfiles/html/templates
/home/webfiles/html/herc

The 'herc' folder is the default location for the Hercule control center. The Hercules system is coded to use shared libraries from the 'logicreate' folder, so if you need to move the 'herc' folder, you'll need to make sure you modify the 'main.php' file in the Hercules system to find the new library path.

Inside the 'logicreate' folder you'll find the following folders:

  • 'content' - this folder holds the HTML files managed by the Hercules HTML manager
  • 'lib' - a collection of system libraries accessible by all modules
  • 'services' - this folder is a collection of modules, each contained withing their own folder.
  • 'lcFiles' - this folder holds files uploaded through the file manager application (see the Hercules Manual)

Services Folder

Each module under services follows the same structure:

modulename/
     main.lcp
     templates/
          main.html
          admin/
               main.lcp
               templates/
                    main.html

Templates Folder

Using our above example templates for your LogiCreate site are stored in /home/webfiles/html/templates. Each set of templates are broken into folders for easy maintenance.

templates/
     default/   ( default site template )
          images/
          header.html
          footer.html
          site.css
     extranet/       ( default extranet template )
          images/
          header.html
          footer.html
          site.css

The images folder is used to store images specific to each template. In the example above you will see two different sets of site templates; default and extranet.

Document Root

The document root folder is the folder where files are accessible through the web server. Using the example above our document root folder is /home/webfiles/html. As noted during installation, all of the core LogiCreate files are stored underneath the document root. Only those files which need to be accessed directly through a browser should be placed in the document root. A default installation will have the following tree structure:

html/
     index.php
     defines.php
     herc
     templates

What does index.php do?

If you look at the URL of any LogiCreate site, you'll quickly notice that every request is passed through an 'index.php' file. The index.php file handles a multitude of request functions that are common to just about every page request. Some of the main functions the index.php handles include:

Session handling Access logging
System constants initialization Security checking
Database connection initialization Template processor initialization

After all the appropriate steps are taken (db connection, logging, authentication), index.php will include the appropriate module referenced in the URL, and pass in the input from the user to that module. When the module is done processing, index.php will take the information in the template variable and pass it to the appropriate template handler ("presentor") for final rendering to the client.

URL Parsing

The index.php file will parse the incoming URL for the name of the module to run, as well as any paramters to pass to the module. Although every module has at least one 'main.lcp' file, there may be other files in the module's directory which also contain code (and are classes themselves) which is related to the specific module and needs to be called directly. For example, although it's technically possible to handle every possible action for a shopping cart system just through the main.lcp file, it would be easier if certain tasks were in files of their own, such as cart.lcp, checkout.lcp, and so on.

In the following example:

http://www.domain.com/index.php/shebaz/main/event=foo/param1=bar/param2=good

the module "shebaz" will be loaded, and the main.lcp file will be run. Because we have an "event" parameter in the URL, the "shebaz" method "fooRun" will be executed, instead of the standard "Run" method. In the method itself, $arg->getvars["param1"] would contain the value "bar", and $arg->getvars["param2"] would c ontain the value "good".

In this example:

http://www.domain.com/index.php/shebaz/check/param1=bar/param2=bad

The module "check.lcp" from the "shebaz" folder in the services folder is loaded, and the default "Run" method is called. The method would have access to the $arg->getvars array, which again would have access to the param1 and param2 values.

Any parameters passed in via the URL are processed and handed to the module in the $arg->getvars variable, except for the "event" value. The "event" value in a URL is reserved for system use, and is used to signal which method in a class should be executed. As described earlier, by default, the "Run" method of a class is executed. If, however, an "event" value is passed in the URL, then the event value is appended with "Run" and that method is run instead.

Consider the following class:

       

Services Folder

Each module under services follows the same structure: class foo extends BasicAuth ( function Run(&$db,&$u,&$arg.&$t) { // do something here } function checkValueRun(&$db,&$u,&$arg,&$t) { // check a value here } }

By calling

http://www.domain.com/index.php/foo/main

the "Run" method of the foo class would be called. However, by calling:

http://www.domain.com/index.php/foo/main/event=checkValue

the "checkValueRun" method of the foo class would be called.

defines.php

The defines.php file sets up paths and site-wide variables for your LogiCreate site. This file will not normally need to be modified unless you are adding new functionality. You will however will want to reference these constants setup in the defines file in the applications you build. See the section labeled "System variables and constants" for a description of these constants.

What is a module?

A module is a collection of code created to perform a certain task. In most cases, a module is intended to be primarily a 'standalone' application, meaning that regardless of what other modules may be installed, it will work as a separate application. If you consider popular web applications, a discussion forum may be considered a module, and a shopping system a separate module. Although a website may offer both features, in most cases they will be separate collections of code that don't directly interact with one another.

In LogiCreate, in addition to each module have a specific directory layout, there is a specific code structure that must be adhered to, primarily the fact that your code modules will be classes, and that any class method which needs to be accessed from the LogiCreate engine will need to accept a standard set of parameters.

Modules are Classes

Each module is a subclass of the LogiCreate system class, which provides basic services such as module-level authentication. What you'll notice when examining the sample code is that nearly all the modules provided are actually subclasses of classes "BasicAuth" and "RegAuth". In turn, those classes are subclasses of the primary LogiCreate system class. The "BasicAuth" and "RegAuth" are two different classes which overrise the 'authentication' method of the primary system class.

Let's look at a bit of code:

class foo extends BasicAuth (
 var $presentor = "htmlPresentation";
 function Run(&$db,&$u,&$arg,&$t) {
  $t["bar"] = "hello";
 }
}

If someone tried to access the "foo" module from the URL, but was in a group which did not have "Access" permissions, the authentication portion of the request cycle would fail, and an error message would be printed out.

If we changed the class to:

class foo extends RegAuth (
 var $presentor = "htmlPresentation";
 function Run(&$db,&$u,&$arg,&$t) {
  $t["bar"] = "hello";
 }
}

the authentication method in RegAuth disregards any 'access' properties set in the Hercules control center and simply checks to see if a user is logged in or not. In most cases you'll want to use the BasicAuth subclass, but RegAuth may be more appropriate in some cases.

Finally,

class foo extends NoAuth (
 var $presentor = "htmlPresentation";
 function Run(&$db,&$u,&$arg,&$t) {
  $t["bar"] = "hello";
 }
}

is a way to perform no access checking on this particular module.

Method arguments

In the examples above, you'll notice that the Run method is accepting four arguments. The $db, $u, $arg and $t arguments are standard, and must be accepted by your methods in that order. The four arguments are:

  • &$db - The primary database object.

    Properties and Methods
  • query($sql) - Standard SQL query method.
    Example: $db->query("select * from customers");

  • queryOne($sql) - Used when only getting back one record.
    Example: $db->queryOne("select * from customers where username='acme'");


  • next_record() - Used in conjuction with the while loop to move to the next record in a query result.
    Example: while ($db->next_record()) { echo $db->Record['firstName']; }


  • getHandle() - Used to get database handle or connection. Can be called statically like "$db = DB::getHanlde();". Use this method when you need to process multiple queries to multiple tables.
    Example: $db = DB::getHandle();
    $db2 = DB::getHandle();

  • &$u - The user object created by the system. The object contains the user's name, group membership, session data and other information specific to this user.

    Properties and Methods
  • bindSession() - Links an already started session with a registered user. Sessions can exist w/anonymous users, this function will link userdata to the open session and destroy any multiple logins. Normally only used during authentication.
  • update() - Saves the user's groups, email, username, and password to the lcUsers table. Does not affect the user's profile. Used in the Hercules Control Center to update user information.
  • updateProfile($profile) - Saves the user's profile information where $profile is an array matching the table lcProfile. Profile information is stored separate from user information.
  • saveSession() - Called automatically at the end of every page to save anything placed in the property $sessionvars.
  • destroySession() - Removes the current user session from database. If you want to keep the data for the session for debugging purposes called endSession().
  • addUser() - Adds a new user to the system.
  • &$arg - The system object which contains the arrays $postvars and $getvars, $uploads. Also contains template name, module information, etc. Here is the datastructure of the system object along with the user object.
    lcsystem Object
    (
        [postvars] => Array
            (
            )
    
        [uploads] => Array
            (
            )
    
        [getvars] => Array
            (
                [0] => news
            )
    
        [module] => welcome
        [templateName] => news
        [module_root] => /cvs/services/welcome/
    	[user] => lcuser Object
            (
                [username] => anonymous
                [groups] => Array
                    (
                        [0] => public
                    )
    
                [sessionvars] => 
                [_sessionKey] => 94eda77de12ec3ece546c9c8148a7fbe
                [perms] => Array
                    (
                        [0] => access
                    )
    
            )
    	
  • &$t - Refered to as the "template" variable, $t is an array which holds all data you wish to pass to the template system.
    Example: $t['title'] = 'Welcome to my Home Page';

Also note that all arguments are prepended with a & - this is necessary to indicate that they are being called 'by reference'.

Example:

class foo extends BasicAuth  {
var $presentor = "htmlPresentation";
 function Run(&$db,&$u,&$arg,&$t) {
  if ($arg->postvars["checkSales"] == "yes") {
     $db->queryOne("select sales from tableX");
     $t["results"] = $db->Record["sales"];
     $t["username"] = $u->username;
  }
 }
}

In this example, we check the value of 'checkSales' which we're expecting to be POSTed from the client. If it's 'yes', we then run a database query to grab the sales information, then place it in a template variable called "results". We also put the user's username in the template variable called "username". Now in our HTML template, we can simply reference $t["username"] and $t["results"] to have those values presented.

Presentor variable

You've seen a $presentor variable set in the previous examples and are probably curious as to what it does. The $presentor variable indicates to the LogiCreate engine which set of code to run during the template processing phase. In most cases, the standard "htmlPresentation" value will be fine - this uses a standard header and footer and helps keep a uniform look. However, if you wanted to skip the header and footer, you could use

var $presentor = "emptyPresentation";

which will render the content portion of the page, without the common header and footer.

Presentation variables ( or handlers as we refer to them ) are defined in the "lib/pellet.php" file. By default there are the following presentation handlers defined in LogiCreate. If you wish to add new ones it is suggested you add them to "changes.php" so future upgrades will not overwrite your work.

  • htmlPresentation - default presentation handler used in conjuction with assigned template
  • errorMessage - uses assisgned template with header / footer and displays error message
  • emptyPresentation - use for things that do not require header / footer, for example a pop up window or a self contain file
Internal Debugger
If you wish to activate the debugger to see all of the system information, set the $presentor variable to "" or null. Not defining a presentor at all will cause the system to go into a 'debugging' mode, echoing out a structure of the system input (postvars and getvars) and the output that was intended for the template process. This can be useful to get a quick, structured look at what's going on in your applications during development.

Registry overview

LogiCreate stores some basic information about modules in the system table lcRegistry. Currently the index.php file handles mappings between the requested module name in the URL and module-level configuration settings editable through the Hercules control panel. The registry also contains information about the module's author, the creation date, and the internal database key assigned to the module. As more functionality is added to future versions of LogiCreate the registry will become a more crucial operating component.

Security considerations

By default, LogiCreate allows an administrator to grant access to a module based on group permission. If you need security that is more fine-grained than that (for example, at the data level) you'll need to spec out your requirements before coding.

Requirements for a pellet module

Main.php class, main.html template, and same for optional /admin folder

Writing a basic module

Creating the directory structures

To start with, you will need to create a directory structure in the 'services' directory with the name of the module you are planning to write. The name of the module, the directory name, and the main class name must all be the same. To help speed things up, we've included a skeleton structure for you in the services directory called 'skel'. Let's create a module called 'first' by first copying the skeleton structure to 'first'.

cp skel/ first/ -R

If you now look in the 'first' folder, you'll see the main.lcp file, a templates directory, and a main.html file in that templates directory.

Registering the application

Before you go any further, it's a good idea to register the module in the system registry table as well. Via MySQL, run the following command

INSERT INTO lcRegistry (mid, moduleName, displayName, author, copyright, perms, lastModified, showInMenu) VALUES ('first', 'first', 'My first module', 'My name', '2002', '', '', 1);

Change 'My name' to your name and submit the command to MySQL.

Edit the class file

Go into the 'first' folder and edit the main.lcp file.

<?
/*
* MODULENAME
* created MODULEDATE
*/
    class MODULENAME extends BasicAuth {
         function MODULENAME() {
         }
         function Run(&$db,&$u,&$arg,&$t) {
         }
    }
?>

Change the MODULENAME to 'first' and the MODULEDATE to today's date, and for purposes of this exercise, change BasicAuth to NoAuth.

Now we simply need to come up with a suitable task for our first module. A simple "hello world" might be almost too easy here: printing out a static piece of text is something which should be handled in the template file itself, not in the logic portion of the code. Let's print out today's time and date as well as "hello world". The time and date can be determined using PHP's date() command, and placed in the template variable.

Our "Run" method should look like this:

function Run($db,&$u,&$arg,&$t) {
              $t["datetime"] = date("m/d/Y h:i A");
         }

That's it for the logic portion of the module. Let's open up the corresponding template file and place our display code in there. After editing the main.html file, it should look something like:

Hello World!  The current date and time is <?=$t["datetime"];?>

And that's it for the template portion!

Viewing the results

Let's look at the results of the module by going to:

http://www.domain.com/index.php/first/main

You'll notice that you probably didn't see what you'd expect. Because we've not defined a presentation handler, we're seeing the raw internal view of the system data. As mentioned before, this can sometimes be useful for debugging modules, but it's not what we wanted! To remedy this, add the line var $presentor="htmlPresentation" to your module. Your module will now start with:

    class first extends NoAuth {
       var $presentor = "htmlPresentation";

Reload the URL in a browser and you'll see

Hello World!  The current data and time is 5/2/2002 10:43 AM

Remember we changed BasicAuth to NoAuth? If you switch NoAuth back to BasicAuth and reload the page in the browser, you'll get an error stating that you do not have permission to view the page. That's because the BasicAuth class is checking to see if your user groups have 'access' permission to the 'first' module. Even though you're simply in the system standard "public/anonymous" group, that doesn't grant you access to the module.

A central tenet of LogiCreate is that you need to explicitly grant permission to modules - the default behaviour (with BasicAuth, the default skeletal module building block) is to deny unless granted access. This is in direct contrast to many other environments, including web servers, where you are assumed to have access to data and files unless you are prohibited from doing so. With LogiCreate, you are prevented from doing anything unless otherwise permitted. It can seem cumbersome at times, but strong security habits are worthwhile in the long term.

Enhancing our first module

So we've now built a simple module which detrmined the current date and time then passes that information to the template system for display. Obviously most scripts are much more complex than that. As a sample of something a bit more complicated, let's create a basic album collection database. We'll create a form to display a collection of CDs, a form to add a new album, and the ability to delete an album. This will demonstrate usage of the database object and a method of handling form data from the user.

Below, we'll show the code for this module, followed by the template files.

<?
/*
* first
* created 5/2/2002
*/
    class first extends NoAuth {

        var $presentor = "htmlPresentation";

		 function first() {
         }

        function Run(&$db,&$u,&$arg,&$t) {
            $db->query("select albumKey,albumName,albumArtist from cds");
            while($db->next_record()) { 
                $t["cds"][] = $db->Record;
            }
            $arg->templateName = "main";
        }

        function addRun(&$db,&$u,&$arg,&$t) {
            extract($arg->postvars["input"]);
            $sql = "insert into cds (albumName,albumArtist) values ('$albumName','$albumArtist')";
            $db->query($sql);
            $t["message"] = "Album added";
            $this->run($db,$u,$arg,$t);
        }

        function deleteRun(&$db,&$u,&$arg,&$t) {
            $key = $arg->getvars["albumKey"];
            $sql = "delete from cds where albumKey = $key";
            $db->query($sql);
            $t["message"] = "Album deleted";
            $this->run($db,$u,$arg,$t);
        }
    }
?>
The main.html file would look like this:
<? if ($t["cds"]) { ?>
<table border="1">
<tr>
    <td>Album name</td>
    <td>Artist name</td>
    <td>Del</td>
</tr>
<? while (list($key,$val) = each($t["cds"]) ) { 
extract($val); ?>
<tr>

    <td><?=$albumName;?></td>
    <td><?=$albumArtist;?></td>
    <td><a href="<?=APP_URL."first/main/event=delete/albumKey=$albumKey";?>">del</a></td>
</tr>
<? } ?>
</table>
<? } else { ?>
There are no CDs at this time
<? } ?>
<form method="post">
<input type="hidden" name="event" value="add">
Album name <input type="text" name="input[albumName]"><br>
Artist name <input type="text" name="input[albumArtist]"><br>
<input type="submit" value="Add this CD">
</form>
Lastly, you'll need a database table for the CD information. If you're using MySQL, run the following command to create a new table:
CREATE TABLE cds (
albumKey INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
albumName VARCHAR(50) NOT NULL, 
albumArtist VARCHAR(50) NOT NULL,
INDEX (albumKey)
); 
You should be able to access your new module by going to:
http://www.yourdomain.com/index.php/first

Library overview

LogiCreate includes some standard system libraries designed to make application development easier. This section will include an overview of these, with some code examples to help illustrate their usage. This is not intended as an exhaustive analysis of functionality, but should be enough to help you understand their basic usage.

The database object

The database object abstracts the database function calls away from the developer, meaning less time is required to learn the syntax of the various PHP database functions. Rather than learning the syntax for the various functions to access PostgreSQL, MySQL, ODBC, MSSQL and others, you are presented with one standard syntax to use in your code. Should the need arise to port your applications to another database, in many cases no code in your application need change at all - the only thing you would need to change at the application level would be the database library.

Fortunately, LogiCreate provides a number of database libraries for some of the more popular databases on the market, including MSSQL, MySQL, PostgreSQL, ODBC and Oracle. Although some SQL code in your applications may need adjusting to work with a new database, and the database tables themselves would need to be created in the new database, the mechanism to connect, send queries and retrieve results remains the same in your LogiCreate code.

Let's cover the basics of dealing with the LogiCreate database object. First, every database object needs to know the name or address of the database server, the name of the database itself, a username and a password. These rarely change, and Wa are normally set in the core database file itself. See the LC_db.php file in the logicreate/lib folder for more instructions.

Every LogiCreate request creates a database object which is passed to any module that needs it (recall the 4 standard arguments passed to each method). To run a query against the database, pass the SQL to the query method of the object like so:

$db->query("select foo from mytable");

You've successfully asked the database to grab the data from every column 'foo' found in the table named 'mytable'. Now, we'll need a way to get the data from the object into our application. The database object has a method called 'next_record()' which returns an array of the data in the current row of the database result set. If there are no records left in the result set, the next_record() method will return 'false', which makes it pretty easy for us to loop through while the object has more results for us. For example:

$db->query("select foo from mytable");
while ($db->next_record() ) {
     $fooValue = $db->Record["foo"];
     echo $fooValue."<BR>";
}

In the example above, the program will loop through every value for the column 'foo' in the table 'mytable'. During this looping, we grab the value 'foo' from the associative array "Record" in the database object then print it out to the browser (you'd normally want to store the values in the template array instead of printing it out to the browser).

The above example is fine for looping through multiple rows, but what if you know for certain that you'll only get one row of data back? Or what if you specifically only want to deal with the first row of results? LogiCreate provides a method called 'queryOne' which performs the query method then advances the results pointer and stores the information in the $db->Record array, all in one step. To show an extremely brief example of this:

$db->queryOne("select count(users) from mytable");
echo $db->Record[0];

Note that in the above we've referenced the ordinal position of the data in the $db->Record array, because the database most likely wouldn't assign a specific name to that piece of data, only a position. Many databases will let you "select ... as foo", so we could do the following instead:

$db->queryOne("select count(users) as usercount from mytable");
echo $db->Record["usercount"];

Nested queries

LogiCreate's database object handles nesting queries for you, such that if you needed to do another query while in the middle of looping through an earlier result set, the system won't get lost as to what data it's dealing with. Example:

$db->query("select * from orderinfo");
while($db->next_record()) {
     $name = $db->Record["name"];
     $shippingNumber = $db->Record["shippingNumber"];
     $db->query("select notes from orderstatus where number=$shippingNumber");
     while($db->next_record()) {
          $notes .= $db->Record["notes"];
     }
     echo "Name: $name<BR>Ship no: $shippingNumber<BR>Account notes: $notes<hr>";
}

NOTE: queryOne() and selectOne() may not be compatible with nested queries. If you experience problems, add an extra array_pop($db->resultSet); either to your code or directly into the LC_db library.

Database handles

PHP creates resource handles when talking to external entities such as the filesystem and databases. The LogiCreate database object handles the complexities of the resource handles automatically, creating and deleting them as required. However, if you need to access a database handle to the current database connection, (for example, in a function call where a database handle is not explicitly passed in as an argument), you can use the following:

$dbh = DB::getHandle();

You may then use that object as a separate database object, as follows:

$dbh = DB::getHandle();
$dbh->query("select foo from tablex");
...etc...

HTML functions

In the LC_html.php library, there are some functions aimed at making life a bit easier. The library file contains standard documention with each function, but we'll cover a couple here to give you an idea of what's included.

Make options

One of the standard HTML form components is the option box. An option box contains multiple elements selectable by the end user, and is normally rendered on the screen in the form of a pull down. By passing in an array to the makeOptions function, LogiCreate will return a complete set of option tags, ready to be dropped into a template. Additionally, you may include a key or array of keys which are to be shown as 'selected' in the option box. If there are 5 color choices, and you know that someone has selected color number 3 you can preselect option 3 again by passing it in the argument list.

Examples:

$array["car1"] = "Ford";
$array["car2"] = "Nissan";
$array["car3"] = "Volkswagen";
$options = makeOptions($array);
The $options variable will now contain:
<option value="car1">Ford
<option value="car2">Nissan
<option value="car3">Volkswagen
If we passed the function call the key "car2":
$options = makeOptions($array,"car2");
we'd get back
<option value="car1">Ford
<option value="car2" SELECTED>Nissan
<option value="car3">Volkswagen
Finally, if we passed an array of keys to the function call:
$options = makeOptions($array,array("car2","car3"));
we'd get back
<option value="car1">Ford
<option value="car2" SELECTED>Nissan
<option value="car3" SELECTED>Volkswagen

Date dropdowns

Another special use of option dropdowns is when displaying date information to users. You've most likely seen these used on travel reservation sites or similar systems - three dropdown boxes corresponding to month, day and year values. Creating those dropdown values isn't that difficult, but it can be tedious and timeconsuming. LogiCreate provides a standard function to process a date and return those dropdowns for you in HTML, ready to be placed in your template file.

Consider the following code:

$datehtml = dateDropDown("5/2/2002");

You would get back a set of HTML in the $datehtml variable which would render 3 dropdown boxes - the first one as a list of months with May preselected, the second as a list of day values from 1-31 with "2" preselected, and the third as a range of years from 2002-2007 (5 years in the future).

You can also place textual dates and Unix timestamp numbers in the argument list and LogiCreate will attempt to parse them. For example:

$datehtml1 = dateDropDown(1021048948);
$datehtml2 = dateDropDown("February 12, 1998");

are both valid as well.

LogiCreate will give the dropdown boxes names which may be passed back in FORM data to another module through the $arg list. The fields will be elements of one array, with a default name of "date". This is the second argument in the dateDropDown function and can be overridden with another text string. The names of the resulting select tags will be named in such a way that the resulting php variable is a hash such that
$date[month]
$date[day]
$date[year]
are valid php values.

Lastly, the default year range is the current year through five years in the future. If you want to override these, the third and fourth parameters in the dateDropDown function correspond to the starting and ending year. For example:

$datehtml = dateDropDown(time(),"dates",1990,2030);
would return a dropdown set defaulting to today's month, day and year, but the year list would stretch from 1990 through 2030.

Persistent Object

LogiCreate provides a class to help ease object => RDBMS translation. The "Persistent Object" class provides a simplistic attribute-to-column mapping for arbitrary objects to an SQL table. Objects created from PersistantObject subclasses inherit the following methods: _load(), _save(), _delete(), and _getTransient(). The methods _load() and _delete() are meant to be called from a static context.

Example:

$dataObj = PersistentObject::_load("myClassName","dbTableName",50);

This would create an object of type "myClassName" and return the row of data from the table dbTableName where the primary key was equal to '50'. If the "myClassName" was an existing class definition, the object would become another instantiation of that class. If the "myClassName" was not an existing class definition, the object returned would be a standard object, with no available methods and no other properties beyond the database table column names.

To grab only certain fields, pass in a comma-delimited string with the desired field names as an additional argument. By default, the primary key name which LogiCreate looks for is "pkey" ("primary key"). This can be overridden as the last parameter in the argument list. For example:

$dataObj = PersistentObject::_load("myClassName","invoices","80596","invoiceNo,amount,custNo","invoiceNo");

would return an object with the properties "invoiceNo", "amount" and "custNo" where the invoice number was "80596".

The Persistent Object class also provides for saving data in the object. The only argument required is the table name to save to. For example, to reduce the amount of invoice number "80596" by 50:

$dataObj = PersistentObject::_load("myClassName","invoices","80596","invoiceNo,amount,custNo","invoiceNo");
$dataObj->amount = $dataObj->amount - 50;
$dataObj->save("invoices");

Most objects that are designed to be persistant objects are better off not having a constructor as the PersistantObject::createFromArray() calls empty constructors. If you require special pre-instantiation logic it is suggested that the _load() function be overridden, the same holds true for the _save() function. A common use for overriding the _load() and _save() functions is to make sure the objects data is ready for an SQL table, date formatting and array serialization are just two examples of preparing php data for SQL table storage.

TreeList and Category

These two classes work in conjunction to provide arbitrary and hierarchical labels to any application. The Category class uses the TreeList class to organize text labels that are grouped into category 'systems'. A category system is meant to tag categories for use within a certain application and guarantee uniqueness of category names across distinct applications. For example, the photo gallery application may have a gallery titled 'International', while the news system may also have a category titled 'International'. In order to keep track of which category belongs to which application, the first category would have a system called 'photo', whereas the second category's system would be 'news'.

The TreeList class can be used independantly of the Category class, as it is in the menu object, to provide hierarchical data orginazation resembling the layout of the expanding/collapsing folders used in most filesystem browsers. This is not a traditional tree structure used for searching or sorting, like a red-black tree or a binary search tree. The TreeList comes with two functions for initial population, loadData() and loadObjects(). loadData() expects its arguments to be php arrays with the keys 'pkey' and 'parentKey' being the signal to link one piece of data as a child of another. The loadObjects() expects to recieve an array of objects where each member has a ->pkey and ->parentID describing the relationship. The loadObjects() is specifically tailored to work with LogiCreate's menu system and the loadData() is tailored for the Category system. Writing alternative load functions should not be troubling if you use one of the existing loaders as a starting point.

Extras

Here are a few extra pieces of information which you may find useful.

Development suggestions

Changing core libraries

It is suggested that you take the following steps if you are planning to make any modification to the core libraries.

  • Make an empty file called "changes.php" and store this in the standard /lib directory
  • Edit the LC_include.php file to include the changes.php file

Any changes you make to core library files should be removed and placed in the changes.php file. This will allow a smoother upgrade path when new library files are shipped. You will still need to make some modifications when those upgrade files arrive, but the changes you made will be safely stored in the changes.php file, safe from accidental overwriting.

Debugging

In addition to removing the $presentor variable in a class to show debugging information, you may also wish to remove the @ symbol from the include() function in the index.php file. This is normally on line 146, and will show you any error message the system may have during the loading of a module. If there are syntax errors in the module, or something else preventing it from loading, the default system behaviour will simply show a 'this module does not exist' error message. This can be frustrating if you know that the module does in fact exist. Removing the @ symbol during development will show you if there are any error messages during the loading of that module.

System variables and constants

Here are a number of predefined system variables and constants to help make development easier.

Constants

BASE_URL

The base URL of the domain - http://www.mydomain.com/ for example

SECURE_BASE_URL

The same as BASE_URL, but using https:// instead of http:// (for SSL)

APP_URL

The BASE_URL, but with the LogiCreate engine file appended on. This is normally "index.php", so from the BASE_URL above, APP_URL would be http://www.mydomain.com/index.php

SECURE_APP_URL

Same as APP_URL, but with https:// instead of http:// (for SSL)

TEMPLATE_URL

URL of the template path used for the common system header and footer. Depending on the template being used, TEMPLATE_URL might be http://www.mydomain.com/templates/template1/ or http://www.mydomain.com/templates/template2/, for example, resulting in potentially very different-looking pages.

IMAGES_URL

URL of the system images path. This does not point to images in the template directories, but only in the main system /images path. To continue our example, IMAGES_URL would point to http://www.mydomain.com/images

CONTENT_PATH

This points to an internal directory which holds arbitrary content files for the LogiCreate applications. Typically this is outside of the webserver's "document root" path, meaning these files can't be accessed except via a LogiCreate application.

LOG_USAGE_DB

True/false flag to determine whether or not to log each request to the lcLogging table.

LOG_EXECUTION_TIME

True/false flag to determine whether or not to log each request's execution time to the lcLogging table. Only checked if LOG_USAGE_DB is true.

LOG_OUTPUT

True/false flag to determine whether or not to log each request's browser output to the lcLogging table. Only checked if LOG_USAGE_DB is true.

Variables

You'll see things referenced here as $obj->name - this is interchangeable with $arg->name. In the LogiCreate system, $obj, $lcObj and $arg are used to represent the same object, but the context dictates the name you'll use. This is known to be a confusing issue and will be addressed in future versions of the system.

$obj->templateStyle

This controls which template folder is used for the header, footer and stylesheet file when rendering templates. By default it is set to 'default'.

$obj->cssFile

This controls which stylesheet file is loaded in the templates. By default it is set to 'site.css'. You can hav e multiple stylesheet files in one template folder, switching based on user preference, for example.

Defining Modules Properties

LogiCreate has an easy way for developers to define properties for each application. If you have familiarized yourself with the Hercules Control Center you will have noticed a button labeled "config". This button when selected will display a set of properties which can be defined for the selected module. For example, the HTML module has a built in service called "formToEmail.lcp" which processes forms which need to be emailed such as "contactus.html". A configuration variable for the formToEmail service was created called "_ToEmail" which is the default email address all HTML forms will sent to. By simply adding new records to the lcConfig table in your database, these new properties are automatically passed to the module you are working in as properties of that class. Example code from formToEmail.lcp:

mail($this->_ToEmail, "New Input from ".BASE_URL, "$body", "$headers");

If you are building an application which needs to have their properties changed, get into the habit of defining your properties in the lcConfig table for your specific module. The fields of the lcConfig table are as follows:
  • mid - Module ID name
  • k - Property or variable name you wish to defined (example: _ToEmail)
  • v - Value of the property
  • type - Reserved for future upgrade path. (example: int, string, email)

Extending the Search Engine

LogiCreate comes with a pre-built module called the search module which allows users to search all portions of your web site. The search module or manager job is to take pieces of content from certain database tables which are associated with different application and fulltext index the content. There are typically three search plugins which come with a LogiCreate install: html, FAQ, news. As you create new modules or applications you may want these applications indexed for searching as well. As a developer all that is required to add new content to the search module is to create a search plugin in the admin directory in the search module. Save the file with the extension of .lcs so the system will automatically find the file. Example search plugin:
<?
class FAQSearchTool extends searchTool { // notice the naming convention

	function FAQSearchTool() { // define table properties to index
		$this->moduleName =  'FAQ';
		$this->tableName  =  'lcFaqs';
		$this->keyName    =  'pkey';
		$this->mid	  =  'faq';
		// number of records available for indexing
		$this->getCountSQL = 'Select count(pkey) from lcFaqs';
	}

	function initQuery() {
		$this->db = DB::getHandle();

		// SQL to access data to index
		$this->db->query('select * from '.$this->tableName);
	}

	function nextItem() {
		$tmp = new SearchEntry();
		if (!$this->db->next_record() ) {
			return false;
		}
		$tmp->searchdata = $this->db->Record[question] . ' ';  // store question in data

		$tmp->searchdata .= $this->db->Record[answer];  // store answer in data
		$tmp->searchdata = searchTool::stripNoiseWords($tmp->searchdata); // clean data

		$tmp->title = $this->db->Record[question]; 
		$tmp->type = 'FAQ';
		$tmp->link = 'faq/main/'.$this->db->Record[pkey].'/event=read'; // link to content
		$tmp->modID = $this->mid;
		$tmp->mkey = $this->db->Record[pkey];
		$tmp->groups = $this->db->Record[groups];  // groups/notgroups which can/can't view content
		$tmp->notgroups = $this->db->Record[notgroups];  
		return $tmp;
	}
}
?>
In the above directory this file is called FAQ.lcs and is saved to search/admin/FAQ.lcs. To test the module select the search module in the Hercules Control Center and press "admin". You should see your new search plugin listed. If you do not, check the permissions on your .lcs file and check the naming covention of your class.

Frequently Asked Questions

What global variable exist that we should be aware of?

By default no global variables are used – everything is passed between object methods by value or reference. Many of the variables that would normally exist in a 'global' setting (the input values from forms, for example) are store in the system object's 'getvars' and 'postvars' arrays (example: $arg->postvars['myName']).

There are a number of values that exist by default in a global setting which can be pulled in to a specific method by using the 'global' function of PHP first. For example, you may wish to know the IP address of the visitor. This is available as $REMOTE_ADDR, and could be used in a method by calling:

global $REMOTE_ADDR;
echo “Your IP is $REMOTE_ADDR”

Many of these values are not stored in the system object because they are normally used in frequently and passing around many extra values incurs unnecessary overhead. As PHP continues to develop, performance may be less of an issue and more system-level values may be placed in the LogiCreate system object. If you find that you will be needing a value (the $REMOTE_ADDR, for example) in numerous modules, you can edit the index.php file to place the $REMOTE_ADDR value directly into the $lcObj object. Remember, in the index.php file, the system object is referred to as '$lcObj', but depending on the module and method, it may have a different name ($arg, for example) – it's always the third parameter passed into a module, however.

What objects and variables are available to templates? We’ve seen references such as $obj->user->username and would like to know what else is out there. Is this $obj available under both module and template contexts?

The '$obj' object is synonymous with the '$arg' or '$lcObj' you may see in various pieces of the system. When passed to the template engine, the user object is placed into the system object ($obj in the template) so the template engine has access to it. All properties of the user object are available in the template by accessing the $obj->user object. By default this and the $t (template) array are the only pieces of data passed to the template system.

What is areaTitle($TEMPLATE["messagetype"]) referring to and how does $TEMPLATE relate to $t[ ]? And $TEMPLATE["t_user"]->is_public?

The 'areaTitle' is a function call intended to create specific HTML output with the 'messagetype' message. It is not indicative of something which is a system requirement or default function.

The ->is_public() is legacy code which is slated for removal. It should be replaced with an in_array(“pub”,$obj->user->groups) reference.

Where do we define the global page layout/template?

public_html/templates/<templatename>

How do we embed templates within templates and choose which template displays? We see the lcObj->templateName, but when looking in the forum code as a sample there are more templates than those referenced in the main.lcp and the Forum.html seems to override the global page layout somehow.

By default, the system's templateName value will be set to 'main' meaning the templating system will use the 'main.html' template if no other is specified. Various module events may choose to use different templates for specific reasons – in the forum system, for example, depending on which event I'm in (read message, show forum list, etc.) I may want to use a different template. The code to use a different template would be

$lcObj->templateName = “newtemplate”

You may also see it referenced as '$arg' instead of '$lcObj' (use whatever the third argument in the method's parameter list is).

Nested templates within templates is not supported, but you can include() other files within your template code directly if you so choose. You'll need to use the appropriate system constant. For example, if there was an extra template in the forum/templates directory you wanted to include in the main forum template page, you'd write:

include(SERVICE_PATH.”/forum/templates/myfile.html”);

in your template. Be advised that this can become difficult to manage – use this approach with caution.

How do we interface with the security module from within code? For instance, is the current user a member of some group or not?)

The user object's 'groups' property is a simply array of the group ID codes to which that particular user belongs. To test for membership, simply use PHP's 'in_array' function. Example:

<? if (in_array(“admin”,$u->groups)) { echo 'do something'; } ?>

Is there a way to store custom fields with a user profile, like associated customer number and name, so that they are available to the global template for display on every page?

User profile information is held in the user object's 'profile' property, which is an array of profile information about that particular user. The array is built during user creation based on the fields in the 'profile' table. Adding new fields in the 'profile' table will automatically create new entries in the profile array which will be saved when the updateProfile() method of the user object is invoked.