Index: /main/trunk/greenstone2/perllib/cgiactions/metadataaction.pm
===================================================================
--- /main/trunk/greenstone2/perllib/cgiactions/metadataaction.pm	(revision 21715)
+++ /main/trunk/greenstone2/perllib/cgiactions/metadataaction.pm	(revision 21716)
@@ -72,5 +72,9 @@
 
     "remove-metadata"       => { 'compulsory-args' => [ "d", "metaname" ],
-			         'optional-args'   => [ "metapos" ] }
+			         'optional-args'   => [ "metapos" ] },
+
+    "insert-metadata"       => { 'compulsory-args' => [ "d", "metaname", "metavalue" ],
+			         'optional-args'   => [ ]
+			       }
 };
 
@@ -383,7 +387,6 @@
     }
 
-    # raw extended
-    # Someone please write some comments on why adding ':'.$tagname => $attrHash
-    return (':'.$tagname => $attrHash, [$tagname => $attrHash]);
+    # RAW is [$tagname => $attrHash] not $tagname => $attrHash!!
+    return [$tagname => $attrHash];
 }
 
@@ -412,6 +415,7 @@
     }
 
-    # raw
-    return $tagname => $attrHash;
+
+    # RAW is [$tagname => $attrHash] not $tagname => $attrHash!!
+    return [$tagname => $attrHash];
 }
 
@@ -461,5 +465,5 @@
     # Set the call back functions
     my @rules = 
-	( _default => 'raw extended',
+	( _default => 'raw',
 	  'Metadata' => \&dxml_metadata,
 	  'Description' => \&dxml_description );
@@ -530,4 +534,11 @@
     my $metamode = $parser->{'parameters'}->{'metamode'};
 
+    # Report error if we don't see FileName tag before this
+    die "Fatel Error: Unexpected metadata.xml structure. Undefind current_file, possiblely encountered Description before FileName" if (!defined($parser->{'parameters'}->{'current_file'}));
+    
+    # Don't do anything if we are not in the right FileSet
+    my $file_regexp = $parser->{'parameters'}->{'current_file'};
+    return [$tagname => $attrHash] if (!($parser->{'parameters'}->{'src_file'} =~ /$file_regexp/));
+    
     # Find the right metadata tag and checks if we are going to override it
     # Note: This over writes the first metadata block it encountered even if it doesn't belong to the source file we specified
@@ -542,7 +553,6 @@
     }
 
-    # raw extended
-    # Someone please write some comments on why adding ':'.$tagname => $attrHash
-    return (':'.$tagname => $attrHash, [$tagname => $attrHash]);
+    # RAW is [$tagname => $attrHash] not $tagname => $attrHash!!
+    return [$tagname => $attrHash];
 }
 
@@ -551,5 +561,12 @@
 {
     my ($tagname, $attrHash, $contextArray, $parentDataArray, $parser) = @_;
-    my $metamode = $parser->{'parameters'}->{'metamode'};
+    my $metamode = $parser->{'parameters'}->{'metamode'};    
+
+    # Failed... Report error if we don't see FileName tag before this
+    die "Fatel Error: Unexpected metadata.xml structure. Undefind current_file, possiblely encountered Description before FileName" if (!defined($parser->{'parameters'}->{'current_file'}));
+
+    # Don't do anything if we are not in the right FileSet
+    my $file_regexp = $parser->{'parameters'}->{'current_file'};
+    return [$tagname => $attrHash] if (!($parser->{'parameters'}->{'src_file'} =~ /$file_regexp/));
 
     # Accumulate the metadata block to the end of the description block
@@ -571,6 +588,34 @@
     }
 
-    # raw
-    return $tagname => $attrHash;
+    # RAW is [$tagname => $attrHash] not $tagname => $attrHash!!
+    return [$tagname => $attrHash];
+}
+
+
+sub mxml_filename
+{
+    my ($tagname, $attrHash, $contextArray, $parentDataArray, $parser) = @_;
+
+    # Store the filename of the Current Fileset
+    # Note: According to http://greenstone.org/dtd/DirectoryMetadata/1.0/DirectoryMetadata.dtd
+    # FileName tag must come before Description tag
+    $parser->{'parameters'}->{'current_file'} = $attrHash->{'_content'};
+
+    # RAW is [$tagname => $attrHash] not $tagname => $attrHash!!
+    return [$tagname => $attrHash];
+}
+
+
+sub mxml_fileset
+{
+    my ($tagname, $attrHash, $contextArray, $parentDataArray, $parser) = @_;
+
+    # Initilise the current_file
+    # Note: According to http://greenstone.org/dtd/DirectoryMetadata/1.0/DirectoryMetadata.dtd
+    # FileName tag must come before Description tag
+    $parser->{'parameters'}->{'current_file'} = "";
+
+    # RAW is [$tagname => $attrHash] not $tagname => $attrHash!!
+    return [$tagname => $attrHash];
 }
 
@@ -579,15 +624,18 @@
 {
     my $self = shift @_;
-    my ($gsdl_cgi, $metadata_xml_filename, $metaname, $metavalue, $metamode) = @_;
+    my ($gsdl_cgi, $metadata_xml_filename, $metaname, $metavalue, $metamode, $src_file) = @_;
 
     # Set the call-back functions for the metadata tags
     my @rules = 
-	( _default => 'raw extended',
+	( _default => 'raw',
+          'FileName' => \&mxml_filename,
 	  'Metadata' => \&mxml_metadata,
-	  'Description' => \&mxml_description );
+	  'Description' => \&mxml_description,
+          'FileSet' => \&mxml_fileset);
 
     # use XML::Rules to add it in (read in and out again)
     my $parser = XML::Rules->new(rules => \@rules, 
-				 style => 'filter' );
+				 style => 'filter',
+                                 output_encoding => 'utf8');
 
     my $xml_in = "";
@@ -607,10 +655,16 @@
 	$parser->filter($xml_in,\$xml_out, { metaname => $metaname,
 					     metavalue => $metavalue,
-                                             metamode => $metamode } );
-        
+                                             metamode => $metamode,
+					     src_file => $src_file,
+					     current_file => undef} );
+
 	if (!open(MOUT,">$metadata_xml_filename")) {
 	    $gsdl_cgi->generate_error("Unable to write out to $metadata_xml_filename: $!");
 	}
 	else {
+            # Some wise person please find out how to keep the DTD and encode lines in after it gets filtered by this XML::Rules
+            # At the moment, I will just hack it!
+            my $header_with_utf8_dtd = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE DirectoryMetadata SYSTEM \"http://greenstone.org/dtd/DirectoryMetadata/1.0/DirectoryMetadata.dtd\">";
+            $xml_out =~ s/\<\?xml\sversion\=\"1.0\"\?\>/$header_with_utf8_dtd/;
 	    print MOUT $xml_out;
 	    close(MOUT);	    
@@ -641,5 +695,5 @@
     # Make sure the collection isn't locked by someone else
     $self->lock_collection($username, $collect);
-
+    
     # look up additional args
     # want either d= or f=
@@ -675,6 +729,5 @@
         $import_filename = &util::filename_cat($collect_dir,$collect,$import_file);
     }
-    
-    
+        
     # figure out correct metadata.xml file [?]
     # Assuming the metadata.xml file is next to the source file
@@ -685,9 +738,11 @@
 
     # Edit the metadata.xml
-    # Note: At moment it doesn't correctly on metadata.xml with multiple FileSets
-    # "accumulate" mode will add new metadata block to all FileSets
-    # "override" mode will over write the first encountered metadata block, even if it doesn't have the right source
-    $self->edit_metadata_xml($gsdl_cgi,$metadata_xml_filename,
-			     $metaname,$metavalue,$metamode);
+    # Modified by Jeffrey from DL Consulting
+    # Handle the case where there is one metadata.xml file for multiple FileSets
+    # The XML filter needs to know whether it is in the right FileSet
+    # TODO: This doesn't fix the problem where the metadata.xml is not next to the src file.
+    # TODO: This doesn't handle the common metadata (where FileName doesn't point to a single file)
+    $self->edit_metadata_xml($gsdl_cgi, $metadata_xml_filename,
+                             $metaname, $metavalue, $metamode, $import_tailname);
 
     # Release the lock once it is done
@@ -859,3 +914,174 @@
 
 
+# Was trying to reused the codes, but the functions need to be broken down more before they can be reused, otherwise there will be too much overhead and duplicate process...
+sub insert_metadata
+{
+    my $self = shift @_;
+    
+    my $username  = $self->{'username'};
+    my $collect   = $self->{'collect'};
+    my $gsdl_cgi  = $self->{'gsdl_cgi'};
+    my $gsdlhome  = $self->{'gsdlhome'};
+
+    # If the import metadata and gdbm database have been updated, we need to insert some notification to warn user that the the text they see at the moment is not indexed and require a rebuild.
+    my $rebuild_pending_macro = "_rebuildpendingmessage_";
+
+    # don't user authenticate for now
+    if ($baseaction::authentication_enabled) {
+	# Ensure the user is allowed to edit this collection
+	$self->authenticate_user($username, $collect);
+    }
+
+    # Obtain the collect and archive dir    
+    my $collect_dir = &util::filename_cat($gsdlhome, "collect");
+    my $archive_dir = &util::filename_cat($collect_dir,$collect,"archives");
+
+    # Make sure the collection isn't locked by someone else
+    $self->lock_collection($username, $collect);
+    
+    # Check additional args
+    my $docid = $self->{'d'};
+    if (!defined($docid)) {
+	$gsdl_cgi->generate_error("No document id is specified: d=...");
+    } 
+    my $metaname = $self->{'metaname'};
+    if (!defined($metaname)) {
+	$gsdl_cgi->generate_error("No metaname is specified: metadataname=...");
+    } 
+    my $metavalue = $self->{'metavalue'};
+    if (!defined($metavalue) || $metavalue eq "") {
+	$gsdl_cgi->generate_error("No metavalue or empty metavalue is specified: metadataname=...");
+    } 
+    # make "accumulate" the default (less destructive, as won't actually 
+    # delete any existing values)
+    my $metamode = "accumulate";
+
+    #=======================================================================#
+    # set_import_metadata [START]
+    #=======================================================================#
+    # Obtain where the metadata.xml is from the archiveinfo-doc.gdb file
+    # If the doc oid is not specified, we assume the metadata.xml is next to the specified "f"
+    my $metadata_xml_file;
+    my $arcinfo_doc_filename = &dbutil::get_infodb_file_path("gdbm", "archiveinf-doc", $archive_dir);
+    my $archive_doc_rec_string = &dbutil::read_infodb_entry("gdbm", $arcinfo_doc_filename, $docid);
+    my $archive_doc_rec = &dbutil::convert_infodb_string_to_hash($archive_doc_rec_string);
+    
+    # This now stores the full pathname
+    my $import_filename = $archive_doc_rec->{'src-file'}->[0];
+    
+    # figure out correct metadata.xml file [?]
+    # Assuming the metadata.xml file is next to the source file
+    # Note: This will not work if it is using the inherited metadata from the parent folder
+    my ($import_tailname, $import_dirname) 
+	= File::Basename::fileparse($import_filename);
+    my $metadata_xml_filename = &util::filename_cat($import_dirname,"metadata.xml");
+
+    # Shane's escape characters
+    $metavalue = pack "U0C*", unpack "C*", $metavalue;
+    $metavalue =~ s/\,/&#44;/g;
+    $metavalue =~ s/\:/&#58;/g;
+    $metavalue =~ s/\|/&#124;/g;
+    $metavalue =~ s/\(/&#40;/g;
+    $metavalue =~ s/\)/&#41;/g;
+    $metavalue =~ s/\[/&#91;/g;
+    $metavalue =~ s/\\/&#92;/g;
+    $metavalue =~ s/\]/&#93;/g;
+    $metavalue =~ s/\{/&#123;/g;
+    $metavalue =~ s/\}/&#125;/g;
+    $metavalue =~ s/\"/&#34;/g;
+    $metavalue =~ s/\`/&#96;/g;
+    $metavalue =~ s/\n/_newline_/g;
+
+    # Edit the metadata.xml
+    # Modified by Jeffrey from DL Consulting
+    # Handle the case where there is one metadata.xml file for multiple FileSets
+    # The XML filter needs to know whether it is in the right FileSet
+    # TODO: This doesn't fix the problem where the metadata.xml is not next to the src file.
+    # TODO: This doesn't handle the common metadata (where FileName doesn't point to a single file)
+    $self->edit_metadata_xml($gsdl_cgi, $metadata_xml_filename,
+                             $metaname, $metavalue, $metamode, $import_tailname);
+    #=======================================================================#
+    # set_import_metadata [END]
+    #=======================================================================#
+
+
+    #=======================================================================#
+    # set_metadata (accumulate version) [START]
+    #=======================================================================#
+    # To people who know $collect_tail please add some comments
+    # Obtain path to the database
+    my $collect_tail = $collect;
+    $collect_tail =~ s/^.*[\/\\]//;
+    my $index_text_directory = &util::filename_cat($collect_dir,$collect,"index","text");
+    my $infodb_file_path = &dbutil::get_infodb_file_path("gdbm", $collect_tail, $index_text_directory);
+
+    # Read the docid entry
+    my $doc_rec_string = &dbutil::read_infodb_entry("gdbm", $infodb_file_path, $docid);
+    my $doc_rec = &dbutil::convert_infodb_string_to_hash($doc_rec_string);
+    foreach my $k (keys %$doc_rec) {
+	my @escaped_v = ();
+	foreach my $v (@{$doc_rec->{$k}}) {
+	    if ($k eq "contains") {
+		# protect quotes in ".2;".3 etc
+		$v =~ s/\"/\\\"/g;
+		push(@escaped_v, $v);
+	    }
+	    else {
+		my $ev = &ghtml::unescape_html($v);
+		$ev =~ s/\"/\\\"/g;
+		push(@escaped_v, $ev);
+	    }
+	}
+	$doc_rec->{$k} = \@escaped_v;
+    }
+
+    # Protect the quotes
+    $metavalue =~ s/\"/\\\"/g;
+
+    # Adds the pending macro
+    my $macro_metavalue = $rebuild_pending_macro . $metavalue;
+
+    # If the metadata doesn't exist, create a new one
+    if (!defined($doc_rec->{$metaname})){    
+	$doc_rec->{$metaname} = [ $macro_metavalue ];
+    }
+    # Else, let's acculumate the values
+    else {
+        push(@{$doc_rec->{$metaname}},$macro_metavalue);
+    }
+
+    # Generate the record string
+    my $serialized_doc_rec = &dbutil::convert_infodb_hash_to_string($doc_rec);
+
+    # Store it into GDBM
+    my $cmd = "gdbmset \"$infodb_file_path\" \"$docid\" \"$serialized_doc_rec\"";
+    my $status = system($cmd);
+    if ($status != 0) {
+        # Catch error if gdbmget failed
+	my $mess = "Failed to set metadata key: $docid\n";
+	
+	$mess .= "PATH: $ENV{'PATH'}\n";
+	$mess .= "cmd = $cmd\n";
+	$mess .= "Exit status: $status\n";
+	$mess .= "System Error Message: $!\n";
+
+	$gsdl_cgi->generate_error($mess);
+    }
+    else {
+	my $mess = "insert-metadata successful: Key[$docid]\n";
+	$mess .= "  [In metadata.xml] $metaname";
+	$mess .= " = $metavalue\n";
+	$mess .= "  [In database] $metaname";
+	$mess .= " = $macro_metavalue\n";
+	$mess .= "  The new text has not been indexed, rebuilding collection is required\n";
+        $gsdl_cgi->generate_ok_message($mess);
+    }    
+    #=======================================================================#
+    # set_metadata (accumulate version) [END]
+    #=======================================================================#
+
+    # Release the lock once it is done
+    $self->unlock_collection($username, $collect);
+}
+
 1;
