[Bio] / FigKernelPackages / WikiTools.pm Repository:
ViewVC logotype

Annotation of /FigKernelPackages/WikiTools.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.8 - (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 :    
20 :     package WikiTools;
21 :    
22 :     use strict;
23 :     use Tracer;
24 :     use LWP::UserAgent;
25 :     use FIG_Config;
26 :    
27 :     =head1 Wiki-Handling Methods
28 :    
29 :     =head2 Introduction
30 :    
31 : parrello 1.6 This package handles functions related to the NMPDR Wiki. It is also
32 :     used by [[Erdbpm]] to generate Wiki output. It can be replaced by a different
33 :     package if documentation output of a different type is desired.
34 : parrello 1.1
35 :     The fields in this object are as follows.
36 :    
37 :     =over 4
38 :    
39 :     =item userAgent
40 :    
41 :     An LWP object that can be used to send requests to the Wiki server.
42 :    
43 :     =item username
44 :    
45 :     User name for logging on to the wiki server.
46 :    
47 :     =item password
48 :    
49 :     Password for logging on to the wiki server.
50 :    
51 :     =item url
52 :    
53 :     URL of the wiki server access script.
54 :    
55 :     =back
56 :    
57 :     =cut
58 :    
59 :     =head2 Public Methods
60 :    
61 :     =head3 new
62 :    
63 :     my $wiki = WikiTools->new();
64 :    
65 :     Construct a new Wiki object.
66 :    
67 :     =cut
68 :    
69 :     sub new {
70 :     # Get the parameters.
71 :     my ($class, %options) = @_;
72 :     # Create the Wiki-Handling Methods object.
73 :     my $retVal = {
74 :     username => $FIG_Config::nmpdr_wiki->{username},
75 :     password => $FIG_Config::nmpdr_wiki->{password},
76 :     url => $FIG_Config::nmpdr_wiki->{url}
77 :     };
78 :     # Add the user agent.
79 :     $retVal->{userAgent} = LWP::UserAgent->new();
80 :     # Bless and return it.
81 :     bless $retVal, $class;
82 :     return $retVal;
83 :     }
84 :    
85 : parrello 1.6 =head3 WebApplicationObject
86 : parrello 1.1
87 : parrello 1.6 my $appObject = WikiTools::WebApplicationObject($session);
88 : parrello 1.1
89 : parrello 1.6 This method returns a [[WebApplication]] application object for the Wiki.
90 :     It is used by the authentication and access control methods to access the
91 :     [[WebAppBackend]] database.
92 : parrello 1.1
93 :     =cut
94 :    
95 : parrello 1.6 sub WebApplicationObject {
96 :     # Get the parameters.
97 :     my ($session) = @_;
98 :     # Declare the return variable.
99 : parrello 1.7 my $retVal = DBMaster->new(-database => $FIG_Config::webapplication_db,
100 :     -host => $FIG_Config::webapplication_host,
101 :     -user => $FIG_Config::webapplication_user);
102 : parrello 1.6 # Return the result.
103 :     return $retVal;
104 : parrello 1.1 }
105 :    
106 : parrello 1.6 =head3 ComputeTitle
107 : parrello 1.1
108 : parrello 1.6 my ($title, $project) = WikiTools::ComputeTitle($fileName, $path);
109 : parrello 1.1
110 : parrello 1.6 This method computes the project name and Wiki title for a source file.
111 :     The specified path must be relative to the distribution directory. So,
112 :     for example, the path for the Ajax component would be
113 :     C<WebApplication/WebComponent>. For almost every source file, the path
114 :     will be a single word (e. g. C<FigKernelPackages>). The project name is
115 :     the first directory in the incoming path, and the Wiki title is computed
116 :     by converting the file name to capital case and squeezing out the
117 :     underscores.
118 : parrello 1.1
119 :     =over 4
120 :    
121 : parrello 1.6 =item fileName
122 : parrello 1.1
123 : parrello 1.6 Unqualified name of the specified source file (e.g. C<FIG.pm>).
124 : parrello 1.1
125 : parrello 1.6 =item path
126 : parrello 1.1
127 : parrello 1.6 Path in the source file tree to the specified file.
128 : parrello 1.1
129 :     =item RETURN
130 :    
131 : parrello 1.6 Returns a two-element list. The first element is a WikiWord for the file
132 :     title and the second is a project name.
133 : parrello 1.1
134 :     =back
135 :    
136 :     =cut
137 :    
138 : parrello 1.6 sub ComputeTitle {
139 : parrello 1.1 # Get the parameters.
140 : parrello 1.6 my ($fileName, $path) = @_;
141 :     # Compute the project name from the project path.
142 :     my $project = ($path =~ /^(\w+)/ ? $1 : $path);
143 :     # Turn the project name into a wiki word.
144 :     $project = Wikify($project, "Project");
145 :     # Compute the file title from the file name.
146 :     my $title = Wikify($fileName);
147 :     # Return the results.
148 :     return ($title, $project);
149 : parrello 1.1 }
150 :    
151 : parrello 1.6 =head3 Wikify
152 : parrello 1.1
153 : parrello 1.6 my $wikiword = WikiTools::Wikify($string, $suffix);
154 : parrello 1.1
155 : parrello 1.6 Convert a string into a [[WikiWord]]. WikiWords are in [[capital case]],
156 :     cannot contain underscores, and must have at least two humps (that is,
157 :     two transitions from a capital letter to a digit or small letter). To
158 :     convert a string to a Wiki Word, we split it on punctuation boundaries
159 :     and convert each segment to capital case. There are some glitches in this
160 :     process. We need to insure the result starts with a letter, and we need
161 :     to convert acronyms to words. For example, C<FIGPm> is not valid, but
162 :     C<FigPm> is. To get a valid result, we have to convert C<FIG> to C<Fig>.
163 :     Also, we have to make sure we don't end a word with C<Tmpl>.
164 : parrello 1.1
165 :     =over 4
166 :    
167 : parrello 1.6 =item string
168 : parrello 1.1
169 : parrello 1.6 String to convert.
170 : parrello 1.1
171 : parrello 1.6 =item suffix
172 : parrello 1.1
173 : parrello 1.6 A suffix to add to the string if it has two few humps.
174 : parrello 1.1
175 :     =item RETURN
176 :    
177 : parrello 1.6 Incoming string as a WikiWord, with a minimum loss of meaning.
178 : parrello 1.1
179 :     =back
180 :    
181 :     =cut
182 :    
183 : parrello 1.6 sub Wikify {
184 : parrello 1.1 # Get the parameters.
185 : parrello 1.6 my ($string, $suffix) = @_;
186 :     # Declare the return variable.
187 :     my $retVal;
188 :     # Check to see if there's any work to do.
189 :     if (IsWikiWord($string)) {
190 :     $retVal = $string;
191 :     } else {
192 :     # Here we need to convert what we have into a wiki word. First, bust
193 :     # it into segments. Note that underscore (normally a "word" character)
194 :     # is treated as punctuation.
195 :     my @segments = split /[\W_]+/, $string;
196 :     # Insure each segment is capital case.
197 :     for my $segment (@segments) {
198 :     if ($segment =~ /^[A-Z0-9]+$/) {
199 :     # Here we have a probable acronym. Convert it to capital case.
200 :     $segment = ucfirst lc $segment;
201 :     } elsif ($segment =~ /^[a-z0-9]+$/) {
202 :     # This is a lower-case word. Capitalize it.
203 :     $segment = ucfirst $segment;
204 :     }
205 :     }
206 :     # Paste the segments together.
207 :     $retVal = join("", @segments);
208 :     # See if we're done.
209 :     if (! IsWikiWord($retVal) && $suffix) {
210 :     # We're not a WikiWord, but the caller has supplied a suffix.
211 :     # Tack it on.
212 :     $retVal .= $suffix;
213 :     }
214 :     }
215 : parrello 1.1 # Return the result.
216 :     return $retVal;
217 :     }
218 :    
219 :    
220 :     =head3 Save
221 :    
222 :     my $rc = $wiki->Save($title, $web, $category, $content);
223 :    
224 :     Store text for the specified page. If successful, return TRUE. If an
225 :     error occurs, return FALSE. In this last case, an error message will be
226 :     in the member C<$wiki->{error}>.
227 :    
228 :     =over 4
229 :    
230 :     =item title
231 :    
232 :     Page title, consisting of one or more words. The title will be converted
233 :     to the format required by the particular Wiki in use.
234 :    
235 :     =item web
236 :    
237 :     Namespace for this page.
238 :    
239 :     =item category (optional)
240 :    
241 :     Category to contain the page.
242 :    
243 :     =item content
244 :    
245 :     New content for the page.
246 :    
247 :     =item RETURN
248 :    
249 :     Returns TRUE if successful, else FALSE. If FALSE is returned, an error message
250 :     will be stashed in the C<error> member.
251 :    
252 :     =back
253 :    
254 :     =cut
255 :    
256 :     sub Save {
257 :     # Get the parameters.
258 :     my ($self, $title, $web, $category, $content) = @_;
259 :     # The first task is to create the real page title. First, we separate the incoming
260 :     # title into words.
261 :     my @words = split /\s+|_+/, $title;
262 :     # Form them into a WikiWord with a web name prefix.
263 :     my $wordTitle = join("", map { ucfirst $_ } @words);
264 :     my $realTitle = "$web.$wordTitle";
265 :     # Set up the parent information (if any).
266 :     my @parentData = ();
267 :     if ($category) {
268 :     push @parentData, parent => $category;
269 :     }
270 :     # Send a request to the wiki server.
271 :     my $ua = $self->{userAgent};
272 :     my $response = $ua->post($self->{url}, { username => $self->{username},
273 :     password => $self->{password},
274 :     topic => $realTitle,
275 :     text => $content,
276 :     @parentData });
277 :     # Declare the return variable. We assume failure. If we succeed, we'll change
278 :     # the value.
279 :     my $retVal = 0;
280 :     # Save the response content.
281 :     my $message = $response->content;
282 : parrello 1.6 Trace("Message returned after Save:\n$message") if T(3);
283 : parrello 1.1 # Check for a response error.
284 :     if (! $response->is_success()) {
285 :     # Denote failure, and save the content as the error message.
286 :     if ($message =~ /<p>(.+)<\/p>/si) {
287 :     # Here the message is formatted as HTML. We return the meat.
288 :     $self->{error} = $1;
289 :     $self->{error} =~ s/\n/ /gs;
290 :     } else {
291 :     $self->{error} = $message;
292 :     }
293 :     } elsif ($message =~ /^ERROR:\s*(.*)/) {
294 :     # Here we have an error detected by the plugin script. We get the
295 :     # meat of the message error field.
296 :     $self->{error} = $1;
297 :     } else {
298 :     # Denote success.
299 :     $retVal = 1;
300 :     }
301 :     # Return the result.
302 :     return $retVal;
303 :     }
304 :    
305 : parrello 1.6
306 :     =head3 Bar
307 :    
308 :     my $line = $wt->Bar;
309 :    
310 :     Return the code for a horizontal bar.
311 :    
312 :     =cut
313 :    
314 :     sub Bar {
315 :     return "---";
316 :     }
317 :    
318 :     =head3 HeadParse
319 :    
320 :     my ($level, $name) = $wiki->HeadParse($line);
321 :    
322 :     Determine whether or not the specified line is a wiki heading. If it is,
323 :     return the heading level and the heading name. If it is not, return 0
324 :     and C<undef>
325 :    
326 :     =over 4
327 :    
328 :     =item line
329 :    
330 :     Wiki line to parse.
331 :    
332 :     =item RETURN
333 :    
334 :     Returns a two-element list. The first element indicates the heading level, and is
335 :     C<0> for a non-heading. The second contains the heading name, or C<undef> for a
336 :     non-heading.
337 :    
338 :     =back
339 :    
340 :     =cut
341 :    
342 :     sub HeadParse {
343 :     # Allow static calling for backward compatability.
344 :     shift if UNIVERSAL::isa($_[0], __PACKAGE__);
345 :     # Get the parameters.
346 :     my ($line) = @_;
347 :     # Assume this is not a heading line.
348 :     my ($level, $name) = (0, undef);
349 :     # Check for the heading format.
350 :     if ($line =~ /^\s*---(\++)\s*(.+)\s*$/) {
351 :     # Here we have a heading line. The heading level is the number of plus
352 :     # signs matched, which is also the length of $1.
353 :     $level = length $1;
354 :     $name = $2;
355 :     }
356 :     # Return the result.
357 :     return ($level, $name);
358 :     }
359 :    
360 :     =head3 HeadLevel
361 :    
362 :     my $level = $wiki->HeadLevel($line);
363 :    
364 :     Return the heading level of the specified line, or 0 if it is not a
365 :     heading.
366 :    
367 :     =over 4
368 :    
369 :     =item line
370 :    
371 :     Wiki line to parse.
372 :    
373 :     =item RETURN
374 :    
375 :     Returns C<0> if the line is not a heading, and the heading level otherwise.
376 :    
377 :     =back
378 :    
379 :     =cut
380 :    
381 :     sub HeadLevel {
382 :     # Allow static calling for backward compatability.
383 :     shift if UNIVERSAL::isa($_[0], __PACKAGE__);
384 :     # Get the parameters.
385 :     my ($line) = @_;
386 :     # Parse the header. We keep the heading level and throw away the text.
387 :     my ($retVal) = HeadParse($line);
388 :     # Return the result.
389 :     return $retVal;
390 :     }
391 :    
392 :     =head3 BoldCode
393 :    
394 :     my $boldCode = $wiki->BoldCode();
395 :    
396 :     Returns the Wiki code for bold text.
397 :    
398 :     =cut
399 :    
400 :     sub BoldCode {
401 :     # Return the result.
402 :     return "*";
403 :     }
404 :    
405 :     =head3 ItalicCode
406 :    
407 :     my $italicCode = $wiki->BoldCode();
408 :    
409 :     Returns the Wiki code for italic text.
410 :    
411 :     =cut
412 :    
413 :     sub ItalicCode {
414 :     # Return the result.
415 :     return "_";
416 :     }
417 :    
418 :     =head3 ListCode
419 :    
420 :     my $listCode = $wiki->ListCode();
421 :    
422 :     Returns the Wiki code for a list element.
423 :    
424 :     =cut
425 :    
426 :     sub ListCode {
427 :     # Return the result.
428 :     return " * ";
429 :     }
430 :    
431 :     =head3 IsWikiWord
432 :    
433 :     my $flag = WikiTools::IsWikiWord($string);
434 :    
435 :     Return TRUE if the specified string is a [[TWiki.WikiWord][wiki word]], else FALSE.
436 :    
437 :     =over 4
438 :    
439 :     =item string
440 :    
441 :     String to evaluate.
442 :    
443 :     =item RETURN
444 :    
445 :     Returns TRUE if the string conforms to the allowable format for a Wiki page title,
446 :     else FALSE.
447 :    
448 :     =back
449 :    
450 :     =cut
451 :    
452 :     sub IsWikiWord {
453 :     # Get the parameters.
454 :     my ($string) = @_;
455 :     # Test the string.
456 :     return $string =~ /^[A-Z]+[a-z]+(?:[A-Z]+[a-zA-Z0-9]*)$/;
457 :     }
458 :    
459 :    
460 :     =head2 Rendering Methods
461 :    
462 :     These are the methods that need to be replicated in any object used for
463 :     rendering ERDB documentation.
464 :    
465 :     =head3 Heading
466 :    
467 :     my $line = $wiki->Heading($level, $text);
468 :    
469 :     Return the code for a heading line at the specified level.
470 :    
471 :     =over 4
472 :    
473 :     =item level
474 :    
475 :     Desired heading level.
476 :    
477 :     =item text
478 :    
479 :     Title for the heading's section.
480 :    
481 :     =item RETURN
482 :    
483 :     Returns a formatted heading line.
484 :    
485 :     =back
486 :    
487 :     =cut
488 :    
489 :     sub Heading {
490 :     # Allow static calling for backward compatability.
491 :     shift if UNIVERSAL::isa($_[0], __PACKAGE__);
492 :     # Get the parameters.
493 :     my ($level, $text) = @_;
494 :     # Create the heading line.
495 :     my $retVal = "---" . ("+" x $level) . " $text";
496 :     # Return the result.
497 :     return $retVal;
498 :     }
499 :    
500 :    
501 : parrello 1.1 =head3 Prolog
502 :    
503 : parrello 1.6 my @lines = $wiki->Prolog();
504 : parrello 1.1
505 :     Returns a set of text lines to put at the beginning of a typical Wiki
506 :     output stream.
507 :    
508 :     =cut
509 :    
510 :     sub Prolog {
511 :     # Return the result.
512 :     return ('<noautolink>', '%TOC%');
513 :     }
514 :    
515 :     =head3 Epilog
516 :    
517 : parrello 1.6 my @lines = $wiki->Epilog();
518 : parrello 1.1
519 :     Returns a set of text lines to put at the end of a typical Wiki
520 :     output stream.
521 :    
522 :     =cut
523 :    
524 :     sub Epilog {
525 :     # Return the result.
526 :     return ('</noautolink>');
527 :     }
528 :    
529 : parrello 1.6 =head3 Bold
530 : parrello 1.1
531 : parrello 1.6 my $markup = $wiki->Bold($text);
532 : parrello 1.1
533 : parrello 1.6 Bold the specified text.
534 : parrello 1.1
535 :     =cut
536 :    
537 : parrello 1.6 sub Bold {
538 :     my ($self, $text) = @_;
539 :     return "*$text*";
540 : parrello 1.1 }
541 :    
542 : parrello 1.6 =head3 Italic
543 : parrello 1.1
544 : parrello 1.6 my $markup = $wiki->Italic($text);
545 : parrello 1.1
546 : parrello 1.6 Italicize the specified text.
547 : parrello 1.1
548 :     =cut
549 :    
550 : parrello 1.6 sub Italic {
551 :     my ($self, $text) = @_;
552 :     return "_" . $text . "_";
553 : parrello 1.1 }
554 :    
555 :     =head3 LinkMarkup
556 :    
557 : parrello 1.6 my $boldCode = $wiki->LinkMarkup($link, $text);
558 : parrello 1.1
559 :     Returns the Wiki code for a link.
560 :    
561 :     =over 4
562 :    
563 :     =item link
564 :    
565 :     URL or topic name referenced by the link.
566 :    
567 :     =item text (optional)
568 :    
569 :     Text of the link.
570 :    
571 :     =back
572 :    
573 :     =cut
574 :    
575 :     sub LinkMarkup {
576 : parrello 1.6 # Allow static calling for backward compatability.
577 :     shift if UNIVERSAL::isa($_[0], __PACKAGE__);
578 : parrello 1.1 # Get the parameters.
579 :     my ($link, $text) = @_;
580 :     # Declare the return variable.
581 :     my $retVal;
582 :     # Check to see if we have text.
583 :     if ($text) {
584 :     # Yes, so we have a two-part link.
585 :     $retVal = "[[$link][$text]]";
586 :     } else {
587 :     # No, so we have a one-part link.
588 :     $retVal = "[[$link]]";
589 :     }
590 :     # Return the result.
591 :     return $retVal;
592 :     }
593 :    
594 :     =head3 Table
595 :    
596 : parrello 1.6 my $wikiText = $wiki->Table(@rows);
597 : parrello 1.1
598 :     Create a Wiki table. The parameters are all list references. The first
599 :     describes the header row, and the remaining rows are presented
600 :     sequentially. This is a very simple table, using only default settings
601 :     and with everything left-aligned.
602 :    
603 :     =over 4
604 :    
605 :     =item rows
606 :    
607 :     List of table rows. Each table row is a list reference containing the
608 :     cells of the row in column order. The first row is used as the header.
609 :    
610 :     =item RETURN
611 :    
612 :     Returns a string that will generate a Wiki table.
613 :    
614 :     =back
615 :    
616 :     =cut
617 :    
618 :     sub Table {
619 : parrello 1.6 # Allow static calling for backward compatability.
620 :     shift if UNIVERSAL::isa($_[0], __PACKAGE__);
621 : parrello 1.1 # Get the parameters.
622 :     my (@rows) = @_;
623 : parrello 1.2 # Get the header row.
624 :     my $headers = shift @rows;
625 :     # We put asterisks around the title of each column so that TWiki knows these are headers.
626 :     my $headerRow = "| " . join(" | ", map { "*$_*" } @{$headers}) . " |";
627 :     # Save the header, and build the rest of the rows normally.
628 : parrello 1.5 my @rowStrings = $headerRow;
629 :     for my $row (@rows) {
630 :     # Remove line-feeds from the cells.
631 :     my @cells = map { $_ =~ s/\n/ /g; $_ } @{$row};
632 :     # Add them together to make a row.
633 :     push @rowStrings, "| " . join(" | ", @cells) . " |";
634 :     }
635 :     # Put the rows together with blank lines on either side.
636 : parrello 1.1 my $retVal = join("\n", "", @rowStrings, "");
637 :     # Return the result.
638 :     return $retVal;
639 :     }
640 :    
641 :    
642 :     =head3 List
643 :    
644 : parrello 1.6 my $wikiText = $wiki->List(@items);
645 : parrello 1.1
646 :     Create a Wiki list. The parameters are all strings that are put into the
647 : parrello 1.6 list sequentially. The strings are trimmed, and empty entries at the
648 :     beginning are deleted. This makes the coding of asides in ERDB a little
649 :     more user-friendly.
650 : parrello 1.1
651 :     =over 4
652 :    
653 :     =item items
654 :    
655 :     List of items to be formatted into a wiki list.
656 :    
657 :     =item RETURN
658 :    
659 :     Returns wiki markup text that will display as an unordered list.
660 :    
661 :     =back
662 :    
663 :     =cut
664 :    
665 :     sub List {
666 : parrello 1.6 # Allow static calling for backward compatability.
667 :     shift if UNIVERSAL::isa($_[0], __PACKAGE__);
668 :     # Get the list elements, trimmed.
669 :     my (@items) = map { Tracer::Trim($_) } @_;
670 :     # Get the list code.
671 :     my $code = ListCode();
672 :     # Remove any null entries at the beginning.
673 :     while (@items && $items[0] eq "") { shift @items };
674 : parrello 1.1 # Format the list.
675 : parrello 1.8 my $retVal = join("\n", "", map { "$code$_" } @items);
676 : parrello 1.1 # Return the result.
677 :     return $retVal;
678 :     }
679 :    
680 : parrello 1.6 =head3 Para
681 : parrello 1.1
682 : parrello 1.6 my $markup = $wiki->Para($text);
683 : parrello 1.1
684 : parrello 1.6 Create a paragraph from the specified text.
685 : parrello 1.1
686 :     =over 4
687 :    
688 : parrello 1.6 =item text
689 : parrello 1.1
690 : parrello 1.6 Text to format as a paragraph.
691 : parrello 1.1
692 :     =item RETURN
693 :    
694 : parrello 1.6 Returns the text followed by a blank line, so that it is treated as a
695 :     paragraph.
696 : parrello 1.1
697 :     =back
698 :    
699 :     =cut
700 :    
701 : parrello 1.6 sub Para {
702 : parrello 1.1 # Get the parameters.
703 : parrello 1.6 my ($self, $text) = @_;
704 :     # Add the blank line.
705 :     my $retVal = "$text\n\n";
706 : parrello 1.1 # Return the result.
707 :     return $retVal;
708 :     }
709 :    
710 :    
711 : parrello 1.6 =head3 Finalize
712 : parrello 1.1
713 : parrello 1.6 $wiki->Finalize(\@lines);
714 : parrello 1.1
715 : parrello 1.6 Finalize a list of lines into a wiki page. This method is not used in the
716 :     current implementation, but would be needed by an HTML utility to
717 :     generate the table of contents.
718 : parrello 1.4
719 :     =over 4
720 :    
721 : parrello 1.6 =item lines
722 : parrello 1.4
723 : parrello 1.6 List of lines containing markup. The list is modified in place.
724 : parrello 1.4
725 :     =back
726 :    
727 :     =cut
728 :    
729 : parrello 1.6 sub Finalize {
730 :     # Stub.
731 : parrello 1.4 }
732 : parrello 1.1
733 :    
734 :     1;

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3