From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

use strict;
use vars qw($VERSION $AUTOLOAD);
$VERSION = '5.32';
=head1 NAME
Labyrinth::Request - Request Manager for Labyrinth
=head1 SYNOPSIS
use Labyrinth::Request;
# database object creation
my ($content,@actions) = Request($realm);
=head1 DESCRIPTION
The Request package, given a request string (or defaults), will retrieve
the appropriate actions, template file and continuations for that request.
The configuration of request settings can either be held within INI files
within a designated path or in request table within a database.
If using INI files, each file represents a collection of commands within a
single section. There is one special section, 'realm', which describes the
overall layout files, actions and continuations for the type of account.
Typically there are at least two realms, 'public' and 'admin'. To describe
the path to these request files, the following should exist within your global
settings file:
requests=/path/to/request/files
Alternative if you wish to use the request settings from a database table, in
your globale settings file, you will need the following setting:
requests=dbi
For more information for the database method, please see the
L<Labyrinth::Request> distribution.
=cut
# -------------------------------------
# Library Modules
# -------------------------------------
# Variables
my @configkeys = qw(layout actions content onsuccess onfailure onerror secure rewrite);
my %resetkeys = (onsuccess => 1, onfailure => 1, onerror => 1);
my %stored;
my @autosubs = qw(
layout
content
onsuccess
onerror
onfailure
);
my %autosubs = map {$_ => 1} @autosubs;
# -------------------------------------
# The Subs
=head1 FUNCTIONS
=head2 Constructor
=over 4
=item new()
Create a new request object.
=back
=cut
sub new {
my $self = shift;
my $realm = shift;
my $request = shift || 'home-'.$realm;
my @actions;
## split the reset request into it's component parts
my ($section,$command) = split("-",$request);
$tvars{request} = $request;
$tvars{section} = $section;
$tvars{command} = $command;
# sort the realm out
my ($layout,$actions,$content,$onsuccess,$onfailure,$onerror)
= $self->_read_config('realm',$realm,@configkeys);
$onsuccess = $request;
@actions = split(",",$actions) if($actions);
# create an attributes hash
my $atts = {
'actions' => \@actions,
'layout' => $layout,
'content' => $content,
'onsuccess' => $onsuccess,
'onfailure' => $onfailure,
'onerror' => $onerror,
};
LogDebug("--new:actions=[@actions]");
# LogDebug("--new:layout=[$layout]");
# LogDebug("--new:content=[$content]");
# create the object
bless $atts, $self;
return $atts;
}
=head2 Methods
=head3 Handling Actions
=over 4
=item next_action()
For the current command request, return the next action within its action list.
=item add_actions(@actions)
Add actions to the action list for the current command request.
=back
=cut
sub next_action { my $self = shift; shift @{$self->{actions}} }
sub add_actions { my $self = shift; push @{$self->{actions}}, @_ }
=head3 Handling Command Resets
=over 4
=item reset_realm($realm)
Reloads settings for a new realm setting.
=item reset_request($request)
Reloads settings for a new command request.
=item redirect
Instead of a local template file or a continuation, a redirect may be used.
This method reformats the URL within a redirect request.
=back
=cut
sub reset_realm {
my $self = shift;
my $realm = shift;
my %hash;
@hash{@configkeys} = $self->_read_config('realm',$realm,@configkeys);
$self->{section} = 'realm';
$self->{command} = $realm; # needed to check onsuccess, etc.
for(@configkeys) {
next unless($hash{$_});
if($_ eq 'actions') {
my @actions = split(",",$hash{$_});
$self->add_actions(@actions);
LogDebug("--reset_realm:actions=@actions");
} else {
$self->{$_} = $hash{$_};
#LogDebug("--reset_realm:$_=$self->{$_}");
}
}
}
sub reset_request {
my $self = shift;
my $request = shift;
my %hash;
## split the reset request into it's component parts
my ($section,$command) = split("-",$request);
$tvars{request} = $request;
$tvars{section} = $section;
$tvars{command} = $command;
return unless($section && $command);
# remove any remaining actiona
while($self->next_action){};
@hash{@configkeys} = $self->_read_config($section,$command,@configkeys);
#if($settings{$protocol} eq 'https' && !$hash{secure} || $hash{secure} !~ /^(on|either|both)$/) {
# # redirect to HTTP string
# $self->redirect('http',$hash{rewrite},$request);
# return;
#} elsif($settings{$protocol} eq 'http' && $hash{secure} && $hash{secure} =~ /^(on|either|both)$/) {
# # redirect to HTTPS string
# $self->redirect('https',$hash{rewrite});
# return;
#}
for(@configkeys) {
next unless($hash{$_} || $resetkeys{$_});
if($_ eq 'actions') {
my @actions = split(",",$hash{$_});
$self->add_actions(@actions);
LogDebug("--reset_request:actions=@actions");
} else {
$self->{$_} = $hash{$_};
LogDebug("--reset_request:$_=" . (defined $self->{$_} ? $self->{$_} : ''));
}
}
}
sub redirect {
my ($self,$protocol,$rewrite,$request) = @_;
return $tvars{redirect} if(!$protocol);
# set to existing query string, with new protocol
$tvars{redirect} = "$protocol://$ENV{HTTP_HOST}";
if(defined $rewrite) {
$tvars{redirect} .= $rewrite;
} else {
$tvars{redirect} .= $ENV{REQUEST_URI} if($ENV{REQUEST_URI});
# rewrite query string
if(defined $request) {
$tvars{redirect} =~ s/\?.*//;
$tvars{redirect} .= "?act=$request" if($request);
}
}
}
# private method to read config data
sub _read_config {
my ($self,$section,$command,@keys) = @_;
my @values;
LogDebug("--read_config:section=$section,command=$command,request=$settings{requests}");
if($settings{requests} eq 'dbi') {
my @rows = $dbi->GetQuery('hash','GetRequest',$section,$command);
if(@rows) {
push @values, map {$rows[0]->{$_}} @keys;
} else {
push @values, map {''} @keys;
}
} else {
my $file = "$settings{requests}/$section.ini";
Croak("Cannot read configuration file [$file]\n") unless(-r $file);
my $cfg = Config::IniFiles->new( -file => $file );
Croak("Cannot access configuration file [$file]: @Config::IniFiles::errors\n") unless($cfg);
for my $key (@keys) {
my $value = $cfg->val( $command, $key );
#LogDebug("--_read_config:[$command-$key]=[$value], file=[$file]");
push @values, ($value ? $value : undef);
}
}
return @values;
}
=head2 Accessor Methods
=over 4
=item layout
Layout template to be used
=item content
Content template to be used
=item onsuccess
Command to execute if this command succeeds.
=item onerror
Command to execute if this command fails.
=item onfailure
Command to execute if this command fails with an unrecoverable error.
=back
=cut
sub AUTOLOAD {
no strict 'refs';
my $name = $AUTOLOAD;
$name =~ s/^.*:://;
die "Unknown sub $AUTOLOAD\n" unless($autosubs{$name});
*$name = sub {
my $self = shift;
my $value = $self->{$name};
if($name =~ /^on/) { $self->{$name} = undef } # once seen, forget it
return $value;
};
goto &$name;
}
sub DESTROY {}
1;
__END__
=head1 SEE ALSO
Config::IniFiles
Labyrinth
=head1 AUTHOR
Barbie, <barbie@missbarbell.co.uk> for
Miss Barbell Productions, L<http://www.missbarbell.co.uk/>
=head1 COPYRIGHT & LICENSE
Copyright (C) 2002-2015 Barbie for Miss Barbell Productions
All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the Artistic License 2.0.
=cut