Changeset 25097 for main/trunk/greenstone2
- Timestamp:
- 2012-02-20T15:14:01+13:00 (12 years ago)
- Location:
- main/trunk/greenstone2/perllib/cgiactions
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
main/trunk/greenstone2/perllib/cgiactions/baseaction.pm
r24362 r25097 67 67 $err_mess .= " check-installation\n\n"; 68 68 69 foreach my $a ( keys %$action_table) {69 foreach my $a (sort keys %$action_table) { 70 70 $err_mess .= " $a:\n"; 71 71 $err_mess .= " Compulsory args: "; -
main/trunk/greenstone2/perllib/cgiactions/metadataaction.pm
r24950 r25097 41 41 } 42 42 43 44 43 @metadataaction::ISA = ('baseaction'); 45 44 46 47 45 my $action_table = 48 { 49 "get-live-metadata" => { 'compulsory-args' => [ "d", "metaname" ], 50 'optional-args' => [] }, 51 52 "get-metadata" => { 'compulsory-args' => [ "d", "metaname" ], 53 'optional-args' => [ "metapos" ] }, 54 55 "set-live-metadata" => { 'compulsory-args' => [ "d", "metaname", "metavalue" ], 56 'optional-args' => [ ] }, 57 58 "set-metadata" => { 'compulsory-args' => [ "d", "metaname", "metavalue" ], 59 'optional-args' => [ "metapos" ] }, 60 61 "set-metadata-array" => { 'compulsory-args' => [ "json" ], 62 'optional-args' => [ ] }, 46 { 47 #GET METHODS 48 "get-import-metadata" => { 49 'compulsory-args' => [ "d", "metaname" ], 50 'optional-args' => [ ] }, 51 52 "get-archives-metadata" => { 53 'compulsory-args' => [ "d", "metaname" ], 54 'optional-args' => [ "metapos" ] }, 55 56 "get-metadata" => { 57 'compulsory-args' => [ "d", "metaname" ], 58 'optional-args' => [ "metapos" ] }, 59 60 "get-live-metadata" => { 61 'compulsory-args' => [ "d", "metaname" ], 62 'optional-args' => [ ] }, 63 64 #SET METHODS 65 "set-live-metadata" => { 66 'compulsory-args' => [ "d", "metaname", "metavalue" ], 67 'optional-args' => [ ] }, 68 69 "set-metadata" => { 70 'compulsory-args' => [ "d", "metaname", "metavalue" ], 71 'optional-args' => [ "metapos" ] }, 72 73 "set-archives-metadata" => { 74 'compulsory-args' => [ "d", "metaname", "metavalue" ], 75 'optional-args' => [ "metapos", "metamode", "prevmetavalue" ] }, # metamode can be "accumulate", "override", 76 77 "set-import-metadata" => { 78 'compulsory-args' => [ "metaname", "metavalue" ], 79 'optional-args' => [ "d", "f", "metamode" ] }, # metamode can be "accumulate", "override", or "unique-id". Also need to add the ability to specify a previous metadata value to overwrite (because we can't use metapos) 80 81 #SET METHODS (ARRAY) 82 "set-metadata-array" => { 83 'compulsory-args' => [ "json" ], 84 'optional-args' => [ ] }, 63 85 64 "set-archives-metadata" => { 'compulsory-args' => [ "d", "metaname", "metavalue" ], 65 'optional-args' => [ "metapos", "metamode" ] 66 # metamode can be "accumulate", "override", 67 }, 68 69 "set-archives-metadata-array" => { 'compulsory-args' => [ "json" ], 70 'optional-args' => [ "metamode" ] 71 }, 72 73 "set-import-metadata" => { 'compulsory-args' => [ "metaname", "metavalue" ], 74 'optional-args' => [ "d", "f", "metamode" ] # Need to add the ability to specify a previous metadata value to overwrite (because we can't use metapos) 75 # metamode can be "accumulate", "override", or "unique-id" 76 }, 77 78 "remove-import-metadata" => { 'compulsory-args' => [ "d", "metaname", "metavalue" ], #TODO: add f argument 79 'optional-args' => [ ] }, 86 "set-archives-metadata-array" => { 87 'compulsory-args' => [ "json" ], 88 'optional-args' => [ ] }, 89 90 "set-import-metadata-array" => { 91 'compulsory-args' => [ "json" ], 92 'optional-args' => [ ] }, 93 94 #REMOVE METHODS 95 "remove-import-metadata" => { 96 'compulsory-args' => [ "d", "metaname", "metavalue" ], #TODO: add f argument 97 'optional-args' => [ ] }, 80 98 81 "remove-archives-metadata" => { 'compulsory-args' => [ "d", "metaname" ], #TODO: add f argument 82 'optional-args' => [ "metapos", "metavalue" ] }, 83 84 "remove-live-metadata" => { 'compulsory-args' => [ "d", "metaname" ], 85 'optional-args' => [ ] }, 86 87 "remove-metadata" => { 'compulsory-args' => [ "d", "metaname" ], 88 'optional-args' => [ "metapos", "metavalue" ] }, 89 90 "insert-metadata" => { 'compulsory-args' => [ "d", "metaname", "metavalue" ], 91 'optional-args' => [ ] 92 } 99 "remove-archives-metadata" => { 100 'compulsory-args' => [ "d", "metaname" ], #TODO: add f argument 101 'optional-args' => [ "metapos", "metavalue" ] }, 102 103 "remove-live-metadata" => { 104 'compulsory-args' => [ "d", "metaname" ], 105 'optional-args' => [ ] }, 106 107 "remove-metadata" => { 108 'compulsory-args' => [ "d", "metaname" ], 109 'optional-args' => [ "metapos", "metavalue" ] }, 110 111 #INSERT METHODS 112 "insert-metadata" => { 113 'compulsory-args' => [ "d", "metaname", "metavalue" ], 114 'optional-args' => [ ] } 93 115 }; 94 116 … … 242 264 243 265 266 sub get_import_metadata 267 { 268 my $self = shift @_; 269 270 my $username = $self->{'username'}; 271 my $collect = $self->{'collect'}; 272 my $gsdl_cgi = $self->{'gsdl_cgi'}; 273 my $gsdlhome = $self->{'gsdlhome'}; 274 275 # Authenticate user if it is enabled 276 if ($baseaction::authentication_enabled) { 277 # Ensure the user is allowed to edit this collection 278 &authenticate_user($gsdl_cgi, $username, $collect); 279 } 280 281 # Obtain the collect dir 282 my $site = $self->{'site'}; 283 my $collect_dir = $gsdl_cgi->get_collection_dir($site); 284 ##my $collect_dir = &util::filename_cat($gsdlhome, "collect"); 285 286 # Make sure the collection isn't locked by someone else 287 $self->lock_collection($username, $collect); 288 289 # look up additional args 290 my $docid = $self->{'d'}; 291 my $metaname = $self->{'metaname'}; 292 my $infodbtype = $self->{'infodbtype'}; 293 if (!defined $docid) 294 { 295 $gsdl_cgi->generate_error_message("No docid (d=...) specified.\n"); 296 } 297 298 # Obtain where the metadata.xml is from the archiveinfo-doc.gdb file 299 # If the doc oid is not specified, we assume the metadata.xml is next to the specified "f" 300 my $metadata_xml_file; 301 my $import_filename = undef; 302 303 304 my $archive_dir = &util::filename_cat($collect_dir, $collect, "archives"); 305 my $arcinfo_doc_filename = &dbutil::get_infodb_file_path($infodbtype, "archiveinf-doc", $archive_dir); 306 my $doc_rec = &dbutil::read_infodb_entry($infodbtype, $arcinfo_doc_filename, $docid); 307 308 # This now stores the full pathname 309 $import_filename = $doc_rec->{'src-file'}->[0]; 310 311 # figure out correct metadata.xml file [?] 312 # Assuming the metadata.xml file is next to the source file 313 # Note: This will not work if it is using the inherited metadata from the parent folder 314 my ($import_tailname, $import_dirname) = File::Basename::fileparse($import_filename); 315 my $metadata_xml_filename = &util::filename_cat($import_dirname, "metadata.xml"); 316 317 $gsdl_cgi->generate_ok_message($self->get_metadata_from_metadata_xml($gsdl_cgi, $metadata_xml_filename, $metaname, $import_tailname)); 318 319 # Release the lock once it is done 320 $self->unlock_collection($username, $collect); 321 } 322 323 sub get_metadata_from_metadata_xml 324 { 325 my $self = shift @_; 326 my ($gsdl_cgi, $metadata_xml_filename, $metaname, $src_file) = @_; 327 328 my @rules = 329 ( 330 _default => 'raw', 331 'Metadata' => \&gfmxml_metadata, 332 'FileName' => \&mxml_filename 333 ); 334 335 my $parser = XML::Rules->new 336 ( 337 rules => \@rules, 338 output_encoding => 'utf8' 339 ); 340 341 my $xml_in = ""; 342 if (!open(MIN,"<$metadata_xml_filename")) 343 { 344 $gsdl_cgi->generate_error("Unable to read in $metadata_xml_filename: $!"); 345 } 346 else 347 { 348 # Read them in 349 my $line; 350 while (defined ($line=<MIN>)) { 351 $xml_in .= $line; 352 } 353 close(MIN); 354 355 $parser->parse($xml_in, {metaname => $metaname, src_file => $src_file}); 356 357 if(defined $parser->{'pad'}->{'metavalue'}) 358 { 359 return $parser->{'pad'}->{'metavalue'}; 360 } 361 else 362 { 363 return ""; 364 } 365 } 366 } 367 368 sub gfmxml_metadata 369 { 370 my ($tagname, $attrHash, $contextArray, $parentDataArray, $parser) = @_; 371 372 if (($parser->{'parameters'}->{'src_file'} eq $parser->{'parameters'}->{'current_file'}) && ($attrHash->{'name'} eq $parser->{'parameters'}->{'metaname'})) 373 { 374 $parser->{'pad'}->{'metavalue'} = $attrHash->{'_content'}; 375 } 376 } 377 378 sub get_archives_metadata 379 { 380 my $self = shift @_; 381 382 my $username = $self->{'username'}; 383 my $collect = $self->{'collect'}; 384 my $gsdl_cgi = $self->{'gsdl_cgi'}; 385 my $gsdlhome = $self->{'gsdlhome'}; 386 my $infodbtype = $self->{'infodbtype'}; 387 388 # Authenticate user if it is enabled 389 if ($baseaction::authentication_enabled) { 390 # Ensure the user is allowed to edit this collection 391 &authenticate_user($gsdl_cgi, $username, $collect); 392 } 393 394 # Obtain the collect dir 395 my $site = $self->{'site'}; 396 my $collect_dir = $gsdl_cgi->get_collection_dir($site); 397 398 my $archive_dir = &util::filename_cat($collect_dir, $collect, "archives"); 399 400 # Make sure the collection isn't locked by someone else 401 $self->lock_collection($username, $collect); 402 403 # look up additional args 404 my ($docid, $docid_secnum) = ($self->{'d'} =~ m/^(.*?)(\..*)?$/); 405 $docid_secnum = "" if (!defined $docid_secnum); 406 407 my $metaname = $self->{'metaname'}; 408 my $metapos = $self->{'metapos'}; 409 $metapos = 0 if (!defined $metapos); 410 411 my $arcinfo_doc_filename = &dbutil::get_infodb_file_path($infodbtype, "archiveinf-doc", $archive_dir); 412 my $doc_rec = &dbutil::read_infodb_entry($infodbtype, $arcinfo_doc_filename, $docid); 413 414 # This now stores the full pathname 415 my $doc_filename = $doc_rec->{'doc-file'}->[0]; 416 417 $gsdl_cgi->generate_ok_message($self->get_metadata_from_archive_xml($gsdl_cgi, &util::filename_cat($archive_dir, $doc_filename), $metaname, $metapos, $docid_secnum)); 418 419 # Release the lock once it is done 420 $self->unlock_collection($username, $collect); 421 } 422 423 sub get_metadata_from_archive_xml 424 { 425 my $self = shift @_; 426 my ($gsdl_cgi, $doc_xml_filename, $metaname, $metapos, $secid) = @_; 427 428 my @start_rules = ('Section' => \&dxml_start_section); 429 430 my @rules = 431 ( 432 _default => 'raw', 433 'Metadata' => \&gfdxml_metadata 434 ); 435 436 my $parser = XML::Rules->new 437 ( 438 start_rules => \@start_rules, 439 rules => \@rules, 440 output_encoding => 'utf8' 441 ); 442 443 my $xml_in = ""; 444 if (!open(MIN,"<$doc_xml_filename")) 445 { 446 $gsdl_cgi->generate_error("Unable to read in $doc_xml_filename: $!"); 447 } 448 else 449 { 450 # Read them in 451 my $line; 452 while (defined ($line=<MIN>)) { 453 $xml_in .= $line; 454 } 455 close(MIN); 456 457 $parser->parse($xml_in, {metaname => $metaname, metapos => $metapos, secid => $secid}); 458 459 if(defined $parser->{'pad'}->{'metavalue'}) 460 { 461 return $parser->{'pad'}->{'metavalue'}; 462 } 463 else 464 { 465 return ""; 466 } 467 } 468 } 469 470 sub gfdxml_metadata 471 { 472 my ($tagname, $attrHash, $contextArray, $parentDataArray, $parser) = @_; 473 474 if(!($parser->{'parameters'}->{'secid'} eq $parser->{'parameters'}->{'curr_section_num'})) 475 { 476 return; 477 } 478 479 if ($parser->{'parameters'}->{'metaname'} eq $attrHash->{'name'}) 480 { 481 if (!defined $parser->{'parameters'}->{'poscount'}) 482 { 483 $parser->{'parameters'}->{'poscount'} = 0; 484 } 485 else 486 { 487 $parser->{'parameters'}->{'poscount'}++; 488 } 489 } 490 491 if (($parser->{'parameters'}->{'metaname'} eq $attrHash->{'name'}) && ($parser->{'parameters'}->{'poscount'} == $parser->{'parameters'}->{'metapos'})) 492 { 493 $parser->{'pad'}->{'metavalue'} = $attrHash->{'_content'}; 494 } 495 } 496 244 497 sub set_live_metadata 245 498 { … … 394 647 $self->unlock_collection($username, $collect); 395 648 } 396 397 398 649 399 650 … … 471 722 472 723 473 474 724 sub dxml_metadata 475 725 { … … 491 741 ## print STDERR "**** metamode = $metamode\n"; 492 742 493 if ((!defined $opt_doc_secnum) || ($opt_doc_secnum eq $curr_secnum)) { 494 my $name_attr = $attrHash->{'name'}; 495 if (($name_attr eq $metaname) && ($metamode eq "override")) { 496 ## print STDERR "**** got match!!\n"; 497 # Get the value and override the current value 498 my $metavalue = $parser->{'parameters'}->{'metavalue'}; 499 $attrHash->{'_content'} = $metavalue; 500 501 # Don't want it to wipe out any other pieces of metadata 502 $parser->{'parameters'}->{'metamode'} = "done"; 503 } 743 if ((!defined $opt_doc_secnum) || ($opt_doc_secnum eq $curr_secnum)) 744 { 745 my $name_attr = $attrHash->{'name'}; 746 if (($name_attr eq $metaname) && ($metamode eq "override")) 747 { 748 if (!defined $parser->{'parameters'}->{'poscount'}) 749 { 750 $parser->{'parameters'}->{'poscount'} = 0; 751 } 752 else 753 { 754 $parser->{'parameters'}->{'poscount'}++; 755 } 756 757 if(defined $parser->{'parameters'}->{'metapos'} && $parser->{'parameters'}->{'poscount'} == $parser->{'parameters'}->{'metapos'}) 758 { 759 ##print STDERR "**** got match!!\n"; 760 # Get the value and override the current value 761 my $metavalue = $parser->{'parameters'}->{'metavalue'}; 762 $attrHash->{'_content'} = $metavalue; 763 764 # Don't want it to wipe out any other pieces of metadata 765 $parser->{'parameters'}->{'metamode'} = "done"; 766 } 767 else if(defined $parser->{'parameters'}->{'prevmetavalue'} && $parser->{'parameters'}->{'prevmetavalue'} == $attrHash->{'_content'}) 768 { 769 $attrHash->{'_content'} = $metavalue; 770 $parser->{'parameters'}->{'metamode'} = "done"; 771 } 772 } 504 773 } 505 774 … … 544 813 545 814 546 547 815 sub dxml_start_section 548 816 { … … 581 849 $parser->{'parameters'}->{'curr_section_num'} = $new_secnum; 582 850 583 print STDERR "*** In Section: $new_secnum\n";851 ##print STDERR "*** In Section: $new_secnum\n"; 584 852 } 585 853 … … 625 893 } 626 894 627 628 895 sub edit_doc_xml 629 896 { 630 897 my $self = shift @_; 631 my ($gsdl_cgi, $doc_xml_filename, $metaname, $metavalue, $metapos, $metamode, $opt_secnum ) = @_;898 my ($gsdl_cgi, $doc_xml_filename, $metaname, $metavalue, $metapos, $metamode, $opt_secnum, $prevmetavalue) = @_; 632 899 633 900 # To monitor which section/subsection number we are in … … 646 913 'metapos' => $metapos, 647 914 'metavalue' => $metavalue, 648 'metamode' => $metamode }; 915 'metamode' => $metamode, 916 'prevmetavalue' => $prevmetavalue }; 649 917 650 918 if (defined $opt_secnum) { … … 658 926 { 659 927 my $self = shift @_; 660 my ($gsdl_cgi,$archive_dir, $collect_dir,$collect, $infodbtype,$docid,$metaname,$metapos,$metavalue,$metamode ) = @_;928 my ($gsdl_cgi,$archive_dir, $collect_dir,$collect, $infodbtype,$docid,$metaname,$metapos,$metavalue,$metamode,$prevmetavalue) = @_; 661 929 662 930 # Obtain the doc.xml path for the specified docID … … 676 944 # Running import.pl -groupsize will cause this to have multiple sections in one doc.xml 677 945 678 print STDERR "** away to call edit_doc_xml\n";679 680 946 $self->edit_doc_xml($gsdl_cgi,$doc_xml_filename, 681 $metaname,$metavalue,$metapos,$metamode,$docid_secnum); 682 683 print STDERR "*** finished edit_doc_xml\n"; 947 $metaname,$metavalue,$metapos,$metamode,$docid_secnum,$prevmetavalue); 684 948 685 949 return 0; # return 0 for now to indicate no error … … 717 981 my $metaname = $self->{'metaname'}; 718 982 my $metavalue = $self->{'metavalue'}; 983 my $prevmetavalue = $self->{'prevmetavalue'} 719 984 720 985 my $metapos = $self->{'metapos'}; … … 729 994 730 995 my $status = $self->set_archives_metadata_entry($gsdl_cgi,$archive_dir, $collect_dir,$collect, $infodbtype,$docid, 731 $metaname,$metapos,$metavalue,$metamode );996 $metaname,$metapos,$metavalue,$metamode,$prevmetavalue); 732 997 733 998 # Release the lock once it is done … … 876 1141 877 1142 # This now stores the full pathname 878 my $ import_filename = $doc_rec->{'doc-file'}->[0];879 880 my $status = $self->remove_from_doc_xml($gsdl_cgi, &util::filename_cat($archive_dir, $ import_filename), $metaname, $metapos, undef, $docid_secnum);1143 my $doc_filename = $doc_rec->{'doc-file'}->[0]; 1144 1145 my $status = $self->remove_from_doc_xml($gsdl_cgi, &util::filename_cat($archive_dir, $doc_filename), $metaname, $metapos, undef, $docid_secnum); 881 1146 882 1147 # Release the lock once it is done … … 1000 1265 1001 1266 # Report error if we don't see FileName tag before this 1002 die "Fat el Error: Unexpected metadata.xml structure. Undefind current_file, possiblely encountered Description before FileName" if (!defined($parser->{'parameters'}->{'current_file'}));1267 die "Fatal Error: Unexpected metadata.xml structure. Undefind current_file, possiblely encountered Description before FileName" if (!defined($parser->{'parameters'}->{'current_file'})); 1003 1268 1004 1269 # Don't do anything if we are not in the right FileSet … … 1040 1305 1041 1306 # Failed... Report error if we don't see FileName tag before this 1042 die "Fat el Error: Unexpected metadata.xml structure. Undefind current_file, possiblely encountered Description before FileName" if (!defined($parser->{'parameters'}->{'current_file'}));1307 die "Fatal Error: Unexpected metadata.xml structure. Undefind current_file, possiblely encountered Description before FileName" if (!defined($parser->{'parameters'}->{'current_file'})); 1043 1308 1044 1309 # Don't do anything if we are not in the right FileSet … … 1218 1483 1219 1484 if ($baseaction::authentication_enabled) { 1220 # Ensure the user is allowed to edit this collection1221 $self->authenticate_user($username, $collect);1485 # Ensure the user is allowed to edit this collection 1486 $self->authenticate_user($username, $collect); 1222 1487 } 1223 1488 … … 1297 1562 } 1298 1563 1564 sub set_import_metadata_array 1565 { 1566 my $self = shift @_; 1567 1568 my $username = $self->{'username'}; 1569 my $collect = $self->{'collect'}; 1570 my $gsdl_cgi = $self->{'gsdl_cgi'}; 1571 my $gsdlhome = $self->{'gsdlhome'}; 1572 1573 if ($baseaction::authentication_enabled) { 1574 # Ensure the user is allowed to edit this collection 1575 &authenticate_user($gsdl_cgi, $username, $collect); 1576 } 1577 1578 my $site = $self->{'site'}; 1579 my $collect_dir = $gsdl_cgi->get_collection_dir($site); 1580 1581 $gsdl_cgi->checked_chdir($collect_dir); 1582 1583 # Make sure the collection isn't locked by someone else 1584 $self->lock_collection($username, $collect); 1585 1586 # look up additional args 1587 1588 my $infodbtype = $self->{'infodbtype'}; 1589 1590 my $archive_dir = &util::filename_cat($collect_dir,$collect,"archives"); 1591 1592 my $json_str = $self->{'json'}; 1593 my $doc_array = decode_json $json_str; 1594 1595 my $global_status = 0; 1596 my $global_mess = ""; 1597 1598 my @all_docids = (); 1599 1600 foreach my $doc_array_rec ( @$doc_array ) 1601 { 1602 my $docid = $doc_array_rec->{'docid'}; 1603 my $metaname = $doc_array_rec->{'metaname'}; 1604 my $metamode = $self->{'metamode'}; 1605 my $metavalue = $doc_array_rec->{'metavalue'}; 1606 1607 if ((!defined $metamode) || ($metamode =~ m/^\s*$/)) { 1608 # make "accumulate" the default (less destructive, as won't actually 1609 # delete any existing values) 1610 $metamode = "accumulate"; 1611 } 1612 1613 push(@all_docids,$docid); 1614 1615 # Obtain where the metadata.xml is from the archiveinfo-doc.gdb file 1616 # If the doc oid is not specified, we assume the metadata.xml is next to the specified "f" 1617 my $metadata_xml_file; 1618 my $import_filename = undef; 1619 1620 my $arcinfo_doc_filename = &dbutil::get_infodb_file_path($infodbtype, "archiveinf-doc", $archive_dir); 1621 my $doc_rec = &dbutil::read_infodb_entry($infodbtype, $arcinfo_doc_filename, $docid); 1622 1623 # This now stores the full pathname 1624 $import_filename = $doc_rec->{'src-file'}->[0]; 1625 1626 # figure out correct metadata.xml file [?] 1627 # Assuming the metadata.xml file is next to the source file 1628 # Note: This will not work if it is using the inherited metadata from the parent folder 1629 my ($import_tailname, $import_dirname) = File::Basename::fileparse($import_filename); 1630 my $metadata_xml_filename = &util::filename_cat($import_dirname,"metadata.xml"); 1631 1632 $self->edit_metadata_xml($gsdl_cgi, $metadata_xml_filename, $metaname, $metavalue, $metamode, $import_tailname); 1633 } 1634 1635 my $mess = "set-archives-metadata-array successful: Keys[ ".join(", ",@all_docids)."]\n"; 1636 $gsdl_cgi->generate_ok_message($mess); 1637 1638 # Release the lock once it is done 1639 $self->unlock_collection($username, $collect); 1640 } 1641 1299 1642 sub remove_import_metadata 1300 1643 { … … 1328 1671 if ((!defined $docid) || ($docid =~ m/^\s*$/)) 1329 1672 { 1330 die "No docid (d=...) specified.\n";1673 $gsdl_cgi->generate_error_message("No docid (d=...) specified.\n"); 1331 1674 } 1332 1675 … … 1350 1693 if((!defined $import_filename) || ($import_filename =~ m/^\s*$/)) 1351 1694 { 1352 die "There is no metadata\n";1695 $gsdl_cgi->generate_error_message("There is no metadata\n"); 1353 1696 } 1354 1697
Note:
See TracChangeset
for help on using the changeset viewer.