Previous Chapter
Next Chapter
Table of Contents
Index

Module types
Calling sequence
Module writing
   Module fundamentals
   Callback functions
   The complete module
   Returning values
Module variables
Wizards
   Wizard.pike functions
Using your module

Roxen module programming


Roxen is a very flexible information management engine as it is. In addition to that, we provide you with an easy way of programming new features into Roxen - and you can add them easily even while the server is running.

This text assumes that you have a basic knowledge of programming in general and of Pike in particular. If you haven't, check out the web pages at http://www.roxen.com/documentation/pike_tutorial. Good luck!

Module types

The three most common types of modules are file extension, location and parser modules.
  • A location module handles everything under a certain directory in the virtual file system, for instance /cgi-bin/. It is usually this type of module that finds files and passes them on to the different extension modules.

  • A file extension module handles files of one or several extension types, e.g. .cgi, after a location module has located the file.

  • The main parser module is where all RXML parsing is organized. This module keeps track of which parser modules are currently loaded, and calls the appropriate parser module when there is a RXML tag to be parsed. A parser module defines one or more RXML tags. When a parser module is loaded, it registers itself with the main parser module.

    There can be any number of parser modules, but only one main parser module. The main parser must also be a file extension module or a location module, or at least an extension (without "file") module. Otherwise, it'll never be called, and will not be able to do any parsing (See the section on module calling sequence below).

There are also several module types which are used less frequently;
  • Authentication A module type that handles authentication of users. It also keeps a database of users to be used by, for instance, the user file system module (which is an example of a location module).

  • Directory This type of module handles file listings. If you don't have a module of this type, Roxen won't be able to generate file listings of directories, and the automatic use of index files, like , won't work. There can only be one instance of this type of module present in each virtual server.

  • Extension This is a kind of extension module that, just like the file extension module, looks at the extension of the requested file to determine whether it should do something with this particular request. However, unlike the file extension module, this module is called before the location module, and so there is no actual file object to work on. This is useful for generating data on the fly for virtual files.

  • First try A module type that is called before all other module types, except for Authentication type modules. This is useful when you want to make an exception for a certain type of request. You may catch it here and thus bypass the normal module request path.

  • Last try A module that is called after all other modules, if none of the other modules could handle the request.

  • Filter This type of module is called after all the other modules has treated a file or request, but before the last try modules. The filter modules are always called, even if some other modules managed to resolve the request.

  • Types This module type handles extension to content-type mapping in those cases where the modules that have already run have not told the system what content-type the file has. For example, the file things.html should have content-type text/html.

  • URL This type of module receives one URL and returns another. This module type should be used if you wish to implement redirecting modules of any kind.

  • Logger A module type that should be used for implementing logging modules. Logger modules are called at the same time as the response is sent out.

A module can have one or more module types. Modules can even lack type entirely. This is quite pointless, though, since such a module would never be called by Roxen. To see why, refer to figure 13.1, which is a flowchart that shows how the modules are called during the treatment of a request. The section following that is a written description of the procedure. Note how different module types are called at specific phases in the parsing of a request. A module without a type, or a module that only has types that are not a part of this sequence (e.g. parser modules) is never called.

Do you need to know this, then, in order to program your own Roxen modules? Strictly speaking, no. If you know what kind of module you intend to make and register it properly (see the beginning of "How to write a module"), Roxen will call it when it is time and take care of the output it generates. However, it will come in handy when debugging or just as a source of information about the internal workings of Roxen.

Figure 13.1 Module calling sequence.

Calling sequence

The following is a description of the order of calling modules in Roxen. Generally speaking, an incoming request passes through a number of type levels, which will be described in turn. A failure at a type level means that none of the modules of that type could treat the request. The case where there are no modules of a certain type is a trivial case of failure.

A failure usually means that the request is passed on to the next level. What happens when a module succeeds in treating the request depends on the level, and will be described in each case.

Protocol
This is the arrival point of the request. The protocol modules decide what protocol is to be used, and passes on the request accordingly.

Authentication
If the request needs to be authenticated in any way, this is done here. Regardless of success or failure, these modules set a flag and pass the request on.

First try
If the request is successfully handled by a first try module, it is passed directly to the filter type level.

URL
The modules at this level try to remap the URL in the request to another URL. If this is a success, the request will be passed back to the top of this level again, in case the new, remapped URL should need to be remapped once more.

Extension
Modules at this level handle "imaginary" files. The idea is that the user may request certain information by referring to it as if it was contained in a file. The extension module looks at the file extension and then generates the information on the fly; That is, if the requested file has an extension the module can handle. That information is then passed directly to the filter level. Since the request has not yet passed any location modules, no actual file is associated with the request. Thus, no file object is sent to the module

Location
Here, the request is matched first against the location modules, then against the actual file system. There are four possible outcomes of this:

  • A location module that matches the request was found. This is normally used for virtual file systems, just as extension modules may be used for virtual files. The resulting data is passed to the filter level.
  • An actual directory was requested and found. The result is passed along to the directory level.
  • An actual file was requested and found. The result is passed along to the file extension level.
  • Nothing matching the request was found. No modules so far has been able to handle the request, and it does not match anything in the file system. The request is passed to the filter level.
Directory
The modules at this level try to generate a directory listing, if Roxen believes the request points to a directory. The result is sent to the filter level, regardless of whether this was a success or a failure.

File extension
A file sent to this level is taken care of according to its file extension, e.g. a module for parsing cgi scripts could be implemented as a file extension module to work on files with the extension .cgi. If it is a success, the result, along with a type marking, is passed to the filter level.

Content type
At this level, files with no associated file extension module are given a content type determined by its extension. Regardless of success or failure, the result from this level is passed on to the filter level.

Filter
These modules take care of any final modifications or transformations before passing the result of the request back to the protocol modules. If the request has failed at every level before, and there is a failure here too, it will result in the request being passed along to the last try modules. If there aren't any last try modules, or there is a failure there as well, an error message will be the result.

Protocol and logger
Finally, the result is passed through the proper protocol module and back to the user. If any applicable logger modules are present, the request will be logged.

That was the theoretical part. Now, let's go on to some practical examples.

Module writing

First of all, configure a test server. In this test server it would be useful to change the variable Module directory under Global Variables, which tells the server where to look for modules. This is a comma separated list of directories. You may want to add your own working directory for module programming to this list. This way you can keep your own experimental modules apart from the ones that are in daily use. The directories in the list can be given either as full directories or relative to the server directory.

Now you have all you need to write your own module. Below is a step-by-step instruction on how to accomplish this. Throughout it, we will assume that you already have a basic knowledge of Pike programming, and therefore we don't give a lot of detailed information. For this, refer to the Pike manual or the Pike web site on http://pike.idonex.com/. In addition, study already written Pike code, such as the source code to the various modules that are distributed with Roxen.

Module fundamentals

We start by introducing a module skeleton, containing all the necessary functions and inheritances for a module.


#include <module.h>
inherit "module";
inherit "roxenlib";
array register_module()
{
        return ({
                MODULE_TYPE,
                        "module name",
                        "module documentation",
                        0,
                        0|1,
        });
}
First the file module.h is included. It contains a lot of constant definitions, e.g. all module types. It also contains some macros that can be used whenever you want the value of a certain module variable.

After that, the file module is inherited. In this file, functions like defvar(), set() and query() are defined. It also contains several default values and values to use for checks in your modules.

The file roxenlib contains a lot of functions. Though it is not necessary to include it, it can be very useful. For ideas on how to use this file, please take look at section "Returning values".

The function register_module() is expected to return an array that describes the module. This array is to have five elements, defining the name of the module and if it can have more than one concurrent active instance in each virtual server, amongst other things.

The first element of the array is the module type. This is a bitwise-or (|) of module types. Usually each module has only one type, but not always. htmlparse.pike in the Roxen distribution, for instance, is a combination of a MODULE_MAIN_PARSER, a MODULE_PARSER and a MODULE_FILE_EXTENSION.

TABLE 13.1 Module type identifiers and their meanings.
Module type name Module Type
MODULE_AUTH Authentication module and user data base. There can be only one active instance at a time.
MODULE_DIRECTORIES Directory parsing module. Only one active at any given time.
MODULE_EXTENSION Extension module called before location modules.
MODULE_FILE_EXTENSION Extension module called after location modules.
MODULE_FILTER Used for "post-treatment" of files.
MODULE_FIRST First try modules.
MODULE_LAST Last resort modules.
MODULE_LOCATION Location module, finds files.
MODULE_LOGGER Logger module, used when implementing different logging schemes.
MODULE_MAIN_PARSER Main parser. Only one active at a time.
MODULE_PARSER Normal parser, handles one or more tags.
MODULE_TYPES Type module that handles extension to content type mappings.
MODULE_URL URL modules can internally rebuild requests.

The second element is the module name, and the third element is a short description of what the module does. The fourth element is reserved for future use. The fifth element is "0" if there can be multiple copies of the module in each virtual server (e.g. a location module) or "1" if there can only be one active copy in each virtual server (e.g. the Main RXML Parser).

Since this example is a module which adds tags to RXML, we choose the following look of the function register_module():


array register_module()
{
   return({
          MODULE_PARSER,
               "Gazonk",
               "The gazonk module. Adds a container and "
               "a non-container: "
               "<foo>...</foo> and "
               "<bar>",
               0,
               1,
  });
}

That's all that is needed for the module to register properly as a valid module. Still, it doesn't do very much in the way of providing new tags yet.

Callback functions

This brings us to the module specific callback functions. Table 13.2 is a general overview of the module types and which callback functions they require. This is followed by a more detailed description of each function. In several of them you can see the argument object request_id. This object is a representation of the information request sent to Roxen. It is described in more detail in the chapter Request-ID contents .

TABLE 13.2 Module type specific callback functions
Module type Initialization routines callback routines
All create, start, stop status, info, check_variable, query_name
MODULE_AUTH   auth, userinfo, userlist, user_from_id
MODULE_DIRECTORIES   parse_directory
MODULE_EXTENSION query_extensions handle_extension
MODULE_FILE_EXTENSION query_file_extensions handle_file_extension
MODULE_FILTER   filter
MODULE_FIRST   first_try
MODULE_LAST   last_resort
MODULE_LOGGER   log
MODULE_LOCATION query_location find_file, find_dir, stat_file, real_file
MODULE_MAIN_PARSER   add_parse_module, remove_parse_module
MODULE_PARSER query_tag_callers, query_container_callers Provided by the initialization routines
MODULE_TYPES   type_from_extension
MODULE_URL   remap_url
All modules

void create();
Called automatically by Pike when the object (instance of your module) is created. Here you define the variables that are local to the module with defvar(). Takes the config object as an optional argument. However, it is not recommended that you change any variables in the config object in create().

void start();
Called by Roxen just before the module should be ready to receive a request (requests are handled with the module specific callback functions). Takes an integer and the config object as arguments. If the integer is not zero, then this means some variables defined in create() has been changed. Obviously, this means that the first time start() is called, the integer is zero.

void stop();
Called by Roxen just after the module is done with a request (requests are handled with the module specific callback functions). Does not take any arguments.

string status();
Called by the configuration interface at any time to get the status of the module. Should return a string, containing HTML code that can be fitted in the <dd> part of a definition list.

string info();
If you define this function, its result is used instead of the third element in the return value of register_module(). If you look at the beginning of the example, you'll recall that this string was used to describe the module in the configuration interface.

string|void check_variable(string variable, mixed will_be_set_to);
If you need to check the value of your module variables before they are "permanently" set, this function is where you should do it. Usually you don't have to check on the validity of the values of variables, since all values that the administrator can set them to are valid. Writing string|void means that the function does not necessarily return a value.

string query_name();
Returns the name of the module, and is used instead of the second element in the array returned by register_module(). This can be very useful if you allow more than one copy of a module.
The user can, however, use any module name he/she wishes in the configuration interface.

MODULE_AUTH

array auth(array from);
array userinfo(string username);
array userlist();
array user_from_uid(int uid);
Usually, you won't need to implement your own user database and authentication module, and it is not part of fundamentals of Roxen programming. If such needs arise, please refer to the source of the userdb.pike module for further information on these functions.

MODULE_DIRECTORIES

mapping parse_directory(object request_id);
Given an internal url (in request_id->not_query) this function returns an HTML-coded directory listing or an index file depending on how the virtual server making use of the module is set up.

MODULE_EXTENSION
MODULE_FILE_EXTENSION

array (string) query_extensions();
array (string) query_file_extensions();
Returns an array with the extensions that this module is intended to handle. It is suitable to implement this as follows:

array (string) query_extensions()
{
        return query("extensions");
}
This requires that the variable extensions is defined as a TYPE_STRING_LIST (see below).

mapping handle_extension(string extension, object request_id);
mapping handle_file_extension(object file, string ext, object request_id);
Returns either a result or zero. Called by Roxen when the module should handle an extension.
The common case is to make file extension modules. In that case it's the second definition that applies. The object file is a clone of /precompiled/file, taken from Pike. This object contains, among other things, the method read().

MODULE_FILTER

mapping|void filter(mapping result, object request_id);
If you need to modify the data from the previous modules before you return it to the client, this is where it is done. The mapping result contains the data so far. If no other modules have been able to return any data, result will be nil.

MODULE_FIRST

mapping first_try(object request_id);
If a response mapping is returned, this will be used as the return value.
This function is never called by internal requests, i.e. the requests that are the results of using <insert> to incorporate another file in your document and similar things.

MODULE_LAST

mapping last_resort(object request_id);
Used by the relay module, if priority is set to last.
Called only if no other module has found a matching document, but never by internal requests.

MODULE_LOGGER

int log(object request_id);
Returns "1" if no further logging is to take place, i.e. when a request have been satisfactorily logged. Note that the request will not even be logged by the main logger when this is done.

MODULE_LOCATION

string query_location();
Returns which position in the virtual file system the module should have. Use a variable here since this lets the user choose where the module should end up.

object|mapping find_file(string file_name, object request_id);
Returns an open file object of the class /precompiled/file if there is a file matching file_name that this module handles, or a response mapping (usually generated with the http_* functions) if you do not wish that extension modules should be used. The mapping can be very practical if you want the user to enter a password, or in redirecting to a new file. Instead of using the clone() function you can use the much more intuitive new(File) in Roxen. File is a constant that is usable everywhere except when inheriting when you have to use /precompiled/file. A useful convenience function is open(filename, mode), which returns one of these open file objects.

Note that if the module has a mount point (the value that query_location() returns) /foo/ and the file /foo/bar/ is asked for, the location module will get bar/ as the first argument, not /foo/bar/, i.e. the mount point is stripped from the filename.

array find_dir(string dir_name, object request_id);
Returns an array with filenames if there is a directory matching dir_name. Otherwise it returns zero.
There is a default find_dir in module.pike that returns zero. So if you don't want directory listings, you don't have to define this function.

array stat_file(string file_name, object request_id);
Returns the result or zero if there is a file matching file_name.
If you don't want to, you don't have to define this function, but the directory module and some other modules might require this in order to function exactly as a "regular" file system.

string real_file(string file_name, object request_id);
Return what the file file_name really is called, e.g. if the module test looks for its files in /usr/www/foo/, and the file bar is requested, it should return /usr/www/foo/bar.
This method isn't necessary. For instance, it might be that your files really don't exist on the disk. However, it speeds up things like CGI and Pike scripts. Both of these work perfectly fine with only "virtual" files, though.

MODULE_MAIN_PARSER

void add_parse_module(object module);
void remove_parse_module(object module);
Register and unregister a parse module in the main_parse module. Only used by the main parser module. We recommend you to study htmlparse.pike for working examples.

MODULE_PARSER

mapping (string:function(string, mapping(string:string), object, object, mapping(string:string):string))
query_tag_callers();
mapping (string:function(string, mapping(string:string), string, object, mapping(string:string):string))
query_container_callers();
The query_tag_callers() and query_container_callers() functions are called from the main parser to register a parse module. The return value should be a mapping on the following form:

(["tag":function, ... ]),
or an empty mapping ([]).

MODULE_TYPES

array (string|int) type_from_extension(string ext);
Returns the file type of the requested file, based on its extension. The result is ({content-type, content-encoding}) or 0 (zero). There can, as noted earlier, only be one content-type module in each virtual server. This is a kind of fallback, to account for the case when none of the other modules could supply type information.

MODULE_URL

object|mapping remap_url(object request_id);
Returns request_id (with some variables modified), in which case it is used to make a new request. This means that the module will be called again, so be careful not to create an infinite recursion. It can also return a mapping with the help of some of the http_*() functions.
Finally you could return zero, in which case the program just continues.
Note that you can change variables in request_id and then return zero. This is not allowed in any other module type than MODULE_PARSER.

The complete module

Now, back to the example! As you can see in table 13.2 this type of module (MODULE_PARSER) must have the initialization functions query_container_callers and query_tag_callers. These functions define the actual effect of the two tags we wish to add. Let's start off with two empty functions:
mapping query_tag_callers()
{
}

mapping query_container_callers()
{

}
Then, we fill in the details and complete the module.


/* A simple parse type module for Roxen.
* This module defines two new tags, foo and bar.
* The first is a container and the second is a
* stand-alone tag.
*/
#include 
inherit "module";
inherit "roxenlib";
array register_module()
{
     return({
             MODULE_PARSER,
                  "Gazonk",
                  "The gazonk module. Adds a container and "
                  "a stand-alone tag: "
                  "<foo>...</foo> and "
                  "<bar>",
                   0,
                   1,
     });
}
/* A container gets the contents as the third
* argument. Example: <foo Bar=Gazonk>Hi!</foo> -->
* container_foo("foo", (["bar":"Gazonk"]),
* "Hi!", ...);
* Note the lowercasing of Bar.
*/
string container_foo(string tag_name, mapping
                     arguments, string contents,
                     object request_id, mapping
                     defines)
{
	  if (arguments->lower)
		  return lower_case(contents);
	  if (arguments->upper)
		  return upper_case(contents);
	  if (arguments->reverse)
		  return reverse(contents);
}
string tag_bar(string tag_name, mapping arguments,
		 object request_id, object file,
		 mapping defines)
{
	  int i;
	  string res="";
	  if (arguments->num)
		  i=(int)arguments->num;
	  else
		  i=30;
#define LETTERS
(("abcdefghijklmnopqrstuvwxyzåäö")/"")
	  while(i--)
	     res += LETTERS[random(sizeof(LETTERS))];
#undef LETTERS
	  return res;
}
mapping query_tag_callers()
{
	  return (["bar":tag_bar, ]);
}
mapping query_container_callers()
{
	  return (["foo":container_foo, ]);
}

}

Now we actually have a working parser module. If we start using the module (see "Start using your new module") you can enter <foo lower|upper|reverse>Some string</foo> in your pages which should give you the text "Some string", changed as indicated by the attribute; lower changes the string to lowercase, upper to uppercase letters and reverse reverses the string, printing it backwards.

If you put <bar> on a page, you'll get a randomly selected string of 30 characters. If you include an integer attribute, e.g. <bar num=17>, you'll get a randomly selected string of, in this case, 17 characters.

Returning values

It might be somewhat tricky, once you've found the requested data, to put together a mapping or structure of the kind that Roxen would like you to return. To alleviate this, there are several useful helper functions available, which you gain access to by inheriting roxenlib in your modules. These are described below.

Low-level calls

mapping http_pipe_in_progress();
Returns a structure that indicates to Roxen that it should leave this socket alone. This is normally not to be used, since it might cause a socket leak. Only use this if you're very sure about what you're doing. The best way to learn more about the use of this function is to study the source code to a proxy module.

mapping http_auth_required(string realm, string message);
Sends an auth-challenge to the client, from the realm realm. A default message that will be shown if the user choose not to send any password can be supplied.

mapping http_proxy_auth_required(string realm, string message);
Like http_auth_required(), but for proxies.

int get_size(mixed x);
This function accepts any argument, and it returns the memory usage of that argument.

int ipow(int n, int exp);
Returns n to the power of exp. There is, of course, also a pow(float n, float exp);

mapping build_env_vars(object id, string path_info);
mapping build_roxen_env_vars(object id);
Sets up the correct environment variables for cgi scripts. For further information, see the cgi module.

string sizetostring(int size);
Describes the size of anything, e.g. a file, an amount of data or whatever, as a string. Size is measured in bytes and the unit is reasonably intelligently scaled using kB, MB, etc.

File management

string short_name(string long_name);
Returns a short name, useful as a filename, from the given long name.
The short name is not really all that much shorter, but it is often easier to handle within a shell.

string dirname( string file );
Returns the directory name from the file file, just like the dirname program available in most Unix flavours.

string extension(string f);
Returns the extension of the file f.

extension("foo.gif");

gives

"gif"

extension("foo.html~");

gives

"html"

extension("foo.html.old");

gives

"html"

int backup_extension( string f );
Returns "1" if the filename of f indicates that it might be an backup copy of an original file.

string simplify_path(string file);
This one will remove ../, ./ etc. in the path.

simplify_path("foo/../bar/../")

gives

"/"

simplify_path("foo/../bar/././")

gives

"/bar/"

string gif_size(object o);
(o is an open file object pointing at a gif) Returns the size of a gif picture.

object o = open("foo.gif", "r");

returns file object o

gif_size(o)

returns

"width=10 height=20"

string program_filename(program p);
string program_directory(program p);
Given a program p, tries to find the file in which it is defined and the directory of that file, respectively.

URL management

mapping http_redirect(string url, object|void request_id);
Sends a redirect to the file url, which can be either relative or absolute. If relative, request_id must also be supplied to resolve the path correctly.

string add_pre_state(string url, multiset state);
Adds the prestate described in the multiset state to the URL url.

add_pre_state("/", (< "foo", "bar" >))

returns

/(bar,foo)/

string add_config( string url, array config, multiset prestate );
Adds a config modification and a prestate to a URL.

add_config( "/", ({ "+foo", "-bar" }), (< "foo", "bar" >));

gives

/<+foo,-bar>/(bar,foo)/

When this request is sent to Roxen, a redirect will be generated immediately to /(bar,foo)/, and the cookie config database will be modified.

string make_tag_attributes(mapping in);
Primarily used by the make_tag and make_container functions. Returns a list of tag attributes from the mapping.

make_tag_attributes((["plain":"plain", "foo":"bar", "hej":"\"hopp gazonk"]));

returns

'plain foo=bar hej="&quote;hopp gazonk"'

string make_tag(string name, mapping arguments);
Returns the string '<name arguments>' where arguments are encoded with make_tag_attributes.

string make_container(string name, mapping arguments, string contents);
Returns the string '<name arguments>contents</name>' where arguments are encoded with make_container_attributes.

Data formatting

string http_encode_string(string f);
Encodes the string f so that it can safely be used as a URL.

string http_encode_cookie(string f);
Encode the string f so that it can safely be used as a cookie value or name.

string http_decode_string(string f);
This one is of course the opposite of http_encode_string() but is seldom needed.

string parse_rxml(string what, object|void request_id);
Parses the string what as if it was generated as a reply from the request described in request_id and then sent from the the HTML parser. If no request_id is supplied, a dummy will be generated. In that case, tags like <clientname> will not work.
Can be quite useful in modules, but not in scripts, since if a script returns a string (i.e. does not use any of the http_*() functions to return a value, but instead simply returns a string) it will be parsed automatically.

string number2string(int n,mapping m,mixed names);
Converts the number n to a string, using the conversion indicated by m->type, and optionally the conversion function names. This is used in all tags in the main RXML parser that converts a number to a string. Look at htmlparse.pike for examples.

string image_from_type(string t);
Returns a suitable internal-gopher-* image from the mime type specified by t. The function is used by the directory modules.

string html_encode_string(string s);
Encodes an arbitrary string literal to protect it from being interpreted by a HTML parser, e.g:

pagestr += "<p>Current value: " + html_encode_string(value) + "<br>";

string html_encode_tag_value(string s);
Encodes an arbitrary string to make it safe for use as a HTML tag value, e.g:

pagestr += "<input type=text name=foo value=" + html_encode_tag_value(foo) + ">";

Note: The functions make_tag and make_tag_attributes does this transformation of tag arguments too.

Time handling

string cern_http_date(int t);
Formats the timestamp t according to the CERN common log-file date format.

string http_date(int t);
Returns an http_date, as specified by the HTTP-protocol standard. This is used for the Last-Modified and Time headers in the reply.

int is_modified(string a, int t, void|int len);
Is the time described in a before the integer time in t, as returned from time().
a is as given by the If-Modified: header, and if len is included in the header, and in the call as well, it is used as a first quick check to see if the file has indeed been modified.
You will probably not have to use this unless you are writing a proxy module or a protocol module.

string msectos(int t);
Returns a description of the time t, given in milliseconds.

string short_date(int timestamp);
Returns a reasonably short date string from a time integer.

string int2roman(int m);
Returns the integer m as a roman number.
That is, int2roman(10) returns "X", etc.

Returning data

mapping http_string_answer(string text, string|void type);
Convenience function to use in Roxen modules. When you just want to return a string of data, with an optional type, this is the easiest way to do it if you don't want to worry about the internal Roxen structures.

mapping http_file_answer(object file, string|void type, void|int len);
Like http_string_answer(), but it returns a file object instead.

mapping http_low_answer(int errno, string data);
Returns a filled out structure with the error and data specified. The error is in fact the status response, so "200" is "OK", and "500" is "Internal Server Error", etc.
Mostly used by the other functions in http.pike.

Strictly speaking, these functions don't all come from roxenlib. Some of them come from http.pike, some even come from Pike itself. Usually, it doesn't matter, though. If the distinction matters to you, we recommend you to take a look at the source code to determine what functions reside where.

Module variables

If you have studied the configuration interface, you have probably noticed that almost all of the included modules contain a plethora of variables that can be set by the administrator. To have the same kind of functionality in your own modules, use the function defvar() in create() to set the value. Use query() to get the value of one of the variables.

The function synopsis is:

int defvar(string name, mixed value, string long_name, int type, string documentation_string[, array choices, function|int hidden])

where:

name is the name of the variable, used internally in your program to obtain its value through query("name") or QUERY(name); value is the value of the variable; long_name is the variable name seen in the configuration interface; and type is the type of the variable (See table 13.3).

Table 13.3 Available variable types
Variable type Explanation
TYPE_STRING The variable is a string like "Informative text"
TYPE_FILE A file variable. It is in fact like a string variable, but the help text seen by the user differs.
TYPE_LOCATION A position in the virtual file system. Once again you treat this like a string, but the help text is different.
TYPE_INT An integer
TYPE_DIR A directory. The user can only enter existing directories and they always end with a "/", so you won't have to care about that in your modules.
TYPE_FLOAT A floating point number.
TYPE_TEXT_FIELD A field that can contain several lines of text.
TYPE_FLAG A variable that is either true or false is of this type.
TYPE_COLOR A color variable, a number between 0 and 16777215=224-1. This number is a three-byte value where each of the R, G and B colors is represented by one of the bytes. This was earlier used in the configuration interface.
TYPE_PASSWORD A password, the variable is automatically encrypted when set.
TYPE_STRING_LIST An array of strings, like {"foo", "bar", "gazonk"}.
TYPE_MULTIPLE_STRING One of several strings. The strings are chosen from the array sent with the optional field choices.
TYPE_INT_LIST An array of integers.
TYPE_MULTIPLE_INT One of several integers. Cf. TYPE_MULTIPLE_STRING above.
TYPE_DIR_LIST An array of directories.
TYPE_FILE_LIST An array of files, treated just like variables of the TYPE_STRING_LIST type but the help text differs.
TYPE_MODULE The variable is a module object.
As an illustration, here is an example of the use of create().

void create()
{
     defvar("BG", 1, 
               "Configuration Interface "
               "Background", TYPE_FLAG, "Should the "
               "background be set by the "
               "configuration interface?");
     defvar("NumAccept", 1, 
               "Number of accepts to "
               "attempt", TYPE_MULTIPLE_INT,
               "The maximum number of accepts to "
               "attempt for each read callback from "
               "the main socket." 
	       "Increasing this "
               "will make the server faster for "
               "users making many simultaneous "
               "connections to it, or if you have a "
               "very busy server.", ({1, 2, 4, 8, 16,
               32, 64, 128, 256, 512, 1024}));
    defvar("ConfigurationPort", 22202,
               "Configuration port", TYPE_INT, "The "
               "port number of the configuration "
               "interface. Anything will do, but you "
               "will have to rembemer it to be able "
               "to configure the server.");
}

In this piece of code three variables are defined. The first is a flag (BG), whose value is "on", then an integer (NumAccept)chosen from a list of integers (2n, 0ðnð10) and finally a single integer (ConfigurationPort).

Wizards

As you may have noticed, Roxen frequently uses wizards to accomplish tasks in a user-friendly way. As an example, look under Actions/Maintenance/Check your Roxen configuration for problems... or Actions/Cache/Flush caches... in your Roxen configuration pages. The object wizard.pike contains the necessary functions to put wizards in your own modules.

To make a wizard, you start by inheriting wizard.pike and setting the name, doc and wizard_name constants to the relevant strings. These are later used when automatically generating lists of wizards. Then, the separate pages of the wizard are defined through the use of page_NUM() functions.

page_NUM(object request_id, mixed .. args);
NUM is an integer, starting off at 0. That is, for a four page wizard you'd use page_0, page_1, page_2 and page_3. Every page returns a string of HTML code, describing the page.

The output from the page_NUM functions is parsed somewhat. You may use a <var> tag in the output from these functions to define a variable, and wizard.pike will insert the appropriate <input...> statements. You may also put text in a <help>...</help> container, and this text will only be shown if the user has requested help or verbose descriptions.

The <var> tag looks like this:


<var type='int|float|text|string|list|checkbox|toggle|
              select|select_multiple'
     default=value
     (if type=select*: options='a,b,c,')
     size=x_size
     rows=rows
     cols=columns> 

The function wizard_done(object request_id, mixed ... args) is called when the user is finished and clicks on the OK button. This is when the actual work will take place. In this function, you should check which variables has been changed during the users passage through the wizard, and take the proper actions. Note that this function is not called when the user ends with cancel.

Wizard.pike functions

mapping|string wizard_for(object request_id, string cancel_url, mixed ... args);
This method returns the wizard, either as HTML code in a string, or as a redirect using a mapping. The cancel_url is the page to be requested if the user exits the wizard by way of cancelling. Normally, this function is not called directly, see wizard_menu() below.

string html_table(array (string) titles, array (array (string)) rows);
Creates a table where the rows alternate between a white and a light blue background, to increase legibility. The overall effect is somewhat reminiscent of old pyjama-stripe style printer paper.

string html_notice(string notice, object request_id);
string html_warning(string warn, object request_id);
string html_error(string error, object request_id);
Returns the string, prepended with a bullet. The bullet is either a notice, a warning or an error sign, respectively.

string html_border(string what, int|void extra_width, int|void inner_width);
Gives a black border around a white text field. The arguments are the text (or general HTML data) to be framed, the thickness of the black border - 1 and the width of the white padding between the text and the border. The two width arguments defaults to 0.

html_border(string)
gives a one pixel wide black border directly around a string.

html_border(string,1)
gives a two pixels wide border directly around a string.

html_border(string,0,10)
Gives a one pixel wide border around a string, with ten pixels of white padding between the border and the string.

void filter_checkbox_variables(mapping variables);
Removes all unchecked checkbox variables from the variables mapping.

mapping|string wizard_menu(object request_id,string dir,string base,mixed ... args);
This function generates a list of wizards. This is done by looking through all files with the extension .pike in the directory given by dir, and determining which files define wizards. Then a list is generated, using the name and doc variables, and the wizard_for methods are used to actually call the specific wizard when it is selected from the list. For an example of the end result, look at the lists under the actions tab in the Roxen configuration interface.

In the reference section, you can study the code to a complete module.

Using your module

To start using a newly written module, place it in the module directory of your server. This is chosen in the Global Variables, Module directory in the configuration menus. Then choose Add module in the configuration pages of your server, and your new module should show up in the list of available modules. If it doesn't, check the debug log for errors.

If you have made changes in an already existing and loaded module and want to load the new version, focus on that module and choose Reload. The new correct version will be loaded into your server.

Previous Chapter
Next Chapter
Table of Contents
Index