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 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!
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.
|
|
|
|
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.
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.
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.
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.
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.
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=""e;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.
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).
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.
- 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.
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
| |