[Bio] / Sprout / SearchSkeleton.pm Repository:
ViewVC logotype

Annotation of /Sprout/SearchSkeleton.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.5 - (view) (download) (as text)

1 : parrello 1.1 #!/usr/bin/perl -w
2 :    
3 :     #
4 :     # Copyright (c) 2003-2006 University of Chicago and Fellowship
5 :     # for Interpretations of Genomes. All Rights Reserved.
6 :     #
7 :     # This file is part of the SEED Toolkit.
8 :     #
9 :     # The SEED Toolkit is free software. You can redistribute
10 :     # it and/or modify it under the terms of the SEED Toolkit
11 :     # Public License.
12 :     #
13 :     # You should have received a copy of the SEED Toolkit Public License
14 :     # along with this program; if not write to the University of Chicago
15 :     # at info@ci.uchicago.edu or the Fellowship for Interpretation of
16 :     # Genomes at veronika@thefig.info or download a copy from
17 :     # http://www.theseed.org/LICENSE.TXT.
18 :     #
19 :     package SearchSkeleton;
20 :    
21 :     use strict;
22 :     use Tracer;
23 : parrello 1.2 use CGI qw(-nosticky);
24 : parrello 1.1 use Sprout;
25 :     use SearchHelper;
26 :     use POSIX qw(ceil);
27 :     use File::stat;
28 :     use FIGRules;
29 :     use TWiki::Func;
30 :     use HTML::Template;
31 :    
32 :     =head1 NMPDR Search Skeleton
33 :    
34 :     This package executes a search and displays the results. If, on entry,
35 :     it sees a session ID, then it will assume search results have been
36 :     cached and the cached results are to be displayed. Otherwise, it
37 :     will perform the search, cache the results, and display the first
38 :     page. The search itself is performed by an object that subclasses
39 :     B<SearchHelper>. The results are formatted by an object that
40 :     subclasses C<ResultHelper>.To allow for additional search types, you need
41 :     merely implement a new subclass of B<SearchHelper> and possibly a
42 :     new subclass of B<ResultHelper>. By convention, all search helper
43 :     subclasses begin with the letters C<SH> and all result helper
44 :     subclasses begin with the letters C<RH>. This is not consistent
45 :     with normal PERL practice, but it fits better into the way we
46 :     do builds.
47 :    
48 :     =head2 Session Data
49 :    
50 :     The following parameters are expected from the CGI query object.
51 :     Additional parameters may be required by whichever B<SearchHelper>
52 :     subclass is selected. By convention, the parameters required by
53 :     the subclasses will be lower-case and the parameters used by this
54 :     script are capital-case. Note that some parameters are only required
55 :     by old sessions, that is, sessions which are established with
56 :     existing search result cache files.
57 :    
58 :     =over 4
59 :    
60 :     =item Trace
61 :    
62 :     Trace level and list of trace modules to turn on, space-delimited.
63 :    
64 :     =item NoForm
65 :    
66 :     If specified, then no search form will be generated.
67 :    
68 :     =item SessionID
69 :    
70 :     Unique session ID for this user. This is used to generate the name of the user's
71 :     cache file in the temporary directory. The actual filename will be
72 :     C<tmp_>I<SessionID>C<.cache>.
73 :    
74 :     =item Page (old only)
75 :    
76 :     Number of the current page to display.
77 :    
78 :     =item PageSize
79 :    
80 :     Number of items per page.
81 :    
82 :     =item ResultCount (old only)
83 :    
84 :     Total number of search result lines.
85 :    
86 :     =item ResultType (old only)
87 :    
88 :     Type of result displayed.
89 :    
90 :     =item Class
91 :    
92 :     Name of the B<SearchHelper> subclass for this type of search. The name does not include
93 :     the C<SH> prefix. So, to specify a B<SHFidSearch> type of
94 :     search, you would specify a class of C<FidSearch>. If this parameter is omitted,
95 :     then all of the advanced search forms will be displayed.
96 :    
97 :     =item Alternate
98 :    
99 :     If specified, then a list of advanced search forms will be shown.
100 :    
101 :     =item Download
102 :    
103 :     If specified, then the table of search results will be downloaded. The value
104 :     indicates the download format. Currently, C<tbl> will download the search results
105 :     in a tab-delimited file, and C<fasta> will download the search results as a
106 :     FASTA file.
107 :    
108 :     =item DownloadItem
109 :    
110 :     If specified, then only the item specified will be downloaded rather than
111 :     all of the search results. At some point this will be a list-type thing so
112 :     the user can download more than one item.
113 :    
114 :     =back
115 :    
116 :     =head2 The Cache File
117 :    
118 :     The cache file is a tab-delimited file. The first line of the file contains the
119 :     column names and the remaining lines contain the data for each result item.
120 :    
121 :     The column contents may contain HTML tags, including hyperlinks and buttons. For best
122 :     results, all links should be relative.
123 :    
124 :     Some columns will consist of a doubled percent sign followed by a name, an equal sign,
125 :     and some text. This tells the display code to call the B<RunTimeColumns> method of
126 :     the B<SearchHelper> object to compute the column value. This facility is designed for
127 :     columns that require a lot of time to calculate, so we don't want to calculate them
128 :     until we absolutely have to display them.
129 :    
130 :     If the cache file is empty or has only a single line, a stock "No Search Results"
131 :     message will be displayed.
132 :    
133 :     =cut
134 :    
135 :     # Global variable containing the names of the parameters that get stored in the status URL.
136 : parrello 1.2 my @Keepers = qw(SessionID Trace NoForm ResultCount ResultType Page PageSize Class
137 :     SPROUT Download FavoredAlias);
138 : parrello 1.1 # Map of old class names to new class names.
139 :     my %ClassMap = (BlastSearch => 'ToolSearch', FidSearch => 'GeneSearch');
140 :     # List of searches we want to hide from users. This will go away when we've fixed up the wiki.
141 : parrello 1.3 my %Hidden = (PropSearch => 1);
142 : parrello 1.1
143 :     =head3 Package Methods
144 :    
145 :     =head3 main
146 :    
147 :     SearchSkeleton::main($cgi, $session, $varHash);
148 :    
149 :     Process a search request. This method will analyze the CGI parameters and
150 :     write output directly to the user's browser.
151 :    
152 :     =over 4
153 :    
154 :     =item cgi
155 :    
156 :     CGI query object to use for accessing the search parameters.
157 :    
158 :     =item session
159 :    
160 :     TWiki session object.
161 :    
162 :     =item varHash
163 :    
164 :     Hash to use for computing screen data to display.
165 :    
166 :     =back
167 :    
168 :     =cut
169 :    
170 :     sub main {
171 :     # Get the parameters.
172 :     my ($cgi, $session, $varHash) = @_;
173 :     # Turn on emergency tracing.
174 :     ETracing($cgi);
175 :     # If this next variable is set to Download, then a download is in progress and the output
176 :     # is saved to the user's hard drive. If it's set to "Search", then a search is in
177 :     # progress and we don't produce the template at the end.
178 :     my $mode = "Display";
179 :     # If search mode is 1, the search helper will be in here.
180 :     my $shelp;
181 :     # If there are results, the results helper will be in here.
182 :     my $rhelp;
183 :     eval {
184 :     # Get the search class.
185 :     my $class = $cgi->param("Class");
186 :     # Get the result type (if any).
187 :     my $resultType = $cgi->param("ResultType");
188 :     # Check for advanced mode.
189 :     if ($cgi->param("Alternate")) {
190 :     Trace("Advanced mode selected.") if T(3);
191 : parrello 1.2 # In advanced mode, we list all the search forms in
192 : parrello 1.1 # $FIG_Config::advanced_class.
193 :     my @classes = split(/\s+/, $FIG_Config::advanced_class);
194 :     # Set the page size to the default.
195 :     $cgi->param(-name => 'PageSize', -value => $FIG_Config::results_per_page);
196 :     # Tell the template we have no search results and no class.
197 :     $varHash->{result_count} = 0;
198 :     $varHash->{class} = "";
199 :     # Loop through the classes, creating the table of contents and
200 :     # the forms.
201 : parrello 1.2 $varHash->{formIndex} = CGI::h3("Contents") . CGI::start_ul();
202 : parrello 1.1 for my $className (@classes) {
203 :     my $shelp = SearchHelper::GetHelper($cgi, SH => $className);
204 :     # Produce the contents entry.
205 : parrello 1.2 $varHash->{formIndex} .= CGI::li(CGI::a({href => "#X$className"}, $className) .
206 : parrello 1.1 ": " . $shelp->Description());
207 :     # Produce the bookmark.
208 : parrello 1.2 $varHash->{form} .= CGI::a({ name => "X$className" });
209 : parrello 1.1 # Produce the form.
210 :     $varHash->{form} .= $shelp->Form();
211 :     # Produce the help text.
212 :     $varHash->{form} .= $shelp->GetHelpText();
213 :     # Put some space between us and whatever comes next.
214 :     $varHash->{form} .= "<p>&nbsp;</p>";
215 :     }
216 :     # Check the number of classes.
217 :     if (@classes < 2) {
218 :     # Only one class, so we don't need the table of contents.
219 :     $varHash->{formIndex} = "";
220 :     } else {
221 :     # Multiple classes, so close the table of contents.
222 : parrello 1.2 $varHash->{formIndex} .= CGI::end_ul();
223 : parrello 1.1 }
224 :     } elsif (! $class) {
225 :     Trace("Producing index of search tools.") if T(3);
226 :     # No class specified, so we simply generate an index of the
227 :     # searches. First, make sure the template knows there are no search results.
228 :     $varHash->{result_count} = 0;
229 :     Trace("Building URL.") if T(3);
230 :     # Get a copy of our URL and append a question mark.
231 :     my $selfURL = "$FIG_Config::cgi_url/SearchSkeleton.cgi?";
232 :     # Loop through the search classes building a table of contents.
233 :     my @contents = ();
234 :     for my $className (SearchHelper::AdvancedClassList()) {
235 :     if ($Hidden{$className}) {
236 :     Trace("Skipping hidden class $className.") if T(3);
237 :     } else {
238 :     Trace("Processing $className") if T(3);
239 :     my $shelp = SearchHelper::GetHelper($cgi, SH => $className);
240 : parrello 1.2 push @contents, "<a href=\"${selfURL}Class=$className\">$className</a>: " .
241 :     $shelp->Description();
242 : parrello 1.1 }
243 :     }
244 :     # Create the table of contents.
245 :     Trace("Building index.") if T(3);
246 : parrello 1.2 my $index = CGI::h3("Index of Search Tools" .
247 : parrello 1.4 SearchHelper::Hint('NmpdrSearch')) .
248 : parrello 1.2 CGI::ul(CGI::li(\@contents));
249 :     # Add the main help link.
250 : parrello 1.1 # Store it as the results.
251 :     $varHash->{results} = $index;
252 :     # Tell the template we don't have a class.
253 :     $varHash->{class} = "";
254 :     Trace("Index built.") if T(3);
255 :     } else {
256 :     Trace("Class $class detected.") if T(3);
257 :     # If this class has had its name changed, use the new name.
258 :     if (exists $ClassMap{$class}) {
259 :     $class = $ClassMap{$class};
260 :     $cgi->param(Class => $class);
261 :     Trace("New class name is $class.") if T(3);
262 :     }
263 :     # Here we have a class, so we're working with a single type of search.
264 :     $shelp = SearchHelper::GetHelper($cgi, SH => $class);
265 :     # Tell the template what the class is.
266 :     $varHash->{class} = $class;
267 : parrello 1.2 # Generate its header stuff.
268 :     $varHash->{head} = $shelp->HeaderHtml();
269 : parrello 1.1 # Insure we have a page size.
270 :     if (! $cgi->param("PageSize")) {
271 :     $cgi->param(-name => 'PageSize', -value => $FIG_Config::results_per_page);
272 :     }
273 :     # Declare the result count variable.
274 :     my $result_count = 0;
275 :     # Now there are three different directions we can go. If a
276 :     # "Search" button has been pressed, then we need to perform a
277 :     # search. If this is a new session and the button has not
278 :     # been pressed, we do nothing. If this is an old session
279 :     # and the button has not been pressed, we display results. Note
280 :     # that we allow for regular buttons (Search) or image buttons
281 :     # (Search.x).
282 :     if (!$cgi->param("Search") && !$cgi->param("Search.x")) {
283 :     # No button, so check for results. Note we only do this if this is not
284 :     # a new session. A new session won't have results.
285 :     Trace("No search requested.") if T(3);
286 :     # Check for a result type.
287 :     if (defined $resultType) {
288 :     # Get the object that controls the result type.
289 :     $rhelp = SearchHelper::GetHelper($shelp, RH => $resultType);
290 :     }
291 :     # Get the result count, which should have been set when we did the search. If
292 :     # we did no search, it won't be set, so in that case we want to make it zero.
293 :     $result_count = $cgi->param("ResultCount") || 0;
294 :     # Get the download type (if any).
295 :     my $dlType = $cgi->param("Download") | "";
296 :     # Check for a Download request.
297 :     if ($dlType) {
298 :     # Here we're downloading.
299 :     $mode = "Download";
300 :     # Download the results.
301 :     DownloadResults($dlType, $shelp, $rhelp, $cgi);
302 :     } else {
303 :     # If we have a saved search, load its parameters so they show up in the form.
304 :     LoadSearchParms($cgi, $shelp);
305 : parrello 1.2 # Display the form, if desired. This absolutely must happen before we do the
306 :     # ShowURL thing when we display the results. The form can actually appear
307 :     # after the results, however, thanks to the template.
308 : parrello 1.1 my $formShown = ! $cgi->param("NoForm");
309 :     if (! $cgi->param("NoForm")) {
310 :     Trace("Displaying form.") if T(3);
311 :     $varHash->{form} = $shelp->Form();
312 :     }
313 :     if (! $shelp->IsNew()) {
314 : parrello 1.2 # We have results, so display them.
315 : parrello 1.1 $varHash->{results} = DisplayResults($shelp, $rhelp, $cgi);
316 :     }
317 :     # Save the result count so that the results helper text appears if it
318 :     # is needed. This text is in the template, but it's protected by a TMPL_IF
319 :     # on "result_count".
320 :     $varHash->{result_count} = $result_count;
321 :     }
322 :     } else {
323 :     # Here we have a button press, so we need to find stuff. In this case the
324 :     # template is not used. Instead, status is displayed while we search, and
325 :     # then a JavaScript trick is used to switch the user to the first page of
326 :     # results. This prevents the server from giving up if the search takes a long
327 :     # time.
328 :     Trace("Performing the search.") if T(3);
329 : parrello 1.2 # Save the search parameters so we can display them on the result pages.
330 :     Trace("Saving search parameters.") if T(3);
331 :     SaveSearchParms($cgi, $shelp);
332 :     # Denote we're in searching mode. This means we'll be displaying the HTML
333 :     # as we go along.
334 : parrello 1.1 $mode = "Searching";
335 :     # Make sure the output is unbuffered.
336 :     $| = 1;
337 :     # Start the HTML page.
338 : parrello 1.2 print CGI::header();
339 :     print CGI::start_html(-title => 'NMPDR Search in Progress',
340 : parrello 1.1 -style => { src => "$FIG_Config::cgi_url/wiki/pub/Main/TWikiPreferences/NmpdrStyleOverrides.css" }
341 :     );
342 :     # Print the banner.
343 : parrello 1.2 print CGI::img({ src => "$FIG_Config::cgi_url/wiki/pub/Main/TWikiPreferences/banner2.png" });
344 : parrello 1.1 # Tell the user the type of this search.
345 : parrello 1.2 print CGI::h2($shelp->SearchTitle()) . "\n";
346 : parrello 1.1 # Start a paragraph.
347 :     print "<p>\n";
348 :     # Perform the search.
349 :     Trace("Calling FIND method.") if T(3);
350 :     $result_count = $shelp->Find();
351 :     Trace("Processing results.") if T(3);
352 :     # End the paragraph.
353 :     print "</p>\n";
354 :     # Check to see what kind of results we got.
355 :     if (! defined($result_count)) {
356 :     # Here an error occurred, so we display the error message.
357 : parrello 1.2 $shelp->PrintLine(CGI::h3("ERROR: " . $shelp->Message()));
358 : parrello 1.1 $result_count = 0;
359 : parrello 1.2 $shelp->PrintLine(CGI::p("Use your browser's BACK button to try again."));
360 : parrello 1.1 } else {
361 :     # Here we have results (even though there may be zero of them. Save
362 :     # the result count and set up to display the first page of results.
363 :     $cgi->param(-name => "ResultCount", -value => $result_count);
364 :     $cgi->param(-name => "Page", -value => 1);
365 :     # Now we create the URL for the first page of results.
366 :     my $page1Url = StatusURL($cgi);
367 :     # Create the Javascript thingie to pull up the results.
368 :     $shelp->PrintLine('<script type="text/javascript">');
369 :     $shelp->PrintLine(" location.href = \"$page1Url\";");
370 :     $shelp->PrintLine('</script>');
371 :     }
372 :     }
373 :     }
374 :     };
375 :     if ($@) {
376 : parrello 1.2 # Compute the error message HTML.
377 :     my $message = $@;
378 :     $varHash->{results} = FIGRules::NmpdrErrorPage(SearchSkeleton => $message);
379 : parrello 1.1 if ($mode eq "Searching") {
380 :     # Here we've already started the page, so we output the error message immediately.
381 :     $shelp->PrintLine($varHash->{results});
382 :     } elsif ($mode eq "Download") {
383 : parrello 1.2 # A download is in progress, so we have to put an error message in
384 :     # the download file. Note that we trim off the extra trace garbage
385 :     # to reduce the yuckiness.
386 :     my ($realMessage) = split /\n/, $message, 2;
387 :     print "\n\n*** ERROR: Download failed: $realMessage\n";
388 : parrello 1.1 }
389 :     }
390 :     if ($mode eq "Searching") {
391 :     # We've already started the page, so all we have to do is terminate it.
392 : parrello 1.2 $shelp->PrintLine(CGI::end_html());
393 : parrello 1.1 } elsif ($mode eq "Display") {
394 : parrello 1.2 # Here there's been no output, so we're showing either a form or results.
395 :     # Print the CGI header. We suppress browser caches so that the javascript
396 :     # doesn't go haywire on us.
397 :     print "Cache-Control: no-cache\n";
398 : parrello 1.1 print $cgi->header();
399 :     # Produce the output using a template.
400 :     print MyPage($varHash);
401 :     }
402 :     }
403 :    
404 :     =head3 MyPage
405 :    
406 :     my $html = MyPage($varHash);
407 :    
408 :     Output a search form or result page. The incoming hash contains
409 :     parameters that are plugged into a template.
410 :    
411 :     =over 4
412 :    
413 :     =item varHash
414 :    
415 :     Hash containing the variable data to be placed on the page.
416 :    
417 :     =item RETURN
418 :    
419 :     Returns the page HTML to display.
420 :    
421 :     =back
422 :    
423 :     The variables in the hash should be as follows.
424 :    
425 :     =over 4
426 :    
427 :     =item result_count
428 :    
429 :     Number of search results.
430 :    
431 :     =item results
432 :    
433 :     Table displaying the search results.
434 :    
435 :     =item formIndex
436 :    
437 :     If multiple search forms are displayed, this should contain the table of contents.
438 :    
439 :     =item form
440 :    
441 :     Search form.
442 :    
443 :     =item tracing
444 :    
445 :     Debugging output.
446 :    
447 :     =back
448 :    
449 :     =cut
450 :    
451 :     sub MyPage {
452 :     # Get the parameters.
453 :     my ($varHash) = @_;
454 :     # Compute the page title.
455 :     my $resultsFound = $varHash->{result_count};
456 :     my $title = ($resultsFound ? 'SearchResults' : 'SearchPage');
457 :     # Get the text template. This is an HTML template, not a TWiki template.
458 :     my $htmlTemplate = TWiki::Func::readAttachment('Main', 'TWikiPreferences', 'Search.tmpl');
459 :     my $templateObject = HTML::Template->new(scalarref => \$htmlTemplate,
460 :     die_on_bad_params => 0);
461 :     # Next, we pass in the variable values.
462 :     for my $varKey (keys %{$varHash}) {
463 :     # Get the variable value.
464 :     my $varValue = $varHash->{$varKey};
465 :     # Check for an undefined value.
466 :     if (! defined($varValue)) {
467 :     # Treat it as a null string.
468 :     $templateObject->param($varKey => "");
469 :     } else {
470 :     # Check for an array of scalars. We convert this into a string
471 :     # for compatibility with earlier stuff. An array of hashes is
472 :     # okay, because it's used for loops.
473 :     if (ref $varValue eq 'ARRAY') {
474 :     if (scalar @{$varValue} > 0 && ! ref $varValue->[0]) {
475 :     $varValue = join("\n", @{$varValue});
476 :     }
477 :     }
478 :     # Record the parameter.
479 :     $templateObject->param($varKey => $varValue);
480 :     }
481 :     }
482 :     # Finally, we produce the text.
483 :     my $text = $templateObject->output();
484 :     # Get the view template. This IS a TWiki template.
485 :     my $template = TWiki::Func::loadTemplate('view');
486 :     # Set the meta-variable values.
487 :     $template =~ s/%TEXT%/$text/g;
488 :     $template =~ s/%REVTITLE%//g;
489 :     # Expand it.
490 :     my $raw = TWiki::Func::expandCommonVariables($template, $title, 'Main');
491 :     # Render it into HTML.
492 :     my $retVal = TWiki::Func::renderText($raw, 'Main');
493 :     # Clean the nops.
494 :     $retVal =~ s/<nop>//g;
495 :     # Return the result.
496 :     return $retVal;
497 :     }
498 :    
499 :    
500 :     =head3 DownloadResults
501 :    
502 :     DownloadResults($dlType, $shelp, $rhelp, $cgi);
503 :    
504 :     Download the search results as a text file. We use a content-disposition header to create
505 :     output that will be saved automatically to the user's hard drive.
506 :    
507 :     =over 4
508 :    
509 :     =item dlType
510 :    
511 :     Download type (e.g. C<tbl>, C<fasta>).
512 :    
513 :     =item shelp
514 :    
515 :     Actice search helper.
516 :    
517 :     =item rhelp
518 :    
519 :     Relevant result helper. This is used to retrieve and process the results.
520 :    
521 :     =item cgi
522 :    
523 :     CGI query object used to format the output.
524 :    
525 :     =back
526 :    
527 :     =cut
528 :    
529 :     sub DownloadResults {
530 :     # Get the parameters.
531 :     my ($dlType, $shelp, $rhelp, $cgi) = @_;
532 :     # Get the operating system type.
533 :     my $osType = $cgi->param('os');
534 :     # Compute the appropriate EOL marker based on the web user's OS. Unfortunately,
535 :     # on the Mac download files are always treated as binary, and in all environments,
536 :     # FireFox doesn't display the download dialog correctly unless it's binary.
537 :     my $eol = FIGRules::ComputeEol($osType);
538 :     # Compute a file name.
539 :     my $defaultName = $cgi->param('Class') . ".$dlType";
540 :     # Check the state of the session file.
541 :     my $fileName = $shelp->GetCacheFileName();
542 :     if (! -e $fileName) {
543 :     Confess("Search session has expired. Please resubmit your query.");
544 :     } else {
545 :     # Write the CGI header.
546 : parrello 1.2 print CGI::header(-type => 'application/octet-stream',
547 : parrello 1.1 -attachment => $defaultName);
548 :     # Put us in binary mode so that the output doesn't do screwy stuff with new-lines.
549 :     binmode(STDOUT);
550 :     # The session file is here, so we can open it.
551 :     my $sessionH = Open(undef, "<$fileName");
552 :     if (T(3)) {
553 :     my $fileData = stat($sessionH);
554 :     Trace($fileData->size . " bytes in $fileName.");
555 :     }
556 :     # Read the column headers.
557 :     my @colHdrs = $shelp->ReadColumnHeaders($sessionH);
558 :     # Get the list of items to keep. If the list is empty, we keep everything. The idea here is that the user
559 :     # might have the option to select certain rows to be downloaded. The rows are identified by the row key,
560 :     # which is the first column in each row.
561 :     my %keepers = map { $_ => 1 } $cgi->param('DownloadItem');
562 :     # Here we get the number of lines to be downloaded. If we're not downloading everything,
563 :     # we'll decrement this number each time we download a kept item, and stop when it hits zero.
564 :     my $selections = scalar(keys %keepers);
565 :     my $selective = ($selections > 0);
566 :     # Download the header.
567 :     Trace("Downloading header. " . scalar(@colHdrs) . " columns present.") if T(3);
568 :     my @lines = $rhelp->DownloadDataLine(undef, $dlType, 'header', \@colHdrs);
569 :     DownloadLines($eol, @lines);
570 :     Trace("Downloading data lines.") if T(3);
571 :     # Now loop through the lines in the file, converting them to output text.
572 :     while (! eof $sessionH && (! $selective || $selections > 0)) {
573 :     # Get the current line of columns.
574 :     Trace("Reading line from session file.") if T(3);
575 :     my @cols = Tracer::GetLine($sessionH);
576 :     # Extract the object ID, which is the first column of the results.
577 :     my $objectID = shift @cols;
578 :     # Test to see if we're keeping this line.
579 :     if (! $selective || $keepers{$objectID}) {
580 :     # If so, we download it. Decrement the selection counter.
581 :     $selections--;
582 :     # Call the DownloadDataLine method to produce the lines of data to write.
583 :     @lines = $rhelp->DownloadDataLine($objectID, $dlType, \@cols, \@colHdrs);
584 :     # Write them out with the appropriate line-end character.
585 :     DownloadLines($eol, @lines);
586 :     }
587 :     }
588 :     # Download the footer.
589 :     Trace("Downloading footer.") if T(3);
590 :     @lines = $rhelp->DownloadDataLine(undef, $dlType, 'footer', \@colHdrs);
591 :     DownloadLines($eol, @lines);
592 :     }
593 :     }
594 :    
595 :     =head3 DownloadLines
596 :    
597 :     DownloadLines($eol, @lines);
598 :    
599 :     Write the specified lines to the download output using the given end-of-line character.
600 :    
601 :     =over 4
602 :    
603 :     =item eol
604 :    
605 :     End-of-line character to use.
606 :    
607 :     =item lines
608 :    
609 :     List of lines to write.
610 :    
611 :     =back
612 :    
613 :     =cut
614 :    
615 :     sub DownloadLines {
616 :     # Get the parameters.
617 :     my ($eol, @lines) = @_;
618 :     # Output the lines.
619 :     print join($eol, @lines, "");
620 :     }
621 :    
622 :     =head3 DisplayResults
623 :    
624 :     my $htmlText = DisplayResults($shelp, $rhelp, $cgi);
625 :    
626 :     Display the results of a search. A page of results will be displayed, along with links to get to
627 :     other pages. The HTML for the results display is returned.
628 :    
629 :     =over 4
630 :    
631 :     =item shelp
632 :    
633 :     Search helper object representing the search. The column headers and search rows will be
634 :     stored in the session file attached to it.
635 :    
636 :     =item rhelp
637 :    
638 :     Result helper object used to format the results.
639 :    
640 :     =item cgi
641 :    
642 :     CGI query object for the current session. This includes the page number, size, and result
643 :     counts.
644 :    
645 :     =item RETURN
646 :    
647 :     Returns the HTML text for displaying the current page of search results.
648 :    
649 :     =back
650 :    
651 :     =cut
652 :    
653 :     sub DisplayResults {
654 :     # Get the parameters.
655 :     my ($shelp, $rhelp, $cgi) = @_;
656 :     # Declare the return variable.
657 :     my $retVal = "";
658 :     # Check for a title.
659 :     my $title = $shelp->SearchTitle();
660 :     if ($title) {
661 : parrello 1.2 $title = CGI::h3($title);
662 : parrello 1.1 }
663 :     # Extract the result parameters.
664 : parrello 1.2 my ($pageSize, $pageNum, $resultCount) = map { $cgi->param($_) } qw(PageSize Page ResultCount);
665 :     my $rhelperClass = $cgi->param('ResultType');
666 :     my $shelperClass = $cgi->param('Class');
667 : parrello 1.1 Trace("Result count is $resultCount on page $pageNum for $pageSize/page.") if T(3);
668 :     Trace("Preferred ID style is " . $shelp->GetPreferredAliasType() . ".") if T(3);
669 :     # Only proceed if there are actual results.
670 :     if ($resultCount <= 0) {
671 : parrello 1.2 $retVal .= CGI::h3("No matches found.");
672 : parrello 1.1 } else {
673 :     # Check the state of the session file.
674 :     my $fileName = $shelp->GetCacheFileName();
675 :     if (! -e $fileName) {
676 : parrello 1.2 $retVal .= CGI::h3("Search session has expired. Please resubmit your query.");
677 : parrello 1.1 } else {
678 :     # The file is here, so we can open it.
679 :     my $sessionH = Open(undef, "<$fileName");
680 :     if (T(3)) {
681 :     my $fileData = stat($sessionH);
682 :     Trace($fileData->size . " bytes in $fileName.");
683 :     }
684 :     # Read the column headers.
685 :     my @colHdrs = $shelp->ReadColumnHeaders($sessionH);
686 : parrello 1.2 if (T(3)) {
687 :     Trace(scalar(@colHdrs) . " column headers found.");
688 :     for (my $i = 0; $i <= $#colHdrs; $i++) {
689 :     my $col = $colHdrs[$i];
690 :     $col = "X=$col->{name}" if (ref $col eq 'HASH');
691 :     Trace("Column $i is $col.");
692 :     }
693 :     }
694 : parrello 1.1 # Compute the page navigation string.
695 :     my $formFlag = ($cgi->param('NoForm') ? 0 : 1);
696 :     my $pageNavigator = PageNavigator($shelp, $formFlag);
697 :     # Now we need to find our page. The line number we compute will be
698 :     # zero-based. We'll read from the session file until it drops to zero.
699 :     # This may push us past end-of-file, but it won't cause an exception, and
700 :     # it's something that should only happen very rarely in any case.
701 :     my $linesToSkip = ($pageNum - 1) * $pageSize;
702 :     Trace("Skipping $linesToSkip lines in session file $fileName.") if T(3);
703 :     for (my $lines = $linesToSkip; $lines > 0; $lines--) {
704 :     Tracer::GetLine($sessionH);
705 :     }
706 :     # The session file is now positioned at the beginning of our line.
707 :     # We build the table rows one line at a time until we run out of data
708 :     # or exceed the page size.
709 :     my @tableRows = ();
710 :     my $linesLeft = $pageSize;
711 :     Trace("$linesLeft lines to read from session file.") if T(3);
712 :     while ($linesLeft-- > 0) {
713 :     Trace("Reading line from session file.") if T(3);
714 :     my @cols = Tracer::GetLine($sessionH);
715 :     if (! @cols) {
716 :     Trace("End of file read.") if T(3);
717 :     $linesLeft = 0;
718 :     } else {
719 :     Trace("Line has " . scalar(@cols) . " columns. $linesLeft lines left.") if T(3);
720 :     # Peel off the first column. This is the ID of the result object. We don't use
721 :     # it, but other methods do.
722 :     shift @cols;
723 :     # Check the columns for run-time generation.
724 :     my @actual = $rhelp->GetRunTimeValues(@cols);
725 :     # Put the actual data into the table list.
726 :     push @tableRows, \@actual;
727 :     }
728 :     }
729 :     # Start the list of links. The first one is the URL-save link.
730 : parrello 1.2 my $downloadScript = CGI::start_table({ border => "2" });
731 : parrello 1.1 if (! $cgi->param("NoForm")) {
732 :     my $searchURL = $shelp->ComputeSearchURL();
733 : parrello 1.2 # This will only work if the URL is short enough.
734 :     if (length $searchURL < 2048) {
735 :     $downloadScript .= CGI::Tr(CGI::td("URL to repeat this search (right-click to save)"),
736 :     CGI::td({ style => 'text-align: center; font-weight: bold' },
737 :     CGI::a({ href => $searchURL }, "Repeat"))) . "\n";
738 :     }
739 :     }
740 :     # The remaining links are disguised in buttons using this style string.
741 :     my $buttonClass = "button button2";
742 :     # Next is the optional download-to-seedviewer thing.
743 :     if ($resultCount <= $FIG_Config::max_seedviewer_table) {
744 :     my $sessionID = $shelp->ID();
745 :     my $svURL = "$FIG_Config::cgi_url/wiki/rest.cgi/NmpdrPlugin/SeedViewer?" .
746 :     "page=NmpdrGeneTable;SessionID=$sessionID;PageSize=$pageSize;" .
747 :     "Class=$shelperClass;ResultType=$rhelperClass";
748 :     $downloadScript .= CGI::Tr(CGI::td("View these results in a [[FIG.InteractiveTable][sortable, expandable table]]"),
749 :     CGI::td({ class => $buttonClass }, CGI::a({ href => $svURL }, "Table View"))) . "\n";
750 : parrello 1.1 }
751 :     # Now compute the download links. This is actually a JavaScript thing, because we need to know
752 :     # name of the user's operating system and handle fields inside the link text.
753 :     my $downloadURL = StatusURL($cgi);
754 :     my $dlType;
755 :     # Ask the result helper which download types are supported.
756 :     my %myDlTypes = $rhelp->DownloadFormatsAvailable();
757 :     # First we display the links themselves.
758 :     for $dlType (sort keys %myDlTypes) {
759 :     my $dlDesc = $myDlTypes{$dlType};
760 :     # Check the description for a data field.
761 :     if ($dlDesc =~ /^([^\[]+)(\[[^\]]+\])(.+)/) {
762 :     my ($prefix, $data, $suffix) = ($1, $2, $3);
763 :     # We want to replace the data thing with a text field. First, we parse out the field name.
764 :     $data =~ /\[(\w+)\]/;
765 :     my ($fieldName) = ($1, $2);
766 :     # Generate the text field HTML.
767 : parrello 1.2 my $textField = CGI::textfield(-name => $fieldName, -size => 5,
768 : parrello 1.1 -onKeyUp => "updateAnchor('$dlType', '$fieldName', this.value)");
769 :     # Put it all together.
770 :     $dlDesc = "$prefix$textField$suffix";
771 :     }
772 : parrello 1.2 $downloadScript .= CGI::Tr(CGI::td("$dlDesc"), CGI::td({ class => $buttonClass },
773 :     CGI::a({ id => "dlLink$dlType" }, "Download"))) . "\n";
774 : parrello 1.1 }
775 : parrello 1.2 $downloadScript .= CGI::end_table();
776 : parrello 1.1 # Now we create the javascript to fill the URLs into the link anchors. Each URL adds the download
777 :     # type, operating system ID, and data-thing parameters to the link URL. We have one method that
778 :     # initializes all the links, and another that updates a link when a parameter changes.
779 :     $downloadScript .= "<script type=\"text/javascript\">\n" .
780 :     " function setAnchors() {\n" .
781 :     " var sysType = checkOS();\n" .
782 :     " var linkAnchor;\n";
783 :     for $dlType (keys %myDlTypes) {
784 :     $downloadScript .= " linkAnchor = document.getElementById('dlLink$dlType');\n" .
785 :     " linkAnchor.href = '$downloadURL;Download=$dlType;os=' + sysType;\n";
786 :     }
787 :     $downloadScript .= " };\n";
788 :     # Now we've got the method for initializes all the links. The next one updates a link when its parameter
789 :     # field changes.
790 :     $downloadScript .= " function updateAnchor(dlType, name, value) {\n" .
791 :     " var sysType = checkOS();\n" .
792 :     " var linkAnchor;\n" .
793 :     " linkAnchor = document.getElementById('dlLink' + dlType);\n" .
794 :     " linkAnchor.href = '$downloadURL;Download=' + dlType + ';os=' + sysType + ';' + name + '=' + value;\n" .
795 :     " };\n" .
796 :     " setAnchors();\n" .
797 :     "</script>";
798 :     # Finally, a spacer to separate the table from the page navigator.
799 :     $downloadScript .= "<p>&nbsp;</p>\n";
800 :     # Now we build the table. Create an array for the row styles.
801 :     my @styles = ('even', 'odd');
802 :     # Start the table.
803 : parrello 1.2 my @tableLines = (CGI::start_table({border => 0}));
804 : parrello 1.1 # Put in the column headers.
805 : parrello 1.2 push @tableLines, CGI::Tr({class => $styles[1]}, map { CGI::th({ class => $rhelp->ColumnStyle($_) },
806 : parrello 1.1 $rhelp->ColumnTitle($_)) } @colHdrs );
807 :     # Start the first data row with the even style.
808 :     my $styleMode = 0;
809 :     # Loop through the rows.
810 :     for my $row (@tableRows) {
811 :     # We'll put the table cells in here.
812 :     my @cells = ();
813 :     # Loop through the cells in this row. We use a numeric index because we're moving through
814 :     # the column headers list and the row list in parallel.
815 :     for (my $i = 0; $i <= $#colHdrs; $i++) {
816 : parrello 1.2 push @cells, CGI::td({class => $rhelp->ColumnStyle($colHdrs[$i]) }, $row->[$i]);
817 : parrello 1.1 }
818 :     # Push this row into the result list.
819 : parrello 1.2 push @tableLines, CGI::Tr({class => $styles[$styleMode]}, @cells);
820 : parrello 1.1 # Flip the style.
821 :     $styleMode = 1 - $styleMode;
822 :     }
823 :     # Close the table.
824 : parrello 1.2 push @tableLines, CGI::end_table();
825 : parrello 1.1 # Assemble the result.
826 :     my $tableText = join("\n", @tableLines);
827 :     # Finally, we compute the page label, which contains the page number, the number of results
828 :     # displayed, and the total results found. If the total found is zero, we would not even be here,
829 :     # so when we create our fancy English result count, we only have to worry about singular or
830 :     # plural.
831 :     my $resultCountLine;
832 :     my $linesFound = scalar @tableRows;
833 :     if ($resultCount == 1) {
834 :     $resultCountLine = "One Result Found.";
835 :     } elsif ($resultCount <= $linesFound) {
836 :     $resultCountLine = "$resultCount Results Found";
837 :     } else {
838 :     $resultCountLine = "Search Results Page $pageNum: $linesFound of $resultCount Results Displayed.";
839 :     }
840 : parrello 1.2 # Check for notices.
841 :     my $noticeLines = "";
842 :     my $noticeFileName = $shelp->GetTempFileName('notices');
843 :     if (-s $noticeFileName) {
844 :     # Read them in and format them into a bullet list.
845 :     my @lines = Tracer::GetFile($noticeFileName);
846 :     $noticeLines = CGI::h4('Notices') . CGI::ul(CGI::li(\@lines));
847 :     }
848 :     # Now we're ready. All the little pieces are put together. Note that
849 :     # the page navigator appears twice.
850 : parrello 1.1 $retVal = join("\n",
851 :     $title,
852 :     $downloadScript,
853 : parrello 1.2 $noticeLines,
854 : parrello 1.1 $pageNavigator,
855 : parrello 1.2 CGI::p("&nbsp;"),
856 :     CGI::h3($resultCountLine),
857 : parrello 1.1 $tableText,
858 : parrello 1.2 CGI::p("&nbsp;"),
859 : parrello 1.1 $pageNavigator,
860 :     "");
861 :     }
862 :     }
863 :     # Return the result.
864 :     return $retVal;
865 :     }
866 :    
867 :     =head3 PageNavigator
868 :    
869 :     my $htmlText = PageNavigator($shelp, $formFlag);
870 :    
871 :     Return a page navigation string for the specified query.
872 :    
873 :     =over 4
874 :    
875 :     =item shelp
876 :    
877 :     Search helper object for the current session.
878 :    
879 :     =item formFlag
880 :    
881 :     TRUE if a form has been displayed, else FALSE.
882 :    
883 :     =item RETURN
884 :    
885 :     Returns a page navigation string for the specified search operation. If a form
886 :     has been displayed, the navigation elements will include the complete form
887 :     information; otherwise they will only include position and status.
888 :    
889 :     =back
890 :    
891 :     =cut
892 :    
893 :     sub PageNavigator {
894 :     # Get the parameters.
895 :     my ($shelp, $formFlag) = @_;
896 :     # Get the CGI query object.
897 :     my $cgi = $shelp->Q();
898 :     # Extract the result parameters.
899 :     my ($pageSize, $pageNum, $resultCount) = ($cgi->param('PageSize'),
900 :     $cgi->param('Page'),
901 :     $cgi->param('ResultCount'));
902 :     # Declare the return variable.
903 :     my $retVal = "";
904 :     # Compute the number of the last page.
905 :     my $lastPage = ceil($resultCount / $pageSize);
906 :     # Only proceed if there's more than one page.
907 :     if ($lastPage > 1) {
908 :     # Create a URL without a page number. All the other URLs will be generated
909 :     # from this one by appending the new page number.
910 :     my $url = StatusURL($cgi, SessionID => $shelp->ID(), Page => undef);
911 :     # Now compute the start and end pages for the display. We display ten pages,
912 :     # with the current one more or less centered.
913 :     my $startPage = $pageNum - 4;
914 :     if ($startPage < 1) { $startPage = 1; }
915 :     my $endPage = $startPage + 9;
916 :     if ($endPage > $lastPage) { $endPage = $lastPage; }
917 :     # Create a list of URL/page-number combinations.
918 :     my @pageThings = ();
919 :     for (my $linkPage = $startPage; $linkPage <= $endPage; $linkPage++) {
920 :     # Check for the current page. It gets a page number with no link.
921 :     if ($linkPage == $pageNum) {
922 :     push @pageThings, $linkPage;
923 :     } else {
924 :     # This is not the current page, so it gets the full treatment.
925 :     push @pageThings, PageThing($cgi, $linkPage, $linkPage, $url);
926 :     }
927 :     }
928 :     # Now add some jump links at the end.
929 :     my @forePointers = ();
930 :     my $pg;
931 :     if ($endPage < $lastPage) {
932 :     for ($pg = $endPage + 5; $pg < $lastPage; $pg += 15) {
933 :     push @forePointers, PageThing($cgi, $pg, $pg, $url);
934 :     }
935 :     push @forePointers, PageThing($cgi, ">>", $lastPage, $url);
936 :     }
937 :     # Finally, add some jump links at the front.
938 :     my @backPointers = ();
939 :     if ($startPage > 1) {
940 :     for ($pg = $startPage - 5; $pg > 1; $pg -= 15) {
941 :     unshift @backPointers, PageThing($cgi, $pg, $pg, $url);
942 :     }
943 :     unshift @backPointers, PageThing($cgi, "<<", 1, $url);
944 :     }
945 :     # Put it all together.
946 :     my $middle = join(" ", @pageThings);
947 :     $retVal = join " ... ", @backPointers, $middle, @forePointers;
948 :     }
949 :     # Return the result.
950 :     return $retVal;
951 :     }
952 :    
953 :     =head3 PageThing
954 :    
955 :     my $htmlText = PageThing($cgi, $pageLabel, $pageNumber, $url);
956 :    
957 :     Create an entry for the page navigator. The entry consists of a label that
958 :     is hyperlinked to the specified page number of the search results.
959 :    
960 :     =over 4
961 :    
962 :     =item cgi
963 :    
964 :     CGI object, used to access the CGI HTML-building methods.
965 :    
966 :     =item pageLabel
967 :    
968 :     Text to be hyperlinked. This is usually the page number, but sometimes it will be
969 :     arrows.
970 :    
971 :     =item pageNumber
972 :    
973 :     Number of the page to be presented when the link is followed.
974 :    
975 :     =item url
976 :    
977 :     Base URL for viewing a page.
978 :    
979 :     =item RETURN
980 :    
981 :     Returns HTML for the specified label, hyperlinked to the desired page.
982 :    
983 :     =back
984 :    
985 :     =cut
986 :    
987 :     sub PageThing {
988 :     # Get the parameters.
989 :     my ($cgi, $pageLabel, $pageNumber, $url) = @_;
990 :     # Compute the full URL.
991 :     my $purl = "$url&Page=$pageNumber";
992 :     # Form it into a hyperlink.
993 :     my $retVal = "<a href=\"$purl\" title=\"Results page $pageNumber\">$pageLabel</a>";
994 :     # Return the result.
995 :     return $retVal;
996 :     }
997 :    
998 :     =head3 StatusURL
999 :    
1000 :     my $queryUrl = StatusURL($cgi, %overrides);
1001 :    
1002 :     Create a URL for the current script containing status information for the search in progress.
1003 :     The values in the incoming CGI object will be used for all parameters except the ones
1004 :     specified as overrides. So, for example
1005 :    
1006 :     StatusURL($cgi, PageNum => 3)
1007 :    
1008 :     would specify a page number of 3, but all the other parameters will be taken as is from
1009 :     the CGI object. The complete list of session variables is given in the L</Session Data>
1010 :     section.
1011 :    
1012 :     =over 4
1013 :    
1014 :     =item cgi
1015 :    
1016 :     CGI query object containing the session variables.
1017 :    
1018 :     =item overrides
1019 :    
1020 :     A hash mapping key names to override values. These are used to override values in the
1021 :     I<$cgi> parameter.
1022 :    
1023 :     =item RETURN
1024 :    
1025 :     Returns a relative URL for the current page with GET-style values for all the session
1026 :     variables.
1027 :    
1028 :     =back
1029 :    
1030 :     =cut
1031 :    
1032 :     sub StatusURL {
1033 :     # Get the parameters.
1034 :     my ($cgi, %overrides) = @_;
1035 :     # Create a hash of the session variables we want to keep.
1036 :     my %varHash;
1037 :     for my $varKey (@Keepers) {
1038 :     # Check for an override.
1039 :     if (exists $overrides{$varKey}) {
1040 :     my $override = $overrides{$varKey};
1041 :     # Use the override if it is not null or undefined.
1042 :     if (defined($override) && $override ne "") {
1043 :     $varHash{$varKey} = $override;
1044 :     }
1045 :     } else {
1046 :     # Check for a CGI value.
1047 :     my $normal = $cgi->param($varKey);
1048 :     # Use it if it exists.
1049 :     if (defined($normal)) {
1050 :     $varHash{$varKey} = $normal;
1051 :     }
1052 :     }
1053 :     }
1054 :     # Compute the full URL.
1055 :     my $retVal = Tracer::GenerateURL("$FIG_Config::cgi_url/SearchSkeleton.cgi", %varHash);
1056 :     # Return the result.
1057 :     return $retVal;
1058 :     }
1059 :    
1060 :     =head3 SaveSearchParms
1061 :    
1062 :     SaveSearchParms($cgi, $shelp);
1063 :    
1064 :     Save the search parameters from the CGI object to a session file. The
1065 :     session file will be in the temporary directory named by the session
1066 :     ID with a suffix of C<.parms>.
1067 :    
1068 :     =over 4
1069 :    
1070 :     =item cgi
1071 :    
1072 :     CGI object containing the parameters to save.
1073 :    
1074 :     =item shelp
1075 :    
1076 :     Currently-active search helper object (used to compute the file name).
1077 :    
1078 :     =back
1079 :    
1080 :     =cut
1081 :    
1082 :     sub SaveSearchParms {
1083 :     # Get the parameters.
1084 :     my ($cgi, $shelp) = @_;
1085 :     # Get the name for the parameters file.
1086 :     my $parmFileName = $shelp->GetTempFileName('parms');
1087 :     # Create a hash of the parameters we don't want to keep.
1088 :     my %excludeParms = map { $_ => 1 } @Keepers;
1089 :     # Create a list to store the parameter lines.
1090 :     my @lines = ();
1091 :     # Loop through the parameters, writing them to the file in tab-delimited format.
1092 :     for my $parm ($cgi->param) {
1093 :     # Only proceed if this is NOT an excluded parm.
1094 :     if (! exists $excludeParms{$parm}) {
1095 :     # We output the parameters in tab-delimited format. The first field is the parameter
1096 :     # itself. The remaining fields are the values of the parameter. Normally there is
1097 :     # only one value, but quite a few of the search forms have at least one multi-valued
1098 :     # parameter. Note that we can have new-lines in a field, just not tabs.
1099 :     my @values = $cgi->param($parm);
1100 :     my $line = join("\t", $parm, @values);
1101 :     push @lines, $line;
1102 :     }
1103 :     }
1104 :     # Open the parameters file for output.
1105 :     my $oh = Open(undef, ">$parmFileName");
1106 :     # Because there are new-lines inside fields, we use a special marker to join the lines into
1107 :     # a result file.
1108 :     my $wholeFile = join("\n##\n", @lines);
1109 :     print $oh $wholeFile;
1110 :     # Close the output file.
1111 :     close $oh;
1112 :     }
1113 :    
1114 :     =head3 LoadSearchParms
1115 :    
1116 :     LoadSearchParms($cgi, $shelp);
1117 :    
1118 :     Load the saved search parameters into the specified CGI object. This reads the search data
1119 :     saved by L</SaveSearchParms>.
1120 :    
1121 :     =over 4
1122 :    
1123 :     =item cgi
1124 :    
1125 :     CGI object into which the parameters will be stored.
1126 :    
1127 :     =item shelp
1128 :    
1129 :     Currently-active search helper object (used to compute the file name).
1130 :    
1131 :     =back
1132 :    
1133 :     =cut
1134 :    
1135 :     sub LoadSearchParms {
1136 :     # Get the parameters.
1137 :     my ($cgi, $shelp) = @_;
1138 : parrello 1.2 # Get the name for the parameters file.
1139 :     my $parmFileName = $shelp->GetTempFileName('parms');
1140 :     # Only proceed if the parameter file exists. If the file does not exist,
1141 :     # we assume all the parameter values are blank and do nothing.
1142 :     if (-f $parmFileName) {
1143 :     # Read the parameters file.
1144 :     my $wholeFile = Tracer::GetFile($parmFileName);
1145 :     # Split it into sections. The delimiter is ## surrounded by new-lines. We
1146 :     # can't use just plain \n because it might occur in the middle of a parameter
1147 :     # value.
1148 :     my @lines = split /\n##\n/, $wholeFile;
1149 :     # Loop through the lines.
1150 :     for my $line (@lines) {
1151 :     # Parse this line into fields.
1152 :     my ($parm, @fields) = split /\t/, $line;
1153 :     # Store them in the CGI object.
1154 :     $cgi->param($parm, @fields);
1155 : parrello 1.1 }
1156 :     }
1157 :     }
1158 :    
1159 :     1;

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3