Index: main/trunk/model-sites-dev/atea/collect/voxel-vr/ACTIVATE.sh
===================================================================
--- main/trunk/model-sites-dev/atea/collect/voxel-vr/ACTIVATE.sh	(revision 34473)
+++ main/trunk/model-sites-dev/atea/collect/voxel-vr/ACTIVATE.sh	(revision 34473)
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+cwd=`pwd`
+
+col=${cwd##*/}
+
+site_with_subdirs=${cwd##*/sites/}
+site=${site_with_subdirs%%/*}
+
+library_url=http://localhost:8383/greenstone3/library
+
+
+sudo -u www-data -- \
+     bash -c "pushd ../../../../../ && . gs3-setup.sh && popd && activate.pl -site $site -library_url $library_url $col"
+
+
Index: main/trunk/model-sites-dev/atea/collect/voxel-vr/BUILDCOL.sh
===================================================================
--- main/trunk/model-sites-dev/atea/collect/voxel-vr/BUILDCOL.sh	(revision 34473)
+++ main/trunk/model-sites-dev/atea/collect/voxel-vr/BUILDCOL.sh	(revision 34473)
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+cwd=`pwd`
+
+col=${cwd##*/}
+
+site_with_subdirs=${cwd##*/sites/}
+site=${site_with_subdirs%%/*}
+
+
+sudo -u www-data -- \
+     bash -c "pushd ../../../../../ && . gs3-setup.sh && popd && buildcol.pl -removeold -site $site $col $*"
Index: main/trunk/model-sites-dev/atea/collect/voxel-vr/FULL-REBUILD.sh
===================================================================
--- main/trunk/model-sites-dev/atea/collect/voxel-vr/FULL-REBUILD.sh	(revision 34473)
+++ main/trunk/model-sites-dev/atea/collect/voxel-vr/FULL-REBUILD.sh	(revision 34473)
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./IMPORT.sh && ./BUILDCOL.sh  && ./ACTIVATE.sh && ./RECONFIGURE-COL.sh
Index: main/trunk/model-sites-dev/atea/collect/voxel-vr/IMPORT.sh
===================================================================
--- main/trunk/model-sites-dev/atea/collect/voxel-vr/IMPORT.sh	(revision 34473)
+++ main/trunk/model-sites-dev/atea/collect/voxel-vr/IMPORT.sh	(revision 34473)
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+cwd=`pwd`
+
+col=${cwd##*/}
+
+site_with_subdirs=${cwd##*/sites/}
+site=${site_with_subdirs%%/*}
+
+
+sudo -u www-data -- \
+     bash -c "pushd ../../../../../ && . gs3-setup.sh && popd && import.pl -removeold -site $site $col $*"
Index: main/trunk/model-sites-dev/atea/collect/voxel-vr/RECONFIGURE-COL.sh
===================================================================
--- main/trunk/model-sites-dev/atea/collect/voxel-vr/RECONFIGURE-COL.sh	(revision 34473)
+++ main/trunk/model-sites-dev/atea/collect/voxel-vr/RECONFIGURE-COL.sh	(revision 34473)
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+cwd=`pwd`
+col=${cwd##*/}
+
+site_tail=${cwd%*/*/*}
+site=${site_tail##*/}
+
+echo "Full reconfiguration of site/col:"
+echo "  $site/$col"
+echo ""
+
+
+#reconfig_url="http://localhost:8383/greenstone3/$site-library?a=s&sa=c&sc=$col"
+reconfig_url="http://localhost:8383/greenstone3/library?a=s&sa=c&sc=$col"
+
+echo "Running:"
+echo "  wget -O /dev/null \"$reconfig_url\""
+
+wget -O /dev/null "$reconfig_url" >/dev/null 2>&1 
+
+status=$?
+
+if [ $status != 0 ] ; then
+    echo "Error encountered when requesting Greenstone3 reconfiguration" 1>&2
+else
+    echo "Done"
+fi
+
+
Index: main/trunk/model-sites-dev/atea/collect/voxel-vr/etc/collectionConfig.xml
===================================================================
--- main/trunk/model-sites-dev/atea/collect/voxel-vr/etc/collectionConfig.xml	(revision 34473)
+++ main/trunk/model-sites-dev/atea/collect/voxel-vr/etc/collectionConfig.xml	(revision 34473)
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CollectionConfig xmlns:gsf="http://www.greenstone.org/greenstone3/schema/ConfigFormat" xmlns:gslib="http://www.greenstone.org/skinning" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+	<metadataList>
+		<metadata lang="en" name="creator"/>
+		<metadata lang="en" name="maintainer"/>
+		<metadata lang="en" name="public">true</metadata>
+	</metadataList>
+	<displayItemList>
+		<displayItem assigned="true" lang="en" name="name">Voxel VR</displayItem>
+		<displayItem assigned="true" lang="en" name="text">text</displayItem>
+		<displayItem assigned="true" lang="en" name="dc.Title,ex.dc.Title,Title">titles</displayItem>
+		<displayItem assigned="true" lang="en" name="Source">filenames</displayItem>
+		<displayItem assigned="true" lang="en" name="document:text">text</displayItem>
+		<displayItem assigned="true" lang="en" name="document:ex.Title">Title</displayItem>
+		<displayItem assigned="true" lang="en" name="document:ex.Source">Source</displayItem>
+		<displayItem assigned="true" lang="en" name="document">document</displayItem>
+		<displayItem assigned="true" lang="en" name="section">section</displayItem>
+		<displayItem assigned="true" lang="en" name="shortDescription">Voxel VR</displayItem>
+		<displayItem assigned="true" lang="en" name="description">A selection of Voxel VR recordings.</displayItem>
+	</displayItemList>
+	<format>
+			<gsf:template name="choose-title">
+				<gsf:choose-metadata>
+					<gsf:metadata name="dc.Title"/>
+					<gsf:metadata name="exp.Title"/>
+					<gsf:metadata name="ex.dc.Title"/>
+					<gsf:metadata name="Title"/>
+					<gsf:default>Untitled</gsf:default>
+				</gsf:choose-metadata>
+			</gsf:template>
+		</format>
+	<search type="lucene">
+		<level name="document">
+			<displayItem lang="en" name="name">document</displayItem>
+		</level>
+		<level name="section">
+			<displayItem lang="en" name="name">section</displayItem>
+		</level>
+		<defaultLevel name="document"/>
+		<index name="dc.Title,ex.dc.Title,Title">
+			<displayItem lang="en" name="name">titles</displayItem>
+		</index>
+		<defaultIndex name="dc.Title,ex.dc.Title,Title"/>
+		<sort name="rank">
+			<displayItem lang="en" name="name">rank</displayItem>
+		</sort>
+		<sort name="none">
+			<displayItem lang="en" name="name">natural (build) order</displayItem>
+		</sort>
+		<searchType name="plain"/>
+		<searchType name="simpleform"/>
+		<searchType name="advancedform"/>
+		<format>  <gsf:template match="documentNode">
+    <td valign="top">
+      <gsf:link type="source">
+        <gsf:metadata name="document"/>
+      </gsf:link>
+    </td>
+    <td>
+      <gsf:link type="document">
+        <xsl:call-template name="choose-title"/>
+      </gsf:link>
+    </td>
+  </gsf:template>
+
+</format>
+	</search>
+	<infodb type="jdbm"/>
+	<import>
+		<pluginList>
+			<plugin name="ZIPPlugin"/>
+			<plugin name="GreenstoneXMLPlugin"/>
+			<plugin name="MetadataYAMLPlugin"/>
+						  
+			<plugin name="UnknownPlugin">
+				<option name="-file_format" value="exe"/>
+				<option name="-mime_type" value="/application/vnd.microsoft.portable-executable"/> <!-- or "application/octet-stream" ??? -->
+				<option name="-srcicon" value="unknown"/>
+				<option name="-process_extension" value="exe"/>
+			</plugin>
+			<plugin name="TextPlugin"/>
+			<plugin name="HTMLPlugin"/>
+			<plugin name="EmailPlugin"/>
+			<plugin name="PDFv2Plugin"/>
+			<plugin name="RTFPlugin"/>
+			<plugin name="WordPlugin"/>
+			<plugin name="PostScriptPlugin"/>
+			<plugin name="PowerPointPlugin"/>
+			<plugin name="ExcelPlugin"/>
+			<plugin name="ImagePlugin"/>
+			<plugin name="ISISPlugin"/>
+			<plugin name="NulPlugin"/>
+			<plugin name="OAIPlugin"/>
+			<plugin name="MetadataXMLPlugin"/>
+			<plugin name="ArchivesInfPlugin"/>
+			<plugin name="DirectoryPlugin"/>
+		</pluginList>
+	</import>
+	<browse>
+		<classifier name="List">
+			<option name="-metadata" value="dc.Title;Title"/>
+			<option name="-partition_type_within_level" value="approximate_size"/>
+		</classifier>
+		<format>  <gsf:template match="documentNode">
+    <td valign="top">
+      <gsf:link type="source">
+	<gsf:icon type="document"/>
+<!--
+        <gsf:metadata name="srcicon"/>
+-->
+      </gsf:link>
+    </td>
+<!--
+<td valign="top">
+<gsf:link type="source">
+<gsf:choose-metadata>
+<gsf:metadata name="thumbicon"/>
+<gsf:metadata name="srcicon"/>
+</gsf:choose-metadata>
+</gsf:link>
+</td>
+-->
+    <td valign="top">
+      <gsf:link type="document">
+<!--
+Defined in the global format statement
+-->
+        <xsl:call-template name="choose-title"/>
+      </gsf:link>
+    </td>
+  </gsf:template>
+  <gsf:template match="classifierNode[@classifierStyle = 'VList']">
+    <td valign="top">
+      <gsf:link style="static" type="classifier">
+        <gsf:icon type="classifier"/>
+      </gsf:link>
+    </td>
+    <td valign="top">
+      <gsf:link type="classifier">
+        <gsf:metadata name="Title"/>
+      </gsf:link>
+    </td>
+  </gsf:template>
+  <gsf:template match="classifierNode[@classifierStyle = 'HList']">
+    <gsf:link style="static" type="classifier">
+      <gsf:metadata name="Title"/>
+    </gsf:link>
+  </gsf:template>
+
+</format>
+	</browse>
+	<display>
+		<format>  <gsf:option name="TOC" value="true"/>
+  <gsf:option name="AllowUserComments" value="false"/>
+<!--
+Overwriting this template allows you to change the heading of the document.
+-->
+<!--
+<gsf:template name="documentHeading">
+<span style="font-weight:bold; font-size: 120%;">
+<xsl:call-template name="choose-title"/>
+</span>
+</gsf:template>
+-->
+<!--
+Overwriting this template can be used to redefine the content of the whole document. This is useful for simple documents, but not recommended for more complex documents (e.g. hierachical and paged documents) as it can prevent any sub-sections from showing.
+-->
+  <gsf:template name="documentContent">
+    <div id="gs-document">
+      <xsl:call-template name="documentPre"/>
+      <div class="gs-voxelvr-player" id="viewer" style="text-align: center;">
+        <div style="padding: 20px;">
+          <object controls="controls" style="width: 100%"><source><xsl:attribute name="src"><xsl:value-of select="$httpPath"/>/index/assoc/<gsf:metadata name="assocfilepath" pos="1"/>/<gsf:metadata name="srclink_file"/></xsl:attribute></source> Your web browser does not support the this form of Voxel VR.</object>
+        </div>
+      </div>
+<!--
+<xsl:call-template name="wrappedSectionImage"/>
+<div id="gs-document-text">
+<xsl:call-template name="documentNodeText"/>
+</div>
+-->
+    </div>
+  </gsf:template>
+<!--
+Overwriting this template can be used to change the content of section headings.
+-->
+<!--
+<gsf:template name="sectionHeading">
+<xsl:call-template name="choose-title"/>
+</gsf:template>
+-->
+<!--
+Overwriting this template can be used to change the content of the top-level section.
+-->
+<!--
+<gsf:template name="topLevelSectionContent">
+<xsl:call-template name="wrappedSectionImage"/>
+<xsl:call-template name="wrappedSectionText"/>
+</gsf:template>
+-->
+<!--
+Overwriting this template can be used to change the content of sections.
+-->
+<!--
+<gsf:template name="sectionContent">
+<xsl:call-template name="wrappedSectionImage"/>
+<xsl:call-template name="wrappedSectionText"/>
+</gsf:template>
+-->
+
+</format>
+	</display>
+	<replaceListRef id="gs2-standard"/>
+	<replaceListRef id="gs2-image"/>
+	<serviceRackList><!-- comment out the following serviceRack if you want to disable RSS for this collection -->
+		<serviceRack name="RSSRetrieve"/><!-- comment out the following serviceRack if you want to disable OAI for this collection -->
+		<serviceRack name="OAIPMH">
+			<setName>voxel-vr</setName>
+			<setDescription/><!-- uncomment the following and set the name attribute if 
+		       you want this collection to be part of a super set. --><!--<oaiSuperSet name="xxx"/>-->
+			<ListMetadataFormats><!--What metadata sets to support for this collection. You can select any set that is specified in OAIConfig.xml. To use a new set, you need to add a set definition in OAIConfig.xml, then reference it here, like:
+				   <metadataFormat metadataPrefix="prefix"/> -->
+				<metadataFormat metadataPrefix="oai_dc"><!-- you can customize the mappings by adding elements in here. See resources/oai/OAIConfig.xml for the format--></metadataFormat>
+			</ListMetadataFormats>
+		</serviceRack>
+	</serviceRackList>
+</CollectionConfig>
Index: main/trunk/model-sites-dev/atea/collect/voxel-vr/perllib/plugins/MetadataYAMLPlugin.pm
===================================================================
--- main/trunk/model-sites-dev/atea/collect/voxel-vr/perllib/plugins/MetadataYAMLPlugin.pm	(revision 34473)
+++ main/trunk/model-sites-dev/atea/collect/voxel-vr/perllib/plugins/MetadataYAMLPlugin.pm	(revision 34473)
@@ -0,0 +1,424 @@
+###########################################################################
+#
+# MetadataYAMLPlugin.pm -- A plugin for metadata in YAML format
+#
+# A component of the Greenstone digital library software
+# from the New Zealand Digital Library Project at the 
+# University of Waikato, New Zealand.
+#
+# Copyright 2006 New Zealand Digital Library Project
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+###########################################################################
+
+package MetadataYAMLPlugin;
+
+
+use BaseImporter;
+use MetadataRead;
+# use CSVFieldSeparator;
+
+use strict;
+no strict 'refs';
+
+use extrametautil;
+use multiread;
+use util;
+
+use Encode;
+use YAML;
+
+# methods with identical signatures take precedence in the order given in the ISA list.
+sub BEGIN {
+#    @MetadataYAMLPlugin::ISA = ('MetadataRead', 'BaseImporter', 'CSVFieldSeparator');
+    @MetadataYAMLPlugin::ISA = ('MetadataRead', 'BaseImporter');
+}
+
+
+
+my $arguments = [
+      { 'name' => "process_exp",
+	'desc' => "{BaseImporter.process_exp}",
+	'type' => "regexp",
+	'reqd' => "no",
+	'deft' => &get_default_process_exp() }
+
+];
+
+
+my $options = { 'name'     => "MetadataYAMLPlugin",
+		'desc'     => "{MetadataYAMLPlugin.desc}",
+		'abstract' => "no",
+		'inherits' => "yes",
+		'args'     => $arguments };
+
+
+sub new
+{
+    my ($class) = shift (@_);
+    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
+    push(@$pluginlist, $class);
+
+    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
+    push(@{$hashArgOptLists->{"OptList"}},$options);
+
+##    new CSVFieldSeparator($pluginlist, $inputargs, $hashArgOptLists);
+    my $self = new BaseImporter($pluginlist, $inputargs, $hashArgOptLists);
+
+    return bless $self, $class;
+}
+
+
+sub get_default_process_exp
+{
+    return q^(?i)\.y(a?)ml$^;
+}
+
+sub file_block_read {
+    my $self = shift (@_);
+    my ($pluginfo, $base_dir, $file, $block_hash, $metadata, $gli) = @_;
+
+    my ($filename_full_path, $filename_no_path) = &util::get_full_filenames($base_dir, $file);
+
+    if (!-f $filename_full_path || !$self->can_process_this_file($filename_full_path)) {
+	return undef; # can't recognise
+    }
+
+    # set this so we know this is a metadata file - needed for incremental 
+    # build
+    # if this file changes, then we need to reimport everything
+    $block_hash->{'metadata_files'}->{$filename_full_path} = 1;
+
+    return 1;
+}
+
+sub metadata_read
+{
+    my $self = shift (@_);
+    my ($pluginfo, $base_dir, $file, $block_hash, 
+	$extrametakeys, $extrametadata, $extrametafile,
+	$processor, $gli, $aux) = @_;
+
+    # can we process this file??
+    my ($filename_full_path, $filename_no_path) = &util::get_full_filenames($base_dir, $file);
+    return undef unless $self->can_process_this_file_for_metadata($filename_full_path);
+    
+    print STDERR "\n<Processing n='$file' p='MetadataYAMLPlugin'>\n" if ($gli);
+    print STDERR "MetadataYAMLPlugin: processing $file\n" if ($self->{'verbosity'}) > 1;
+
+    my $outhandle = $self->{'outhandle'};
+    my $failhandle = $self->{'failhandle'};
+
+    # add the file to the block list so that it won't be processed in read, as we will do all we can with it here
+    $self->block_raw_filename($block_hash,$filename_full_path);
+
+    # Read the YAML file to get the metadata
+    my $yaml_file_content;
+    open(YAML_FILE, "$filename_full_path");
+    my $yaml_file_reader = new multiread();
+    $yaml_file_reader->set_handle('MetadataYAMLPlugin::YAML_FILE');
+    $yaml_file_reader->read_file(\$yaml_file_content);
+
+    # Would be nice if MetadataYAMLPlugin was extended to support a minus
+    # option to choose the character encoding the YAML file is in 
+    # For now we will assume it is always in UTF8
+    $yaml_file_content = decode("utf8",$yaml_file_content);
+
+    close(YAML_FILE);
+
+    # my $config = YAML::LoadFile("$filename_full_path");
+    my $yaml_rec = YAML::Load("$yaml_file_content");
+
+    # print Dumper($yaml_rec);
+
+    if (!defined $yaml_rec->{'Filename'}) {
+	$self->print_error($outhandle, $failhandle, $gli, $filename_full_path, "No Filename field in YAML metadata file");
+	return -1; # error
+    }
+
+    # We can't associate any metadata without knowing the file to associate it with
+    my $yaml_rec_filename = $yaml_rec->{"Filename"};
+    delete $yaml_rec->{"Filename"};
+
+    # Build a hash of metadata name to metadata value for this line
+    my %yaml_rec_metadata = ();
+
+    foreach my $md_name (keys(%{$yaml_rec})) {
+
+	my $md_val = $yaml_rec->{$md_name};
+	
+	my @md_vals;
+
+	# Treat everything as an array of metadata values to simplify later code
+	if (ref(\$md_val) eq 'SCALAR') {
+	    @md_vals = ( $md_val );
+	}
+	else {
+	    @md_vals = @$md_val;
+	}
+	
+	# protect square brackets in metadata values by hex entity encoding them
+	# As unescaped square bracket chars in metadata
+	# have special meaning in GS' Java runtime code
+	my @escaped_md_vals = ();
+
+	for my $meta_value (@md_vals) {
+	    print STDERR "**** meta_value = $meta_value\n";
+	    
+	    $meta_value =~ s/\[/&\#091;/g;
+	    $meta_value =~ s/\]/&\#093;/g;
+	    push(@escaped_md_vals, $meta_value);
+	}
+	
+	push (@{$yaml_rec_metadata{$md_name}}, @escaped_md_vals);
+    }
+    
+
+#	my $csv_line_section_array = $csv_line_metadata{"Section"};
+#	my $section_suffix = "";
+#	if (defined $csv_line_section_array) {
+#		my $section_value = shift(@$csv_line_section_array);
+#		if ($section_value =~ /[\d.]+/m){
+#			my $section_suffix = "///Section/" . $section_value;
+#			foreach my $metaname (keys %csv_line_metadata) {
+#				my $new_name = $metaname . $section_suffix;
+#				$csv_line_metadata{$new_name} = delete $csv_line_metadata{$metaname};
+#			}
+#		} else{
+#			unshift(@$csv_line_section_array, $section_value);
+#		}
+#	}
+
+
+ 	# Associate the metadata now
+	# Indexing into the extrameta data structures requires the filename's style of slashes to be in URL format
+	# Then need to convert the filename to a regex, no longer to protect windows directory chars \, but for
+	# protecting special characters like brackets in the filepath such as "C:\Program Files (x86)\Greenstone".
+    $yaml_rec_filename = &util::filepath_to_url_format($yaml_rec_filename);
+    $yaml_rec_filename = &util::filename_to_regex($yaml_rec_filename);
+
+    print STDERR "**** yaml_rec_filename = $yaml_rec_filename\n";
+
+
+    use Data::Dumper;
+    print Dumper(\%yaml_rec_metadata);
+    
+    if (defined &extrametautil::getmetadata($extrametadata, $yaml_rec_filename)) { # merge with existing meta    
+	
+	my $file_metadata_table = &extrametautil::getmetadata($extrametadata, $yaml_rec_filename);
+	
+	foreach my $metaname (keys %yaml_rec_metadata) {
+	    # will create new entry if one does not already exist
+	    push(@{$file_metadata_table->{$metaname}}, @{$yaml_rec_metadata{$metaname}});	    
+	}
+	
+	# no need to push $file on to $extrametakeys as it is already in the list
+    }
+    else { # add as new meta
+	
+	&extrametautil::setmetadata($extrametadata, $yaml_rec_filename, \%yaml_rec_metadata);
+	&extrametautil::addmetakey($extrametakeys, $yaml_rec_filename);
+    }
+    
+    # record which file the metadata came from 
+    if (!defined &extrametautil::getmetafile($extrametafile, $yaml_rec_filename)) {
+	&extrametautil::setmetafile($extrametafile, $yaml_rec_filename, {});
+    }
+    
+    # maps the file to full path
+    &extrametautil::setmetafile_for_named_file($extrametafile, $yaml_rec_filename, $file, $filename_full_path);
+}
+
+
+sub foo
+{
+    my ($self) = shift(@_);
+    my ($pluginfo, $base_dir, $file, $block_hash,
+        $extrametakeys, $extrametadata, $extrametafile,
+        $processor, $gli, $aux) = @_;
+    
+    # can we process this file??                                                                                
+    my ($filename_full_path, $filename_no_path) = &util::get_full_filenames($base_dir, $file);
+    return undef unless $self->can_process_this_file_for_metadata($filename_full_path);
+
+    print STDERR "\n<Processing n='$file' p='MetadataYAMLPlugin'>\n" if ($gli);
+    print STDERR "MetadataYAMLPlugin: processing $file\n" if ($self->{'verbosity'}) > 1;
+
+    my $outhandle = $self->{'outhandle'};
+    my $failhandle = $self->{'failhandle'};
+
+	
+    my $csv_file_field_line;
+    my @csv_file_lines = ();
+    my $md_val_sep = ",";
+    
+    my $csv = Text::CSV->new();                                                                                
+    #    $csv->sep_char($separate_char);                                                                            
+	
+    my @csv_file_fields = undef;                                                                               
+    if ($csv->parse($csv_file_field_line)) {                                                                   
+       @csv_file_fields = $csv->fields;                                                                        
+    }                                                                                                          
+    else {                                                                                                     
+       $self->print_error($outhandle, $failhandle, $gli, $filename_full_path, "Error: Badly formatted CSV head\
+er line: $csv_file_field_line");                                                                                
+       return -1;                                                                                              
+    }                                          
+
+    my $found_filename_field = 0;
+    for (my $i = 0; $i < scalar(@csv_file_fields); $i++) {
+	# Remove any spaces from the field names, and surrounding quotes too
+	$csv_file_fields[$i] =~ s/ //g;
+	$csv_file_fields[$i] =~ s/^"//;
+	$csv_file_fields[$i] =~ s/"$//;
+
+	if ($csv_file_fields[$i] eq "Filename") {
+	    $found_filename_field = 1;
+	}
+    }
+
+    if (!$found_filename_field) {
+	$self->print_error($outhandle, $failhandle, $gli, $filename_full_path, "No Filename field in CSV file");
+	return -1; # error
+    }
+
+    # Read each line of the file and assign the metadata appropriately
+    foreach my $csv_line (@csv_file_lines) {
+	# Ignore lines containing only whitespace
+	next if ($csv_line =~ /^\s*$/);
+	my $orig_csv_line = $csv_line;
+
+	# Build a hash of metadata name to metadata value for this line
+	my %csv_line_metadata;
+
+	if ($csv->parse($csv_line)) {
+	    my @md_vals = $csv->fields;
+	    my $md_vals_len = scalar(@md_vals);
+	    
+	    for (my $i=0; $i<$md_vals_len; $i++) {
+		my $md_val = $md_vals[$i];
+		# Only bother with non-empty values
+		if ($md_val ne "" && defined($csv_file_fields[$i])) {
+
+		    my $md_name = $csv_file_fields[$i];
+
+		    if (!defined $md_name) {
+			$csv_line_metadata{$md_name} = [];
+		    }
+
+		    if (defined $md_val_sep) {			
+			
+			my @within_md_vals = split(/${md_val_sep}/,$md_val);
+			#push (@{$csv_line_metadata{$md_name}}, @within_md_vals);
+
+			# protect square brackets in metadata values by hex entity encoding them
+			# As unescaped square bracket chars in metadata
+			# have special meaning in GS' Java runtime code
+			my @escaped_within_md_vals = ();
+			for my $meta_value (@within_md_vals) {
+			    $meta_value =~ s/\[/&\#091;/g;
+			    $meta_value =~ s/\]/&\#093;/g;
+			    push(@escaped_within_md_vals, $meta_value);
+			}
+			push (@{$csv_line_metadata{$md_name}}, @escaped_within_md_vals);
+
+#			foreach my $within_md_val (@within_md_vals) {
+#			    push (@{$csv_line_metadata{$md_name}}, $within_md_val);
+#			}
+		    }
+		    else {
+			#push (@{$csv_line_metadata{$md_name}}, $md_val);
+			# protect square brackets in metadata values by hex entity encoding them
+			my $escaped_metadata_value = $md_val;
+			$escaped_metadata_value =~ s/\[/&\#091;/g;
+			$escaped_metadata_value =~ s/\]/&\#093;/g;
+			push (@{$csv_line_metadata{$md_name}}, $escaped_metadata_value);
+		    }
+		}
+	    }
+	}
+	else {
+	    $self->print_error($outhandle, $failhandle, $gli, $filename_full_path, "Badly formatted CSV line: $csv_line");
+	    last;
+	}
+
+	# We can't associate any metadata without knowing the file to associate it with
+	my $csv_line_filename_array = $csv_line_metadata{"Filename"};
+	if (!defined $csv_line_filename_array) {
+	    $self->print_error($outhandle, $failhandle, $gli, $filename_full_path, "No Filename metadata in CSV line: $orig_csv_line");
+	    next;
+	}
+	my $csv_line_filename = shift(@$csv_line_filename_array);
+	delete $csv_line_metadata{"Filename"};
+
+	my $csv_line_section_array = $csv_line_metadata{"Section"};
+	my $section_suffix = "";
+	if (defined $csv_line_section_array) {
+		my $section_value = shift(@$csv_line_section_array);
+		if ($section_value =~ /[\d.]+/m){
+			my $section_suffix = "///Section/" . $section_value;
+			foreach my $metaname (keys %csv_line_metadata) {
+				my $new_name = $metaname . $section_suffix;
+				$csv_line_metadata{$new_name} = delete $csv_line_metadata{$metaname};
+			}
+		} else{
+			unshift(@$csv_line_section_array, $section_value);
+		}
+	}
+
+
+ 	# Associate the metadata now
+	# Indexing into the extrameta data structures requires the filename's style of slashes to be in URL format
+	# Then need to convert the filename to a regex, no longer to protect windows directory chars \, but for
+	# protecting special characters like brackets in the filepath such as "C:\Program Files (x86)\Greenstone".
+	$csv_line_filename = &util::filepath_to_url_format($csv_line_filename);
+	$csv_line_filename = &util::filename_to_regex($csv_line_filename);
+
+	if (defined &extrametautil::getmetadata($extrametadata, $csv_line_filename)) { # merge with existing meta    
+
+	    my $file_metadata_table = &extrametautil::getmetadata($extrametadata, $csv_line_filename);
+	    
+	    foreach my $metaname (keys %csv_line_metadata) {
+		# will create new entry if one does not already exist
+		push(@{$file_metadata_table->{$metaname}}, @{$csv_line_metadata{$metaname}});	    
+	    }
+	    
+	    # no need to push $file on to $extrametakeys as it is already in the list
+	} else { # add as new meta
+	    
+	    &extrametautil::setmetadata($extrametadata, $csv_line_filename, \%csv_line_metadata);
+	    &extrametautil::addmetakey($extrametakeys, $csv_line_filename);
+	}
+	# record which file the metadata came from 
+	if (!defined &extrametautil::getmetafile($extrametafile, $csv_line_filename)) {
+	    &extrametautil::setmetafile($extrametafile, $csv_line_filename, {});
+	}
+	# maps the file to full path
+	&extrametautil::setmetafile_for_named_file($extrametafile, $csv_line_filename, $file, $filename_full_path);
+    }
+}
+
+sub print_error
+{
+
+    my $self = shift(@_);
+    my ($outhandle, $failhandle, $gli, $file, $error) = @_;
+
+    print $outhandle "MetadataCSVPlugin Error: $file: $error\n";
+    print $failhandle "MetadataCSVPlugin Error: $file: $error\n";
+    print STDERR "<ProcessingError n='$file' r='$error'/>\n" if ($gli);
+}
+1;
