Excuse me for sending unsolecitated kb of mail. I'm developing a quiz-managing tool, based on a prototype already in use at the Engineering Faculty of the University of Florence.
My users and myself use LaTeX + some target to set up a database of questions, a perl script for selecting randomly from the database and a template for printing the results and various reports. I started re-writing the tool in a clearer way. My fundamental goal is: let people use their preferred tool, either LaTeX or Microsoft Word or Wiki (TWiki). So I started enhancing (I hope) tpage to allow variables (eventually interpolated into a template) to be specified as xml, perl dump file or (in a near future version) tagged text file. In these days I'm writing the "combinatorial machine" for extracting random quizzes and computing marks. The documentation is included in the attached script. Questions: 1) Am I completely wrong? (is there a simpler approach?) 2) Has it already been done? (there are several quizz-making scripts, but all force people to adopt a particular style, and few (none) allow LaTeX and/or templates..) 3) (more techical): How can I share a stash between templates (as when there is a [% PROCESS %] directive) and how can i cleanly extract variables (includeing structured variables, like loh) defined in the templates from a stash ? 4) I would like to use my tool as a CGI script, but I need to [% INCLUDE %] or evaluate perl. I would like to change the way INCLUDE directives are processed, to avoid arbitrary acceesses to filesystem, and wrap perl inside a Safe module. Which is the suggested way of proceding? I can write a plugin, but I would like a syntax similar to Template keywords: [% MYINCLUDE file %] istead of [% myinclude('file') %] Is it possible? Thanks. -- Franco Bagnoli (franchino) <[EMAIL PROTECTED]> virtual location: Dipartimento di Energetica "S. Stecco" real location: Dip. Matematica Applicata "G. Sansone", Universita' Firenze, Via S. Marta, 3 I-50139 Firenze, Italy. Tel. +39 0554796422, fax: +39 055471787 GPG Key fingerprint = 169D 9EA5 8FD3 7EDA E43A 9830 255F BCEC 0D63 3728
#!/usr/bin/perl -w # itpage is an evolution (I hope) of tpage, from the tt2 toolkit. # it does essentially the same things, with the # possibility of loading an xml or per file to be interpolated # (whence the "i" in the name) in the template. # description is at the end # to do: # - add tagged text (wiki text) format : grammar definition file? # (this could allow other fomats, like tex or rtf) # - allow passing of stash between templates and saving, # so that templates can be used for defining variables # - possibility of dynamically loading plugins to elaborate variables # before interpolation eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}' if 0; # not running under some shell use strict; use Template; use Getopt::Long; use XML::Simple; use Storable qw/nstore_fd/; use Data::Dumper; use Safe; use Pod::Text; use IO::Handle; use vars qw /$vars/; #definition of parameter handling my $DEBUG = 1; $vars = {}; #this is the hash ref interpolated in the template my $define = {}; my $output = ''; my $outputhandle = \*STDOUT; my $save = ''; my $forcearray=1; my $forcecontent=1; my $allowperl = 1; GetOptions ( 'help|h|?' => \&help, 'define|d=s' => $define, 'load|l=s' => \&load, 'save|s=s' => \&save, 'output|o=s' => $output, 'forcearray|a!' => \$forcearray, 'forcecontent|c!' => \$forcecontent, 'eval_perl|p!' => \$allowperl, ); addkeys($vars, $define); # read from STDIN if no files specified push(@ARGV, '-') unless (@ARGV); # if just one file and no --output and it is named something # like try.tt2.tex or try.tex.tt2, then the ".tt2" # is striped off and output is try.tex # unless ($output) { if (@ARGV == 1 and $ARGV[0] =~ /\.tt2/) { ($output = $ARGV[0]) =~ s/\.tt2//; } } print "output: $output\n" if $DEBUG; # open output file if ($output) { open($outputhandle, ">$output") || die ("cannot open $output for writing"); } # create a template processor my $template = Template->new({ ABSOLUTE => 1, RELATIVE => 1, PRE_CHOMP=>1, POST_CHOMP=>0, OUTPUT_PATH=>".", EVAL_PERL=>$allowperl, PLUGIN_BASE => "", }); # process each input file foreach my $file (@ARGV) { $vars->{INPUTFILE} = $ARGV[0]; ($vars->{INPUTBASE} = $ARGV[0]) =~ s/\..*?$//; $file = \*STDIN if $file eq '-'; $template->process($file, $vars, $output) || die $template->error(); } # subroutines # loads a structure into %{$vars}. # extensions: perl (.pl) xml (.xml) # storable (.stor) # to do : # # others considered tagged text: # there should be somewhere a grammar file for parsing # named as the extension (say something like tex.itp for # tagged tex file or wk.itp for wiki files sub load { my $compartment = new Safe 'Load'; my $load = $_[1]; $Load::vars = {}; # refresh $Load::vars unless (-e $load) { die "Error: --load requires a valid file (you wrote \"$load\")"; } if ($load =~ /\.pl$/) { # perl file print "load: found perl file $load\n" if $DEBUG; $compartment->rdo($load) or die "load error: $@\n"; } elsif ($load =~ /\.xml$/) { #xml file if ($DEBUG) { print "load: found xml file $load\n forcearray: $forcearray\n forcecontent: $forcecontent\n"; } $Load::vars=XMLin( $load, searchpath=>'.', forcearray=>$forcearray, forcecontent=>$forcecontent, cache=>'storable', parseropts => [ProtocolEncoding => 'ISO-8859-1'], ); } elsif ($load =~ /\.stor$/) { # storable if ($DEBUG) { print "load: found storable file $load\n"; } $Load::vars = retrieve ($load); } else { # tagged text if ($DEBUG) { print "load: found tagged text $load\n"; } die "error: tagged text non jet implemented"; } addkeys($vars, $Load::vars); print "load: ended\n" if $DEBUG; } # saves %{$vars} on disk. sub save { $save = $_[1]; open OUT, ">$save" or die ("cannot open $save"); if ($save =~ /\.pl$/) { # perl file print "save: producing perl file $save\n" if $DEBUG; $Data::Dumper::Indent=1; print OUT Data::Dumper->Dump([$vars], ['vars']); } elsif ($save =~ /\.xml$/) { #xml file print "save: producing xml file $save\n" if $DEBUG; print OUT XMLout($vars); } elsif ($save =~ /\.stor$/) { #storable file print "save: producing storable file $save\n" if $DEBUG; nstore_fd($vars,\*OUT); } else { print "save: producing tagged text file file $save\n" if $DEBUG; die "tagged file saving not yet implemented"; } close OUT; print "save: ended\n" if $DEBUG; } sub addkeys { my ($dest, $src) = @_; my ($key, $value); while ( ($key,$value) = each(%{$src}) ) { $dest->{$key} = $value; } } sub help { my $parser = Pod::Text->new (sentence => 0, width => 78); $parser->parse_from_filehandle(*DATA); exit; } __END__ =head1 NAME itpage - Load definition files and process templates from command line version 1.0.20020124 =head1 USAGE itpage [options] [file(s)] =head1 DESCRIPTION The B<itpage> script is a simple extension of B<tpage>, from the Template Toolkit processor (L<www.tt2.org>). It is used (almost) like the B<tpage> script, for processing template files, but variables to be interpolated can be defined into "definition files" in a variety of formats (see DEFINITION FILES). It can also be used for merging and changing format of definition files. but see the TODO section..... =head1 OPTIONS -h (--help) Print this page -d key=value (--define) Define a variable on command line -l FILE (--load) Read a definition file. See DEFINITION FILES. -s FILE (--save) Save a definition file. See DEFINITION FILES. -o FILE (--output) Output file. See OUTPUT FILES -[no]a (--[no]forcearray) Option for XML files, see DEFINITION FILES -[no]c (--[no]forcecontent) Option for XML files, see DEFINITION FILES -p (--eval_perl) Evaluate [% PERL %] ... [% END %] code blocks =head1 DEFINITION FILES Definition files are loaded using the --load option and interpolated into templates. If more than one definition file is loaded, their contents are merged. Variables can also be defined on command line using the --define option. Definition files (eventually merged) can be dumped to disk using the --save option. There are several possible formats: xml: read and saved using XML::Simple. One can define variable to be interpolated using an xml file: variables.xml: <head> this is the first line </head> <foot> this is the last line </foot> try.tt2.txt: [% head %] this is the body [% foot %] and using itpage -noc -noa -l variables.xml try.tt2.txt produces try.txt: this is the first line this is the body this is the last line WARNING: if you do not use the -noc -noa options, you have to write a template like [% head.0.content %] this is the body [% foot.0.content %] see the XML::Simple man pages. Variables can also be defined in a perl file (same format ad Data::Dumper) using the hash ref "$vars": example: variables.pl $vars = $vars = { 'head' => ' this is the first line ', 'foot' => ' this is the last line ' }; or as storable file (see Storable). Tagged text format based on grammar will be implemented in a future version (se TODO). In a future version, variables defined inside a template will be merged in the %{$vars} hash, so that also tt2 files will be used as definition files (see TODO). By combining --load and --save one can merge definition files and/or convert from one format to another. =head1 OUTPUT FILES More than one file can be specified on command line. Output is on STDOUT unless the --output option is passed or, if there is just one file on command line, it contains the ".tt2" ssubstring. In this latter case, the ".tt2" string is stripped, definig the output file. Example: B<itpage try.tt2.txt> processes try.tt2.txt and writes on try.txt. =head1 TODO 1) Debugging 2) Tagged text format: for instance: HEAD: this is the first line FOOT: and this is the last one VECTOR: * first component VECTOR.0 * second component VECTOR.1 * third component VECTOR.2 HASH: first: this is the component HASH.first second: this is the component HASH.second or: \Q This is a question for a quizz file in \LaTeX \R right answer \W wrong answer \Q And this as another question.... The tags are defined in a grammar file. Why this? I developed itpage as a first step toward a script for managing multiple-choiches quiz. In this way people can define databases of questions using their preferred tool, LaTeX in particolar, and validate the syntax (the LaTeX syntax, not the tag one) simply by compiling the file. The first example instead is inspired by the idea of wiki syntax: write like you use doing in e-mails an let the computer do the work. But modernity calls for xml files..... Then, a plugin (next todo) can elaborate the variables (for instance, build the quiz or compute marks. The elaborated variables can be saved on disk by using the --save option. All "reports" are produced by means of a suitable template. 3) Plugins. One should be able to elaborate variables by loading a small perl fragment. In this way all input processing is done by the script, and output by templates. 4) Allow templates to export variables. Templates themself can be used to export variables to other templates (similar to using the [% PROCESS %] statement). I have found no "clean" way of extracting variables from stash except custom filtering internal from external variables. Maybe it is for templates to share the stash. 5) Relax syntax. --load can be eliminated, I suppose. 6) Configuration files (i.e. something similar to ttree) 7) Possibility of a graphics interface for editing variables (XML editors?) and choosing template files. Needed for Windows users.... Maybe using a miniserver http? =head1 AUTHOR Franco (franchino) Bagnoli E<lt>[EMAIL PROTECTED]<gt> =head1 VERSION 1.0.20020124 (24/01/2002) =head1 COPYRIGHT Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. Copyright (C) 1998-2001 Canon Research Centre Europe Ltd. Copyright (C) 2001 Franco Bagnoli. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L<tpage|Template::Tools::ttpage> L<ttree|Template::Tools::ttree> L<XML::Simple> L<Data::Dumper>