[system] / trunk / webwork-modperl / lib / WeBWorK / ContentGenerator / Instructor / SetMaker.pm Repository:
ViewVC logotype

Diff of /trunk/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 2114 Revision 2115
120} 120}
121 121
122 122
123############# List of sets of problems in templates directory 123############# List of sets of problems in templates directory
124 124
125sub getalllibsets { 125sub get_problem_directories {
126 my $ce = shift; 126 my $ce = shift;
127 my @all_library_sets = get_library_sets(1, $ce->{courseDirs}->{templates}); 127 my @all_problem_directories = get_library_sets(1, $ce->{courseDirs}->{templates});
128 my $includetop = shift @all_library_sets; 128 my $includetop = shift @all_problem_directories;
129 my $j; 129 my $j;
130 for ($j=0; $j<scalar(@all_library_sets); $j++) { 130 for ($j=0; $j<scalar(@all_problem_directories); $j++) {
131 $all_library_sets[$j] =~ s|^$ce->{courseDirs}->{templates}/?||; 131 $all_problem_directories[$j] =~ s|^$ce->{courseDirs}->{templates}/?||;
132 } 132 }
133 @all_library_sets = sort @all_library_sets; 133 @all_problem_directories = sort @all_problem_directories;
134 unshift @all_library_sets, ' -- Top -- ' if($includetop); 134 unshift @all_problem_directories, ' -- Top -- ' if($includetop);
135 return (\@all_library_sets); 135 return (\@all_problem_directories);
136} 136}
137 137
138### The browsing panel has three versions 138### The browsing panel has three versions
139##### Version 1 is local problems 139##### Version 1 is local problems
140sub browse_local_panel { 140sub browse_local_panel {
141 my $self = shift; 141 my $self = shift;
142 my $library_selected = shift; 142 my $library_selected = shift;
143 143
144 my $list_of_sets= getalllibsets($self->r->ce); 144 my $list_of_prob_dirs= get_problem_directories($self->r->ce);
145 my $libstr = ""; 145 if(scalar(@$list_of_prob_dirs) == 0) {
146 $library_selected = "Found no directories containing problems";
147 unshift @{$list_of_prob_dirs}, $library_selected;
148 } else {
146 my $default_value = "Select a Local Problem Collection"; 149 my $default_value = "Select a Local Problem Collection";
147 $libstr = CGI::br() . CGI::em($self->{libmsg}) if($self->{libmsg});
148
149 if (not $library_selected or $library_selected eq $default_value) { 150 if (not $library_selected or $library_selected eq $default_value) {
150 unshift @{$list_of_sets}, $default_value; 151 unshift @{$list_of_prob_dirs}, $default_value;
151 $library_selected = $default_value; 152 $library_selected = $default_value;
152 } 153 }
153 154 }
154 155
155 print CGI::Tr(CGI::td({-class=>"InfoPanel"}, "Local Problems: ", 156 print CGI::Tr(CGI::td({-class=>"InfoPanel"}, "Local Problems: ",
156 CGI::popup_menu(-name=> 'library_sets', 157 CGI::popup_menu(-name=> 'library_sets',
157 -values=>$list_of_sets, 158 -values=>$list_of_prob_dirs,
158 -default=> $library_selected), 159 -default=> $library_selected),
159 CGI::br(), 160 CGI::br(),
160 CGI::submit(-name=>"view_local_set", -value=>"View Problems"), 161 CGI::submit(-name=>"view_local_set", -value=>"View Problems"),
161 $libstr
162 )); 162 ));
163} 163}
164 164
165##### Version 2 is local problem sets 165##### Version 2 is local problem sets
166sub browse_mysets_panel { 166sub browse_mysets_panel {
167 my $self = shift; 167 my $self = shift;
168 my $library_selected = shift; 168 my $library_selected = shift;
169 my $list_of_local_sets = shift; 169 my $list_of_local_sets = shift;
170 my $default_value = "Select a Problem Set"; 170 my $default_value = "Select a Problem Set";
171 171
172 my $libstr = CGI::br() . CGI::em($self->{libmsg}) if($self->{libmsg});
173
174 if(scalar(@$list_of_local_sets) == 0) { 172 if(scalar(@$list_of_local_sets) == 0) {
175 $list_of_local_sets = ['There are no local sets yet']; 173 $list_of_local_sets = ['There are no local sets yet'];
176 } elsif (not $library_selected or $library_selected eq $default_value) { 174 } elsif (not $library_selected or $library_selected eq $default_value) {
177 unshift @{$list_of_local_sets}, $default_value; 175 unshift @{$list_of_local_sets}, $default_value;
178 $library_selected = $default_value; 176 $library_selected = $default_value;
182 CGI::popup_menu(-name=> 'library_sets', 180 CGI::popup_menu(-name=> 'library_sets',
183 -values=>$list_of_local_sets, 181 -values=>$list_of_local_sets,
184 -default=> $library_selected), 182 -default=> $library_selected),
185 CGI::br(), 183 CGI::br(),
186 CGI::submit(-name=>"view_mysets_set", -value=>"View This Set"), 184 CGI::submit(-name=>"view_mysets_set", -value=>"View This Set"),
187 $libstr
188 )); 185 ));
189} 186}
190 187
191##### Version 3 is the problem library 188##### Version 3 is the problem library
192 189
208 return; 205 return;
209 } 206 }
210 # Test if the Library directory exists. If not, try to make it 207 # Test if the Library directory exists. If not, try to make it
211 unless(-d "$ce->{courseDirs}->{templates}/Library") { 208 unless(-d "$ce->{courseDirs}->{templates}/Library") {
212 unless(symlink($libraryRoot, "$ce->{courseDirs}->{templates}/Library")) { 209 unless(symlink($libraryRoot, "$ce->{courseDirs}->{templates}/Library")) {
213 $self->{libmsg} .= <<"HERE"; 210 my $msg = <<"HERE";
214You are missing the directory <code>templates/Library</code>, which is needed 211You are missing the directory <code>templates/Library</code>, which is needed
215for the Problem Library to function. It should be a link pointing to 212for the Problem Library to function. It should be a link pointing to
216<code>$libraryRoot</code>, which you set in <code>conf/global.conf</code>. 213<code>$libraryRoot</code>, which you set in <code>conf/global.conf</code>.
217I tried to make the link for you, but that failed. Check the permissions 214I tried to make the link for you, but that failed. Check the permissions
218in your <code>templates</code> directory. 215in your <code>templates</code> directory.
219HERE 216HERE
217 $self->addmessage(CGI::div({class=>"ResultsWithError"}, $msg));
220 } 218 }
221 } 219 }
222 220
223 my $default_chap = "All Chapters"; 221 my $default_chap = "All Chapters";
224 my $default_sect = "All Sections"; 222 my $default_sect = "All Sections";
225
226 my $libstr = CGI::br() . CGI::em($self->{libmsg}) if($self->{libmsg});
227 223
228 my @chaps = WeBWorK::Utils::ListingDB::getAllChapters($r->{ce}); 224 my @chaps = WeBWorK::Utils::ListingDB::getAllChapters($r->{ce});
229 unshift @chaps, $default_chap; 225 unshift @chaps, $default_chap;
230 my $chapter_selected = $r->param('library_chapters') || $default_chap; 226 my $chapter_selected = $r->param('library_chapters') || $default_chap;
231 227
270# CGI::td({-colspan=>2}, CGI::textfield(-name=>"keywords", 266# CGI::td({-colspan=>2}, CGI::textfield(-name=>"keywords",
271# -default=>"Keywords not implemented yet", 267# -default=>"Keywords not implemented yet",
272# -override=>1, -size=>60))), 268# -override=>1, -size=>60))),
273 CGI::Tr(CGI::td({-colspan=>3},CGI::submit(-name=>"lib_view", -value=>"View Problems"))), 269 CGI::Tr(CGI::td({-colspan=>3},CGI::submit(-name=>"lib_view", -value=>"View Problems"))),
274 CGI::end_table(), 270 CGI::end_table(),
275 $libstr
276 )); 271 ));
277} 272}
278 273
279sub make_top_row { 274sub make_top_row {
280 my $self = shift; 275 my $self = shift;
285 my $have_local_sets = scalar(@$list_of_local_sets); 280 my $have_local_sets = scalar(@$list_of_local_sets);
286 my $browse_which = $data{browse_which}; 281 my $browse_which = $data{browse_which};
287 my $library_selected = $r->param('library_sets'); 282 my $library_selected = $r->param('library_sets');
288 my $set_selected = $r->param('local_sets'); 283 my $set_selected = $r->param('local_sets');
289 284
290 my $list_of_sets;
291 my ($dis1, $dis2, $dis3) = ("","",""); 285 my ($dis1, $dis2, $dis3) = ("","","");
292 $dis1 = '-disabled' if($browse_which eq 'browse_library'); 286 $dis1 = '-disabled' if($browse_which eq 'browse_library');
293 $dis2 = '-disabled' if($browse_which eq 'browse_local'); 287 $dis2 = '-disabled' if($browse_which eq 'browse_local');
294 $dis3 = '-disabled' if($browse_which eq 'browse_mysets'); 288 $dis3 = '-disabled' if($browse_which eq 'browse_mysets');
295
296 my $locstr = "";
297 $locstr = CGI::br() . CGI::em($self->{localmsg}) if($self->{localmsg});
298 289
299 my $these_widths = "width: 27ex"; 290 my $these_widths = "width: 27ex";
300 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"center"}, 291 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"center"},
301 CGI::submit(-name=>"browse_library", -value=>"Browse Problem Library", -style=>$these_widths, $dis1), 292 CGI::submit(-name=>"browse_library", -value=>"Browse Problem Library", -style=>$these_widths, $dis1),
302 CGI::submit(-name=>"browse_local", -value=>"Browse Local Problems", -style=>$these_widths, $dis2), 293 CGI::submit(-name=>"browse_local", -value=>"Browse Local Problems", -style=>$these_widths, $dis2),
336 " ", 327 " ",
337 CGI::textfield(-name=>"new_set_name", 328 CGI::textfield(-name=>"new_set_name",
338 -default=>"Name for new set here", 329 -default=>"Name for new set here",
339 -override=>1, -size=>30), 330 -override=>1, -size=>30),
340 CGI::br(), 331 CGI::br(),
341 $locstr
342 )); 332 ));
343 333
344 print CGI::Tr(CGI::td({-bgcolor=>"black"})); 334 print CGI::Tr(CGI::td({-bgcolor=>"black"}));
345 335
346 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"center"}, 336 print CGI::Tr(CGI::td({-class=>"InfoPanel", -align=>"center"},
406 CGI::hidden(-name=>"filetrial$cnt", -default=>[$sourceFileName]). 396 CGI::hidden(-name=>"filetrial$cnt", -default=>[$sourceFileName]).
407 CGI::p($problem_output), 397 CGI::p($problem_output),
408 )); 398 ));
409} 399}
410 400
411sub title {
412 return "Problem Set Maker";
413}
414 401
415sub body { 402sub pre_header_initialize {
416 my ($self) = @_; 403 my ($self) = @_;
417
418 my $r = $self->r; 404 my $r = $self->r;
419 my $ce = $r->ce; # course environment 405 ## For all cases, lets set some things
406 $self->{error}=0;
407 my $ce = $r->ce;
420 my $db = $r->db; # database 408 my $db = $r->db;
421 my $j; # garden variety counter 409
422 410
423 my $userName = $r->param('user'); 411 my $userName = $r->param('user');
424
425 my $user = $db->getUser($userName); # checked 412 my $user = $db->getUser($userName); # checked
426 die "record for user $userName (real user) does not exist." 413 die "record for user $userName (real user) does not exist."
427 unless defined $user; 414 unless defined $user;
428
429 ### Check that this is a professor
430 my $authz = $r->authz; 415 my $authz = $r->authz;
431 unless ($authz->hasPermissions($userName, "modify_problem_sets")) { 416 unless ($authz->hasPermissions($userName, "modify_problem_sets")) {
432 print "User $userName returned " . 417 return(""); # Error message already produced in the body
433 $authz->hasPermissions($user, "modify_problem_sets") . 418 }
434 " for permission"; 419
435 return(CGI::em("You are not authorized to access the Instructor tools.")); 420 ## Now one action we have to deal with here
421 if ($r->param('edit_local')) {
422 my $urlpath = $r->urlpath;
423 my $db = $r->db;
424 my $checkset = $db->getGlobalSet($r->param('local_sets'));
425 if (not defined($checkset)) {
426 $self->{error} = 1;
427 $self->addmessage(CGI::div({class=>"ResultsWithError"}, 'You need to select a "Current Set" before you can edit it.'));
428 } else {
429 my $page = $urlpath->newFromModule('WeBWorK::ContentGenerator::Instructor::ProblemSetEditor', setID=>$r->param('local_sets'), courseID=>$urlpath->arg("courseID"));
430 my $url = $self->systemLink($page);
431 $self->reply_with_redirect($url);
436 } 432 }
433 }
437 434
435 ## Next, lots of set up so that errors can be reported with message()
438 436
439 ############# List of problems we have already printed 437 ############# List of problems we have already printed
440 438
441 my @past_problems = get_past_problem_files($r); 439 my @past_problems = get_past_problem_files($r);
442 my (@pg_files, @pg_html); 440 my @pg_files=();
443 my $use_previous_problems = 1; 441 my $use_previous_problems = 1;
444 my $first_shown = $r->param('first_shown') || 0; 442 my $first_shown = $r->param('first_shown') || 0;
445 my $last_shown = $r->param('last_shown'); 443 my $last_shown = $r->param('last_shown');
446 if (not defined($last_shown)) { 444 if (not defined($last_shown)) {
447 $last_shown = -1; 445 $last_shown = -1;
448 } 446 }
449 my @all_past_list = (); # these are include requested, but not shown 447 my @all_past_list = (); # these are include requested, but not shown
450 $j = 0; 448 my $j = 0;
451 while (defined($r->param("all_past_list$j"))) { 449 while (defined($r->param("all_past_list$j"))) {
452 push @all_past_list, $r->param("all_past_list$j"); 450 push @all_past_list, $r->param("all_past_list$j");
453 $j++; 451 $j++;
454 } 452 }
455 453
456 ############# Default of which problem selector to display 454 ############# Default of which problem selector to display
457 455
458 my $browse_which = 'browse_local'; 456 my $browse_which = $r->param('browse_which') || 'browse_local';
459 $browse_which = $r->param('browse_which') if defined($r->param('browse_which'));
460 457
461 my $problem_seed = $r->param('problem_seed') || 0; 458 my $problem_seed = $r->param('problem_seed') || 0;
462 $r->param('problem_seed', $problem_seed); # if it wasn't defined before 459 $r->param('problem_seed', $problem_seed); # if it wasn't defined before
463 460
464 ########### Start the logic through if elsif elsif ... 461 ########### Start the logic through if elsif elsif ...
489 ##### View problems selected from the local list 486 ##### View problems selected from the local list
490 487
491 } elsif ($r->param('view_local_set')) { 488 } elsif ($r->param('view_local_set')) {
492 489
493 my $set_to_display = $r->param('library_sets'); 490 my $set_to_display = $r->param('library_sets');
494 if (not defined($set_to_display) or $set_to_display eq "Select a Local Problem Collection") { 491 if (not defined($set_to_display) or $set_to_display eq "Select a Local Problem Collection" or $set_to_display eq "Found no directories containing problems") {
495 $self->{libmsg} = "You need to select a set to view."; 492 $self->addmessage(CGI::div({class=>"ResultsWithError"},
493 'You need to select a set to view.'));
496 } else { 494 } else {
497 $set_to_display = '.' if $set_to_display eq ' -- Top -- '; 495 $set_to_display = '.' if $set_to_display eq ' -- Top -- ';
498 @pg_files = list_pg_files($ce->{courseDirs}->{templates}, 496 @pg_files = list_pg_files($ce->{courseDirs}->{templates},
499 "$set_to_display"); 497 "$set_to_display");
500 $use_previous_problems=0; 498 $use_previous_problems=0;
506 504
507 my $set_to_display = $r->param('library_sets'); 505 my $set_to_display = $r->param('library_sets');
508 if (not defined($set_to_display) 506 if (not defined($set_to_display)
509 or $set_to_display eq "Select a Problem Set" 507 or $set_to_display eq "Select a Problem Set"
510 or $set_to_display eq 'There are no local sets yet') { 508 or $set_to_display eq 'There are no local sets yet') {
509 $self->addmessage(CGI::div({class=>"ResultsWithError"},
511 $self->{libmsg} = "You need to select a set from this course to view."; 510 "You need to select a set from this course to view."));
512 } else { 511 } else {
513 my @problemList = $db->listGlobalProblems($set_to_display); 512 my @problemList = $db->listGlobalProblems($set_to_display);
514 my $problem; 513 my $problem;
515 @pg_files=(); 514 @pg_files=();
516 for $problem (@problemList) { 515 for $problem (@problemList) {
544 $use_previous_problems=0; 543 $use_previous_problems=0;
545 544
546 ##### Edit the current local problem set 545 ##### Edit the current local problem set
547 546
548 } elsif ($r->param('edit_local')) { ## Jump to set edit page 547 } elsif ($r->param('edit_local')) { ## Jump to set edit page
549 # This is handled in pre_header_initialize -- it redirects 548
550 # If there is an error, so no redirect, we want to be ready 549 ; # already handled
551 # and do something here 550
552 551
553 ##### Make a new local problem set 552 ##### Make a new local problem set
554 553
555 } elsif ($r->param('new_local_set')) { 554 } elsif ($r->param('new_local_set')) {
556 if ($r->param('new_set_name') !~ /^[\w.-]*$/) { 555 if ($r->param('new_set_name') !~ /^[\w.-]*$/) {
556 $self->addmessage(CGI::div({class=>"ResultsWithError"},
557 $self->{localmsg} = "The name ".$r->param('new_set_name')." is not a valid set name. Use only letters, digits, -, _, and ."; 557 "The name ".$r->param('new_set_name')." is not a valid set name. Use only letters, digits, -, _, and ."));
558 } else { 558 } else {
559 my $newSetName = $r->param('new_set_name'); 559 my $newSetName = $r->param('new_set_name');
560 $newSetName =~ s/^set//; 560 $newSetName =~ s/^set//;
561 $newSetName =~ s/\.def$//; 561 $newSetName =~ s/\.def$//;
562 $r->param('local_sets',$newSetName); 562 $r->param('local_sets',$newSetName);
563 my $newSetRecord = $db->getGlobalSet($newSetName); 563 my $newSetRecord = $db->getGlobalSet($newSetName);
564 if (defined($newSetRecord)) { 564 if (defined($newSetRecord)) {
565 $self->{localmsg} = "The set name $newSetName is already in use. Pick a different name if you would like to start a new set."; 565 $self->addmessage(CGI::div({class=>"ResultsWithError"}, "The set name $newSetName is already in use. Pick a different name if you would like to start a new set."));
566 } else { # Do it! 566 } else { # Do it!
567 $newSetRecord = $db->{set}->{record}->new(); 567 $newSetRecord = $db->{set}->{record}->new();
568 $newSetRecord->set_id($newSetName); 568 $newSetRecord->set_id($newSetName);
569 $newSetRecord->set_header(""); 569 $newSetRecord->set_header("");
570 $newSetRecord->problem_header(""); 570 $newSetRecord->problem_header("");
585 @selected = map {$_->[0]} @pg_files; 585 @selected = map {$_->[0]} @pg_files;
586 586
587 if (scalar(@selected)>0) { # if some are to be added, they need a place to go 587 if (scalar(@selected)>0) { # if some are to be added, they need a place to go
588 $localSet = $r->param('local_sets'); 588 $localSet = $r->param('local_sets');
589 if (not defined($localSet)) { 589 if (not defined($localSet)) {
590 $self->addmessage(CGI::div({class=>"ResultsWithError"},
590 $self->{localmsg} = "Trying to add problems to something, you did not select a current set name as a target."; 591 'You are trying to add problems to something, but you did not select a "Current Set" name as a target.'));
591 } else { 592 } else {
592 my $newSetRecord = $db->getGlobalSet($localSet); 593 my $newSetRecord = $db->getGlobalSet($localSet);
593 if (not defined($newSetRecord)) { 594 if (not defined($newSetRecord)) {
594 $self->{localmsg} = "You need to select a local problem set to add the problems to."; 595 $self->addmessage(CGI::div({class=>"ResultsWithError"},
596 'You are trying to add problems to something, but you did not select a "Current Set" name as a target.'));
595 } else { 597 } else {
596 add_selected($self, $db, $localSet, @selected); 598 add_selected($self, $db, $localSet, @selected);
597 } 599 }
598 } 600 }
599 } 601 }
644 } else { 646 } else {
645 $first_shown = 0; 647 $first_shown = 0;
646 $last_shown = scalar(@pg_files)<MAX_SHOW ? scalar(@pg_files) : MAX_SHOW; 648 $last_shown = scalar(@pg_files)<MAX_SHOW ? scalar(@pg_files) : MAX_SHOW;
647 $last_shown--; # to make it an array index 649 $last_shown--; # to make it an array index
648 } 650 }
651 ############# Now store data in self for retreival by body
652 $self->{first_shown} = $first_shown;
653 $self->{last_shown} = $last_shown;
654 $self->{browse_which} = $browse_which;
655 $self->{problem_seed} = $problem_seed;
656 $self->{pg_files} = \@pg_files;
657 $self->{all_set_defs} = \@all_set_defs;
649 658
659}
660
661
662sub title {
663 return "Problem Set Maker";
664}
665
666sub body {
667 my ($self) = @_;
668
669 my $r = $self->r;
670 my $ce = $r->ce; # course environment
671 my $db = $r->db; # database
672 my $j; # garden variety counter
673
674 my $userName = $r->param('user');
675
676 my $user = $db->getUser($userName); # checked
677 die "record for user $userName (real user) does not exist."
678 unless defined $user;
679
680 ### Check that this is a professor
681 my $authz = $r->authz;
682 unless ($authz->hasPermissions($userName, "modify_problem_sets")) {
683 print "User $userName returned " .
684 $authz->hasPermissions($user, "modify_problem_sets") .
685 " for permission";
686 return(CGI::div({class=>'ResultsWithError'},
687 CGI::em("You are not authorized to access the Instructor tools.")));
688 }
689
690 ########## Extract information computed in pre_header_initialize
691
692 my $first_shown = $self->{first_shown};
693 my $last_shown = $self->{last_shown};
694 my $browse_which = $self->{browse_which};
695 my $problem_seed = $self->{problem_seed};
696 my @pg_files = @{$self->{pg_files}};
697 my @all_set_defs = @{$self->{all_set_defs}};
698
650 @pg_html=($last_shown>=$first_shown) ? 699 my @pg_html=($last_shown>=$first_shown) ?
651 renderProblems($r,$user, @pg_files[$first_shown..$last_shown]) : (); 700 renderProblems($r,$user, @pg_files[$first_shown..$last_shown]) : ();
652 701
653 ########## Top part 702 ########## Top part
654 print CGI::startform({-method=>"POST", -action=>$r->uri}), 703 print CGI::startform({-method=>"POST", -action=>$r->uri}),
655 $self->hidden_authen_fields, 704 $self->hidden_authen_fields,
697 return ""; 746 return "";
698} 747}
699 748
700############################################## End of Body 749############################################## End of Body
701 750
702sub pre_header_initialize {
703 my ($self) = @_;
704 my $r = $self->r;
705 ## For all cases, lets set some things
706 $self->{error}=0;
707 $self->{libmsg}="";
708 $self->{localmsg}="";
709
710
711 ### Maybe FIXME: this needs to check permissions before doing anything
712
713 ## Now one action we have to deal with here
714 if ($r->param('edit_local')) {
715 my $urlpath = $r->urlpath;
716 my $db = $r->db;
717 my $checkset = $db->getGlobalSet($r->param('local_sets'));
718 if (not defined($checkset)) {
719 $self->{error} = 1;
720 $self->{localmsg} = "You need to select a local set before you can edit it.";
721 return();
722 }
723 my $page = $urlpath->newFromModule('WeBWorK::ContentGenerator::Instructor::ProblemSetEditor', setID=>$r->param('local_sets'), courseID=>$urlpath->arg("courseID"));
724 my $url = $self->systemLink($page);
725 $self->reply_with_redirect($url);
726 }
727}
728
729
730# SKEL: To emit your own HTTP header, uncomment this: 751# SKEL: To emit your own HTTP header, uncomment this:
731# 752#
732#sub header { 753#sub header {
733# my ($self) = @_; 754# my ($self) = @_;
734# 755#

Legend:
Removed from v.2114  
changed lines
  Added in v.2115

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9