where template_faq is the name of the template and 12 is the template id in the database. Pretty simple.
Next, I did something else pretty cool IMHO, and made it possible to do <% img_src_name some_image %> and have it replace this with
the approrpiate <img src="" height="" width="" > tag thanks to a little magic from Image::Size.
Not satisfied, I wanted to make it possible to do something like:
<% code_ref Util::Test_Util::test_expand %>
and have it swap in the text output from the sub into the template. That way, unlike other templating applications, I could get away from writing my own mini language and use the full power of perl in a sub called by a simple tag in the template. I only need to make sure that these code subroutines always return an expected set of values to indicate success or failure -- in other words, never call die in the subroutine.
All of this stuff works flawlessly from the command line (see test_parse.pl attached).
And it works in mod_perl, but only sometimes. I've pulled down output using lynx, wget, Mozilla, Konquerer, and IE. I've made sure no content caching is occurring. After shoving a few print STDERR in my perlHandler View.pm (also attached), I've found that it is as if mod_perl is ignoring the
code evaluation regexp from time to time. Sometimes it does it, sometimes not.
I can provide logs to substantiate this. I'm at a loss for why mod_perl would do this. I had thought garbage collection might be failing on my $file variable but I've explicitly undefined it in the handler and still nada.
Please forgive the beta quality of the code, but I'm just trying to get this to work. I'll pretty it up and break it into subs later.
My environment is thus:
Apache/1.3.26 (Unix) mod_perl/1.27 PHP/4.2.3
RedHat Linux 7.3 kernel 2.4.18-10
Dell Latitude Notebook
I can provide compile options I used to build apache, mod_perl, mod_ssl, and php if this is necessary.
Can any of you gurus please help!
Rodney Hampton
package Application::View; #PerlHandler ############################################################### # Copyright (C) 2000-2002 R.A. Hampton and Associates # All Rights Reserved. Proprietary and Confidential. #============================================================== # $Id: View.pm,v 1.21 2002/10/23 08:01:54 rhampton Exp $ #-------------------------------------------------------------- # Tag: $Name: $ # Revision: $Revision: 1.21 $ # Date: $Date: 2002/10/23 08:01:54 $ # Author: $Author: rhampton $ # Description: ################################################################ use strict; use Apache::Constants qw/ :common /; use Util::Constants qw/ :errors /; use Util::Parse_Handler qw/ ph_parse_input ph_abort /; use Util::Output qw/ expandStruct /;#necessary for code_ref eval use Rendering::Common qw/ common_html_header common_html_footer common_session_utility common_return_rendering_object/; use Disk::Disk_Object_Read; #read-only disk object use Business_Logic::User_Object; use Business_Logic::Item_Object; use IO::File; use Image::Size qw/ html_imgsize /; use Safe;
## BEGIN handler sub handler { my $r = shift; ##BEGIN figure out if we are under /ceoadmin or /advertiser so we can react accordingly later my $code; my $href; my $ceoadmin_flag=0; my $advertiser_flag=0; my $action = $r->uri; #must strip of the leading slash $action =~ s/^\///; my ( $type, @directions ) = (split /\//, $action); if ($type eq 'ceoadmin'){$ceoadmin_flag=1;} if ($type eq 'advertiser'){$advertiser_flag=1;} ##END figure out if we are under /ceoadmin or /advertiser so we can react accordingly later ##BEGIN parse inputs my ($input,$msg_i) = ph_parse_input ( r => $r, args_list => ['q_act','q_srn'], ); unless($input) { ph_abort($r,"Error parsing http arguments:",$msg_i); return DONE; } ##END parse inputs ##BEGIN instantiate disk_object_read - database handle comes along for the ride my ($disk_object,$disk_object_error)=Disk::Disk_Object_Read->new();#file_id not identified at this point, but that is ok if($disk_object_error){ ph_abort($r,"Unable to instantiate disk object",$disk_object_error); return DONE; } my $dbh=$disk_object->{'dbh'}; ##END instantiate disk_object_read ##BEGIN find user_id if not in session my $user_id; my %session; my $session_id; ##BEGIN output to browser #$r->no_cache(1); #too bad we have to set this here - images will never be cached! $session_id=common_session_utility($r,session=>\%session); ##END output to browser $user_id=$session{'user_id'}; if (($ceoadmin_flag or $advertiser_flag) && (!(defined $user_id))){ my $user_name = $r->pnotes('ap_state_obj')->{'User'}; my $password = $r->pnotes('ap_state_obj')->{'Password'}; ##BEGIN instantiate the user_object my ($user_object,$user_object_error)=Business_Logic::User_Object->new(dbh=>$dbh); if($user_object_error){ ph_abort($r,"Unable to instantiate user object",$user_object_error); return DONE; } ##END instantiate the user_object ##BEGIN find the user_id my ($user_find,$user_find_error)=$user_object->user_find_user_id(user_name=>"$user_name",password=>"$password"); if ($user_find!=-1){ if ($user_find_error){ ph_abort($r, "Unable to find a user_id that matches the given user_name ($user_name) and password",$user_find_error); return DONE; }else{ $user_id=$user_find; $session{'user_id'}=$user_id; }#end else }#end if ##END find the user_id }#end if we need to find the user_id ##END find user_id if not in session ##BEGIN grab the rendering object my ($rendering_object,$rendering_error)=common_return_rendering_object(); unless($rendering_object){ ph_abort($r, "Rendering object did not load",$rendering_error); return DONE; }#end unless ##END grab the rendering object ##BEGIN set defaults - q_act, q_srn, user_id unless (defined $input->{'q_act'}){$input->{'q_act'}="view";} unless (defined $user_id){ $user_id=2;#if all else fails, become the guest user but don't set this in the session }#end unless unless (defined $input->{'q_srn'}){ if ($ceoadmin_flag){ $input->{'q_srn'}=$rendering_object->{'configs'}{'item'}{'default'}{'ceoadmin'};#default item for /ceoadmin section of website }elsif($advertiser_flag){ $input->{'q_srn'}=$rendering_object->{'configs'}{'item'}{'default'}{'advertiser'};#default item for /advertiser section of website }else{ $input->{'q_srn'}=$rendering_object->{'configs'}{'item'}{'default'}{'unprotected'};#default item for / section of website }#end else }#end unless ##END set defaults ##BEGIN instantiate item object my ($item_object,$item_object_error)=Business_Logic::Item_Object->new(dbh=>$dbh,user_id=>$user_id,item_id=>$input->{'q_srn'}); unless($item_object){ ph_abort($r, "Unable to instantiate item object",$item_object_error); return DONE; }#end item_object ##END instantiate item object ##BEGIN retrieve the file attributes my ($file_attributes,$file_error)=$disk_object->disk_retrieve_file_attributes(file_id=>$item_object->{'file_id'}); unless($file_attributes){ ph_abort($r,"Unable to retrive file attributes for item_id $item_object->{'item_id'}",$file_error); return DONE; }#end unless my $location=$file_attributes->{'location'}; my $mime_type=$file_attributes->{'mime_type'}; ##END retrieve the file attributes ##BEGIN parse template my ($file,$file_error_message); if ($mime_type eq 'text/html'){ ($file,$file_error_message)=$disk_object->disk_slurp_file( location=>$location, ); if ($file_error_message){ ph_abort($r,"Unable to slurp file from location $location for file_id $item_object->{'file_id'}",$file_error_message); return DONE; } $href={stuff=>'goes here'}; #this would come from the rendering object in real life ##BEGIN encapsulate file with prepended and appended template code ##BE SURE TO RANDOMIZE THE EOH TYPE STRING TO PREVENT SOMEONE FROM CLOSING THE BLOCK IN THE TEMPLATE! $$file="sub {\n my \$href = shift;\n print <<EOH;\n".$$file."\nEOH\n }\n"; #print STDERR "File is $$file "; ##END encapsulate file with prepended and appended template code print STDERR "HERE I AM BEGINNING LINK_NAME REGEXP \n"; ##BEGIN link_name #makes use of experimental code evaluation regexp features in perl my @link_names; $$file =~ s/<%[ ]*link_name[ ]*([^ ]*)[ ]*%>(?{push(@link_names,$1);})/<% LINK_NAME $1 %>/g; print STDERR "COMPLETED LINK_NAME REGEXP AND LINK_NAMES ARE: \n"; print STDERR expandStruct(\@link_names); #now map link names to actual variables my $sql; foreach my $link_name (@link_names){ my $original_link_name=$link_name; $link_name=$dbh->quote($link_name); $sql="SELECT item_id FROM item_table where item_name=$link_name"; print STDERR "SQL is $sql \n"; my $sth=$dbh->prepare($sql); if (!$sth) { # return (undef,[$dbh->err,$dbh->errstr]); #always return an aref as the error ph_abort($r,$dbh->err,$dbh->errstr); return DONE; } if (!$sth->execute()){ #return (undef,[$sth->err,$sth->errstr]); ph_abort($r,$sth->err,$sth->errstr); return DONE; } if(!$sth->rows){ $$file =~ s/<%\sLINK_NAME\s$original_link_name\s%>/<!--no match found in database <% link_name $original_link_name %>-->/g; }elsif($sth->rows>1){ $$file =~ s/<%\sLINK_NAME\s$original_link_name\s%>/<!--multiple matches found in database <% link_name $original_link_name %>-->/g; }else{ my $row_aref=$sth->fetchrow_arrayref; my $link_item_id=$row_aref->[0]; my $url=""; if ($ceoadmin_flag){$url="/ceoadmin/";}elsif($advertiser_flag){$url="advertiser";}else{$url="/";}; print STDERR "NOW REPLACING LINK_NAME \n"; $$file =~ s/<%\sLINK_NAME\s$original_link_name\s%>/<a href=\"$url?q_act=view&q_srn=$link_item_id\">$original_link_name<\/a>/g; }#end else }#end foreach ##END link_name ##BEGIN img_src_name #makes use of experimental code evaluation regexp features in perl my @img_src_names; $$file =~ s/<%[ ]*img_src_name[ ]*([^ ]*)[ ]*%>(?{push(@img_src_names,$1);})/<% IMG_SRC_NAME $1 %>/g; #printStruct(\@img_src_names); #now map img_src names to actual variables my $sql; foreach my $img_src_name (@img_src_names){ my $original_img_src_name=$img_src_name; $img_src_name=$dbh->quote($img_src_name); #this changed $sql="SELECT i.item_id,f.location FROM item_table i, files_table f WHERE i.item_name=$img_src_name AND i.file_id=f.file_id"; #print STDERR "SQL is $sql \n"; my $sth=$dbh->prepare($sql); if (!$sth) { # return (undef,[$dbh->err,$dbh->errstr]); #always return an aref as the error ph_abort($r,$dbh->err,$dbh->errstr); return DONE; } if (!$sth->execute()){ #return (undef,[$sth->err,$sth->errstr]); ph_abort($r,$sth->err,$sth->errstr); return DONE; } if(!$sth->rows){ $$file =~ s/<%\sIMG_SRC_NAME\s$original_img_src_name\s%>/<!--no match found in database <% img_src_name $original_img_src_name %>-->/g; }elsif($sth->rows>1){ $$file =~ s/<%\sIMG_SRC_NAME\s$original_img_src_name\s%>/<!--multiple matches found in database <% img_src_name $original_img_src_name %>-->/g; }else{ my $row_aref=$sth->fetchrow_arrayref; my $img_src_item_id=$row_aref->[0]; #this changed - entire block added my $img_file_location=$row_aref->[1]; my $img_size_string = html_imgsize($img_file_location); #from Image::Size, doesn't need to know the extension to work properly #end entire block added my $url=""; if ($ceoadmin_flag){$url="/ceoadmin/";}elsif($advertiser_flag){$url="advertiser";}else{$url="/";}; #this changed $$file =~ s/<%\sIMG_SRC_NAME\s$original_img_src_name\s%>/<img $img_size_string src=\"$url?q_act=view&q_srn=$img_src_item_id\">/g; }#end else }#end foreach ##END img_src_name ##BEGIN code_ref my @code_references; #if success do find and replace with code reference if possible $$file =~ s/<%[ ]*code_ref[ ]*([^ ]*)[ ]*%>(?{push(@code_references,$1);})/<% CODE_REF $1 %>/g; #printStruct(\@code_references); my ($code_reference_return,$code_reference_error); foreach my $code_reference (@code_references){ undef $code_reference_return; undef $code_reference_error; my (@module_information)=split(/::/,$code_reference); my $specific_sub = pop @module_information; my $load_module = join("::",@module_information); my $eval_string=" use $load_module qw/ $specific_sub /; (\$\$code_reference_return,\$\$code_reference_error)=$code_reference(\\\@link_names); if (\$code_reference_return){return(1);}else{return(0);} "; #print STDERR "EVAL STRING is $eval_string \n"; eval $eval_string; if($@){ push (@{$code_reference_error},[$@]); } ##BEGIN must encase in eval due to $$code_ returning an error if undefined can't treat like scalar reference when calling expandStruct my $code_reference_string; my $error_string; if (defined $$code_reference_return){ print STDERR "VIEW.PM code_reference_return success \n"; eval{ $code_reference_string=expandStruct($$code_reference_return); $$file =~ s/<%\sCODE_REF\s$code_reference\s%>/$code_reference_string/g; }; }else{ print STDERR "VIEW.PM code_reference_return fail \n"; eval { $error_string=expandStruct($$code_reference_error); $$file =~ s/<%\sCODE_REF\s$code_reference\s%>/<!--error evaluating code ref: $error_string <% code_ref $code_reference %>-->/g; }; }#end else ##END must encase in eval }#end foreach code_ref ##END code_ref my $compartment = new Safe; $compartment->share('$href'); $code = $compartment->reval($$file); if ($@) { ph_abort( $r, "Restricted eval failed! Unable to compile template:", $@ ); return DONE; } }#end if mime_type is text/html ##END parse template ##BEGIN set caching if ($mime_type eq 'text/html'){ $r->no_cache(1); }else{ $r->no_cache(0); } ##END set caching ##BEGIN complete output to the browser $r->send_http_header($mime_type); if($mime_type eq 'text/html'){ common_html_header(login=>1); #print "<!--session_id is $session_id-->\n"; $code->($href); common_html_footer(); }else{ print STDERR "IN VIEW TRYING TO PRINT A FILE OF MIME TYPE $mime_type \n"; #handle images, etc. from mod_perl cookbook recipie 6.6 pg. 192 my $fh=IO::File->new($location); #$r->set_last_modified((stat $location)[9]); #$r->set_etag; #$r->set_content_length; seek $fh,0,0; $r->send_fd($fh); }#end else ##END complete output to the browser ##BEGIN clean up - be sure to take care of the session tied(%session)->make_modified(); untie %session; undef %session; undef $file; undef $disk_object; ##END clean up - be sure to take care of the session return OK; }#end sub handler 1;
#!/usr/bin/perl -w use strict; use lib "/opt/the_index/lib"; use Util::Output qw/ printStruct expandStruct /; use Rendering::Common qw/ common_html_header common_html_footer common_session_utility common_return_rendering_object/; use Disk::Disk_Object_Read; #read-only disk object use Business_Logic::User_Object; use Business_Logic::Item_Object; #use Util::Test_Util qw /test_expand /; use Image::Size qw/ html_imgsize /; use Safe; ## BEGIN handler ##BEGIN figure out if we are under /ceoadmin or /advertiser so we can react accordingly later my $code; my $href; ##BEGIN instantiate disk_object_read - database handle comes along for the ride my ($disk_object,$disk_object_error)=Disk::Disk_Object_Read->new();#file_id not identified at this point, but that is ok if($disk_object_error){ printStruct($disk_object_error); exit(0); } my $dbh=$disk_object->{'dbh'}; ##END instantiate disk_object_read my $user_id=2; my $input={}; my $ceoadmin_flag=0; my $advertiser_flag=0; ##BEGIN grab the rendering object my ($rendering_object,$rendering_error)=common_return_rendering_object(); unless($rendering_object){ printStruct($rendering_error); exit(0); }#end unless ##END grab the rendering object ##BEGIN set defaults - q_act, q_srn, user_id unless (defined $input->{'q_act'}){$input->{'q_act'}="view";} unless (defined $user_id){ $user_id=2;#if all else fails, become the guest user but don't set this in the session }#end unless unless (defined $input->{'q_srn'}){ if ($ceoadmin_flag){ $input->{'q_srn'}=$rendering_object->{'configs'}{'item'}{'default'}{'ceoadmin'};#default item for /ceoadmin section of website }elsif($advertiser_flag){ $input->{'q_srn'}=$rendering_object->{'configs'}{'item'}{'default'}{'advertiser'};#default item for /advertiser section of website }else{ $input->{'q_srn'}=$rendering_object->{'configs'}{'item'}{'default'}{'unprotected'};#default item for / section of website }#end else }#end unless ##END set defaults ##BEGIN instantiate item object my ($item_object,$item_object_error)=Business_Logic::Item_Object->new(dbh=>$dbh,user_id=>$user_id,item_id=>$input->{'q_srn'}); unless($item_object){ printStruct($item_object_error); exit(0); }#end item_object ##END instantiate item object ##BEGIN retrieve the file attributes my ($file_attributes,$file_error)=$disk_object->disk_retrieve_file_attributes(file_id=>$item_object->{'file_id'}); unless($file_attributes){ printStruct($file_error); exit(0); }#end unless my $location=$file_attributes->{'location'}; my $mime_type=$file_attributes->{'mime_type'}; ##END retrieve the file attributes ##BEGIN parse template my ($file,$file_error_message)=$disk_object->disk_slurp_file( location=>$location, ); if ($file_error_message){ printStruct($file_error_message); exit(0); } if ($mime_type eq 'text/html'){ $href={stuff=>'goes here'}; #this would come from the rendering object in real life ##BEGIN encapsulate file with prepended and appended template code ##BE SURE TO RANDOMIZE THE EOH TYPE STRING TO PREVENT SOMEONE FROM CLOSING THE BLOCK IN THE TEMPLATE! $$file="sub {\n my \$href = shift;\n print <<EOH;\n".$$file."\nEOH\n }\n"; #print STDERR "File is $$file "; ##END encapsulate file with prepended and appended template code ##BEGIN link_name #makes use of experimental code evaluation regexp features in perl my @link_names; $$file =~ s/<%[ ]*link_name[ ]*([^ ]*)[ ]*%>(?{push(@link_names,$1);})/<% LINK_NAME $1 %>/g; printStruct(\@link_names); #now map link names to actual variables my $sql; foreach my $link_name (@link_names){ my $original_link_name=$link_name; $link_name=$dbh->quote($link_name); $sql="SELECT item_id FROM item_table where item_name=$link_name"; print "SQL is $sql \n"; my $sth=$dbh->prepare($sql); if (!$sth) { # return (undef,[$dbh->err,$dbh->errstr]); #always return an aref as the error printStruct($dbh->err); printStruct($dbh->errstr); exit(0); } if (!$sth->execute()){ #return (undef,[$sth->err,$sth->errstr]); printStruct($sth->err); printStruct($sth->errstr); exit(0); } if(!$sth->rows){ $$file =~ s/<%\sLINK_NAME\s$original_link_name\s%>/<!--no match found in database <% link_name $original_link_name %>-->/g; }elsif($sth->rows>1){ $$file =~ s/<%\sLINK_NAME\s$original_link_name\s%>/<!--multiple matches found in database <% link_name $original_link_name %>-->/g; }else{ my $row_aref=$sth->fetchrow_arrayref; my $link_item_id=$row_aref->[0]; my $url=""; if ($ceoadmin_flag){$url="/ceoadmin/";}elsif($advertiser_flag){$url="advertiser";}else{$url="/";}; $$file =~ s/<%\sLINK_NAME\s$original_link_name\s%>/<a href=\"$url?q_act=view&q_srn=$link_item_id\">$original_link_name<\/a>/g; }#end else }#end foreach ##END link_name ##BEGIN img_src_name #makes use of experimental code evaluation regexp features in perl my @img_src_names; $$file =~ s/<%[ ]*img_src_name[ ]*([^ ]*)[ ]*%>(?{push(@img_src_names,$1);})/<% IMG_SRC_NAME $1 %>/g; printStruct(\@img_src_names); #now map img_src names to actual variables my $sql; foreach my $img_src_name (@img_src_names){ my $original_img_src_name=$img_src_name; $img_src_name=$dbh->quote($img_src_name); #this changed $sql="SELECT i.item_id,f.location FROM item_table i, files_table f WHERE i.item_name=$img_src_name AND i.file_id=f.file_id"; print "SQL is $sql \n"; my $sth=$dbh->prepare($sql); if (!$sth) { # return (undef,[$dbh->err,$dbh->errstr]); #always return an aref as the error printStruct($dbh->err); printStruct($dbh->errstr); exit(0); } if (!$sth->execute()){ #return (undef,[$sth->err,$sth->errstr]); printStruct($sth->err); printStruct($sth->errstr); exit(0); } if(!$sth->rows){ $$file =~ s/<%\sIMG_SRC_NAME\s$original_img_src_name\s%>/<!--no match found in database <% img_src_name $original_img_src_name %>-->/g; }elsif($sth->rows>1){ $$file =~ s/<%\sIMG_SRC_NAME\s$original_img_src_name\s%>/<!--multiple matches found in database <% img_src_name $original_img_src_name %>-->/g; }else{ my $row_aref=$sth->fetchrow_arrayref; my $img_src_item_id=$row_aref->[0]; #this changed - entire block added my $img_file_location=$row_aref->[1]; my $img_size_string = html_imgsize($img_file_location); #from Image::Size, doesn't need to know the extension to work properly #end entire block added my $url=""; if ($ceoadmin_flag){$url="/ceoadmin/";}elsif($advertiser_flag){$url="advertiser";}else{$url="/";}; #this changed $$file =~ s/<%\sIMG_SRC_NAME\s$original_img_src_name\s%>/<img $img_size_string src=\"$url?q_act=view&q_srn=$img_src_item_id\">/g; }#end else }#end foreach ##END img_src_name ##BEGIN code_ref my @code_references; #if success do find and replace with code reference if possible $$file =~ s/<%[ ]*code_ref[ ]*([^ ]*)[ ]*%>(?{push(@code_references,$1);})/<% CODE_REF $1 %>/g; printStruct(\@code_references); my ($code_reference_return,$code_reference_error); foreach my $code_reference (@code_references){ undef $code_reference_return; undef $code_reference_error; my (@module_information)=split(/::/,$code_reference); my $specific_sub = pop @module_information; my $load_module = join("::",@module_information); my $eval_string=" use $load_module qw/ $specific_sub /; (\$\$code_reference_return,\$\$code_reference_error)=$code_reference(\\\@link_names); if (\$code_reference_return){return(1);}else{return(0);} "; print "EVAL STRING is $eval_string \n"; eval $eval_string; if($@){ push (@{$code_reference_error},[$@]); } if ($$code_reference_return){ my $code_reference_string=expandStruct($$code_reference_return); $$file =~ s/<%\sCODE_REF\s$code_reference\s%>/$code_reference_string/g; }else{ my $error_string=expandStruct($$code_reference_error); $$file =~ s/<%\sCODE_REF\s$code_reference\s%>/<!--error evaluating code ref: $error_string <% code_ref $code_reference %>-->/g; }#end else }#end foreach code_ref ##END code_ref my $compartment = new Safe; $compartment->share('$href'); $code = $compartment->reval($$file); if ($@) { print "Restricted eval failed! Unable to compile template:". $@."\n"; exit(0); } }#end if mime_type is text/html ##END parse template ##BEGIN complete output to the browser if($mime_type eq 'text/html'){ common_html_header(login=>1); $code->($href); common_html_footer(); }else{ #handle images, etc. while(<$file>){ print $_; }#end while }#end else ##END complete output to the browser 1;
package Util::Test_Util; use strict; use Exporter; use vars qw/ @ISA @EXPORT_OK /; @ISA = qw/ Exporter /; @EXPORT_OK = qw/ test_expand /; use lib "/opt/the_index/lib"; use Util::Output qw/ expandStruct /; sub test_expand{ #print "HERE I AM IN TEST_EXPAND \n"; my $reference = shift; my $return = expandStruct($reference); #my $return; unless ($return){return(undef,["This sucks"]);} return ($return,undef); #die; }#end sub test_expand 1; __END__