Index: /main/trunk/greenstone2/perllib/cgiactions/metadataaction.pm
===================================================================
--- /main/trunk/greenstone2/perllib/cgiactions/metadataaction.pm	(revision 27167)
+++ /main/trunk/greenstone2/perllib/cgiactions/metadataaction.pm	(revision 27168)
@@ -77,5 +77,5 @@
 	"set-index-metadata" => { 
 		'compulsory-args' => [ "d", "metaname", "metavalue" ],
-		'optional-args'   => [ "metapos" ] },
+		'optional-args'   => [ "metapos", "metamode" ] },
 
 	"set-archives-metadata" => { 
@@ -89,5 +89,5 @@
 	#SET METHODS (ARRAY)
 	"set-metadata-array" => { 
-		'compulsory-args' => [ "json" ],
+		'compulsory-args' => [ "where", "json" ],
 		'optional-args'   => [ ] },
 					 
@@ -97,4 +97,12 @@
 		
 	"set-import-metadata-array" => {
+		'compulsory-args' => [ "json" ],
+		'optional-args'   => [ ] },
+
+	"set-index-metadata-array" => {
+		'compulsory-args' => [ "json" ],
+		'optional-args'   => [ ] },
+	
+	"set-live-metadata-array" => {
 		'compulsory-args' => [ "json" ],
 		'optional-args'   => [ ] },
@@ -529,8 +537,9 @@
     my $docid     = $self->{'d'};
     if ((!defined $docid) || ($docid =~ m/^\s*$/)) {
-      $gsdl_cgi->generate_error("No docid (d=...) specified.");
+      $gsdl_cgi->generate_error("No docid (d=...) specified."); # generates error and dies
     }
     my $metavalue = $self->{'metavalue'};
-  
+    my $metamode  = $self->{'metamode'} || "accumulate";
+    my $append_or_not = ($metamode eq "accumulate") ? " \"append\"" : "";
 
     # Generate the dbkey    
@@ -546,5 +555,5 @@
 
     # Set the new value
-    my $cmd = "gdbmset \"$infodb_file_path\" \"$dbkey\" \"$metavalue\"";
+    my $cmd = "gdbmset \"$infodb_file_path\" \"$dbkey\" \"$metavalue\"$append_or_not";
     my $status = system($cmd);
     if ($status != 0) {
@@ -588,9 +597,9 @@
 }
 
-sub set_metadata_entry
-{
-	my $self = shift @_;
-	my ($collect_dir,$collect,$infodbtype,$docid,$metaname,$metapos,$metavalue) = @_;
-	
+sub set_index_metadata_entry
+{
+    my $self = shift @_;
+    my ($collect_dir,$collect,$infodbtype,$docid,$metaname,$metapos,$metavalue,$metamode) = @_;
+    
     # To people who know $collect_tail please add some comments
     # Obtain path to the database
@@ -602,19 +611,32 @@
 #	print STDERR "**** infodb file path = $infodb_file_path\n";
 #	print STDERR "***** infodb type = $infodbtype\n";
-	
+    
     # Read the docid entry
     my $doc_rec = &dbutil::read_infodb_entry($infodbtype, $infodb_file_path, $docid);
-	
+    
     # Set the metadata value
     if (defined $metapos) {
-		$doc_rec->{$metaname}->[$metapos] = $metavalue;
-    }
-    else {
-		$doc_rec->{$metaname} = [ $metavalue ];
+	# if metamode=accumulate AND metapos, warn user and then use metapos
+	if (defined $metamode && $metamode eq "accumulate") {
+	    print STDERR "**** Warning: metamode is set to accumulate yet metapos is also provided for $docid\n";
+	    print STDERR "**** Proceeding by using metapos\n";
+	}
+	$doc_rec->{$metaname}->[$metapos] = $metavalue;
+    }
+    elsif (defined $metamode && $metamode eq "accumulate") {
+	if(defined $doc_rec->{$metaname}) {
+	    push(@{$doc_rec->{$metaname}}, $metavalue); # accumulate the value for that metaname
+	} else {
+	    $doc_rec->{$metaname} = [ $metavalue ];
+	}
+
+    }
+    else { # default for index was to override
+	$doc_rec->{$metaname} = [ $metavalue ];
     }
  
     my $status = &dbutil::set_infodb_entry($infodbtype, $infodb_file_path,$docid,$doc_rec);
 	
-	return $status;
+    return $status;
 	
 }
@@ -728,5 +750,5 @@
 	$metamode = "accumulate";
     }
-	
+
     my $status = $self->set_archives_metadata_entry($gsdl_cgi,$archive_dir, $collect_dir,$collect, $infodbtype,$docid,
 				$metaname,$metapos,$metavalue,$metamode,$prevmetavalue);
@@ -775,6 +797,7 @@
     my $metavalue = $self->{'metavalue'};
     my $infodbtype = $self->{'infodbtype'};
-	
-	my $status = $self->set_metadata_entry($collect_dir,$collect,$infodbtype,$docid,$metaname,$metapos,$metavalue);
+    my $metamode  = $self->{'metamode'};	
+
+	my $status = $self->set_index_metadata_entry($collect_dir,$collect,$infodbtype,$docid,$metaname,$metapos,$metavalue,$metamode);
 	
     if ($status != 0) {
@@ -884,9 +907,8 @@
 	my $docid     = $self->{'d'};
 	if ((!defined $docid) || ($docid =~ m/^\s*$/)) {
-	    $gsdl_cgi->generate_error("No docid (d=...) specified.");
+	    $gsdl_cgi->generate_error("No docid (d=...) specified."); # generates error and dies
 	} 
-	else { # we have a docid, so can set archives meta
-	    $self->_set_archives_metadata(@_);
-	}
+	# we have a docid, so can set archives meta
+	$self->_set_archives_metadata(@_);	
     }
 
@@ -899,7 +921,6 @@
 	    $gsdl_cgi->generate_error("No docid (d=...) specified.");
 	}
-	else { # we have a docid, so can set index meta
-	    $self->_set_index_metadata(@_);
-	}
+	# we have a docid, so can set index meta
+	$self->_set_index_metadata(@_);	
     }	
 
@@ -915,9 +936,14 @@
 {
     my $self = shift @_;
+
+    my $where = $self->{'where'};
+    if(!$where) {	
+	$self->set_index_metadata_array(@_); # default behaviour is the full version of set_index_meta_array
+	return;
+    }
 
     my $username  = $self->{'username'};
     my $collect   = $self->{'collect'};
     my $gsdl_cgi  = $self->{'gsdl_cgi'};
-    my $gsdlhome  = $self->{'gsdlhome'};
 
     if ($baseaction::authentication_enabled) {
@@ -926,4 +952,121 @@
     }
 
+    # Not sure if the checked_chdir is necessary, since lock_collection also does a chdir
+    # But including the stmt during this code reorganisation to preserve as-is what used to happen
+    my $site = $self->{'site'};
+    my $collect_dir = $gsdl_cgi->get_collection_dir($site);
+    $gsdl_cgi->checked_chdir($collect_dir);
+
+    # Make sure the collection isn't locked by someone else
+    $self->lock_collection($username, $collect);
+
+    if($where =~ m/import/) {
+	$self->_set_import_metadata_array(@_);
+    }
+    if($where =~ m/archives/) {
+	$self->_set_archives_metadata_array(@_);
+    }
+    if($where =~ m/index/) {
+	$self->_set_index_metadata_array(@_);
+    }
+    if($where =~ m/live/) {
+    	$self->_set_live_metadata_array(@_);
+    }
+
+    # Release the lock once it is done
+    $self->unlock_collection($username, $collect);
+}
+
+sub _set_index_metadata_array
+{
+    my $self = shift @_;
+
+    my $collect   = $self->{'collect'};
+    my $gsdl_cgi  = $self->{'gsdl_cgi'};
+    my $site = $self->{'site'};
+    my $collect_dir = $gsdl_cgi->get_collection_dir($site);
+
+    
+    # look up additional args
+    
+    my $infodbtype = $self->{'infodbtype'};
+    
+    my $json_str      = $self->{'json'};
+    my $doc_array = decode_json $json_str;
+    
+    
+    my $global_status = 0;
+    my $global_mess = "";
+    
+    my @all_docids = ();
+    
+    foreach my $doc_array_rec ( @$doc_array ) {
+	
+	my $status = -1;
+	my $docid     = $doc_array_rec->{'docid'};
+	
+	push(@all_docids,$docid);
+
+	my $metaname  = $doc_array_rec->{'metaname'};
+	if(defined $metaname) {
+	    my $metapos   = $doc_array_rec->{'metapos'};
+	    my $metavalue = $doc_array_rec->{'metavalue'};
+	    my $metamode = $doc_array_rec->{'metamode'} || $self->{'metamode'};
+
+	    $status = $self->set_index_metadata_entry($collect_dir,$collect,$infodbtype,$docid,$metaname,$metapos,$metavalue,$metamode);
+	} elsif (defined $doc_array_rec->{'metatable'}) { # if no metaname, we expect a metatable
+	    my $metatable = $doc_array_rec->{'metatable'}; # a subarray, or need to generate an error saying JSON structure is wrong
+	    
+	    foreach my $metatable_rec ( @$metatable ) { # the subarray metatable is an array of hashmaps
+		$metaname  = $metatable_rec->{'metaname'};
+		my $metamode  = $metatable_rec->{'metamode'} || $doc_array_rec->{'metamode'} || $self->{'metamode'};
+		my $metapos = undef;
+		my $metavals = $metatable_rec->{'metavals'}; # a sub-subarray
+
+		foreach my $metavalue ( @$metavals ) { # metavals is an array
+		    $status = $self->set_index_metadata_entry($collect_dir,$collect,$infodbtype,$docid,$metaname,$metapos,$metavalue,$metamode); # how do we use metamode in set_meta_entry?
+		    if($metamode eq "override") { # now, having overridden the metavalue for the first, 
+			# need to accumulate subsequent metavals for this metaname, else the just-assigned
+			# metavalue for this metaname will be lost
+			$metamode = "accumulate";
+		    }
+		}			
+	    }
+	}
+	
+	if ($status != 0) {
+	    # Catch error if set infodb entry failed
+	    $global_status = $status;
+	    $global_mess .= "Failed to set metadata key: $docid\n";
+	    $global_mess .= "Exit status: $status\n";
+	    $global_mess .= "System Error Message: $!\n";
+	    $global_mess .= "-" x 20;
+	}
+    }
+    
+    if ($global_status != 0) {
+	$global_mess .= "PATH: $ENV{'PATH'}\n";
+	$gsdl_cgi->generate_error($global_mess);
+    }
+    else {
+	my $mess = "set-metadata-array successful: Keys[ ".join(", ",@all_docids)."]\n";
+	$gsdl_cgi->generate_ok_message($mess);
+    }
+}
+
+sub set_index_metadata_array
+{
+    my $self = shift @_;
+
+    my $username  = $self->{'username'};
+    my $collect   = $self->{'collect'};
+    my $gsdl_cgi  = $self->{'gsdl_cgi'};
+#    my $gsdlhome  = $self->{'gsdlhome'};
+
+    if ($baseaction::authentication_enabled) {
+	# Ensure the user is allowed to edit this collection
+	&authenticate_user($gsdl_cgi, $username, $collect);
+    }
+
 	my $site = $self->{'site'};
 	my $collect_dir = $gsdl_cgi->get_collection_dir($site);
@@ -937,47 +1080,124 @@
     $self->lock_collection($username, $collect);
 
+    $self->_set_index_metadata_array(@_);
+
+    # Release the lock once it is done
+    $self->unlock_collection($username, $collect);
+}
+
+# experimental, newly added in and untested
+sub _set_live_metadata_array
+{
+    my $self = shift @_;
+
+    my $collect   = $self->{'collect'};
+    my $gsdl_cgi  = $self->{'gsdl_cgi'};
+
+    my $site = $self->{'site'};
+    my $collect_dir = $gsdl_cgi->get_collection_dir($site);
+
+    
     # look up additional args
-	
-	my $infodbtype = $self->{'infodbtype'};
-	
-	my $json_str      = $self->{'json'};
-	my $doc_array = decode_json $json_str;
-	
-	
-	my $global_status = 0;
-	my $global_mess = "";
-	
-	my @all_docids = ();
-	
-	foreach my $doc_array_rec ( @$doc_array ) {
-		
-		my $docid     = $doc_array_rec->{'docid'};
-		my $metaname  = $doc_array_rec->{'metaname'};
-		my $metapos   = $doc_array_rec->{'metapos'};
-		my $metavalue = $doc_array_rec->{'metavalue'};
-		
-		push(@all_docids,$docid);
-		
-		my $status = $self->set_metadata_entry($collect_dir,$collect,$infodbtype,$docid,$metaname,$metapos,$metavalue);	
-		
-		if ($status != 0) {
-			# Catch error if set infodb entry failed
-			$global_status = $status;
-			$global_mess .= "Failed to set metadata key: $docid\n";
-			$global_mess .= "Exit status: $status\n";
-			$global_mess .= "System Error Message: $!\n";
-			$global_mess .= "-" x 20;
+    my $infodbtype = $self->{'infodbtype'};
+    # 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($infodbtype, "live-$collect_tail", $index_text_directory);
+
+    
+    my $json_str      = $self->{'json'};
+    my $doc_array = decode_json $json_str;
+    
+    
+    my $global_status = 0;
+    my $global_mess = "";
+    
+    my @all_docids = ();
+
+
+    foreach my $doc_array_rec ( @$doc_array ) {
+	
+	my $status = -1;
+	my $docid     = $doc_array_rec->{'docid'};
+
+	push(@all_docids,$docid);
+
+	my $metaname  = $doc_array_rec->{'metaname'};
+	if(defined $metaname) {
+	    my $dbkey = "$docid.$metaname";
+	    my $metavalue = $doc_array_rec->{'metavalue'};
+	    # no metapos for live_meta, so can default metamode to accumulate
+	    my $metamode  = $doc_array_rec->{'metamode'} || $self->{'metamode'} || "accumulate";
+	
+	    my $append_or_not = ($metamode eq "accumulate") ? " \"append\"" : "";
+
+	    # Set the new value
+	    my $cmd = "gdbmset \"$infodb_file_path\" \"$dbkey\" \"$metavalue\"$append_or_not";
+	    $status = system($cmd);
+
+	} elsif (defined $doc_array_rec->{'metatable'}) { # if no metaname, we expect a metatable
+	    my $metatable = $doc_array_rec->{'metatable'}; # a subarray, or need to generate an error saying JSON structure is wrong
+	    foreach my $metatable_rec ( @$metatable ) {
+		$metaname  = $metatable_rec->{'metaname'};
+		my $metamode = $metatable_rec->{'metamode'} || $doc_array_rec->{'metamode'} || $self->{'metamode'} || "accumulate";
+		my $append_or_not = ($metamode eq "accumulate") ? " \"append\"" : "";
+		my $dbkey = "$docid.$metaname";
+
+		my $metavals = $metatable_rec->{'metavals'}; # a sub-subarray
+		foreach my $metavalue ( @$metavals ) {
+		     my $cmd = "gdbmset \"$infodb_file_path\" \"$dbkey\" \"$metavalue\"$append_or_not";
+		     $status = system($cmd);
+		     $append_or_not = " \"append\"";  # even if metamode=override, need to accumulate all subsequent 
+		     # metavals for this metaname, else the just-assigned metavalue for this metaname will be lost
 		}
-	}
-
-	if ($global_status != 0) {
-		$global_mess .= "PATH: $ENV{'PATH'}\n";
-		$gsdl_cgi->generate_error($global_mess);
+	    }
+	    
+	}
+
+	if ($status != 0) {
+	    # Catch error if gdbmget failed
+	    $global_status = $status;
+	    $global_mess .= "Failed to set metadata key: $docid\n"; # $dbkey
+	    $global_mess .= "Exit status: $status\n";
+	    $global_mess .= "System Error Message: $!\n";
+	    $global_mess .= "-" x 20;
+	}
+    }
+    
+    if ($global_status != 0) {
+	$global_mess .= "PATH: $ENV{'PATH'}\n";
+	$gsdl_cgi->generate_error($global_mess);
     }
     else {
-		my $mess = "set-metadata-array successful: Keys[ ".join(", ",@all_docids)."]\n";
-		$gsdl_cgi->generate_ok_message($mess);
-    }
-    
+	my $mess = "set-live-metadata-array successful: Keys[ ".join(", ",@all_docids)."]\n";
+	$gsdl_cgi->generate_ok_message($mess);
+    }
+}
+
+sub set_live_metadata_array
+{
+    my $self = shift @_;
+
+    my $username  = $self->{'username'};
+    my $collect   = $self->{'collect'};
+    my $gsdl_cgi  = $self->{'gsdl_cgi'};
+
+    if ($baseaction::authentication_enabled) {
+	# Ensure the user is allowed to edit this collection
+	&authenticate_user($gsdl_cgi, $username, $collect);
+    }
+
+	my $site = $self->{'site'};
+	my $collect_dir = $gsdl_cgi->get_collection_dir($site);
+	
+    $gsdl_cgi->checked_chdir($collect_dir);
+
+    # Make sure the collection isn't locked by someone else
+    $self->lock_collection($username, $collect);
+
+    $self->_set_live_metadata_array(@_);
+
     # Release the lock once it is done
     $self->unlock_collection($username, $collect);
@@ -1321,5 +1541,5 @@
 	$self->remove_from_doc_xml($gsdl_cgi, &util::filename_cat($archive_dir, $doc_xml_file), $metaname, $metapos, undef, $docid_secnum, $metamode);
     }
-    
+
     # Edit the doc.xml file with the specified metadata name, value and position.
     # TODO: there is a potential problem here as this edit_doc_xml function 
@@ -1327,4 +1547,5 @@
     # Running import.pl -groupsize will cause this to have multiple sections in one doc.xml
 	
+    # dxml_metadata method ignores metapos if metamode anything other than override
     $self->edit_doc_xml($gsdl_cgi,$doc_xml_filename,
 			$metaname,$metavalue,$metapos,$metamode,$docid_secnum,$prevmetavalue);
@@ -1344,6 +1565,6 @@
 	
     if ($baseaction::authentication_enabled) {
-		# Ensure the user is allowed to edit this collection
-		$self->authenticate_user($username, $collect);
+	# Ensure the user is allowed to edit this collection
+	$self->authenticate_user($username, $collect);
     }
 
@@ -1357,4 +1578,95 @@
 }
 
+sub _set_archives_metadata_array
+{
+    my $self = shift @_;
+    
+    my $collect   = $self->{'collect'};
+    my $gsdl_cgi  = $self->{'gsdl_cgi'};
+    my $site = $self->{'site'};
+    my $collect_dir = $gsdl_cgi->get_collection_dir($site);
+
+    # look up additional args
+	
+    my $infodbtype = $self->{'infodbtype'};
+    
+    my $archive_dir = &util::filename_cat($collect_dir,$collect,"archives");
+	
+    my $json_str      = $self->{'json'};
+    my $doc_array = decode_json $json_str;
+    
+    
+    my $global_status = 0;
+    my $global_mess = "";
+    
+    my @all_docids = ();
+    
+    foreach my $doc_array_rec ( @$doc_array ) {
+	my $status    = -1;
+	my $docid     = $doc_array_rec->{'docid'};
+
+	push(@all_docids,$docid);
+	
+	my $metaname  = $doc_array_rec->{'metaname'};
+	if(defined $metaname) {
+	    
+	    my $metapos   = $doc_array_rec->{'metapos'} || 0;
+	    my $metamode  = $doc_array_rec->{'metamode'} || $self->{'metamode'};
+	    my $metavalue = $doc_array_rec->{'metavalue'};
+	    my $prevmetavalue = $self->{'prevmetavalue'}; # to make this sub behave as _set_archives_metadata
+	    
+	    # Some sanity checks
+	    $metapos = 0 if (!defined $metapos);
+	    
+	    if ((!defined $metamode) || ($metamode =~ m/^\s*$/)) {
+		# make "accumulate" the default (less destructive, as it won't actually 
+		# delete any existing values)
+		$metamode = "accumulate";
+	    }		
+	    
+	    $status = $self->set_archives_metadata_entry($gsdl_cgi,$archive_dir, $collect_dir,$collect, $infodbtype,$docid,
+				$metaname,$metapos,$metavalue,$metamode,$prevmetavalue);
+	} elsif (defined $doc_array_rec->{'metatable'}) { # if no metaname, we expect a metatable
+	    my $metatable = $doc_array_rec->{'metatable'}; # a subarray, or need to generate an error saying JSON structure is wrong
+	    
+	    foreach my $metatable_rec ( @$metatable ) {
+		$metaname  = $metatable_rec->{'metaname'};
+		my $metamode  = $metatable_rec->{'metamode'} || $doc_array_rec->{'metamode'} || $self->{'metamode'};
+		my $metapos = undef;
+		my $prevmetavalue = undef;
+		my $metavals = $metatable_rec->{'metavals'}; # a sub-subarray
+		
+		foreach my $metavalue ( @$metavals ) {
+		    $status = $self->set_archives_metadata_entry($gsdl_cgi,$archive_dir, $collect_dir,$collect,$infodbtype,
+								 $docid,$metaname,$metapos,$metavalue,$metamode,$prevmetavalue);
+		    
+		    if($metamode eq "override") { # now, having overridden the metavalue for the first, 
+			# need to accumulate subsequent metavals for this metaname, else the just-assigned
+			# metavalue for this metaname will be lost
+			$metamode = "accumulate";
+		    }
+		}			
+	    }		
+	}
+	    
+	if ($status != 0) {
+	    # Catch error if set infodb entry failed
+	    $global_status = $status;
+	    $global_mess .= "Failed to set metadata key: $docid\n";
+	    $global_mess .= "Exit status: $status\n";
+	    $global_mess .= "System Error Message: $!\n";
+	    $global_mess .= "-" x 20 . "\n";
+	}
+    }
+    
+    if ($global_status != 0) {
+	$global_mess .= "PATH: $ENV{'PATH'}\n";
+	$gsdl_cgi->generate_error($global_mess);
+    }
+    else {
+	my $mess = "set-archives-metadata-array successful: Keys[ ".join(", ",@all_docids)."]\n";
+	$gsdl_cgi->generate_ok_message($mess);
+    }
+}
 
 sub set_archives_metadata_array
@@ -1365,5 +1677,5 @@
     my $collect   = $self->{'collect'};
     my $gsdl_cgi  = $self->{'gsdl_cgi'};
-    my $gsdlhome  = $self->{'gsdlhome'};
+#    my $gsdlhome  = $self->{'gsdlhome'};
 
     if ($baseaction::authentication_enabled) {
@@ -1383,59 +1695,5 @@
     $self->lock_collection($username, $collect);
 
-    # look up additional args
-	
-	my $infodbtype = $self->{'infodbtype'};
-
-   my $archive_dir = &util::filename_cat($collect_dir,$collect,"archives");
-	
-	my $json_str      = $self->{'json'};
-	my $doc_array = decode_json $json_str;
-	
-	
-	my $global_status = 0;
-	my $global_mess = "";
-	
-	my @all_docids = ();
-	
-	foreach my $doc_array_rec ( @$doc_array ) {
-		
-		my $docid     = $doc_array_rec->{'docid'};
-		my $metaname  = $doc_array_rec->{'metaname'};
-		my $metapos   = $doc_array_rec->{'metapos'};
-		my $metamode   = $self->{'metamode'};
-		my $metavalue = $doc_array_rec->{'metavalue'};
-		
-		# Some sanity checks
-		$metapos = 0 if (!defined $metapos);
-			
-		if ((!defined $metamode) || ($metamode =~ m/^\s*$/)) {
-			# make "accumulate" the default (less destructive, as won't actually 
-			# delete any existing values)
-			$metamode = "accumulate";
-		} 
-	
-		push(@all_docids,$docid);
-		
-		my $status = $self->set_archives_metadata_entry($gsdl_cgi,$archive_dir, $collect_dir,$collect, $infodbtype,$docid,
-				$metaname,$metapos,$metavalue,$metamode);
-		
-		if ($status != 0) {
-			# Catch error if set infodb entry failed
-			$global_status = $status;
-			$global_mess .= "Failed to set metadata key: $docid\n";
-			$global_mess .= "Exit status: $status\n";
-			$global_mess .= "System Error Message: $!\n";
-			$global_mess .= "-" x 20 . "\n";
-		}
-	}
-
-	if ($global_status != 0) {
-		$global_mess .= "PATH: $ENV{'PATH'}\n";
-		$gsdl_cgi->generate_error($global_mess);
-    }
-    else {
-		my $mess = "set-archives-metadata-array successful: Keys[ ".join(", ",@all_docids)."]\n";
-		$gsdl_cgi->generate_ok_message($mess);
-    }
+    $self->_set_archives_metadata_array(@_);
     
     # Release the lock once it is done
@@ -1464,4 +1722,5 @@
 	my $metaname = $self->{'metaname'};
 	my $metapos = $self->{'metapos'};
+
 	my $metavalue = $self->{'metavalue'} || undef; # necessary to force fallback to undef here
 
@@ -1476,6 +1735,6 @@
 	}
 
-	my $metamode = $self->{'metamode'} || undef;	
-	
+	my $metamode = $self->{'metamode'} || undef;
+
 	my $arcinfo_doc_filename = &dbutil::get_infodb_file_path($infodbtype, "archiveinf-doc", $archive_dir);
 	my $doc_rec = &dbutil::read_infodb_entry($infodbtype, $arcinfo_doc_filename, $docid);
@@ -1606,5 +1865,5 @@
 			$parser->{'parameters'}->{'poscount'}++;
 		}
-
+		
 		# if overriding but no metapos, then clear all the meta for this metaname
 		if ((defined $parser->{'parameters'}->{'metamode'}) && ($parser->{'parameters'}->{'metamode'} eq "override") && (!defined $parser->{'parameters'}->{'metapos'})) {		    
@@ -1612,10 +1871,10 @@
 		}
 	}
-	
+
 	if ((defined $parser->{'parameters'}->{'metapos'}) && ($parser->{'parameters'}->{'metaname'} eq $attrHash->{'name'}) && ($parser->{'parameters'}->{'poscount'} == $parser->{'parameters'}->{'metapos'}))
 	{	
 		return [];
 	}
-	
+
 	if ((defined $parser->{'parameters'}->{'metavalue'}) && ($parser->{'parameters'}->{'metaname'} eq $attrHash->{'name'}) && ($parser->{'parameters'}->{'metavalue'} eq $attrHash->{'_content'}))
 	{	
@@ -1866,19 +2125,19 @@
 sub set_import_metadata_array
 {
-	my $self = shift @_;
+    my $self = shift @_;
 
     my $username  = $self->{'username'};
     my $collect   = $self->{'collect'};
     my $gsdl_cgi  = $self->{'gsdl_cgi'};
-    my $gsdlhome  = $self->{'gsdlhome'};
+#    my $gsdlhome  = $self->{'gsdlhome'};
 
     if ($baseaction::authentication_enabled) {
-		# Ensure the user is allowed to edit this collection
-		&authenticate_user($gsdl_cgi, $username, $collect);
-    }
-
-	my $site = $self->{'site'};
-	my $collect_dir = $gsdl_cgi->get_collection_dir($site);
-	
+	# Ensure the user is allowed to edit this collection
+	&authenticate_user($gsdl_cgi, $username, $collect);
+    }
+
+    my $site = $self->{'site'};
+    my $collect_dir = $gsdl_cgi->get_collection_dir($site);
+    
     $gsdl_cgi->checked_chdir($collect_dir);
 
@@ -1886,58 +2145,112 @@
     $self->lock_collection($username, $collect);
 
-    # look up additional args
-	
-	my $infodbtype = $self->{'infodbtype'};
-
-	my $archive_dir = &util::filename_cat($collect_dir,$collect,"archives");
-	
-	my $json_str = $self->{'json'};
-	my $doc_array = decode_json $json_str;
-	
-	my $global_status = 0;
-	my $global_mess = "";
-	
-	my @all_docids = ();
-	
-	foreach my $doc_array_rec ( @$doc_array ) 
-	{	
-		my $docid = $doc_array_rec->{'docid'};
-		my $metaname = $doc_array_rec->{'metaname'};
-		my $metamode = $self->{'metamode'};
-		my $metavalue = $doc_array_rec->{'metavalue'};
-
-		if ((!defined $metamode) || ($metamode =~ m/^\s*$/)) {
-			# make "accumulate" the default (less destructive, as won't actually 
-			# delete any existing values)
-			$metamode = "accumulate";
-		} 
-
-		push(@all_docids,$docid);
-
-		# 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 $import_filename = undef;
-
-		my $arcinfo_doc_filename = &dbutil::get_infodb_file_path($infodbtype, "archiveinf-doc", $archive_dir);
-		my $doc_rec = &dbutil::read_infodb_entry($infodbtype, $arcinfo_doc_filename, $docid);
-
-		# This now stores the full pathname
-		$import_filename = $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");
-
-		$self->edit_metadata_xml($gsdl_cgi, $metadata_xml_filename, $metaname, $metavalue, $metamode, $import_tailname);
-	}
-
-	my $mess = "set-archives-metadata-array successful: Keys[ ".join(", ",@all_docids)."]\n";
-	$gsdl_cgi->generate_ok_message($mess);
-    
+    $self->_set_import_metadata_array(@_);
+
     # Release the lock once it is done
     $self->unlock_collection($username, $collect);
+
+}
+
+
+sub _set_import_metadata_array
+{
+    my $self = shift @_;
+
+    my $collect   = $self->{'collect'};
+    my $gsdl_cgi  = $self->{'gsdl_cgi'};
+
+    my $site = $self->{'site'};
+    my $collect_dir = $gsdl_cgi->get_collection_dir($site);
+	
+    # look up additional args
+	
+    my $infodbtype = $self->{'infodbtype'};
+    
+    my $archive_dir = &util::filename_cat($collect_dir,$collect,"archives");	
+    my $arcinfo_doc_filename = &dbutil::get_infodb_file_path($infodbtype, "archiveinf-doc", $archive_dir);
+    
+    my $json_str = $self->{'json'};
+    my $doc_array = decode_json $json_str;
+    
+    my $global_status = 0;
+    my $global_mess = "";
+    
+    my @all_docids = ();
+    
+    foreach my $doc_array_rec ( @$doc_array ) 
+    {
+	my $status = -1;
+	my $docid = $doc_array_rec->{'docid'};
+	
+	push(@all_docids,$docid);
+	
+	my $metaname = $doc_array_rec->{'metaname'};
+	if (defined $metaname) {
+	    my $metamode = $doc_array_rec->{'metamode'} || $self->{'metamode'};
+	    my $metavalue = $doc_array_rec->{'metavalue'};
+	    
+	    if ((!defined $metamode) || ($metamode =~ m/^\s*$/)) {
+		# make "accumulate" the default (less destructive, as won't actually 
+		# delete any existing values)
+		$metamode = "accumulate";
+	    }
+	    
+	    $self->set_import_metadata_entry($gsdl_cgi, $arcinfo_doc_filename, $infodbtype, $docid, $metaname, $metavalue, $metamode);
+	    
+	} elsif (defined $doc_array_rec->{'metatable'}) { # if no metaname, we expect a metatable
+	    my $metatable = $doc_array_rec->{'metatable'}; # a subarray, or need to generate an error saying JSON structure is wrong
+	    
+	    foreach my $metatable_rec ( @$metatable ) {
+		$metaname  = $metatable_rec->{'metaname'}; 
+		my $metamode  = $metatable_rec->{'metamode'} || $doc_array_rec->{'metamode'} || $self->{'metamode'};
+		if ((!defined $metamode) || ($metamode =~ m/^\s*$/)) {
+		    # make "accumulate" the default (less destructive, as won't actually 
+		    # delete any existing values)
+		    $metamode = "accumulate";
+		}
+		# no metapos for import
+		my $metavals = $metatable_rec->{'metavals'}; # a sub-subarray
+		
+		foreach my $metavalue ( @$metavals ) {
+		    $self->set_import_metadata_entry($gsdl_cgi, $arcinfo_doc_filename, $infodbtype, $docid, $metaname, $metavalue, $metamode);
+		    if($metamode eq "override") { # now, having overridden the first metavalue of the metaname, 
+			# need to accumulate subsequent metavals for this metaname, else the just-assigned
+			# metavalue for this metaname will be lost
+			$metamode = "accumulate";
+		    }
+		}
+	    }
+	}		
+    }
+
+    # always a success message
+    my $mess = "set-archives-metadata-array successful: Keys[ ".join(", ",@all_docids)."]\n";
+    $gsdl_cgi->generate_ok_message($mess);
+}
+
+# always returns true (1)
+sub set_import_metadata_entry
+{
+    my $self = shift @_;
+    my ($gsdl_cgi, $arcinfo_doc_filename, $infodbtype, $docid, $metaname, $metavalue, $metamode) = @_;
+
+    # 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 $import_filename = undef;
+    
+    my $doc_rec = &dbutil::read_infodb_entry($infodbtype, $arcinfo_doc_filename, $docid);
+
+    # This now stores the full pathname
+    $import_filename = $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");
+    
+    $self->edit_metadata_xml($gsdl_cgi, $metadata_xml_filename, $metaname, $metavalue, $metamode, $import_tailname);
+    return 1;
 }
 
@@ -1948,5 +2261,5 @@
 	my $collect   = $self->{'collect'};
 	my $gsdl_cgi  = $self->{'gsdl_cgi'};
-	my $gsdlhome  = $self->{'gsdlhome'};
+#	my $gsdlhome  = $self->{'gsdlhome'};
 	my $infodbtype = $self->{'infodbtype'};
 	
@@ -2263,4 +2576,5 @@
 	    $metapos = 0;
     }
+    
 
     # consider check key is defined before deleting?
