##---------------------------------------------------------------------------## ## File: ## @(#) Page.pm 1.4 97/12/16 16:22:21 @(#) ## Author: ## Earl Hood ehood@medusa.acs.uci.edu ## Description: ## Class for supporting HTML template pages for perl CGI programs. ## More explanation at end of source file. ##---------------------------------------------------------------------------## ## Copyright (C) 1997 Earl Hood, ehood@medusa.acs.uci.edu ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of either: ## ## a) the GNU General Public License as published by the Free ## Software Foundation; either version 2, or (at your option) ## later version, or ## ## b) the "Artistic License" which comes with Perl. ##---------------------------------------------------------------------------## package CGI::Page; use Exporter (); @ISA = qw ( Exporter ); @EXPORT = (); @EXPORT_OK = (); $VERSION = "0.0.1.4"; ##---------------------------------------------------------------------------## use Carp; use FileHandle; ############################################################################### ## Public Class Methods ############################################################################### ##---------------------------------------------------------------------------## ## Constructor ## ## Usage: ## $page = new CGI::Page $filename; ## sub new { my $this = { }; my $mod = shift; # Name of module my $file = shift; # Filename of template my $class = ref($mod) || $mod; $this->{file} = $file; bless $this, $class; $this; } ############################################################################### ## Public Object Methods ############################################################################### ##---------------------------------------------------------------------------## ## bind_site binds a string, function, or filehandle ## to a page site. If a function, it should be a refence to ## a function. If a filehandle, should be a reference to a ## typeglob. ## ## Usage: ## $page->bind_site($sitename, $bind); ## Arguments: ## $sitename: ## Name of the data site. ## $bind: ## Perl structure to bind to site. Value can either be ## a simple scalar string, reference to a function, or ## a reference to a filehandle. ## sub bind_site { my $this = shift; my $site = shift; # Site name my $bind = shift; # Bind value to site $this->{site}{$site} = $bind; $this->{args}{$site} = [ @_ ]; } ##---------------------------------------------------------------------------## ## read_page reads the page. ## ## Usage: ## $page->read_page; ## $page->read_page(\*FILE); ## $page->read_page(\*FILE, $filename); ## Arguments: ## FILE (optional): ## Output file stream. Defaults to STDOUT if not defined. ## $filename (optional): ## Name of file to read. Useful if filename not specified ## during object contruction. ## sub read_page { my $this = shift; my $outfh = shift(@_) || \*STDOUT; # Output stream my $file = shift(@_) || $this->{file}; # File to read (optional) # Open file. If unable, generate error message and return non-zero # status. my $fh = new FileHandle $file; if (not defined $fh) { carp qq(Unable to open "$file"); return -1; } # Parse file my(@list); my($name, $value, $bind); while (<$fh>) { # Split on data site markup @list = split(/##PL_([^#]+)##/, $_); # First item of list is regular data, so just output. print $outfh shift(@list); # If other items are still in the list, then there are data # sites to resolve. LINE: while (@list) { $site = shift @list; $data = shift @list; if ($site =~ /^(\w+)\s*=\s*(.+)/) { ($name, $value) = (lc $1, $2); } else { next LINE; } # Check on type of data site SITE: { # File site: open file and include contents where data # site is located. Filename could also include trailing # pipe to allow a program to be invoked. if ($name eq "file") { my $incfh = new FileHandle $value; if (defined $incfh) { while (<$incfh>) { print $outfh $_; } } undef $incfh; # closes file last SITE; } # Named site: check if binding registered for value of # site. If so, execute binding. if ($name eq "site") { $value =~ s/\s//g; # strip any whitespace $bind = $this->{site}{$value}; if (defined $bind) { BIND: { # Function: Call if defined. If not, silently # ignore. if (ref($bind) eq 'CODE') { &$bind($this, $outfh, $value, @{$this->{args}{$value}}) if defined &$bind; last BIND; } # Filehandle: Have to use regex to check # for filehandle in case a filehandle class # is in use. if ($bind =~ /GLOB/) { while (<$bind>) { print $outfh $_; } last BIND; } # Object: Call the method that the object # should define to work with CGI::Page. We # only check if $bind is a reference. Other # relevant reference types are checked above. if (ref($bind)) { $bind->fill_site( $this, $outfh, $site, @{$this->{args}{$value}}); last BIND; } # String: Fallback case; just output string print $outfh $bind; last BIND; } # End BIND } last SITE; } } # End SITE } continue { print $outfh $data; } # End while (@list) } # End while (<$fh>) } ##---------------------------------------------------------------------------## 1; __END__ Synopsis: use CGI::Page; # Create a new object $page = new CGI::Page "template.html" # Bind a string value to a data site $page->bind_site("astring", "Hello World!"); # Bind a function to a data site $page->bind_site("form", \&generate_form); # Bind a filehandle to a data site $page->bind_site("filehandle", \*FILE); # Bind an object to a data site $some_object = new SomeClass; $page->bind_site("object", $some_object); # Read page $page->read_page; Data Site Syntax: In a page, to define a data site, the syntax is as follows: ##PL_name=value## where the components mean the following: ##PL_ Start of a data site. name Type name of the site, possible values: site Specifies a labeled data site where the data for the site determined thru the bind_site method. file Specifies the name of file that defines the contents of the data site. Works in a similiar manner as server-side file include directive. If a trailing pipe is included in the value, then the value is treated as program to invoke, and the output of the program is used to fill the site. The name is case insensitive. = Separator of name and value. value String value associated with name. Value is case sensitive. ## End of data site definition. Example data sites: ##PL_site=inputform## Site data determined by a call to bind_site. Example: $page->bind_site("inputform", \&create_form) ##PL_file=copyright.html## Include contents of copyright.html. ##PL_file=/bin/ls -l |## Include a file listing via the ls(1) command. Data Site Bindings via bind_site Method The bind_site method takes 2 arguments, the name of the site to bind to and a value to define the value of the site during the read_page method. Example: $page->bind_site($name, $bind); The $name of the data site corresponds to the value of a ##PL_site=value## data site. The bind value can be one of the following Perl data structures: scalar string: The data site is replaced with the value of the scalar string. function: A reference to a function. The function is invoked as follows: &func($page_obj, $outfh, $site_name); Where, $page_obj is the CGI::Page object. $outfh is the output filehandle. The function uses $outfh to output the data that should go in the location of the data site. $site_name is the name of the site the function is being called for. Since the CGI::Page object is passed to the function, the function can change bindings. Any changes will affect and data sites following the site being processed. Additional arguments can be passed to the bind_site method when binding a function. For example: $page->bind_site($site_name, \&func, $arg1, $arg2); A copy of those arguments will be passed as extra arguments when the function is invoked. Continuing with the previous example, the function would be called as follows: &func($page_obj, $outfh, $site_name, $arg1, $arg2); filehandle: A reference to a filehandle (technically a reference to a glob of a filehandle). The filehandle is read until EOF and any data read goes in the location of the site. object: An object reference. CGI::Page will attempt to call the method "fill_site" of the object. Therefore, the object must have defined a method called fill_site, or a runtime error will occur. The method is invoked with the same arguments as in a function binding: $object->fill_site($page_obj, $outfh, $site_name); Any additional arguments passed during the bind_site call will be passed to the registered object's fill_site method like in function bindings: $object->fill_site($page_obj, $outfh, $site_name, $arg1, $arg2, ..., $argN); End