PApp - multi-page-state-preserving web applications
* This module requires quite an elaborate setup (see the INSTALL file). * * Please read the LICENSE file (PApp is neither GPL nor BSD licensed). *
PApp is a complete solution for developing multi-page web applications that preserve state across page views. It also tracks user id's, supports a user access system and provides many utility functions (html, sql...). You do not need (and should not use) the CGI module.
Advantages:
<h1>Names and amounts</h1> <: my $st = sql_exec \my($name, $amount), "select name, amount from ...",
while ($st->fetch) {?> Name: $name, Amount: $amount<p> <:} :> <hr>
That is, mixing html and perl at statement boundaries.
%S
is automaticaly
preserved during the session. Everything you save there will be available
in any subsequent pages that the user accesses.
``string'', either in html or in the perl
source. The ``poedit''-demo-application enables editing of the strings
on-line, so translaters need not touch any text files and can work
diretcly via the web.Disadvantages:
To get a quick start, read the bench.papp module, the dbedit.papp module, the cluster.papp module and the papp.dtd description of the papp file format.
Also, have a look at the doc/ subdirectory of the distribution, which will have some tutorials in sdf and html format.
Some global variables are free to use and even free to change (yes, we
still are about speed, not abstraction). In addition to these variables,
the globs *state
, *S
and *A
(and in future versions *L
)
are reserved. This means that you cannot define a scalar, sub, hash,
filehandle or whatsoever with these names.
Apache-
request>.
papp
are
reserved for use by this module. Everything else is yours.
%state
, but is local to the current application. Input
arguments prefixed with a dash end up here.
%A
, but it instead contains the parameters from
forms submitted via GET or POST (see parse_multipart_form
,
however). Everything in this hash is insecure by nature and must should be
used carefully.
Normally, the values stored in %P
are plain strings (in utf-8,
though). However, it is possible to submit the same field multiple times,
in which case the value stored in $P{field}
is a reference to an array
with all strings, i.e. if you want to evaluate a form field that might be
submitted multiple times (e.g. checkboxes or multi-select elements) you
must use something like this:
my @values = ref $P{field} ? @{$P{field}} : $P{field};
$userid
is zero in case no userid has been assigned yet. In this
case you can force a userid by calling the function getuid
, which
allocated one if necessary,
$userid
== 0).
config the argument to the C<config>option given to C<mount>.
PApp::Package
object (see
the PApp::Package manpage). This variable might be replaced by something else, so
watch out. This might or might not be the same as $PApp::ppkg, so best use
$ppkg when using it. Ah, actually it's best to not use it at all.
mount
.
time
) at the start of the request.
Highly useful for checking cache time-outs or similar things, as it is
faster to use this variable than to call time
.
pappdb The (mysql) database to use as papp-database (default "DBI:mysql:papp") pappdb_user The username when connecting to the database pappdb_pass The password when connecting to the database cipherkey The Twofish-Key to use (16 binary bytes), BIG SECURITY PROBLEM if not set! (you can use 'mcookie' from util-linux twice to generate one) cookie_reset delay in seconds after which papp tries to re-set the cookie (default: one day) cookie_expires time in seconds after which a cookie shall expire (default: one year) logfile The path to a file where errors and warnings are being logged to (the default is stderr which is connected to the client browser on many web-servers)
The following configuration values are used mainly for development:
checkdeps when set, papp will check the .papp file dates for every request (slow!!) and will reload the app when necessary. delayed do not compile applications at server startup, only on first access. This greatly increases memory consumption but ensures that the httpd startup works and is faster. onerr can be one or more of the following characters that specify how to react to an unhandled exception. (default: 'sha') 's' save the error into the error table 'v' view all the information (security problem) 'h' show the error category only 'a' give the admin user the ability to log-in/view the error
mount_appset($appset)
mount_appset PApp 'default'; =item PApp->mount_app($appname)
Can be used to mount a single application.
The following description is no longer valid.
location[*] The URI the application is mounted under, must start with "/". Currently, no other slashes are allowed in it. src[*] The .papp-file to mount there config Will be available to the application as $papp->{config} delayed see C<PApp->configure>.
[*] required attributes
debugbox
function.
print
function, except that it is faster for generating output.
<: my $output = capture {
print "of course, this is easy\n"; echo "this as well"; :> Yes, this is captured as well! <:&this_works:> <?$captureme:>
<:
}; # close the capture :>
$type
. The content-type should be a
registered MIME type (see RFC 2046) like text/html
or image/png
. The
optional argument $charset
can be either ``*'', which selects a suitable
output encoding dynamically (e.g. according to $state{papp_locale}
)
or the name of a registered character set (STD 2). The special value
undef
suppresses output character conversion entirely. If not given,
the previous value will be unchanged (the default; is currently ``*'').
The following is not yet implemented and will probably never be:
The charset argument might also be an array-reference giving charsets that
should be tried in order (similar to the language preferences). The last
charset will be forced, i.e. characters not representable in the output
will be replaced by some implementation defined way (if possible, this
will be &#charcode;
, which is as good a replacement as any other ;)
How this interacts with Accept-Charset is still an open issue (for non-microsoft browsers that actually generate this header ;)
setlocale
is very slow on glibc based systems for
example). If no argument is given it sets the locale to the current user's
default locale (see SURL_SET_LOCALE
). NOTE: Remember that PApp (and
Perl) requires iso-8859-1 or utf-8, so be prepared to do any conversion
yourself. In future versions PApp might help you doing this (e.g. by
setting LC_CTYPE to utf-8, but this is not supported on many systems).
At the moment, PApp does not automatically set the (Perl) locale on each request, so you need to call setlocale before using any locale-based functions.
Please note that PApp-style locale strings might not be compatible to your system's locale strings (this function does the conversion).
$fullurl
is given, a full url (including a protocol
specifier) is generated. Otherwise a partial uri is returned (without
http://host:port/).
This is only a bona-fide attempt: The current module must support starting a new session and only ``import''-variables and input parameters are preserved.
surl
is one of the most often used functions to create urls. The first
argument is a comma-seperated list of target modules that the url should
refer to. If it is missing the url will refer to the current module state,
as will a module name of ``.''. The most common use is just a singular
module name. Examples:
. link to the current module menu link to module "menu" in the current package fall/wahl link to the current module but set the subpackage "fall" to module "wahl". fall/,menu link to the menu module and set the subpackage "fall" to the default module (with the empty name).
The remaining arguments are parameters that are passed to the new
module. Unlike GET or POST-requests, these parameters are directly passed
into the %S
-hash (unless prefixed with a dash), i.e. you can use this
to alter state values when the url is activated. This data is transfered
in a secure way and can be quite large (it will not go over the wire).
When a parameter name is prefixed with a minus-sign, the value will end up
in the (non-persistent) %A
-hash instead (for ``one-shot'' arguments).
Otherwise the argument name is treated similar to a path under unix: If it has a leading ``/'', it is assumed to start at the server root, i.e. with the application location. Relative paths are resolved as you would expect them. Examples:
(most of the following hasn't been implemented yet)
/papp_locale $state{papp_locale} /tt/var $state{'/tt'}{var} -OR- $S{var} in application /tt /tt/mod1/var $state{'/tt'}{'/mod1'}{var} ../var the "var" statekey of the module above in the stack
The following (symbolic) modifiers can also be used:
SURL_PUSH(<path> => <value>) SURL_UNSHIFT(<path> => <value>) treat the following state key as an arrayref and push or unshift the argument onto it. SURL_POP(<path-or-ref>) SURL_SHIFT(<path-or-ref>) treat the following state key as arrayref and pop/shift it.
SURL_EXEC(<coderef>) treat the following parameter as code-reference and execute it after all other assignments have been done.
SURL_SAVE_PREFS call save_prefs (using SURL_EXEC) SURL_STYLE_URL SURL_STYLE_GET SURL_STYLE_STATIC set various url styles, see C<surl_style>. SURL_SUFFIX(<file>) sets the filename in the generated url to the given string. The filename is the last component of the url commonly used by browsers as the default name to save files. Works only with SURL_STYLE_GET only.
Examples:
SURL_PUSH("stack" => 5) push 5 onto @{$S{stack}} SURL_SHIFT("stack") shift @{$S{stack}} SURL_SAVE_PREFS save the preferences on click SURL_EXEC($cref->refer) execute the PApp::Callback object
surl_style
. newstyle
must be one of:
SURL_STYLE_URL The "classic" papp style, the session id gets embedded into the url, like C</admin/+modules-/bhWU3DBm2hsusnFktCMbn0>. SURL_STYLE_GET The session id is encoded as the form field named "papp" and appended to the url as a get request, e.g. C</admin/+modules-?papp=bhWU3DBm2hsusnFktCMbn0>. SURL_STYLE_STATIC The session id is not encoded into the url, e.g. C</admin/+modules->, instead, surl returns two arguments. This must never be set as a default using C<surl_style>, but only when using surl directly.
surl
, but also pushes the current module state
onto the return stack. The sublink-surlargs is an arrayref
containing surl-args used for the ``return jump'' and is usually just
[current_locals]
, i.e. of all local variables.
suburl
but creates an A HREF
link with given contents.
suburl
-caller.
A HREF
link witht he given contents.
sublink
s:
<? sublink [current_locals], "Log me in!", "login" :>
This will create a link to the login-module. In that module, you should provide a link back to the current page with:
<? retlink "Return to the caller" :>
These functions return a <form> or </form>-Tag. sform
(``simple form'')
takes the same arguments as surl
and return a <form>-Tag with a
GET-Method. cform
(``complex form'') does the same, but sets method to
POST. Finally, multipart_form
is the same as cform
, but sets the
encoding-type to ``multipart/form-data''. The latter data is not parsed
by PApp, you will have to call parse_multipart_form (see below)
when evaluating the form data.
All of these functions except endform accept an initial hashref with additional attributes (see the PApp::HTML manpage), e.g. to set the name attribute of the generated form elements.
Endform returns a closing </form>-Tag, and must be used to close forms
created via sform
/cform
/multipart_form
.
If the callback returns true, the remaining parameter-data (if any) is
skipped, and the next parameter is read. If the callback returns false,
the current parameter will be read and put into the %P
hash. This is a
no-op callback:
sub my_callback { my ($fh, $name, $ct, $cta, $cd) = @_; my $data; read($fh, $data, 99999); if ($ct =~ /^text\/i) { my $charset = lc $cta->{charset}; # do conversion of $data } (); # do not return true }
The Handle-object given to the callback function is actually an object of
type PApp::FormBuffer (see the PApp::FormBuffer manpage). It will
not allow you to read more data than you are supposed to. Also, remember
that the READ
-Method will return a trailing CRLF even for data-files.
HINT: All strings (pathnames etc..) are probably in the charset specified
by $state{papp_lcs}
, but maybe not. In any case, they are octet
strings so watch out!
Flushing does not yet harmonize with output stylesheet processing, for the semi-obvious reason that PApp::XSLT does not support streaming operation.
BUGS: No links that have been output so far can be followed until the document is finished, because the neccessary information will not reach the disk until the document.... is finished ;)
You should never need to call this function directly, rather use
internal_redirect
and other functions that use upcalls to do their
work.
redirect_url
creates a http-302 (Page Moved) response,
changing the url the browser sees (and displays). internal_redirect
redirects the request internally (in the web-server), which is faster, but
the browser might or might not see the url change.
internal_redirect
, but works the arguments through
surl
. This is an easy way to switch to another module/webpage as a kind
of exception mechanism. For example, I often use constructs like these:
my ($name, ...) = sql_fetch "select ... from game where id = ", $S{gameid}; abort_to "games_overview" unless defined $name;
This is used in the module showing game details. If it doesn't find the game it just aborts to the overview page with the list of games.
abort_with { content_type "text/plain"; echo "This is the only line ever output"; };
#if admin_p <: debugbox :> #endif
Usually used like this:
<:language_selector $papp_translator, $papp_ppkg_table->lang:>
If you want to build your own language selector, here's how:
# iterate over all languages supported by this translator for my $lang ($translator->langs) {
# translate the language id into the vernacular language name my $name = PApp::I18n::translate_langid($lang, $lang);
if ($lang eq $current) { # this is the currently selected language... echo "[$name]"; } else { # or a language we could switch to echo slink "[$name]", SURL_SET_LOCALE($lang); }
}
This is a relatively costly operation (a database access), so do not do it by default, but only when you need it.
userid
in your newuser
-callback. See also $userid
to get the
current userid (which might be zero).
The macro/admin
-package on the distribution, the demo-applications
(.papp-files).
Marc Lehmann <pcg@goof.com> http://www.goof.com/pcg/marc/