Parent Directory
|
Revision Log
changed #! lines to /usr/bin/env perl THIS SHOULD BE THE LAST TIME #! LINES ARE CHANGED!!!!!!!
1 #!/usr/bin/env perl 2 3 ## $Id$ 4 5 #################################################################### 6 # Copyright @ 1995-1998 University of Rochester 7 # All Rights Reserved 8 #################################################################### 9 10 11 ############################################################################### 12 ############ PRELIMINARY SETUP ####################### 13 ############################################################################### 14 15 use lib '.'; use webworkInit; # WeBWorKInitLine 16 17 use Global; 18 use CGI qw(:standard); 19 use Auth; 20 use TimeLocal; # the module Time::Local.pm has a bug which interacts 21 # with DProf. (They call a subroutine assuming @_ doesn't change. 22 23 use strict; 24 25 # begin Timing code 26 use Benchmark; 27 my $beginTime = new Benchmark; 28 # end Timing code 29 30 31 &CGI::ReadParse(*main::inputs); 32 my %inputs=%main::inputs; 33 34 # get primary data from CGI form 35 my $User = $inputs{'user'}; 36 my $Course = $inputs{'course'}; 37 my $Key = $inputs{'key'}; 38 my $psvn = $inputs{'probSetKey'}; 39 40 # set course environment 41 &Global::getCourseEnvironment($Course); 42 43 my $scriptDirectory = getWebworkScriptDirectory($Course); 44 my $databaseDirectory = getCourseDatabaseDirectory($Course); 45 my $cgiURL = getWebworkCgiURL($Course); 46 my $htmlURL = getCourseHtmlURL($Course); 47 my $logsDirectory = getCourseLogsDirectory($Course); 48 49 require "${scriptDirectory}$Global::DBglue_pl"; 50 require "${scriptDirectory}$Global::classlist_DBglue_pl"; 51 require "${scriptDirectory}$Global::FILE_pl"; 52 require "${scriptDirectory}HTMLglue.pl"; 53 54 my $keyFile = getCourseKeyFile($Course); 55 &verify_key($inputs{'user'}, $inputs{'key'}, "$keyFile", $inputs{'course'}); 56 57 my $permissionsFile = &Global::getCoursePermissionsFile($inputs{'course'}); 58 my $permissions = &get_permissions($inputs{'user'}, $permissionsFile); 59 60 if ($permissions != $Global::instructor_permissions ) { 61 print "permissions = $permissions instructor_permissions = $Global::instructor_permissions\n"; 62 print &html_NO_PERMISSION; 63 exit(0); 64 } 65 66 # get additional data from calling CGI form 67 my ( $Mode, $studentLogin, $setNumber); 68 69 #$Mode = "HTML"; #default viewing mode 70 $Mode = $Global::htmlModeDefault; #default viewing mode #Change suggested by Davide P. Cervone 71 $Mode = $inputs{'Mode'} if defined($inputs{'Mode'}); 72 $studentLogin = $inputs{'studentLogin'}; 73 $setNumber = $inputs{'setNo'}; 74 75 # if the psvn is not given we need to derive it from the set number and 76 # the student login ID 77 if (!defined($psvn) ) { 78 unless ( defined($studentLogin) && defined($setNumber) ) { 79 Global::error("DataMunger: Need psvn, or studentLogin and setNumber"); 80 } 81 my %loginList = &getAllSetNumbersForStudentLoginHash($studentLogin); 82 $psvn =$loginList{$setNumber}; 83 } 84 85 86 # define data to be displayed in the form 87 # the data for this will be loaded by the subroutine load 88 # my (@row1, @row2, @row3, @row4); 89 # my ($StudentLastName, $StudentFirstName, $StudentID); 90 # @row1 = qw(StudentLastName StudentFirstName StudentID); 91 # 92 # my ($StudentStatus, $StudentGrade, $SetNumber); 93 # @row2 = qw(StudentStatus StudentGrade SetNumber); 94 # 95 # my ($OpenDate, $DueDate, $AnswerDate ); 96 # @row3 = qw(OpenDate DueDate AnswerDate ); 97 # 98 # my ($StudentLogin, $ClassSection ); 99 # @row4 = qw(StudentLogin ClassSection ); 100 # my ($SetHeaderFileName, $ProbHeaderFileName ); 101 # @row5 = qw(SetHeaderFileName, ProbHeaderFileName ); 102 103 104 ############################################################################### 105 ############ MODIFY SET DATA IN THE DATABASE ####################### 106 ############################################################################### 107 108 109 # First make sure that the psvn is properly defined 110 # then save the information from the CGI form to the database 111 # Whether or not information is saved, at the end of this block 112 # the most current information has been placed in %probSetRecord 113 # using &fetchProbSetRecord. 114 if (not &fetchProbSetRecord($psvn)) { 115 Global::error("in dataMunger.pl:","Can't find record with psvn = $psvn 116 in the database"); 117 } 118 $studentLogin = getStudentLogin($psvn); 119 attachCLRecord($studentLogin); 120 121 if (defined( $inputs{'save'} ) && $inputs{'save'} eq "ON" ) { 122 # in this case we obtain the data from the CGI from and store it in the database 123 124 # update the database from the CGI script: 125 &updateDatabase($psvn); 126 # loads the information into %probSetRecord, 127 # modifies the data and saves it back to the database 128 # This effectively executes fetchProbSetRecord 129 # so that the contents of %probSetRecord is now current 130 131 #log the changes: -- the format for loggin the changes needs improvement 132 &logChanges( &getProbSetRecord() ); 133 &print_modification_form; 134 135 } elsif (defined($inputs{'save'}) && $inputs{'save'} eq "OFF" ){ 136 # No new information in the calling CGI form 137 # and the information has already been loaded into %probSetRecord using &fetchProbSetRecord($psvn); 138 # Nothing needs to be done in this case except print the form 139 &print_modification_form; 140 } elsif ( defined($inputs{'deleteMode'}) && $inputs{'deleteMode'} eq 'delete') { 141 # In this case the problem is deleted 142 my $psvnSetNumber = getSetNumber($psvn); 143 &deleteProbSetRecord($psvn); 144 # remove .sco file if it exists 145 system ("rm ${databaseDirectory}S${psvnSetNumber}-${psvn}.sco") if (-e "${databaseDirectory}S${psvnSetNumber}-${psvn}.sco"); 146 # remove any l2h files 147 my $l2hDir = getCoursel2hDirectory(); 148 my $tempDir = convertPath("${l2hDir}set${psvnSetNumber}/*-$psvn"); 149 system ("rm -rf $tempDir"); 150 # remove dvipng files -- reusing variables 151 $l2hDir = getCourseTempDirectory(); 152 $tempDir = convertPath("${l2hDir}png/${psvnSetNumber}/$psvn"); 153 system ("rm -rf $tempDir"); 154 &record_successfully_deleted_message; #reload empty record 155 } else { 156 157 # The calling CGI script must define the 'save' variable or the 'deleteMode' variable 158 Global::error( "<P><B>in dataMunger.pl:<BR>", "No value for 'save' mode in the calling CGI form.</B><P>"); 159 } 160 161 # begin Timing code 162 my $endTime = new Benchmark; 163 &Global::logTimingInfo($beginTime,$endTime,"dataMunger.pl",$Course,$User); 164 # end Timing code 165 exit; 166 167 #### END of main program 168 169 ############################################################################### 170 ############################ SUBROUTINES ############################ 171 ############################################################################### 172 173 174 ############################################################################### 175 ########################## PRINT FORM ############################## 176 ############################################################################### 177 sub print_modification_form { 178 print &htmlTOP("data for the problem set version number $psvn"); 179 print <<END_OF_HTML; 180 181 <A HREF="${cgiURL}profLogin.pl?user=$inputs{'user'}&key=$inputs{'key'}&course=$inputs{'course'}"> 182 <IMG SRC="${Global::upImgUrl}" align="right" BORDER=1 ALT="[Up]"></A><p> 183 <H3 ALIGN ="CENTER">Class Identification: $inputs{'course'}</H3> 184 <H3 ALIGN ="CENTER">Data for problem set version number: <FONT COLOR="#AA4400">$psvn</FONT></H3> 185 <HR SIZE =2> 186 <FORM ACTION="${cgiURL}dataMunger.pl" METHOD=POST> 187 188 Changes can be saved only if the Read/Write Mode button is selected: <BR> 189 190 <INPUT TYPE="radio" CHECKED NAME="save" VALUE="OFF" > Read Only Mode <BR> 191 <INPUT TYPE="radio" NAME="save" VALUE="ON"> Read/Write Mode <BR> 192 <INPUT TYPE="hidden" NAME="firsttime" VALUE=0> 193 END_OF_HTML 194 195 196 ###################################################### 197 # return messages 198 #print message about saving to the database and current mode of CGI form 199 if (defined( $inputs{'firsttime'} ) && $inputs{'firsttime'} == 0 ) { 200 if (defined( $inputs{'save'} ) && $inputs{'save'} eq "ON" ) { 201 print "<P><FONT COLOR='#ff00aa'><B>DATABASE MODIFIED</B></font> <P>"; 202 } elsif (defined($inputs{'save'}) && $inputs{'save'} eq "OFF" ){ 203 204 print "<P><FONT COLOR='#ff00aa'><B>READ ONLY MODE: DATABASE UNCHANGED</B></font> <P>"; 205 206 } else { 207 # When initially entering this CGI the 'save' mode is undefined. 208 wwerror( $0, "No value for 'save' mode.</B><P>"); 209 } 210 } 211 # submit button 212 213 print qq!<INPUT TYPE="SUBMIT" VALUE="SAVE CHANGES"><INPUT TYPE = "RESET" VALUE= "RESET FORM"> <BR>!; 214 215 216 # Get set data 217 my $StudentLastName = CL_getStudentLastName($studentLogin); 218 my $StudentFirstName = CL_getStudentFirstName($studentLogin); 219 my $StudentID = CL_getStudentID($studentLogin); 220 my $StudentStatus = CL_getStudentStatus($studentLogin); 221 my $SetNumber = &getSetNumber($psvn); 222 my $OpenDate = &getOpenDate($psvn); 223 my $DueDate = &getDueDate($psvn); 224 my $AnswerDate = &getAnswerDate($psvn); 225 my $ClassSection = CL_getClassSection($psvn); 226 my $ClassRecitation = CL_getClassRecitation($psvn); 227 my $SetHeaderFileName = &getSetHeaderFileName($psvn); 228 my $ProbHeaderFileName = &getProbHeaderFileName($psvn); 229 my $StudentLogin = $studentLogin; 230 231 # replace empty strings by a non breaking space 232 $ClassSection = ' ' unless ($ClassSection =~ /\S/); 233 $ClassRecitation = ' ' unless ($ClassRecitation =~ /\S/); 234 $SetHeaderFileName = ' ' unless ($SetHeaderFileName =~ /\S/); 235 $ProbHeaderFileName = ' ' unless ($ProbHeaderFileName =~ /\S/); 236 237 238 # format the dates properly 239 240 $OpenDate = &formatDateAndTime($OpenDate); 241 $DueDate = &formatDateAndTime($DueDate); 242 $AnswerDate = &formatDateAndTime($AnswerDate); 243 244 # continue printing form ###################################################### 245 print &sessionKeyInputs(\%inputs); 246 print qq!<INPUT TYPE="HIDDEN" NAME="probSetKey" VALUE="$psvn">!; 247 print qq! <TABLE BORDER="1" CELLPADDING="1" CELLSPACING="2" > <FONT SIZE=-2>!; 248 249 print "<TR>\n"; 250 print &formatHeaderCell("Last Name"); 251 print &formatHeaderCell("First Name"); 252 print &formatHeaderCell("Student ID" ); 253 print "</TR>\n"; 254 print "<TR>\n"; 255 print &formatFixedDataCell("StudentLastName",$StudentLastName, "20"); 256 print &formatFixedDataCell("StudentFirstName", $StudentFirstName, "20"); 257 print &formatFixedDataCell("StudentID", $StudentID, "20"); 258 print "</TR>\n"; 259 260 print "<TR>\n"; 261 print &formatHeaderCell("Student login name"); 262 print &formatHeaderCell("Enrollment Status"); 263 print &formatHeaderCell("Set Number" ); 264 print "</TR>\n"; 265 print "<TR>\n"; 266 print &formatFixedDataCell("StudentLogin",$StudentLogin, "20"); 267 print &formatFixedDataCell("StudentStatus",$StudentStatus, "20"); 268 print &formatFixedDataCell("SetNumber", $SetNumber, "20"); 269 print "</TR>\n"; 270 271 print "<TR>\n"; 272 print &formatHeaderCell("Class Section"); 273 print &formatHeaderCell("Class Recitation"); 274 print "</TR>\n"; 275 276 print "<TR>\n"; 277 print &formatFixedDataCell("ClassSection", $ClassSection, "20"); 278 print &formatFixedDataCell("ClassRecitation", $ClassRecitation, "20"); 279 print "</TR>\n"; 280 281 print "<TR>\n"; 282 print &formatHeaderCell("Open Date"); 283 print &formatHeaderCell("Due Date"); 284 print &formatHeaderCell("Answer Date" ); 285 print "</TR>\n"; 286 287 print "<TR>\n"; 288 print &formatDataCell("OpenDate",$OpenDate, "20"); 289 print &formatDataCell("DueDate", $DueDate, "20"); 290 print &formatDataCell("AnswerDate", $AnswerDate, "20"); 291 print "</TR>\n"; 292 293 294 295 print "<TR>\n"; 296 print &formatHeaderCell("Paper Header File Name" ); 297 print &formatHeaderCell("Screen Header File Name" ); 298 print "</TR>\n"; 299 300 print "<TR>\n"; 301 print &formatFixedDataCell("SetHeaderFileName", $SetHeaderFileName, "20"); 302 print &formatFixedDataCell("ProbHeaderFileName", $ProbHeaderFileName, "20"); 303 print "</TR>\n"; 304 305 print "</TR>\n"; 306 print " </FONT></TABLE>"; 307 308 print qq! <TABLE BORDER="1" CELLPADDING="1" CELLSPACING="2" >!; 309 my $i; 310 my @row5; 311 my @problems = sort {$a <=> $b} &getAllProblemsForProbSetRecord($psvn); 312 foreach $i (@problems) { 313 @row5 = ("ProblemFileName$i", "ProblemStatus$i","ProblemSeed$i", "ProblemValue$i", "ProblemMaxNumOfIncorrectAttemps$i"); 314 print "<TR>"; 315 print &formatHeaderCell( qq! 316 <A HREF="${Global::processProblem_CGI}?probSetKey=$psvn&probNum=$i&Mode=$Mode&show_old_answers=1&course=$Course&user=$User&key=$Key" TARGET="ViewProblem"> 317 Problem $i</A>!); 318 319 print &formatHeaderCell( "Attempted"); 320 print &formatHeaderCell( "Frac Corr"); 321 print &formatHeaderCell( "Correct"); 322 print &formatHeaderCell( "Wrong"); 323 print &formatHeaderCell( "Seed"); 324 print &formatHeaderCell( "Value"); 325 print &formatHeaderCell( "MaxAttmp"); 326 print "</TR>"; 327 328 print "<TR>"; 329 330 print &formatFixedDataCell( "ProblemFileName$i" , &getProblemFileName($i,$psvn) ,"30" ); 331 print &formatDataCell( "ProblemAttempted$i" , &getProblemAttempted($i,$psvn) ,"1"); 332 print &formatDataCell( "ProblemStatus$i" , &getProblemStatus($i,$psvn) ,"5"); 333 my $correctAns = &getProblemNumOfCorrectAns($i,$psvn); 334 my $incorrectAns = &getProblemNumOfIncorrectAns($i,$psvn); 335 $correctAns = "0" unless $correctAns; # Force 0 to print instead of a space 336 $incorrectAns = "0" unless $incorrectAns; # ditto 337 print &formatDataCell( "ProblemNumOfCorrectAns$i" , $correctAns ,"2"); 338 print &formatDataCell( "ProblemNumOfIncorrectAns$i" , $incorrectAns ,"2"); 339 print &formatDataCell( "ProblemSeed$i" , &getProblemSeed($i,$psvn) ,"7"); 340 print &formatFixedDataCell( "ProblemValue$i" , &getProblemValue($i,$psvn) ,"4"); 341 print &formatDataCell( "ProblemMaxNumOfIncorrectAttemps$i" , &getProblemMaxNumOfIncorrectAttemps($i,$psvn) ,"4"); 342 print "</TR>"; 343 344 } 345 346 347 print <<END_HTML; 348 </TABLE > 349 350 </FORM> 351 END_HTML 352 353 354 # FORM for downloading postscript versions 355 print qq! 356 <FORM action="${Global::cgiWebworkURL}welcomeAction.pl"> 357 <HR NOSHADE><H4>Download the postscript version of this problem set:</H4>\n 358 Problem Set Number $SetNumber -- psvn: $psvn -- for $StudentLastName, $StudentFirstName<BR> 359 <INPUT TYPE='HIDDEN' NAME='local_psvns' VALUE="$psvn"> 360 <INPUT TYPE='HIDDEN' NAME='action' VALUE='Get_hard_copy'> 361 <B>Download Type:</B> <INPUT TYPE=RADIO NAME=\"downloadType\" VALUE=\"ps\" CHECKED><B>postscript</B> format 362 <INPUT TYPE=RADIO NAME=\"downloadType\" VALUE=\"pdf\"><B>pdf</B> format 363 <INPUT TYPE=RADIO NAME=\"downloadType\" VALUE=\"TeX\"><B>TeX</B> format 364 <INPUT TYPE=RADIO NAME=\"downloadType\" VALUE=\"dvi\"><B>DVI</B> format<BR> 365 <INPUT TYPE=CHECKBOX NAME=\"ShowAns\" VALUE=\"1\"> Show answers in hard copy.<BR> 366 367 !; 368 369 print &sessionKeyInputs(\%inputs); 370 371 print qq! 372 <br><input type="submit" value="Download File"> 373 </FORM> 374 !; 375 376 # form for viewing student summary 377 print qq! 378 <FORM ACTION="studentSummary.pl" METHOD=POST>\n 379 <HR NOSHADE><H4>Form for viewing student summary of homework</H4>\n 380 <INPUT TYPE="HIDDEN" NAME="studentLogin" VALUE = "$StudentLogin"> 381 !; 382 383 print &sessionKeyInputs(\%inputs); 384 385 print qq! 386 <input type="submit" value="Get homework summary">\n 387 </FORM> 388 !; 389 390 # form for deleting problem set 391 print qq! 392 <HR NOSHADE><H3>Delete this problem set:</H3>\n 393 Problem Set Number $SetNumber -- psvn: $psvn -- for $StudentLastName, $StudentFirstName<BR> 394 <B>This action cannot be undone\!</B> 395 <FORM ACTION="${cgiURL}dataMunger.pl" METHOD=POST>\n 396 <INPUT TYPE="HIDDEN" NAME="probSetKey" VALUE="$psvn">\n 397 <INPUT TYPE="RADIO" NAME="deleteMode" VALUE="save" CHECKED> Off<BR>\n 398 <INPUT TYPE="RADIO" NAME="deleteMode" VALUE="delete"> Delete this record 399 !; 400 401 print &sessionKeyInputs(\%inputs); 402 403 print qq! 404 <br><input type="submit" value="Delete this person's problem set">\n 405 </FORM> 406 !; 407 408 409 print &htmlBOTTOM("dataMunger.pl", \%inputs); 410 411 } # end of print_modification_form 412 413 414 sub updateDatabase { 415 my $psvn = shift @_; 416 &fetchProbSetRecord($psvn); 417 418 my $time1 = &unformatDateAndTime($inputs{'OpenDate'}); 419 my $time2 = &unformatDateAndTime($inputs{'DueDate'}); 420 my $time3 = &unformatDateAndTime($inputs{'AnswerDate'}); 421 if ($time2 < $time1 or $time3 < $time2) { 422 &Global::error('Dates not in chronological order', "The open date: $inputs{'OpenDate'}, 423 due date: $inputs{'DueDate'}, and answer date: $inputs{'AnswerDate'} must be in chronologicasl order."); 424 } 425 &putOpenDate ($time1,$psvn); 426 &putDueDate ($time2,$psvn); 427 &putAnswerDate ($time3,$psvn); 428 429 430 # now update the problem information 431 # @problems was defined "globally" within this file 432 my @problems = sort {$a <=> $b} &getAllProblemsForProbSetRecord($psvn); 433 my ($i,$old_seed, $new_seed); 434 my $l2hDir = getCoursel2hDirectory(); 435 foreach $i (@problems) { 436 $old_seed = getProblemSeed($i,$psvn); 437 $new_seed = stripWhiteSpace($inputs{"ProblemSeed$i"}); 438 # &putProblemFileName( stripWhiteSpace($inputs{"ProblemFileName$i"}), $i,$psvn); 439 &putProblemAttempted( stripWhiteSpace($inputs{"ProblemAttempted$i"}), $i,$psvn); 440 &putProblemStatus( stripWhiteSpace($inputs{"ProblemStatus$i"}), $i,$psvn); 441 &putProblemNumOfCorrectAns( stripWhiteSpace($inputs{"ProblemNumOfCorrectAns$i"}), $i,$psvn); 442 &putProblemNumOfIncorrectAns( stripWhiteSpace($inputs{"ProblemNumOfIncorrectAns$i"}), $i,$psvn); 443 # &putProblemValue( stripWhiteSpace($inputs{"ProblemValue$i"}), $i,$psvn); 444 &putProblemMaxNumOfIncorrectAttemps( stripWhiteSpace($inputs{"ProblemMaxNumOfIncorrectAttemps$i"}), $i,$psvn); 445 &putProblemSeed( $new_seed,$i,$psvn); 446 447 if ($old_seed != $new_seed) { ## remove latex2html cached images 448 my $psvnSetNumber = getSetNumber($psvn); 449 my $tempDir = convertPath("${l2hDir}set${psvnSetNumber}/${i}-$psvn"); 450 system ("rm -rf $tempDir"); 451 ## remove dvipng cached images too, reusing variable names 452 $l2hDir = getCourseTempDirectory(); 453 $tempDir = convertPath("${l2hDir}png/${psvnSetNumber}/$psvn"); 454 system ("rm -rf $tempDir"); 455 } 456 } 457 # save the updated information to the database 458 &saveProbSetRecord($psvn); 459 } 460 # logs the incremental changes made by dataMunger to a log file 461 sub logChanges { 462 my @dataArray = @_; 463 my $fullLogFileName ="${logsDirectory}dataMunger.log"; 464 open(LOGFILE,">>$fullLogFileName") || &Global::error( "Can't open $fullLogFileName"); 465 466 my $timeNow = formatDateAndTime(time); 467 print LOGFILE "\n$Course, psvn is $psvn, user is $User, time is $timeNow, data is: "; 468 my $dataString = join( ' ',@dataArray); 469 print LOGFILE "@dataArray\n" ; 470 close(LOGFILE); 471 } 472 473 sub formatDataCell { 474 my ($name,$value,$size) = @_; 475 # if the data hasn't been entered it appears as a blank: 476 $value = '' unless defined($value); 477 478 my $out = qq! 479 <TD ALIGN=CENTER VALIGN=MIDDLE > 480 <INPUT TYPE="TEXT" NAME="$name" VALUE="$value", SIZE="$size"> 481 </TD> 482 !; 483 $out; 484 } 485 sub formatFixedDataCell { 486 my ($name,$value,$size) = @_; 487 # if the data hasn't been entered it appears as a blank: 488 $value = '' unless defined($value); 489 490 my $out = qq! 491 <TD ALIGN=CENTER VALIGN=MIDDLE > 492 <INPUT TYPE="HIDDEN" NAME="$name" VALUE="$value"> 493 $value 494 </TD> 495 !; 496 $out; 497 } 498 sub formatHeaderCell { 499 my ($item,$options) = @_; 500 $options = '' unless defined($options); 501 my $out = qq! 502 <TH ALIGN=CENTER VALIGN=MIDDLE $options> 503 $item 504 </TH> 505 !; 506 $out; 507 } 508 509 510 511 sub record_successfully_deleted_message{ 512 513 print &htmlTOP("data for the problem set version number $psvn"); 514 print <<END_OF_HTML; 515 <A HREF="${cgiURL}profLogin.pl?user=$inputs{'user'}&key=$inputs{'key'}&course=$inputs{'course'}"> 516 <IMG SRC="${Global::upImgUrl}" align="right" BORDER=1 ALT="[Up]"></A> 517 END_OF_HTML 518 519 my $setNumber = getSetNumber($psvn); 520 my $studentName = CL_getStudentName($studentLogin); 521 print qq!<P><B> Problem set version $psvn for set number !, 522 $setNumber, " for ", 523 $studentName, 524 " deleted.</B><P>"; 525 print &htmlBOTTOM("dataMunger.pl", \%inputs); 526 527 }
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |