<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://wiki.eprints.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Tom</id>
	<title>EPrints Documentation - User contributions [en-gb]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.eprints.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Tom"/>
	<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/Special:Contributions/Tom"/>
	<updated>2026-04-05T15:33:16Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.31.8</generator>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5767</id>
		<title>Contribute: Plugins/ImportPluginsAWS</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5767"/>
		<updated>2007-09-28T19:16:02Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* In More Detail */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 2: Amazon Web Services =&lt;br /&gt;
&lt;br /&gt;
In the [[Contribute:_Plugins/ImportPluginsCSV|last tutorial]] we created an import plugin that took data which needed very little modification to import into the respository. The column names in the [http://en.wikipedia.org/wiki/Comma-Separated_Values CSV] file matched the names of metadata fields present in the repository. In this tutorial we'll look at importing data that needs some modification to be imported, needs more error checking and is obtained in a different way.&lt;br /&gt;
&lt;br /&gt;
We will be using Amazon's E-Commerce Webservice to import books from their website into our respository given a list of [http://en.wikipedia.org/wiki/ASIN ASIN].&lt;br /&gt;
&lt;br /&gt;
We will be accessing the service using a [http://en.wikipedia.org/wiki/REST REST] approach, communicating with the server using URL parameters and retrieving an XML document in response to our request. It is also possible to access their services using [http://en.wikipedia.org/wiki/SOAP SOAP], but that will not be discussed here.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
== Amazon Web Services ==&lt;br /&gt;
To use Amazon's web services you must first signup for an account [http://aws.amazon.com here]. Their site has extensive documentation on the services that they offer as well as example programs including some written in Perl.&lt;br /&gt;
&lt;br /&gt;
== Required Modules ==&lt;br /&gt;
To prepare for this tutorial you should make sure the [http://search.cpan.org/~gaas/libwww-perl-5.805/lib/LWP/UserAgent.pm LWP::UserAgent] module is installed. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan LWP::UserAgent&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= AWS.pm = &lt;br /&gt;
The code in the section below should be placed in a file called CSV.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::AWS;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'AWS';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $input) = @_;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        $input =~ m/([A-Za-z0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        #Get Item Object&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
We will use the URI::Escape module in this plugin. As it is included with EPrints we don't need to check if it exists first.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup a number of values for parameters that will be part of our web service requests. The endpoint variable determines which server will be sent the request. Here we have used the UK server, but by changing the [http://en.wikipedia.org/wiki/TLD TLD] we can use the US, Canadian, German or French servers.&lt;br /&gt;
&lt;br /&gt;
The accesskey variable stores the access key you will have gained from signing up to Amazon earlier. You should use the normal access key and not the secret one.&lt;br /&gt;
&lt;br /&gt;
Here we use the ItemLookup operation of the AWSECommerceService with the 2007-07-16 version of the API. Other operations allow searching for items, but here we want to look up specific products. Finally the variable responsegroup determines the amount and nature of the information returned, we select &amp;quot;Large&amp;quot; in this case, which gives us more information about the item.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
The constructor is similar to the one used for the [Contribute:_Plugins/ImportPluginsCSV CSV plugin], except this one will import individual eprints, given an [http://en.wikipedia.org/wiki/ASIN ASIN].&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Like we imported Text::CSV in the [Contribute:_Plugins/ImportPluginsCSV last tutorial], here we import LWP::UserAgent which will be used for making requests to the web service.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
This method is similar to the one used in the [Contribute:_Plugins/ImportPluginsCSV CSV plugin], but doesn't have to do quite so much work.&lt;br /&gt;
&lt;br /&gt;
First we create the array to hold our imported eprint ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next we read all the lines in the supplied file handle into our records array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we iterate over each record, running convert_input on it, importing it into our repository and adding the id to our array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we return a List object of the items imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
&lt;br /&gt;
[http://en.wikipedia.org/wiki/ASIN ASINs] are strings which identify a product. Here we remove any non-alphanumerical characters which are surrounding the [http://en.wikipedia.org/wiki/ASIN ASIN].&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $input =~ m/([A-Za-z0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We form the request from the variables we created earlier and the [http://en.wikipedia.org/wiki/ASIN ASIN] we have just obtained.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now send the request, by creating a new LWP::UserAgent object, setting its timeout to 30 seconds and then performing the request using HTTP GET.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create a [http://en.wikipedia.org/wiki/Document_Object_Model DOM] object from the XML document returned.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each request contains an Items element within the root element of the document which contains a Request element. This element contains an element IsValid. This element will contain the value True or False depending on whether a valid request was made or not.&lt;br /&gt;
&lt;br /&gt;
Here we obtain the Request element and check that the IsValid element within it contains the value True. If it doesn't we  call the error method and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName(&amp;quot;Items&amp;quot;)-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The product found with the ItemLookup method is contained within an Item element within the Items element. Here we attempt to get that element and raise the error and return undef if we can't.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each item contains an ItemAttributes element which contains most of the metadata about an item.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For some specialised repositories it might make sense to import DVDs, computer games and electronic equipment, but we're just going to deal with books. The ProductGroup element within the ItemAttributes element tells you what sort of item we're dealing with. We're looking for the value 'Book'.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set a few fields without consulting the imported data. We know this is a book, so we set the type. We assume that it has not been refereed. We also assume it has been published, because we can buy it. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get and set the title.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set the official URL to the Amazon product page. We have to do a bit of extra work using the uri_unescape method from the URI::Escape package to convert URI escape codes into characters.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the [http://en.wikipedia.org/wiki/ISBN ISBN]. Note that the [http://en.wikipedia.org/wiki/ISBN ISBN] is often the same as the [http://en.wikipedia.org/wiki/ASIN ASIN].&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the number of pages.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publisher.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publication date and finally return our output hash.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
We'll start by collecting a few ASINS. Go to [http://www.amazon.co.uk Amazon] and pick a few books. The URL for each project page is in the form http://www.amazon.co.uk/Combination-of-title-an-author/dp/ASIN... Collect a few different ASINS.&lt;br /&gt;
&lt;br /&gt;
Now we'll demonstrate importing from Amazon with a few sample ASINs.&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0946719616&lt;br /&gt;
0297843877&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;AWS&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5766</id>
		<title>Contribute: Plugins/ImportPluginsAWS</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5766"/>
		<updated>2007-09-28T19:11:22Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* convert_input */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 2: Amazon Web Services =&lt;br /&gt;
&lt;br /&gt;
In the [[Contribute:_Plugins/ImportPluginsCSV|last tutorial]] we created an import plugin that took data which needed very little modification to import into the respository. The column names in the [http://en.wikipedia.org/wiki/Comma-Separated_Values CSV] file matched the names of metadata fields present in the repository. In this tutorial we'll look at importing data that needs some modification to be imported, needs more error checking and is obtained in a different way.&lt;br /&gt;
&lt;br /&gt;
We will be using Amazon's E-Commerce Webservice to import books from their website into our respository given a list of [http://en.wikipedia.org/wiki/ASIN ASIN].&lt;br /&gt;
&lt;br /&gt;
We will be accessing the service using a [http://en.wikipedia.org/wiki/REST REST] approach, communicating with the server using URL parameters and retrieving an XML document in response to our request. It is also possible to access their services using [http://en.wikipedia.org/wiki/SOAP SOAP], but that will not be discussed here.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
== Amazon Web Services ==&lt;br /&gt;
To use Amazon's web services you must first signup for an account [http://aws.amazon.com here]. Their site has extensive documentation on the services that they offer as well as example programs including some written in Perl.&lt;br /&gt;
&lt;br /&gt;
== Required Modules ==&lt;br /&gt;
To prepare for this tutorial you should make sure the [http://search.cpan.org/~gaas/libwww-perl-5.805/lib/LWP/UserAgent.pm LWP::UserAgent] module is installed. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan LWP::UserAgent&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= AWS.pm = &lt;br /&gt;
The code in the section below should be placed in a file called CSV.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::AWS;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'AWS';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $input) = @_;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        $input =~ m/([A-Za-z0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        #Get Item Object&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
We will use the URI::Escape module in this plugin. As it is included with EPrints we don't need to check if it exists first.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup a number of values for parameters that will be part of our web service requests. The endpoint variable determines which server will be sent the request. Here we have used the UK server, but by changing the TLD we can use the US, Canadian, German or French servers.&lt;br /&gt;
&lt;br /&gt;
The accesskey stores the access key you will have gained from signing up to Amazon earlier. You should use the normal access key and not the secret one.&lt;br /&gt;
&lt;br /&gt;
Here we use the ItemLookup operation of the AWSECommerceService with the 2007-07-16 version of the service API. Other operations allow searching for items, but here we want to look up specific products. Finally the variable responsegroup determines the amount and nature of the information returned, we select &amp;quot;Large&amp;quot; in this case, giving a lot of information about the item.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
The constructor is similar to the one used for the CSV plugin, except this one will import individual eprints, given an ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Like we imported Text::CSV in the last tutorial, here we import LWP::UserAgent which will be used for making requests to the web service.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
This method is similar to the one used in the CSV plugin, but doesn't have to do quite so much work.&lt;br /&gt;
&lt;br /&gt;
First we create the array to hold our imported eprint ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next we read all the lines in the supplied file handle into our records array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we iterate over each record, running convert_input on it, importing it into our repository and adding the id to our array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we return a List object of the items imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
&lt;br /&gt;
ASIN are strings which identify a product. Here we remove any non-alphanumerical characters which are surrounding the ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $input =~ m/([A-Za-z0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We form the request from the variables we created earlier and the ASIN we have just obtained.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now send the request, by creating a new LWP::UserAgent object, setting its timeout to 30 seconds and then performing the request using HTTP GET.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create a DOM object from the XML document returned.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each request contains an Items element within the root element of the document which contains a Request element. This element contains an element IsValid. This element will contain the value True or False depending on whether a valid request was made or not.&lt;br /&gt;
&lt;br /&gt;
Here we obtain the Request element and check that the IsValid element within it contains the value True. If it doesn't we  call the error method and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName(&amp;quot;Items&amp;quot;)-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The product found with the ItemLookup method is contained within an Item element within the Items element. Here we attempt to get that element and raise the error and return undef if we can't.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each item contains an ItemAttributes element which contains most of the metadata about an item.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For some specialised repositories it might make sense to import DVDs, computer games and electronic equipment, but we're just going to deal with books. The ProductGroup element within the ItemAttributes element tells you what sort of item we're dealing with. We're looking for the value 'Book'.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set a few fields without consulting the imported data. We know this is a book, so we set the type. We assume that it has not been refereed. We also assume it has been published, because we can buy it. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get and set the title.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set the official URL to the Amazon product page. We have to do a bit of extra work using the uri_unescape method from the URI::Escape package to convert URI escape codes into characters.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the ISBN. Note that the ISBN is often the same as the ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the number of pages.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publisher.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publication date and finally return our output hash.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
We'll start by collecting a few ASINS. Go to [http://www.amazon.co.uk Amazon] and pick a few books. The URL for each project page is in the form http://www.amazon.co.uk/Combination-of-title-an-author/dp/ASIN... Collect a few different ASINS.&lt;br /&gt;
&lt;br /&gt;
Now we'll demonstrate importing from Amazon with a few sample ASINs.&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0946719616&lt;br /&gt;
0297843877&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;AWS&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5765</id>
		<title>Contribute: Plugins/ImportPluginsAWS</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5765"/>
		<updated>2007-09-28T19:10:29Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* AWS.pm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 2: Amazon Web Services =&lt;br /&gt;
&lt;br /&gt;
In the [[Contribute:_Plugins/ImportPluginsCSV|last tutorial]] we created an import plugin that took data which needed very little modification to import into the respository. The column names in the [http://en.wikipedia.org/wiki/Comma-Separated_Values CSV] file matched the names of metadata fields present in the repository. In this tutorial we'll look at importing data that needs some modification to be imported, needs more error checking and is obtained in a different way.&lt;br /&gt;
&lt;br /&gt;
We will be using Amazon's E-Commerce Webservice to import books from their website into our respository given a list of [http://en.wikipedia.org/wiki/ASIN ASIN].&lt;br /&gt;
&lt;br /&gt;
We will be accessing the service using a [http://en.wikipedia.org/wiki/REST REST] approach, communicating with the server using URL parameters and retrieving an XML document in response to our request. It is also possible to access their services using [http://en.wikipedia.org/wiki/SOAP SOAP], but that will not be discussed here.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
== Amazon Web Services ==&lt;br /&gt;
To use Amazon's web services you must first signup for an account [http://aws.amazon.com here]. Their site has extensive documentation on the services that they offer as well as example programs including some written in Perl.&lt;br /&gt;
&lt;br /&gt;
== Required Modules ==&lt;br /&gt;
To prepare for this tutorial you should make sure the [http://search.cpan.org/~gaas/libwww-perl-5.805/lib/LWP/UserAgent.pm LWP::UserAgent] module is installed. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan LWP::UserAgent&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= AWS.pm = &lt;br /&gt;
The code in the section below should be placed in a file called CSV.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::AWS;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'AWS';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $input) = @_;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        $input =~ m/([A-Za-z0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        #Get Item Object&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
We will use the URI::Escape module in this plugin. As it is included with EPrints we don't need to check if it exists first.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup a number of values for parameters that will be part of our web service requests. The endpoint variable determines which server will be sent the request. Here we have used the UK server, but by changing the TLD we can use the US, Canadian, German or French servers.&lt;br /&gt;
&lt;br /&gt;
The accesskey stores the access key you will have gained from signing up to Amazon earlier. You should use the normal access key and not the secret one.&lt;br /&gt;
&lt;br /&gt;
Here we use the ItemLookup operation of the AWSECommerceService with the 2007-07-16 version of the service API. Other operations allow searching for items, but here we want to look up specific products. Finally the variable responsegroup determines the amount and nature of the information returned, we select &amp;quot;Large&amp;quot; in this case, giving a lot of information about the item.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
The constructor is similar to the one used for the CSV plugin, except this one will import individual eprints, given an ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Like we imported Text::CSV in the last tutorial, here we import LWP::UserAgent which will be used for making requests to the web service.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
This method is similar to the one used in the CSV plugin, but doesn't have to do quite so much work.&lt;br /&gt;
&lt;br /&gt;
First we create the array to hold our imported eprint ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next we read all the lines in the supplied file handle into our records array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we iterate over each record, running convert_input on it, importing it into our repository and adding the id to our array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we return a List object of the items imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
&lt;br /&gt;
ASINs are strings of decimal digits which may have leading zeroes which identify a product. Here we remove any non-numerical characters which are surrounding the ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $input =~ m/([0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We form the request from the variables we created earlier and the ASIN we have just obtained.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now send the request, by creating a new LWP::UserAgent object, setting its timeout to 30 seconds and then performing the request using HTTP GET.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create a DOM object from the XML document returned.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each request contains an Items element within the root element of the document which contains a Request element. This element contains an element IsValid. This element will contain the value True or False depending on whether a valid request was made or not.&lt;br /&gt;
&lt;br /&gt;
Here we obtain the Request element and check that the IsValid element within it contains the value True. If it doesn't we  call the error method and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName(&amp;quot;Items&amp;quot;)-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The product found with the ItemLookup method is contained within an Item element within the Items element. Here we attempt to get that element and raise the error and return undef if we can't.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each item contains an ItemAttributes element which contains most of the metadata about an item.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For some specialised repositories it might make sense to import DVDs, computer games and electronic equipment, but we're just going to deal with books. The ProductGroup element within the ItemAttributes element tells you what sort of item we're dealing with. We're looking for the value 'Book'.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set a few fields without consulting the imported data. We know this is a book, so we set the type. We assume that it has not been refereed. We also assume it has been published, because we can buy it. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get and set the title.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set the official URL to the Amazon product page. We have to do a bit of extra work using the uri_unescape method from the URI::Escape package to convert URI escape codes into characters.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the ISBN. Note that the ISBN is often the same as the ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the number of pages.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publisher.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publication date and finally return our output hash.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
We'll start by collecting a few ASINS. Go to [http://www.amazon.co.uk Amazon] and pick a few books. The URL for each project page is in the form http://www.amazon.co.uk/Combination-of-title-an-author/dp/ASIN... Collect a few different ASINS.&lt;br /&gt;
&lt;br /&gt;
Now we'll demonstrate importing from Amazon with a few sample ASINs.&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0946719616&lt;br /&gt;
0297843877&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;AWS&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5764</id>
		<title>Contribute: Plugins/ImportPluginsCSV</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5764"/>
		<updated>2007-09-28T19:09:42Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* convert_input */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 1: CSV =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we will look at creating a relatively simple plugin to import eprints into our repository by reading files containing [http://en.wikipedia.org/wiki/Comma-Separated_Values comma-separated values]. We won't be dealing with documents and files, but will be focusing on importing eprint metadata.&lt;br /&gt;
&lt;br /&gt;
Import plugins are inherently more complicated than export plugins because of the error checking that must be done, however in this example error checking has been kept to a minimum to simplify the example. In a &amp;quot;real&amp;quot; plugin you should check that the appropriate metadata fields are set for a given type of eprint, and unfortunately there appears to be no quick way to do this.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
Create a directory for your import plugins in the main plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/import). The directory used for these examples is called &amp;quot;MyPlugins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~erangel/Text-CSV/CSV.pm Text::CSV] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Text::CSV&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CSV.pm = &lt;br /&gt;
The code in the section below should be placed in a file called CSV.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::CSV;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'CSV';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
        my @fields;&lt;br /&gt;
&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my $plugin = shift;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
                        else&lt;br /&gt;
                        {&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
                }&lt;br /&gt;
                $i++;&lt;br /&gt;
        }&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Modules == &lt;br /&gt;
Here we import the superclass for our plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Inheritance ==&lt;br /&gt;
&lt;br /&gt;
Our plugin will not inherit from the Import class directly, but from the TextFile subclass. This contains some extra file handling code that means we can ignore certain differences in text file formats. If you are creating an import plugin which imports non-text files you should subclass the EPrints::Plugin::Import class directly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For import plugins we must set a 'produce' field, to tell the repository what kinds of objects the plugin can import. This plugin only supports importing lists of eprints, but if it supported importing individual eprints we could add 'dataobj/eprint' to this property. We would then have to implement the &amp;quot;input_dataobj&amp;quot; method. Most plugins implement this method, but it is rarely used in practice. Most imports are done in lists (even if that list only contains one member), via the import items screen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we use a module that is not included with EPrints, Text::CSV, so we import it in a different way. First we check that it is installed, and load it if it is with &amp;quot;EPrints::Utils::require_if_exists&amp;quot;.If it isn't we make the plugin invisible and produce an error message. It is good practice to import non-standard modules in this way rather than with &amp;quot;use&amp;quot;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
Import plugins have to implement a couple of methods to read data from a file or string, manipulate it and turn it into a form which can be imported into the repository. That process will be described below.&lt;br /&gt;
&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
&lt;br /&gt;
This method takes a filehandle, processes it, tries to import DataObjs in to the repository and then returns a List of the DataObjs imported.&lt;br /&gt;
&lt;br /&gt;
This array will be used to create a List of DataObjs later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we open the filehandle passed, and read the lines into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a Text::CSV object to handle the input. Using a dedicated [http://en.wikipedia.org/wiki/Comma-Separated_Values CSV] handling package is preferable to using Perl's split function as it handles a number of more complicated scenarios such as commas within records using double quotes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After setting up an array for metadata field names, we attempt to parse the first line of our file. The parse method does not return an array of fields, but reports success or failure. In the event of success we use the fields method to return the last fields parsed. In the event of failure we use the error_input method to get the last error, and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields;&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that the row of column titles has been dealt with we move onto processing each record in the file.&lt;br /&gt;
&lt;br /&gt;
In import plugins the convert_input method converts individual records into a format that can be imported into the repository. That is a hash whose keys are metadata field names and values are the corresponding values. As a row on its own cannot be imported as we don't know to which field each value belongs we have to construct an array to pass to convert_input first. We pass an array whose first element is the fields row and whose second element is the row we want to import.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we call convert_input on our constructed input_data. If the conversion fails we simply move to the next record.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The epdata_to_dataobj method takes our epdata hash reference and turns it into a new DataObj in our repository. If it is successful it returns the new DataObj, whose id we add to our array of ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a List object containing the ids of the records we have successfully imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
This method takes data in a particular format, in this case [http://en.wikipedia.org/wiki/Comma-Separated_Values CSV] and transforms it into a hash of metadata field names and values.&lt;br /&gt;
&lt;br /&gt;
We take the second argument to the method and convert the array reference into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup another Text::CSV object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the second element of our array and parse it. This is the record we wish to import. If anything goes wrong we return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the first element and get the field names. We then check that we have the same number of fields names as records.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the hash that we'll return later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For convenience we get the DataSet object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now iterate over the fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the field does not exist we look at the next one, remembering to increment our index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get the MetaField object corresponding to the current field.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
We deal with multiple field types by separating individual values with a semi-colon.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Name fields are dealt with by using regular expressions and constructing a hash from the parts matched. The plugin expects names to be of the form Surname, Forenames, Lineage (Sr, Jr, III etc).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Multiple fields which are not names are just added to the hash as an array reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Non-multiple fields are just added to the hash from the array of fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a hash reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
This is a test title,This is a test abstract&lt;br /&gt;
This is another test title,This is another test abstract&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;CSV&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Embedding commas ==&lt;br /&gt;
If you want to include commas in your imports, which is very likely you must enclose the field in double quotations. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
An interesting article,&amp;quot;Damn it Jim, I'm a Doctor, not a Perl hacker.&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When doing this make sure not to leave any whitespace between a quotation mark and a comma or the import will fail.&lt;br /&gt;
&lt;br /&gt;
== Multiple fields ==&lt;br /&gt;
Multiple field types are handled by separating each individual value by a semi-colon, a simple example of this would be the subjects field.&lt;br /&gt;
&lt;br /&gt;
Go back to the import items and proceed as before, but typing this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
abstract,title,subjects&lt;br /&gt;
Testing,Testing,AI;C;M;F;P&lt;br /&gt;
Testing,Testing,AC;DC&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After the records have been imported examine each one on the View Item screen. You will find that a list of subjects are given, and that a more descriptive name is given than the code we imported.&lt;br /&gt;
&lt;br /&gt;
== Compound fields ==&lt;br /&gt;
&lt;br /&gt;
Compound fields are fields that have subfields within them, each with their own name. You don't compound fields in one go, but set the components individually. Subfields have names of the form  mainfieldname_subfieldname.&lt;br /&gt;
&lt;br /&gt;
One of the most commonly used compound fields is the &amp;quot;Creators&amp;quot; field. It has a names subfield &amp;quot;creators_name&amp;quot; and an ID subfield &amp;quot;creators_id&amp;quot; which is most often used for email addresses.&lt;br /&gt;
&lt;br /&gt;
Here is an example of setting the creators field:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,creators_names,creators_ids,&lt;br /&gt;
Setting compound fields.,&amp;quot;Bloggs, Joe;Doe, John&amp;quot;,&amp;quot;joe@bloggs.com;john@doe.com&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you import this record and then examine the view items screen you will find that the &amp;quot;Creators&amp;quot; field has been setup with the values displayed in a table.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5763</id>
		<title>Contribute: Plugins/ImportPluginsCSV</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5763"/>
		<updated>2007-09-28T19:08:31Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* input_fh */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 1: CSV =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we will look at creating a relatively simple plugin to import eprints into our repository by reading files containing [http://en.wikipedia.org/wiki/Comma-Separated_Values comma-separated values]. We won't be dealing with documents and files, but will be focusing on importing eprint metadata.&lt;br /&gt;
&lt;br /&gt;
Import plugins are inherently more complicated than export plugins because of the error checking that must be done, however in this example error checking has been kept to a minimum to simplify the example. In a &amp;quot;real&amp;quot; plugin you should check that the appropriate metadata fields are set for a given type of eprint, and unfortunately there appears to be no quick way to do this.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
Create a directory for your import plugins in the main plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/import). The directory used for these examples is called &amp;quot;MyPlugins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~erangel/Text-CSV/CSV.pm Text::CSV] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Text::CSV&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CSV.pm = &lt;br /&gt;
The code in the section below should be placed in a file called CSV.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::CSV;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'CSV';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
        my @fields;&lt;br /&gt;
&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my $plugin = shift;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
                        else&lt;br /&gt;
                        {&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
                }&lt;br /&gt;
                $i++;&lt;br /&gt;
        }&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Modules == &lt;br /&gt;
Here we import the superclass for our plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Inheritance ==&lt;br /&gt;
&lt;br /&gt;
Our plugin will not inherit from the Import class directly, but from the TextFile subclass. This contains some extra file handling code that means we can ignore certain differences in text file formats. If you are creating an import plugin which imports non-text files you should subclass the EPrints::Plugin::Import class directly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For import plugins we must set a 'produce' field, to tell the repository what kinds of objects the plugin can import. This plugin only supports importing lists of eprints, but if it supported importing individual eprints we could add 'dataobj/eprint' to this property. We would then have to implement the &amp;quot;input_dataobj&amp;quot; method. Most plugins implement this method, but it is rarely used in practice. Most imports are done in lists (even if that list only contains one member), via the import items screen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we use a module that is not included with EPrints, Text::CSV, so we import it in a different way. First we check that it is installed, and load it if it is with &amp;quot;EPrints::Utils::require_if_exists&amp;quot;.If it isn't we make the plugin invisible and produce an error message. It is good practice to import non-standard modules in this way rather than with &amp;quot;use&amp;quot;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
Import plugins have to implement a couple of methods to read data from a file or string, manipulate it and turn it into a form which can be imported into the repository. That process will be described below.&lt;br /&gt;
&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
&lt;br /&gt;
This method takes a filehandle, processes it, tries to import DataObjs in to the repository and then returns a List of the DataObjs imported.&lt;br /&gt;
&lt;br /&gt;
This array will be used to create a List of DataObjs later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we open the filehandle passed, and read the lines into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a Text::CSV object to handle the input. Using a dedicated [http://en.wikipedia.org/wiki/Comma-Separated_Values CSV] handling package is preferable to using Perl's split function as it handles a number of more complicated scenarios such as commas within records using double quotes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After setting up an array for metadata field names, we attempt to parse the first line of our file. The parse method does not return an array of fields, but reports success or failure. In the event of success we use the fields method to return the last fields parsed. In the event of failure we use the error_input method to get the last error, and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields;&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that the row of column titles has been dealt with we move onto processing each record in the file.&lt;br /&gt;
&lt;br /&gt;
In import plugins the convert_input method converts individual records into a format that can be imported into the repository. That is a hash whose keys are metadata field names and values are the corresponding values. As a row on its own cannot be imported as we don't know to which field each value belongs we have to construct an array to pass to convert_input first. We pass an array whose first element is the fields row and whose second element is the row we want to import.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we call convert_input on our constructed input_data. If the conversion fails we simply move to the next record.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The epdata_to_dataobj method takes our epdata hash reference and turns it into a new DataObj in our repository. If it is successful it returns the new DataObj, whose id we add to our array of ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a List object containing the ids of the records we have successfully imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
This method takes data in a particular format, in this case CSV and transforms it into a hash of metadata field names and values.&lt;br /&gt;
&lt;br /&gt;
We take the second argument to the method and convert the array reference into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup another Text::CSV object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the second element of our array and parse it. This is the record we wish to import. If anything goes wrong we return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the first element and get the field names. We then check that we have the same number of fields names as records.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the hash that we'll return later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For convenience we get the DataSet object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now iterate over the fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the field does not exist we look at the next one, remembering to increment our index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get the MetaField object corresponding to the current field.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
We deal with multiple field types by separating individual values with a semi-colon.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Name fields are dealt with by using regular expressions and constructing a hash from the parts matched. The plugin expects names to be of the form Surname, Forenames, Lineage (Sr, Jr, III etc).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Multiple fields which are not names are just added to the hash as an array reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Non-multiple fields are just added to the hash from the array of fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a hash reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
This is a test title,This is a test abstract&lt;br /&gt;
This is another test title,This is another test abstract&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;CSV&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Embedding commas ==&lt;br /&gt;
If you want to include commas in your imports, which is very likely you must enclose the field in double quotations. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
An interesting article,&amp;quot;Damn it Jim, I'm a Doctor, not a Perl hacker.&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When doing this make sure not to leave any whitespace between a quotation mark and a comma or the import will fail.&lt;br /&gt;
&lt;br /&gt;
== Multiple fields ==&lt;br /&gt;
Multiple field types are handled by separating each individual value by a semi-colon, a simple example of this would be the subjects field.&lt;br /&gt;
&lt;br /&gt;
Go back to the import items and proceed as before, but typing this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
abstract,title,subjects&lt;br /&gt;
Testing,Testing,AI;C;M;F;P&lt;br /&gt;
Testing,Testing,AC;DC&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After the records have been imported examine each one on the View Item screen. You will find that a list of subjects are given, and that a more descriptive name is given than the code we imported.&lt;br /&gt;
&lt;br /&gt;
== Compound fields ==&lt;br /&gt;
&lt;br /&gt;
Compound fields are fields that have subfields within them, each with their own name. You don't compound fields in one go, but set the components individually. Subfields have names of the form  mainfieldname_subfieldname.&lt;br /&gt;
&lt;br /&gt;
One of the most commonly used compound fields is the &amp;quot;Creators&amp;quot; field. It has a names subfield &amp;quot;creators_name&amp;quot; and an ID subfield &amp;quot;creators_id&amp;quot; which is most often used for email addresses.&lt;br /&gt;
&lt;br /&gt;
Here is an example of setting the creators field:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,creators_names,creators_ids,&lt;br /&gt;
Setting compound fields.,&amp;quot;Bloggs, Joe;Doe, John&amp;quot;,&amp;quot;joe@bloggs.com;john@doe.com&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you import this record and then examine the view items screen you will find that the &amp;quot;Creators&amp;quot; field has been setup with the values displayed in a table.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5762</id>
		<title>Contribute: Plugins/ImportPluginsCSV</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5762"/>
		<updated>2007-09-28T19:07:28Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Constructor */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 1: CSV =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we will look at creating a relatively simple plugin to import eprints into our repository by reading files containing [http://en.wikipedia.org/wiki/Comma-Separated_Values comma-separated values]. We won't be dealing with documents and files, but will be focusing on importing eprint metadata.&lt;br /&gt;
&lt;br /&gt;
Import plugins are inherently more complicated than export plugins because of the error checking that must be done, however in this example error checking has been kept to a minimum to simplify the example. In a &amp;quot;real&amp;quot; plugin you should check that the appropriate metadata fields are set for a given type of eprint, and unfortunately there appears to be no quick way to do this.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
Create a directory for your import plugins in the main plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/import). The directory used for these examples is called &amp;quot;MyPlugins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~erangel/Text-CSV/CSV.pm Text::CSV] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Text::CSV&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CSV.pm = &lt;br /&gt;
The code in the section below should be placed in a file called CSV.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::CSV;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'CSV';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
        my @fields;&lt;br /&gt;
&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my $plugin = shift;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
                        else&lt;br /&gt;
                        {&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
                }&lt;br /&gt;
                $i++;&lt;br /&gt;
        }&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Modules == &lt;br /&gt;
Here we import the superclass for our plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Inheritance ==&lt;br /&gt;
&lt;br /&gt;
Our plugin will not inherit from the Import class directly, but from the TextFile subclass. This contains some extra file handling code that means we can ignore certain differences in text file formats. If you are creating an import plugin which imports non-text files you should subclass the EPrints::Plugin::Import class directly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For import plugins we must set a 'produce' field, to tell the repository what kinds of objects the plugin can import. This plugin only supports importing lists of eprints, but if it supported importing individual eprints we could add 'dataobj/eprint' to this property. We would then have to implement the &amp;quot;input_dataobj&amp;quot; method. Most plugins implement this method, but it is rarely used in practice. Most imports are done in lists (even if that list only contains one member), via the import items screen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we use a module that is not included with EPrints, Text::CSV, so we import it in a different way. First we check that it is installed, and load it if it is with &amp;quot;EPrints::Utils::require_if_exists&amp;quot;.If it isn't we make the plugin invisible and produce an error message. It is good practice to import non-standard modules in this way rather than with &amp;quot;use&amp;quot;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
Import plugins have to implement a couple of methods to read data from a file or string, manipulate it and turn it into a form which can be imported into the repository. That process will be described below.&lt;br /&gt;
&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
&lt;br /&gt;
This method takes a filehandle, processes it, tries to import DataObjs in to the repository and then returns a List of the DataObjs imported.&lt;br /&gt;
&lt;br /&gt;
This array will be used to create a List of DataObjs later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we open the filehandle passed, and read the lines into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a Text::CSV object to handle the input. Using a dedicated CSV handling package is preferable to using Perl's split function as it handles a number of more complicated scenarios such as commas within records using double quotes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After setting up an array for metadata field names, we attempt to parse the first line of our file. The parse method does not return an array of fields, but reports success or failure. In the event of success we use the fields method to return the last fields parsed. In the event of failure we use the error_input method to get the last error, and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields;&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that the row of column titles has been dealt with we move onto processing each record in the file.&lt;br /&gt;
&lt;br /&gt;
In import plugins the convert_input method converts individual records into a format that can be imported into the repository. That is a hash whose keys are metadata field names and values are the corresponding values. As a row on its own cannot be imported as we don't know to which field each value belongs we have to construct an array to pass to convert_input first. We pass an array whose first element is the fields row and whose second element is the row we want to import.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we call convert_input on our constructed input_data. If the conversion fails we simply move to the next record.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The epdata_to_dataobj method takes our epdata hash reference and turns it into a new DataObj in our repository. If it is successful it returns the new DataObj, whose id we add to our array of ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a List object containing the ids of the records we have successfully imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
This method takes data in a particular format, in this case CSV and transforms it into a hash of metadata field names and values.&lt;br /&gt;
&lt;br /&gt;
We take the second argument to the method and convert the array reference into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup another Text::CSV object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the second element of our array and parse it. This is the record we wish to import. If anything goes wrong we return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the first element and get the field names. We then check that we have the same number of fields names as records.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the hash that we'll return later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For convenience we get the DataSet object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now iterate over the fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the field does not exist we look at the next one, remembering to increment our index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get the MetaField object corresponding to the current field.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
We deal with multiple field types by separating individual values with a semi-colon.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Name fields are dealt with by using regular expressions and constructing a hash from the parts matched. The plugin expects names to be of the form Surname, Forenames, Lineage (Sr, Jr, III etc).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Multiple fields which are not names are just added to the hash as an array reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Non-multiple fields are just added to the hash from the array of fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a hash reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
This is a test title,This is a test abstract&lt;br /&gt;
This is another test title,This is another test abstract&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;CSV&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Embedding commas ==&lt;br /&gt;
If you want to include commas in your imports, which is very likely you must enclose the field in double quotations. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
An interesting article,&amp;quot;Damn it Jim, I'm a Doctor, not a Perl hacker.&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When doing this make sure not to leave any whitespace between a quotation mark and a comma or the import will fail.&lt;br /&gt;
&lt;br /&gt;
== Multiple fields ==&lt;br /&gt;
Multiple field types are handled by separating each individual value by a semi-colon, a simple example of this would be the subjects field.&lt;br /&gt;
&lt;br /&gt;
Go back to the import items and proceed as before, but typing this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
abstract,title,subjects&lt;br /&gt;
Testing,Testing,AI;C;M;F;P&lt;br /&gt;
Testing,Testing,AC;DC&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After the records have been imported examine each one on the View Item screen. You will find that a list of subjects are given, and that a more descriptive name is given than the code we imported.&lt;br /&gt;
&lt;br /&gt;
== Compound fields ==&lt;br /&gt;
&lt;br /&gt;
Compound fields are fields that have subfields within them, each with their own name. You don't compound fields in one go, but set the components individually. Subfields have names of the form  mainfieldname_subfieldname.&lt;br /&gt;
&lt;br /&gt;
One of the most commonly used compound fields is the &amp;quot;Creators&amp;quot; field. It has a names subfield &amp;quot;creators_name&amp;quot; and an ID subfield &amp;quot;creators_id&amp;quot; which is most often used for email addresses.&lt;br /&gt;
&lt;br /&gt;
Here is an example of setting the creators field:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,creators_names,creators_ids,&lt;br /&gt;
Setting compound fields.,&amp;quot;Bloggs, Joe;Doe, John&amp;quot;,&amp;quot;joe@bloggs.com;john@doe.com&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you import this record and then examine the view items screen you will find that the &amp;quot;Creators&amp;quot; field has been setup with the values displayed in a table.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5761</id>
		<title>Contribute: Plugins/ImportPluginsAWS</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5761"/>
		<updated>2007-09-28T19:06:16Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* AWS.pm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 2: Amazon Web Services =&lt;br /&gt;
&lt;br /&gt;
In the [[Contribute:_Plugins/ImportPluginsCSV|last tutorial]] we created an import plugin that took data which needed very little modification to import into the respository. The column names in the [http://en.wikipedia.org/wiki/Comma-Separated_Values CSV] file matched the names of metadata fields present in the repository. In this tutorial we'll look at importing data that needs some modification to be imported, needs more error checking and is obtained in a different way.&lt;br /&gt;
&lt;br /&gt;
We will be using Amazon's E-Commerce Webservice to import books from their website into our respository given a list of [http://en.wikipedia.org/wiki/ASIN ASIN].&lt;br /&gt;
&lt;br /&gt;
We will be accessing the service using a [http://en.wikipedia.org/wiki/REST REST] approach, communicating with the server using URL parameters and retrieving an XML document in response to our request. It is also possible to access their services using [http://en.wikipedia.org/wiki/SOAP SOAP], but that will not be discussed here.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
== Amazon Web Services ==&lt;br /&gt;
To use Amazon's web services you must first signup for an account [http://aws.amazon.com here]. Their site has extensive documentation on the services that they offer as well as example programs including some written in Perl.&lt;br /&gt;
&lt;br /&gt;
== Required Modules ==&lt;br /&gt;
To prepare for this tutorial you should make sure the [http://search.cpan.org/~gaas/libwww-perl-5.805/lib/LWP/UserAgent.pm LWP::UserAgent] module is installed. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan LWP::UserAgent&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= AWS.pm = &lt;br /&gt;
The code in the section below should be placed in a file called CSV.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::AWS;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'AWS';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $input) = @_;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        $input =~ m/([0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        #Get Item Object&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
We will use the URI::Escape module in this plugin. As it is included with EPrints we don't need to check if it exists first.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup a number of values for parameters that will be part of our web service requests. The endpoint variable determines which server will be sent the request. Here we have used the UK server, but by changing the TLD we can use the US, Canadian, German or French servers.&lt;br /&gt;
&lt;br /&gt;
The accesskey stores the access key you will have gained from signing up to Amazon earlier. You should use the normal access key and not the secret one.&lt;br /&gt;
&lt;br /&gt;
Here we use the ItemLookup operation of the AWSECommerceService with the 2007-07-16 version of the service API. Other operations allow searching for items, but here we want to look up specific products. Finally the variable responsegroup determines the amount and nature of the information returned, we select &amp;quot;Large&amp;quot; in this case, giving a lot of information about the item.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
The constructor is similar to the one used for the CSV plugin, except this one will import individual eprints, given an ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Like we imported Text::CSV in the last tutorial, here we import LWP::UserAgent which will be used for making requests to the web service.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
This method is similar to the one used in the CSV plugin, but doesn't have to do quite so much work.&lt;br /&gt;
&lt;br /&gt;
First we create the array to hold our imported eprint ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next we read all the lines in the supplied file handle into our records array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we iterate over each record, running convert_input on it, importing it into our repository and adding the id to our array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we return a List object of the items imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
&lt;br /&gt;
ASINs are strings of decimal digits which may have leading zeroes which identify a product. Here we remove any non-numerical characters which are surrounding the ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $input =~ m/([0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We form the request from the variables we created earlier and the ASIN we have just obtained.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now send the request, by creating a new LWP::UserAgent object, setting its timeout to 30 seconds and then performing the request using HTTP GET.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create a DOM object from the XML document returned.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each request contains an Items element within the root element of the document which contains a Request element. This element contains an element IsValid. This element will contain the value True or False depending on whether a valid request was made or not.&lt;br /&gt;
&lt;br /&gt;
Here we obtain the Request element and check that the IsValid element within it contains the value True. If it doesn't we  call the error method and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName(&amp;quot;Items&amp;quot;)-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The product found with the ItemLookup method is contained within an Item element within the Items element. Here we attempt to get that element and raise the error and return undef if we can't.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each item contains an ItemAttributes element which contains most of the metadata about an item.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For some specialised repositories it might make sense to import DVDs, computer games and electronic equipment, but we're just going to deal with books. The ProductGroup element within the ItemAttributes element tells you what sort of item we're dealing with. We're looking for the value 'Book'.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set a few fields without consulting the imported data. We know this is a book, so we set the type. We assume that it has not been refereed. We also assume it has been published, because we can buy it. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get and set the title.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set the official URL to the Amazon product page. We have to do a bit of extra work using the uri_unescape method from the URI::Escape package to convert URI escape codes into characters.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the ISBN. Note that the ISBN is often the same as the ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the number of pages.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publisher.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publication date and finally return our output hash.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
We'll start by collecting a few ASINS. Go to [http://www.amazon.co.uk Amazon] and pick a few books. The URL for each project page is in the form http://www.amazon.co.uk/Combination-of-title-an-author/dp/ASIN... Collect a few different ASINS.&lt;br /&gt;
&lt;br /&gt;
Now we'll demonstrate importing from Amazon with a few sample ASINs.&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0946719616&lt;br /&gt;
0297843877&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;AWS&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5760</id>
		<title>Contribute: Plugins/ImportPluginsAWS</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5760"/>
		<updated>2007-09-28T19:05:30Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Import Plugin Tutorial 2: Amazon Web Services */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 2: Amazon Web Services =&lt;br /&gt;
&lt;br /&gt;
In the [[Contribute:_Plugins/ImportPluginsCSV|last tutorial]] we created an import plugin that took data which needed very little modification to import into the respository. The column names in the [http://en.wikipedia.org/wiki/Comma-Separated_Values CSV] file matched the names of metadata fields present in the repository. In this tutorial we'll look at importing data that needs some modification to be imported, needs more error checking and is obtained in a different way.&lt;br /&gt;
&lt;br /&gt;
We will be using Amazon's E-Commerce Webservice to import books from their website into our respository given a list of [http://en.wikipedia.org/wiki/ASIN ASIN].&lt;br /&gt;
&lt;br /&gt;
We will be accessing the service using a [http://en.wikipedia.org/wiki/REST REST] approach, communicating with the server using URL parameters and retrieving an XML document in response to our request. It is also possible to access their services using [http://en.wikipedia.org/wiki/SOAP SOAP], but that will not be discussed here.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
== Amazon Web Services ==&lt;br /&gt;
To use Amazon's web services you must first signup for an account [http://aws.amazon.com here]. Their site has extensive documentation on the services that they offer as well as example programs including some written in Perl.&lt;br /&gt;
&lt;br /&gt;
== Required Modules ==&lt;br /&gt;
To prepare for this tutorial you should make sure the [http://search.cpan.org/~gaas/libwww-perl-5.805/lib/LWP/UserAgent.pm LWP::UserAgent] module is installed. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan LWP::UserAgent&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= AWS.pm = &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::AWS;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'AWS';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $input) = @_;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        $input =~ m/([0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        #Get Item Object&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
We will use the URI::Escape module in this plugin. As it is included with EPrints we don't need to check if it exists first.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup a number of values for parameters that will be part of our web service requests. The endpoint variable determines which server will be sent the request. Here we have used the UK server, but by changing the TLD we can use the US, Canadian, German or French servers.&lt;br /&gt;
&lt;br /&gt;
The accesskey stores the access key you will have gained from signing up to Amazon earlier. You should use the normal access key and not the secret one.&lt;br /&gt;
&lt;br /&gt;
Here we use the ItemLookup operation of the AWSECommerceService with the 2007-07-16 version of the service API. Other operations allow searching for items, but here we want to look up specific products. Finally the variable responsegroup determines the amount and nature of the information returned, we select &amp;quot;Large&amp;quot; in this case, giving a lot of information about the item.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
The constructor is similar to the one used for the CSV plugin, except this one will import individual eprints, given an ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Like we imported Text::CSV in the last tutorial, here we import LWP::UserAgent which will be used for making requests to the web service.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
This method is similar to the one used in the CSV plugin, but doesn't have to do quite so much work.&lt;br /&gt;
&lt;br /&gt;
First we create the array to hold our imported eprint ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next we read all the lines in the supplied file handle into our records array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we iterate over each record, running convert_input on it, importing it into our repository and adding the id to our array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we return a List object of the items imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
&lt;br /&gt;
ASINs are strings of decimal digits which may have leading zeroes which identify a product. Here we remove any non-numerical characters which are surrounding the ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $input =~ m/([0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We form the request from the variables we created earlier and the ASIN we have just obtained.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now send the request, by creating a new LWP::UserAgent object, setting its timeout to 30 seconds and then performing the request using HTTP GET.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create a DOM object from the XML document returned.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each request contains an Items element within the root element of the document which contains a Request element. This element contains an element IsValid. This element will contain the value True or False depending on whether a valid request was made or not.&lt;br /&gt;
&lt;br /&gt;
Here we obtain the Request element and check that the IsValid element within it contains the value True. If it doesn't we  call the error method and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName(&amp;quot;Items&amp;quot;)-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The product found with the ItemLookup method is contained within an Item element within the Items element. Here we attempt to get that element and raise the error and return undef if we can't.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each item contains an ItemAttributes element which contains most of the metadata about an item.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For some specialised repositories it might make sense to import DVDs, computer games and electronic equipment, but we're just going to deal with books. The ProductGroup element within the ItemAttributes element tells you what sort of item we're dealing with. We're looking for the value 'Book'.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set a few fields without consulting the imported data. We know this is a book, so we set the type. We assume that it has not been refereed. We also assume it has been published, because we can buy it. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get and set the title.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set the official URL to the Amazon product page. We have to do a bit of extra work using the uri_unescape method from the URI::Escape package to convert URI escape codes into characters.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the ISBN. Note that the ISBN is often the same as the ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the number of pages.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publisher.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publication date and finally return our output hash.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
We'll start by collecting a few ASINS. Go to [http://www.amazon.co.uk Amazon] and pick a few books. The URL for each project page is in the form http://www.amazon.co.uk/Combination-of-title-an-author/dp/ASIN... Collect a few different ASINS.&lt;br /&gt;
&lt;br /&gt;
Now we'll demonstrate importing from Amazon with a few sample ASINs.&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0946719616&lt;br /&gt;
0297843877&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;AWS&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5759</id>
		<title>Contribute: Plugins/ImportPluginsCSV</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5759"/>
		<updated>2007-09-28T19:03:45Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* CSV.pm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 1: CSV =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we will look at creating a relatively simple plugin to import eprints into our repository by reading files containing [http://en.wikipedia.org/wiki/Comma-Separated_Values comma-separated values]. We won't be dealing with documents and files, but will be focusing on importing eprint metadata.&lt;br /&gt;
&lt;br /&gt;
Import plugins are inherently more complicated than export plugins because of the error checking that must be done, however in this example error checking has been kept to a minimum to simplify the example. In a &amp;quot;real&amp;quot; plugin you should check that the appropriate metadata fields are set for a given type of eprint, and unfortunately there appears to be no quick way to do this.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
Create a directory for your import plugins in the main plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/import). The directory used for these examples is called &amp;quot;MyPlugins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~erangel/Text-CSV/CSV.pm Text::CSV] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Text::CSV&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CSV.pm = &lt;br /&gt;
The code in the section below should be placed in a file called CSV.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::CSV;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'CSV';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
        my @fields;&lt;br /&gt;
&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my $plugin = shift;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
                        else&lt;br /&gt;
                        {&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
                }&lt;br /&gt;
                $i++;&lt;br /&gt;
        }&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Modules == &lt;br /&gt;
Here we import the superclass for our plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Inheritance ==&lt;br /&gt;
&lt;br /&gt;
Our plugin will not inherit from the Import class directly, but from the TextFile subclass. This contains some extra file handling code that means we can ignore certain differences in text file formats. If you are creating an import plugin which imports non-text files you should subclass the EPrints::Plugin::Import class directly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For import plugins we must set a 'produce' property, to tell the repository what kinds of objects the plugin can import. This plugin only supports importing lists of eprints, but if it supported importing individual eprints we could add 'dataobj/eprint' to this property. We would then have to implement the &amp;quot;input_dataobj&amp;quot; method. Most plugins implement this method, but it is rarely used in practice. Most imports are done in lists (even if that list only contains one member), via the import items screen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we use a module that is not included with EPrints, Text::CSV, so we import it in a different way. First we check that it is installed, and load it if it is with &amp;quot;EPrints::Utils::require_if_exists&amp;quot;.If it isn't we make the plugin invisible and produce an error message. It is good practice to import non-standard modules in this way rather than with &amp;quot;use&amp;quot;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
Import plugins have to implement a couple of methods to read data from a file or string, manipulate it and turn it into a form which can be imported into the repository. That process will be described below.&lt;br /&gt;
&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
&lt;br /&gt;
This method takes a filehandle, processes it, tries to import DataObjs in to the repository and then returns a List of the DataObjs imported.&lt;br /&gt;
&lt;br /&gt;
This array will be used to create a List of DataObjs later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we open the filehandle passed, and read the lines into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a Text::CSV object to handle the input. Using a dedicated CSV handling package is preferable to using Perl's split function as it handles a number of more complicated scenarios such as commas within records using double quotes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After setting up an array for metadata field names, we attempt to parse the first line of our file. The parse method does not return an array of fields, but reports success or failure. In the event of success we use the fields method to return the last fields parsed. In the event of failure we use the error_input method to get the last error, and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields;&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that the row of column titles has been dealt with we move onto processing each record in the file.&lt;br /&gt;
&lt;br /&gt;
In import plugins the convert_input method converts individual records into a format that can be imported into the repository. That is a hash whose keys are metadata field names and values are the corresponding values. As a row on its own cannot be imported as we don't know to which field each value belongs we have to construct an array to pass to convert_input first. We pass an array whose first element is the fields row and whose second element is the row we want to import.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we call convert_input on our constructed input_data. If the conversion fails we simply move to the next record.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The epdata_to_dataobj method takes our epdata hash reference and turns it into a new DataObj in our repository. If it is successful it returns the new DataObj, whose id we add to our array of ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a List object containing the ids of the records we have successfully imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
This method takes data in a particular format, in this case CSV and transforms it into a hash of metadata field names and values.&lt;br /&gt;
&lt;br /&gt;
We take the second argument to the method and convert the array reference into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup another Text::CSV object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the second element of our array and parse it. This is the record we wish to import. If anything goes wrong we return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the first element and get the field names. We then check that we have the same number of fields names as records.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the hash that we'll return later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For convenience we get the DataSet object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now iterate over the fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the field does not exist we look at the next one, remembering to increment our index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get the MetaField object corresponding to the current field.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
We deal with multiple field types by separating individual values with a semi-colon.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Name fields are dealt with by using regular expressions and constructing a hash from the parts matched. The plugin expects names to be of the form Surname, Forenames, Lineage (Sr, Jr, III etc).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Multiple fields which are not names are just added to the hash as an array reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Non-multiple fields are just added to the hash from the array of fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a hash reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
This is a test title,This is a test abstract&lt;br /&gt;
This is another test title,This is another test abstract&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;CSV&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Embedding commas ==&lt;br /&gt;
If you want to include commas in your imports, which is very likely you must enclose the field in double quotations. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
An interesting article,&amp;quot;Damn it Jim, I'm a Doctor, not a Perl hacker.&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When doing this make sure not to leave any whitespace between a quotation mark and a comma or the import will fail.&lt;br /&gt;
&lt;br /&gt;
== Multiple fields ==&lt;br /&gt;
Multiple field types are handled by separating each individual value by a semi-colon, a simple example of this would be the subjects field.&lt;br /&gt;
&lt;br /&gt;
Go back to the import items and proceed as before, but typing this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
abstract,title,subjects&lt;br /&gt;
Testing,Testing,AI;C;M;F;P&lt;br /&gt;
Testing,Testing,AC;DC&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After the records have been imported examine each one on the View Item screen. You will find that a list of subjects are given, and that a more descriptive name is given than the code we imported.&lt;br /&gt;
&lt;br /&gt;
== Compound fields ==&lt;br /&gt;
&lt;br /&gt;
Compound fields are fields that have subfields within them, each with their own name. You don't compound fields in one go, but set the components individually. Subfields have names of the form  mainfieldname_subfieldname.&lt;br /&gt;
&lt;br /&gt;
One of the most commonly used compound fields is the &amp;quot;Creators&amp;quot; field. It has a names subfield &amp;quot;creators_name&amp;quot; and an ID subfield &amp;quot;creators_id&amp;quot; which is most often used for email addresses.&lt;br /&gt;
&lt;br /&gt;
Here is an example of setting the creators field:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,creators_names,creators_ids,&lt;br /&gt;
Setting compound fields.,&amp;quot;Bloggs, Joe;Doe, John&amp;quot;,&amp;quot;joe@bloggs.com;john@doe.com&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you import this record and then examine the view items screen you will find that the &amp;quot;Creators&amp;quot; field has been setup with the values displayed in a table.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5758</id>
		<title>Contribute: Plugins/ImportPluginsCSV</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5758"/>
		<updated>2007-09-28T19:03:14Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Before You Start */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 1: CSV =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we will look at creating a relatively simple plugin to import eprints into our repository by reading files containing [http://en.wikipedia.org/wiki/Comma-Separated_Values comma-separated values]. We won't be dealing with documents and files, but will be focusing on importing eprint metadata.&lt;br /&gt;
&lt;br /&gt;
Import plugins are inherently more complicated than export plugins because of the error checking that must be done, however in this example error checking has been kept to a minimum to simplify the example. In a &amp;quot;real&amp;quot; plugin you should check that the appropriate metadata fields are set for a given type of eprint, and unfortunately there appears to be no quick way to do this.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
Create a directory for your import plugins in the main plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/import). The directory used for these examples is called &amp;quot;MyPlugins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~erangel/Text-CSV/CSV.pm Text::CSV] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Text::CSV&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CSV.pm = &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::CSV;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'CSV';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
        my @fields;&lt;br /&gt;
&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my $plugin = shift;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
                        else&lt;br /&gt;
                        {&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
                }&lt;br /&gt;
                $i++;&lt;br /&gt;
        }&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Modules == &lt;br /&gt;
Here we import the superclass for our plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Inheritance ==&lt;br /&gt;
&lt;br /&gt;
Our plugin will not inherit from the Import class directly, but from the TextFile subclass. This contains some extra file handling code that means we can ignore certain differences in text file formats. If you are creating an import plugin which imports non-text files you should subclass the EPrints::Plugin::Import class directly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For import plugins we must set a 'produce' property, to tell the repository what kinds of objects the plugin can import. This plugin only supports importing lists of eprints, but if it supported importing individual eprints we could add 'dataobj/eprint' to this property. We would then have to implement the &amp;quot;input_dataobj&amp;quot; method. Most plugins implement this method, but it is rarely used in practice. Most imports are done in lists (even if that list only contains one member), via the import items screen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we use a module that is not included with EPrints, Text::CSV, so we import it in a different way. First we check that it is installed, and load it if it is with &amp;quot;EPrints::Utils::require_if_exists&amp;quot;.If it isn't we make the plugin invisible and produce an error message. It is good practice to import non-standard modules in this way rather than with &amp;quot;use&amp;quot;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
Import plugins have to implement a couple of methods to read data from a file or string, manipulate it and turn it into a form which can be imported into the repository. That process will be described below.&lt;br /&gt;
&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
&lt;br /&gt;
This method takes a filehandle, processes it, tries to import DataObjs in to the repository and then returns a List of the DataObjs imported.&lt;br /&gt;
&lt;br /&gt;
This array will be used to create a List of DataObjs later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we open the filehandle passed, and read the lines into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a Text::CSV object to handle the input. Using a dedicated CSV handling package is preferable to using Perl's split function as it handles a number of more complicated scenarios such as commas within records using double quotes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After setting up an array for metadata field names, we attempt to parse the first line of our file. The parse method does not return an array of fields, but reports success or failure. In the event of success we use the fields method to return the last fields parsed. In the event of failure we use the error_input method to get the last error, and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields;&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that the row of column titles has been dealt with we move onto processing each record in the file.&lt;br /&gt;
&lt;br /&gt;
In import plugins the convert_input method converts individual records into a format that can be imported into the repository. That is a hash whose keys are metadata field names and values are the corresponding values. As a row on its own cannot be imported as we don't know to which field each value belongs we have to construct an array to pass to convert_input first. We pass an array whose first element is the fields row and whose second element is the row we want to import.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we call convert_input on our constructed input_data. If the conversion fails we simply move to the next record.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The epdata_to_dataobj method takes our epdata hash reference and turns it into a new DataObj in our repository. If it is successful it returns the new DataObj, whose id we add to our array of ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a List object containing the ids of the records we have successfully imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
This method takes data in a particular format, in this case CSV and transforms it into a hash of metadata field names and values.&lt;br /&gt;
&lt;br /&gt;
We take the second argument to the method and convert the array reference into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup another Text::CSV object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the second element of our array and parse it. This is the record we wish to import. If anything goes wrong we return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the first element and get the field names. We then check that we have the same number of fields names as records.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the hash that we'll return later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For convenience we get the DataSet object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now iterate over the fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the field does not exist we look at the next one, remembering to increment our index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get the MetaField object corresponding to the current field.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
We deal with multiple field types by separating individual values with a semi-colon.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Name fields are dealt with by using regular expressions and constructing a hash from the parts matched. The plugin expects names to be of the form Surname, Forenames, Lineage (Sr, Jr, III etc).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Multiple fields which are not names are just added to the hash as an array reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Non-multiple fields are just added to the hash from the array of fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a hash reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
This is a test title,This is a test abstract&lt;br /&gt;
This is another test title,This is another test abstract&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;CSV&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Embedding commas ==&lt;br /&gt;
If you want to include commas in your imports, which is very likely you must enclose the field in double quotations. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
An interesting article,&amp;quot;Damn it Jim, I'm a Doctor, not a Perl hacker.&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When doing this make sure not to leave any whitespace between a quotation mark and a comma or the import will fail.&lt;br /&gt;
&lt;br /&gt;
== Multiple fields ==&lt;br /&gt;
Multiple field types are handled by separating each individual value by a semi-colon, a simple example of this would be the subjects field.&lt;br /&gt;
&lt;br /&gt;
Go back to the import items and proceed as before, but typing this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
abstract,title,subjects&lt;br /&gt;
Testing,Testing,AI;C;M;F;P&lt;br /&gt;
Testing,Testing,AC;DC&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After the records have been imported examine each one on the View Item screen. You will find that a list of subjects are given, and that a more descriptive name is given than the code we imported.&lt;br /&gt;
&lt;br /&gt;
== Compound fields ==&lt;br /&gt;
&lt;br /&gt;
Compound fields are fields that have subfields within them, each with their own name. You don't compound fields in one go, but set the components individually. Subfields have names of the form  mainfieldname_subfieldname.&lt;br /&gt;
&lt;br /&gt;
One of the most commonly used compound fields is the &amp;quot;Creators&amp;quot; field. It has a names subfield &amp;quot;creators_name&amp;quot; and an ID subfield &amp;quot;creators_id&amp;quot; which is most often used for email addresses.&lt;br /&gt;
&lt;br /&gt;
Here is an example of setting the creators field:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,creators_names,creators_ids,&lt;br /&gt;
Setting compound fields.,&amp;quot;Bloggs, Joe;Doe, John&amp;quot;,&amp;quot;joe@bloggs.com;john@doe.com&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you import this record and then examine the view items screen you will find that the &amp;quot;Creators&amp;quot; field has been setup with the values displayed in a table.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5757</id>
		<title>Contribute: Plugins/ExportPluginsHelloOld</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5757"/>
		<updated>2007-09-28T19:02:56Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Before You Start */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Export Plugin Tutorial 1: &amp;quot;Hello, World!&amp;quot; =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn how to create a simple export plugin for EPrints, which will generate a list of titles from the results of a search. A basic knowledge of Perl is needed, but the code will be explained fully.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
Create a directory for your export plugins in the main export plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/Export). The directory used for these examples is called &amp;quot;MyPlugins&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= HelloExport.pm =&lt;br /&gt;
&lt;br /&gt;
Replace MyPlugins with the name of the directory you have decided to put your export plugins in and place the code below in a file called HelloExport.pm in that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloExport;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== In More Detail ==&lt;br /&gt;
== Housekeeping ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::Foo::HelloExport;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Export plugins need to inherit from the EPrints::Plugin::Export class.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
After the implicit class reference, a hash of options is given.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a new export plugin by calling the Eprints::Plugin::Export constructor&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we set a number of fields to register our new plugin.&lt;br /&gt;
&lt;br /&gt;
This is the name that will appear in the export dropdown menu. The name should therefore be short and descriptive.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The accept field is a list containing the types of objects this&lt;br /&gt;
plugin can deal with. In this case lists of eprints and individual&lt;br /&gt;
eprints.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The visible field denotes the class of user which will be able to see the plugin.&lt;br /&gt;
For most export plugins the value 'all' will be required, allowing&lt;br /&gt;
all users to see and use the plugin. A value of 'staff' would&lt;br /&gt;
make the plugin visible only to repository staff.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The suffix field contains the extension of files exported by the plugin.&lt;br /&gt;
&lt;br /&gt;
The mimetype field defines the [http://en.wikipedia.org/wiki/MIME MIME] type of the files exported by the plugin&lt;br /&gt;
You can also specify file encoding, for example 'text/plain; charset=utf-8' to specify plain text, encoded using UTF-8.[http://en.wikipedia.org/wiki/UTF-8 UTF-8] is a Unicode character encoding capable of expressing characters from a large number of character sets and so is usually preferable to [http://en.wikipedia.org/wiki/ASCII ASCII]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then return our plugin reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return $self;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Processing DataObjs ==&lt;br /&gt;
&lt;br /&gt;
This method handles the export of each DataObj. DataObjs make up most of the content of an EPrint repository. The three main types are EPrint defining individual eprints, Document defining collections of one or more files belonging to an EPrints, and User which defines users of the repository.&lt;br /&gt;
&lt;br /&gt;
Besides an implicit reference to the Plugin object, this method is also provided with a reference to an individual DataObj. It is called by several [http://en.wikipedia.org/wiki/Common_Gateway_Interface CGI] and command line scripts to export single DataObjs, for instance the item control screen for repository staff. It is also called by the list handling method on each DataObj in a list, for example the results of a search. That will be explained in [[Contribute:_Plugins/ExportPluginsList|the next tutorial]].&lt;br /&gt;
&lt;br /&gt;
In the example below we get the title of each DataObj, but there are large number of fields which you can extract from each DataObj. For example try changing &amp;quot;title&amp;quot; to &amp;quot;abstract&amp;quot; to print the abstract of each eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        # Return a scalar containing the title.&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Finishing Off ==&lt;br /&gt;
&lt;br /&gt;
Standard Perl package requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your web server and perform a search.&lt;br /&gt;
&lt;br /&gt;
If all is well your plugin should appear in the dropdown menu. Select it and click export. As long as the search provided some results, you should get a list of EPrint titles returned.&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphello.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5756</id>
		<title>Contribute: Plugins/ImportPluginsCSV</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5756"/>
		<updated>2007-09-28T19:02:00Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Import Plugin Tutorial 1: CSV */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 1: CSV =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we will look at creating a relatively simple plugin to import eprints into our repository by reading files containing [http://en.wikipedia.org/wiki/Comma-Separated_Values comma-separated values]. We won't be dealing with documents and files, but will be focusing on importing eprint metadata.&lt;br /&gt;
&lt;br /&gt;
Import plugins are inherently more complicated than export plugins because of the error checking that must be done, however in this example error checking has been kept to a minimum to simplify the example. In a &amp;quot;real&amp;quot; plugin you should check that the appropriate metadata fields are set for a given type of eprint, and unfortunately there appears to be no quick way to do this.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
It is sensible to separate the plugins you create for EPrints from those included with it. Create a directory for your import plugins in the main plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/import) for example /opt/eprints3/perl_lib/EPrints/Plugin/import/MyPlugins.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~erangel/Text-CSV/CSV.pm Text::CSV] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Text::CSV&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CSV.pm = &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::CSV;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'CSV';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
        my @fields;&lt;br /&gt;
&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my $plugin = shift;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
                        else&lt;br /&gt;
                        {&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
                }&lt;br /&gt;
                $i++;&lt;br /&gt;
        }&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Modules == &lt;br /&gt;
Here we import the superclass for our plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Inheritance ==&lt;br /&gt;
&lt;br /&gt;
Our plugin will not inherit from the Import class directly, but from the TextFile subclass. This contains some extra file handling code that means we can ignore certain differences in text file formats. If you are creating an import plugin which imports non-text files you should subclass the EPrints::Plugin::Import class directly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For import plugins we must set a 'produce' property, to tell the repository what kinds of objects the plugin can import. This plugin only supports importing lists of eprints, but if it supported importing individual eprints we could add 'dataobj/eprint' to this property. We would then have to implement the &amp;quot;input_dataobj&amp;quot; method. Most plugins implement this method, but it is rarely used in practice. Most imports are done in lists (even if that list only contains one member), via the import items screen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we use a module that is not included with EPrints, Text::CSV, so we import it in a different way. First we check that it is installed, and load it if it is with &amp;quot;EPrints::Utils::require_if_exists&amp;quot;.If it isn't we make the plugin invisible and produce an error message. It is good practice to import non-standard modules in this way rather than with &amp;quot;use&amp;quot;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
Import plugins have to implement a couple of methods to read data from a file or string, manipulate it and turn it into a form which can be imported into the repository. That process will be described below.&lt;br /&gt;
&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
&lt;br /&gt;
This method takes a filehandle, processes it, tries to import DataObjs in to the repository and then returns a List of the DataObjs imported.&lt;br /&gt;
&lt;br /&gt;
This array will be used to create a List of DataObjs later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we open the filehandle passed, and read the lines into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a Text::CSV object to handle the input. Using a dedicated CSV handling package is preferable to using Perl's split function as it handles a number of more complicated scenarios such as commas within records using double quotes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After setting up an array for metadata field names, we attempt to parse the first line of our file. The parse method does not return an array of fields, but reports success or failure. In the event of success we use the fields method to return the last fields parsed. In the event of failure we use the error_input method to get the last error, and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields;&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that the row of column titles has been dealt with we move onto processing each record in the file.&lt;br /&gt;
&lt;br /&gt;
In import plugins the convert_input method converts individual records into a format that can be imported into the repository. That is a hash whose keys are metadata field names and values are the corresponding values. As a row on its own cannot be imported as we don't know to which field each value belongs we have to construct an array to pass to convert_input first. We pass an array whose first element is the fields row and whose second element is the row we want to import.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we call convert_input on our constructed input_data. If the conversion fails we simply move to the next record.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The epdata_to_dataobj method takes our epdata hash reference and turns it into a new DataObj in our repository. If it is successful it returns the new DataObj, whose id we add to our array of ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a List object containing the ids of the records we have successfully imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
This method takes data in a particular format, in this case CSV and transforms it into a hash of metadata field names and values.&lt;br /&gt;
&lt;br /&gt;
We take the second argument to the method and convert the array reference into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup another Text::CSV object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the second element of our array and parse it. This is the record we wish to import. If anything goes wrong we return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the first element and get the field names. We then check that we have the same number of fields names as records.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the hash that we'll return later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For convenience we get the DataSet object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now iterate over the fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the field does not exist we look at the next one, remembering to increment our index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get the MetaField object corresponding to the current field.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
We deal with multiple field types by separating individual values with a semi-colon.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Name fields are dealt with by using regular expressions and constructing a hash from the parts matched. The plugin expects names to be of the form Surname, Forenames, Lineage (Sr, Jr, III etc).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Multiple fields which are not names are just added to the hash as an array reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Non-multiple fields are just added to the hash from the array of fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a hash reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
This is a test title,This is a test abstract&lt;br /&gt;
This is another test title,This is another test abstract&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;CSV&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Embedding commas ==&lt;br /&gt;
If you want to include commas in your imports, which is very likely you must enclose the field in double quotations. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
An interesting article,&amp;quot;Damn it Jim, I'm a Doctor, not a Perl hacker.&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When doing this make sure not to leave any whitespace between a quotation mark and a comma or the import will fail.&lt;br /&gt;
&lt;br /&gt;
== Multiple fields ==&lt;br /&gt;
Multiple field types are handled by separating each individual value by a semi-colon, a simple example of this would be the subjects field.&lt;br /&gt;
&lt;br /&gt;
Go back to the import items and proceed as before, but typing this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
abstract,title,subjects&lt;br /&gt;
Testing,Testing,AI;C;M;F;P&lt;br /&gt;
Testing,Testing,AC;DC&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After the records have been imported examine each one on the View Item screen. You will find that a list of subjects are given, and that a more descriptive name is given than the code we imported.&lt;br /&gt;
&lt;br /&gt;
== Compound fields ==&lt;br /&gt;
&lt;br /&gt;
Compound fields are fields that have subfields within them, each with their own name. You don't compound fields in one go, but set the components individually. Subfields have names of the form  mainfieldname_subfieldname.&lt;br /&gt;
&lt;br /&gt;
One of the most commonly used compound fields is the &amp;quot;Creators&amp;quot; field. It has a names subfield &amp;quot;creators_name&amp;quot; and an ID subfield &amp;quot;creators_id&amp;quot; which is most often used for email addresses.&lt;br /&gt;
&lt;br /&gt;
Here is an example of setting the creators field:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,creators_names,creators_ids,&lt;br /&gt;
Setting compound fields.,&amp;quot;Bloggs, Joe;Doe, John&amp;quot;,&amp;quot;joe@bloggs.com;john@doe.com&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you import this record and then examine the view items screen you will find that the &amp;quot;Creators&amp;quot; field has been setup with the values displayed in a table.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsZip&amp;diff=5755</id>
		<title>Contribute: Plugins/ExportPluginsZip</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsZip&amp;diff=5755"/>
		<updated>2007-09-28T19:01:13Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Handling DataObjs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 5: Zip =&lt;br /&gt;
In this tutorial we'll look at packaging the results of a search into a Zip file. We'll create a directory for each eprint, and a sub-directory for each document belonging to that eprint. We'll also add an HTML index file to the archive to make it easier to navigate.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~miyagawa/Archive-Any-Create-0.02/lib/Archive/Any/Create.pm Archive::Any::Create] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Archive::Any::Create&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zip.pm =&lt;br /&gt;
The code in the section below should be placed in a file called Zip.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::Zip;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use Archive::Any::Create;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
  my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
  my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
  $self-&amp;gt;{name} = 'Zip';&lt;br /&gt;
  $self-&amp;gt;{accept} = [ 'list/eprint' ];&lt;br /&gt;
  $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.zip';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/zip';&lt;br /&gt;
&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Archive::Any::Create');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Archive::Any::Create';&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
  my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
  my $archive = '';&lt;br /&gt;
  open (my $FH, '&amp;gt;', \$archive) or&lt;br /&gt;
    die(&amp;quot;Could not create filehandle: $!&amp;quot;);&lt;br /&gt;
  my $zip = Archive::Any::Create-&amp;gt;new;&lt;br /&gt;
&lt;br /&gt;
  my $index = &amp;lt;&amp;lt;END;&lt;br /&gt;
  &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
      &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;title&amp;gt;EPrints Search Results&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
  my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
    my $heading = $session-&amp;gt;make_element('h2');&lt;br /&gt;
    $heading-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
    $div-&amp;gt;appendChild($heading);&lt;br /&gt;
&lt;br /&gt;
    my $uldoc = $session-&amp;gt;make_element('ul');&lt;br /&gt;
    $div-&amp;gt;appendChild($uldoc);&lt;br /&gt;
&lt;br /&gt;
    my $dirpath = 'eprints-search/'.$dataobj-&amp;gt;get_id().'/';&lt;br /&gt;
&lt;br /&gt;
    my $i = 1;&lt;br /&gt;
    foreach my $doc ($dataobj-&amp;gt;get_all_documents)&lt;br /&gt;
    {&lt;br /&gt;
      my $subdirpath = $dirpath.&amp;quot;doc$i/&amp;quot;;&lt;br /&gt;
      my %files = $doc-&amp;gt;files;&lt;br /&gt;
&lt;br /&gt;
      my $lidoc = $session-&amp;gt;make_element('li');&lt;br /&gt;
      $uldoc-&amp;gt;appendChild($lidoc);&lt;br /&gt;
&lt;br /&gt;
      my $adoc = $session-&amp;gt;make_element('a', href=&amp;gt;$dataobj-&amp;gt;get_id.&amp;quot;/doc$i/&amp;quot;.$doc-&amp;gt;get_main);&lt;br /&gt;
      $lidoc-&amp;gt;appendChild($adoc);&lt;br /&gt;
&lt;br /&gt;
      if ($doc-&amp;gt;exists_and_set('formatdesc'))&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_value('formatdesc')));&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_main));&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      foreach my $filename (sort keys %files)&lt;br /&gt;
      {&lt;br /&gt;
       my $filepath = $subdirpath.$filename;&lt;br /&gt;
        my $file = $doc-&amp;gt;local_path.'/'.$filename;&lt;br /&gt;
&lt;br /&gt;
        if (-d $file)&lt;br /&gt;
        {&lt;br /&gt;
          next;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $data = '';&lt;br /&gt;
        open (my $datafh ,'&amp;gt;', \$data);&lt;br /&gt;
&lt;br /&gt;
        open (INFH, &amp;quot;&amp;lt;$file&amp;quot;) or die (&amp;quot;Could not open file $file&amp;quot;);&lt;br /&gt;
        while (&amp;lt;INFH&amp;gt;)&lt;br /&gt;
        {&lt;br /&gt;
          print {$datafh} $_;&lt;br /&gt;
        }&lt;br /&gt;
        close INFH;&lt;br /&gt;
&lt;br /&gt;
        $zip-&amp;gt;add_file($filepath, $data);&lt;br /&gt;
      }&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $index .= EPrints::XML::to_string($div);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $index .= '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
  $zip-&amp;gt;add_file('eprints-search/index.htm',$index);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $zip-&amp;gt;write_filehandle($opts{fh},'zip');&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
  $zip-&amp;gt;write_filehandle($FH,'zip');&lt;br /&gt;
  return $archive;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For the sake of simplicity this plugin will only deal with lists of eprints. This avoids some code duplication, and it would be fairly easy to modify the plugin to deal with both individual eprints and lists of eprints sensibly.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{accept} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The file extension and [http://en.wikipedia.org/wiki/MIME MIME] type are set to values appropriate for Zip files.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.zip';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/zip';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to import a module that is not included with EPrints for creating zip files. We use the EPrints::Utils::require_if_exists function to check if the module exists, and load it if it does. We then check the value returned from that function, and make the plugin invisible if it failed.&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Archive::Any::Create');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Archive::Any::Create';&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List Handling ==&lt;br /&gt;
=== Setting Up ===&lt;br /&gt;
Here we setup an in-memory file for the Zip, and create an Archive object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $archive = '';&lt;br /&gt;
  open (my $FH, '&amp;gt;', \$archive) or&lt;br /&gt;
    die(&amp;quot;Could not create filehandle: $!&amp;quot;);&lt;br /&gt;
  my $zip = Archive::Any::Create-&amp;gt;new;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navigation ===&lt;br /&gt;
Here we begin to setup the HTML file that we'll add to our archive for navigation. First we setup a header.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $index = &amp;lt;&amp;lt;END;&lt;br /&gt;
  &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
      &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;title&amp;gt;EPrints Search Results&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we get the Session object, we'll be using it to manipulate [http://en.wikipedia.org/wiki/Document_Object_Model DOM] objects later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handling DataObjs ===&lt;br /&gt;
We loop over the DataObjs as we have done before.&lt;br /&gt;
&lt;br /&gt;
This time we setup some [http://en.wikipedia.org/wiki/Document_Object_Model DOM] objects to be added to our index. Each eprint will have it's title printed out followed by a list of documents.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
    my $heading = $session-&amp;gt;make_element('h2');&lt;br /&gt;
    $heading-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
    $div-&amp;gt;appendChild($heading);&lt;br /&gt;
&lt;br /&gt;
    my $uldoc = $session-&amp;gt;make_element('ul');&lt;br /&gt;
    $div-&amp;gt;appendChild($uldoc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a directory for each eprint. Note it is not necessary to explicitly create a directory, we simply have to set the appropriate file path. However this means that if you do not add files to a certain directory it will not be created, rather than having an empty directory for a given eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $dirpath = 'eprints-search/'.$dataobj-&amp;gt;get_id().'/';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dealing With Documents ====&lt;br /&gt;
We then loop over all the documents belonging to each DataObj. The get_all_documents method returns an array of Document objects.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $i = 1;&lt;br /&gt;
    foreach my $doc ($dataobj-&amp;gt;get_all_documents)&lt;br /&gt;
    {&lt;br /&gt;
      my $subdirpath = $dirpath.&amp;quot;doc$i/&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we create a list item for the document containing a link to the main file. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      my $lidoc = $session-&amp;gt;make_element('li');&lt;br /&gt;
      $uldoc-&amp;gt;appendChild($lidoc);&lt;br /&gt;
&lt;br /&gt;
      my $adoc = $session-&amp;gt;make_element('a', href=&amp;gt;$dataobj-&amp;gt;get_id.&amp;quot;/doc$i/&amp;quot;.$doc-&amp;gt;get_main);&lt;br /&gt;
      $lidoc-&amp;gt;appendChild($adoc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If a description of the main file has been set we use that as the link text, otherwise we use the filename.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      if ($doc-&amp;gt;exists_and_set('formatdesc'))&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_value('formatdesc')));&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_main));&lt;br /&gt;
      }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dealing With Files ====&lt;br /&gt;
The files method of the Document object returns a hash whose keys are file names and values are file sizes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      my %files = $doc-&amp;gt;files;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We loop over each file belonging to the document, in most cases there will only be one file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      foreach my $filename (sort keys %files)&lt;br /&gt;
      {&lt;br /&gt;
        my $filepath = $subdirpath.$filename;&lt;br /&gt;
        my $file = $doc-&amp;gt;local_path.'/'.$filename;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to read the contents of the file and add it to a file in the zip. First we'll create another in-memory file to hold the contents.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $data = '';&lt;br /&gt;
        open (my $datafh ,'&amp;gt;', \$data);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We open our file and print it straight out to our in-memory file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        open (INFH, &amp;quot;&amp;lt;$file&amp;quot;) or die (&amp;quot;Could not open file $file&amp;quot;);&lt;br /&gt;
        while (&amp;lt;INFH&amp;gt;)&lt;br /&gt;
        {&lt;br /&gt;
          print {$datafh} $_;&lt;br /&gt;
        }&lt;br /&gt;
        close INFH;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we add the file data to our file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $zip-&amp;gt;add_file($filepath, $data);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we add the [http://en.wikipedia.org/wiki/Document_Object_Model DOM] object for our eprint to the index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $index .= EPrints::XML::to_string($div);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Finishing Off ===&lt;br /&gt;
After finishing off our index file we add it to the zip file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $index .= '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
  $zip-&amp;gt;add_file('eprints-search/index.htm',$index);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a file handle has been provided we write to it, otherwise we write to the scalar file handle created earlier. We then return in the usual fashion.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $zip-&amp;gt;write_filehandle($opts{fh},'zip');&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
  $zip-&amp;gt;write_filehandle($FH,'zip');&lt;br /&gt;
  return $archive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Expzipv2.png]]&lt;br /&gt;
&lt;br /&gt;
The accompanying HTML index.&lt;br /&gt;
&lt;br /&gt;
[[Image:Expzip2.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsZip&amp;diff=5754</id>
		<title>Contribute: Plugins/ExportPluginsZip</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsZip&amp;diff=5754"/>
		<updated>2007-09-28T19:00:43Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Navigation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 5: Zip =&lt;br /&gt;
In this tutorial we'll look at packaging the results of a search into a Zip file. We'll create a directory for each eprint, and a sub-directory for each document belonging to that eprint. We'll also add an HTML index file to the archive to make it easier to navigate.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~miyagawa/Archive-Any-Create-0.02/lib/Archive/Any/Create.pm Archive::Any::Create] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Archive::Any::Create&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zip.pm =&lt;br /&gt;
The code in the section below should be placed in a file called Zip.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::Zip;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use Archive::Any::Create;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
  my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
  my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
  $self-&amp;gt;{name} = 'Zip';&lt;br /&gt;
  $self-&amp;gt;{accept} = [ 'list/eprint' ];&lt;br /&gt;
  $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.zip';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/zip';&lt;br /&gt;
&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Archive::Any::Create');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Archive::Any::Create';&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
  my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
  my $archive = '';&lt;br /&gt;
  open (my $FH, '&amp;gt;', \$archive) or&lt;br /&gt;
    die(&amp;quot;Could not create filehandle: $!&amp;quot;);&lt;br /&gt;
  my $zip = Archive::Any::Create-&amp;gt;new;&lt;br /&gt;
&lt;br /&gt;
  my $index = &amp;lt;&amp;lt;END;&lt;br /&gt;
  &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
      &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;title&amp;gt;EPrints Search Results&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
  my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
    my $heading = $session-&amp;gt;make_element('h2');&lt;br /&gt;
    $heading-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
    $div-&amp;gt;appendChild($heading);&lt;br /&gt;
&lt;br /&gt;
    my $uldoc = $session-&amp;gt;make_element('ul');&lt;br /&gt;
    $div-&amp;gt;appendChild($uldoc);&lt;br /&gt;
&lt;br /&gt;
    my $dirpath = 'eprints-search/'.$dataobj-&amp;gt;get_id().'/';&lt;br /&gt;
&lt;br /&gt;
    my $i = 1;&lt;br /&gt;
    foreach my $doc ($dataobj-&amp;gt;get_all_documents)&lt;br /&gt;
    {&lt;br /&gt;
      my $subdirpath = $dirpath.&amp;quot;doc$i/&amp;quot;;&lt;br /&gt;
      my %files = $doc-&amp;gt;files;&lt;br /&gt;
&lt;br /&gt;
      my $lidoc = $session-&amp;gt;make_element('li');&lt;br /&gt;
      $uldoc-&amp;gt;appendChild($lidoc);&lt;br /&gt;
&lt;br /&gt;
      my $adoc = $session-&amp;gt;make_element('a', href=&amp;gt;$dataobj-&amp;gt;get_id.&amp;quot;/doc$i/&amp;quot;.$doc-&amp;gt;get_main);&lt;br /&gt;
      $lidoc-&amp;gt;appendChild($adoc);&lt;br /&gt;
&lt;br /&gt;
      if ($doc-&amp;gt;exists_and_set('formatdesc'))&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_value('formatdesc')));&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_main));&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      foreach my $filename (sort keys %files)&lt;br /&gt;
      {&lt;br /&gt;
       my $filepath = $subdirpath.$filename;&lt;br /&gt;
        my $file = $doc-&amp;gt;local_path.'/'.$filename;&lt;br /&gt;
&lt;br /&gt;
        if (-d $file)&lt;br /&gt;
        {&lt;br /&gt;
          next;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $data = '';&lt;br /&gt;
        open (my $datafh ,'&amp;gt;', \$data);&lt;br /&gt;
&lt;br /&gt;
        open (INFH, &amp;quot;&amp;lt;$file&amp;quot;) or die (&amp;quot;Could not open file $file&amp;quot;);&lt;br /&gt;
        while (&amp;lt;INFH&amp;gt;)&lt;br /&gt;
        {&lt;br /&gt;
          print {$datafh} $_;&lt;br /&gt;
        }&lt;br /&gt;
        close INFH;&lt;br /&gt;
&lt;br /&gt;
        $zip-&amp;gt;add_file($filepath, $data);&lt;br /&gt;
      }&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $index .= EPrints::XML::to_string($div);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $index .= '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
  $zip-&amp;gt;add_file('eprints-search/index.htm',$index);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $zip-&amp;gt;write_filehandle($opts{fh},'zip');&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
  $zip-&amp;gt;write_filehandle($FH,'zip');&lt;br /&gt;
  return $archive;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For the sake of simplicity this plugin will only deal with lists of eprints. This avoids some code duplication, and it would be fairly easy to modify the plugin to deal with both individual eprints and lists of eprints sensibly.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{accept} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The file extension and [http://en.wikipedia.org/wiki/MIME MIME] type are set to values appropriate for Zip files.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.zip';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/zip';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to import a module that is not included with EPrints for creating zip files. We use the EPrints::Utils::require_if_exists function to check if the module exists, and load it if it does. We then check the value returned from that function, and make the plugin invisible if it failed.&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Archive::Any::Create');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Archive::Any::Create';&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List Handling ==&lt;br /&gt;
=== Setting Up ===&lt;br /&gt;
Here we setup an in-memory file for the Zip, and create an Archive object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $archive = '';&lt;br /&gt;
  open (my $FH, '&amp;gt;', \$archive) or&lt;br /&gt;
    die(&amp;quot;Could not create filehandle: $!&amp;quot;);&lt;br /&gt;
  my $zip = Archive::Any::Create-&amp;gt;new;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navigation ===&lt;br /&gt;
Here we begin to setup the HTML file that we'll add to our archive for navigation. First we setup a header.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $index = &amp;lt;&amp;lt;END;&lt;br /&gt;
  &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
      &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;title&amp;gt;EPrints Search Results&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we get the Session object, we'll be using it to manipulate [http://en.wikipedia.org/wiki/Document_Object_Model DOM] objects later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handling DataObjs ===&lt;br /&gt;
We loop over the DataObjs as we have done before.&lt;br /&gt;
&lt;br /&gt;
This time we setup some DOM objects to be added to our index. Each eprint will have it's title printed out followed by a list of documents.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
    my $heading = $session-&amp;gt;make_element('h2');&lt;br /&gt;
    $heading-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
    $div-&amp;gt;appendChild($heading);&lt;br /&gt;
&lt;br /&gt;
    my $uldoc = $session-&amp;gt;make_element('ul');&lt;br /&gt;
    $div-&amp;gt;appendChild($uldoc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a directory for each eprint. Note it is not necessary to explicitly create a directory, we simply have to set the appropriate file path. However this means that if you do not add files to a certain directory it will not be created, rather than having an empty directory for a given eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $dirpath = 'eprints-search/'.$dataobj-&amp;gt;get_id().'/';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dealing With Documents ====&lt;br /&gt;
We then loop over all the documents belonging to each DataObj. The get_all_documents method returns an array of Document objects.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $i = 1;&lt;br /&gt;
    foreach my $doc ($dataobj-&amp;gt;get_all_documents)&lt;br /&gt;
    {&lt;br /&gt;
      my $subdirpath = $dirpath.&amp;quot;doc$i/&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we create a list item for the document containing a link to the main file. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      my $lidoc = $session-&amp;gt;make_element('li');&lt;br /&gt;
      $uldoc-&amp;gt;appendChild($lidoc);&lt;br /&gt;
&lt;br /&gt;
      my $adoc = $session-&amp;gt;make_element('a', href=&amp;gt;$dataobj-&amp;gt;get_id.&amp;quot;/doc$i/&amp;quot;.$doc-&amp;gt;get_main);&lt;br /&gt;
      $lidoc-&amp;gt;appendChild($adoc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If a description of the main file has been set we use that as the link text, otherwise we use the filename.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      if ($doc-&amp;gt;exists_and_set('formatdesc'))&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_value('formatdesc')));&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_main));&lt;br /&gt;
      }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dealing With Files ====&lt;br /&gt;
The files method of the Document object returns a hash whose keys are file names and values are file sizes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      my %files = $doc-&amp;gt;files;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We loop over each file belonging to the document, in most cases there will only be one file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      foreach my $filename (sort keys %files)&lt;br /&gt;
      {&lt;br /&gt;
        my $filepath = $subdirpath.$filename;&lt;br /&gt;
        my $file = $doc-&amp;gt;local_path.'/'.$filename;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to read the contents of the file and add it to a file in the zip. First we'll create another in-memory file to hold the contents.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $data = '';&lt;br /&gt;
        open (my $datafh ,'&amp;gt;', \$data);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We open our file and print it straight out to our in-memory file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        open (INFH, &amp;quot;&amp;lt;$file&amp;quot;) or die (&amp;quot;Could not open file $file&amp;quot;);&lt;br /&gt;
        while (&amp;lt;INFH&amp;gt;)&lt;br /&gt;
        {&lt;br /&gt;
          print {$datafh} $_;&lt;br /&gt;
        }&lt;br /&gt;
        close INFH;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we add the file data to our file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $zip-&amp;gt;add_file($filepath, $data);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we add the DOM object for our eprint to the index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $index .= EPrints::XML::to_string($div);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Finishing Off ===&lt;br /&gt;
After finishing off our index file we add it to the zip file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $index .= '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
  $zip-&amp;gt;add_file('eprints-search/index.htm',$index);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a file handle has been provided we write to it, otherwise we write to the scalar file handle created earlier. We then return in the usual fashion.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $zip-&amp;gt;write_filehandle($opts{fh},'zip');&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
  $zip-&amp;gt;write_filehandle($FH,'zip');&lt;br /&gt;
  return $archive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Expzipv2.png]]&lt;br /&gt;
&lt;br /&gt;
The accompanying HTML index.&lt;br /&gt;
&lt;br /&gt;
[[Image:Expzip2.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsZip&amp;diff=5753</id>
		<title>Contribute: Plugins/ExportPluginsZip</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsZip&amp;diff=5753"/>
		<updated>2007-09-28T19:00:25Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Constructor */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 5: Zip =&lt;br /&gt;
In this tutorial we'll look at packaging the results of a search into a Zip file. We'll create a directory for each eprint, and a sub-directory for each document belonging to that eprint. We'll also add an HTML index file to the archive to make it easier to navigate.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~miyagawa/Archive-Any-Create-0.02/lib/Archive/Any/Create.pm Archive::Any::Create] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Archive::Any::Create&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zip.pm =&lt;br /&gt;
The code in the section below should be placed in a file called Zip.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::Zip;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use Archive::Any::Create;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
  my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
  my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
  $self-&amp;gt;{name} = 'Zip';&lt;br /&gt;
  $self-&amp;gt;{accept} = [ 'list/eprint' ];&lt;br /&gt;
  $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.zip';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/zip';&lt;br /&gt;
&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Archive::Any::Create');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Archive::Any::Create';&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
  my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
  my $archive = '';&lt;br /&gt;
  open (my $FH, '&amp;gt;', \$archive) or&lt;br /&gt;
    die(&amp;quot;Could not create filehandle: $!&amp;quot;);&lt;br /&gt;
  my $zip = Archive::Any::Create-&amp;gt;new;&lt;br /&gt;
&lt;br /&gt;
  my $index = &amp;lt;&amp;lt;END;&lt;br /&gt;
  &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
      &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;title&amp;gt;EPrints Search Results&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
  my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
    my $heading = $session-&amp;gt;make_element('h2');&lt;br /&gt;
    $heading-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
    $div-&amp;gt;appendChild($heading);&lt;br /&gt;
&lt;br /&gt;
    my $uldoc = $session-&amp;gt;make_element('ul');&lt;br /&gt;
    $div-&amp;gt;appendChild($uldoc);&lt;br /&gt;
&lt;br /&gt;
    my $dirpath = 'eprints-search/'.$dataobj-&amp;gt;get_id().'/';&lt;br /&gt;
&lt;br /&gt;
    my $i = 1;&lt;br /&gt;
    foreach my $doc ($dataobj-&amp;gt;get_all_documents)&lt;br /&gt;
    {&lt;br /&gt;
      my $subdirpath = $dirpath.&amp;quot;doc$i/&amp;quot;;&lt;br /&gt;
      my %files = $doc-&amp;gt;files;&lt;br /&gt;
&lt;br /&gt;
      my $lidoc = $session-&amp;gt;make_element('li');&lt;br /&gt;
      $uldoc-&amp;gt;appendChild($lidoc);&lt;br /&gt;
&lt;br /&gt;
      my $adoc = $session-&amp;gt;make_element('a', href=&amp;gt;$dataobj-&amp;gt;get_id.&amp;quot;/doc$i/&amp;quot;.$doc-&amp;gt;get_main);&lt;br /&gt;
      $lidoc-&amp;gt;appendChild($adoc);&lt;br /&gt;
&lt;br /&gt;
      if ($doc-&amp;gt;exists_and_set('formatdesc'))&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_value('formatdesc')));&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_main));&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      foreach my $filename (sort keys %files)&lt;br /&gt;
      {&lt;br /&gt;
       my $filepath = $subdirpath.$filename;&lt;br /&gt;
        my $file = $doc-&amp;gt;local_path.'/'.$filename;&lt;br /&gt;
&lt;br /&gt;
        if (-d $file)&lt;br /&gt;
        {&lt;br /&gt;
          next;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $data = '';&lt;br /&gt;
        open (my $datafh ,'&amp;gt;', \$data);&lt;br /&gt;
&lt;br /&gt;
        open (INFH, &amp;quot;&amp;lt;$file&amp;quot;) or die (&amp;quot;Could not open file $file&amp;quot;);&lt;br /&gt;
        while (&amp;lt;INFH&amp;gt;)&lt;br /&gt;
        {&lt;br /&gt;
          print {$datafh} $_;&lt;br /&gt;
        }&lt;br /&gt;
        close INFH;&lt;br /&gt;
&lt;br /&gt;
        $zip-&amp;gt;add_file($filepath, $data);&lt;br /&gt;
      }&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $index .= EPrints::XML::to_string($div);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $index .= '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
  $zip-&amp;gt;add_file('eprints-search/index.htm',$index);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $zip-&amp;gt;write_filehandle($opts{fh},'zip');&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
  $zip-&amp;gt;write_filehandle($FH,'zip');&lt;br /&gt;
  return $archive;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For the sake of simplicity this plugin will only deal with lists of eprints. This avoids some code duplication, and it would be fairly easy to modify the plugin to deal with both individual eprints and lists of eprints sensibly.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{accept} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The file extension and [http://en.wikipedia.org/wiki/MIME MIME] type are set to values appropriate for Zip files.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.zip';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/zip';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to import a module that is not included with EPrints for creating zip files. We use the EPrints::Utils::require_if_exists function to check if the module exists, and load it if it does. We then check the value returned from that function, and make the plugin invisible if it failed.&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Archive::Any::Create');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Archive::Any::Create';&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List Handling ==&lt;br /&gt;
=== Setting Up ===&lt;br /&gt;
Here we setup an in-memory file for the Zip, and create an Archive object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $archive = '';&lt;br /&gt;
  open (my $FH, '&amp;gt;', \$archive) or&lt;br /&gt;
    die(&amp;quot;Could not create filehandle: $!&amp;quot;);&lt;br /&gt;
  my $zip = Archive::Any::Create-&amp;gt;new;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navigation ===&lt;br /&gt;
Here we begin to setup the HTML file that we'll add to our archive for navigation. First we setup a header.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $index = &amp;lt;&amp;lt;END;&lt;br /&gt;
  &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
      &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;title&amp;gt;EPrints Search Results&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we get the Session object, we'll be using it to manipulate DOM objects later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handling DataObjs ===&lt;br /&gt;
We loop over the DataObjs as we have done before.&lt;br /&gt;
&lt;br /&gt;
This time we setup some DOM objects to be added to our index. Each eprint will have it's title printed out followed by a list of documents.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
    my $heading = $session-&amp;gt;make_element('h2');&lt;br /&gt;
    $heading-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
    $div-&amp;gt;appendChild($heading);&lt;br /&gt;
&lt;br /&gt;
    my $uldoc = $session-&amp;gt;make_element('ul');&lt;br /&gt;
    $div-&amp;gt;appendChild($uldoc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a directory for each eprint. Note it is not necessary to explicitly create a directory, we simply have to set the appropriate file path. However this means that if you do not add files to a certain directory it will not be created, rather than having an empty directory for a given eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $dirpath = 'eprints-search/'.$dataobj-&amp;gt;get_id().'/';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dealing With Documents ====&lt;br /&gt;
We then loop over all the documents belonging to each DataObj. The get_all_documents method returns an array of Document objects.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $i = 1;&lt;br /&gt;
    foreach my $doc ($dataobj-&amp;gt;get_all_documents)&lt;br /&gt;
    {&lt;br /&gt;
      my $subdirpath = $dirpath.&amp;quot;doc$i/&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we create a list item for the document containing a link to the main file. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      my $lidoc = $session-&amp;gt;make_element('li');&lt;br /&gt;
      $uldoc-&amp;gt;appendChild($lidoc);&lt;br /&gt;
&lt;br /&gt;
      my $adoc = $session-&amp;gt;make_element('a', href=&amp;gt;$dataobj-&amp;gt;get_id.&amp;quot;/doc$i/&amp;quot;.$doc-&amp;gt;get_main);&lt;br /&gt;
      $lidoc-&amp;gt;appendChild($adoc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If a description of the main file has been set we use that as the link text, otherwise we use the filename.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      if ($doc-&amp;gt;exists_and_set('formatdesc'))&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_value('formatdesc')));&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_main));&lt;br /&gt;
      }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dealing With Files ====&lt;br /&gt;
The files method of the Document object returns a hash whose keys are file names and values are file sizes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      my %files = $doc-&amp;gt;files;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We loop over each file belonging to the document, in most cases there will only be one file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      foreach my $filename (sort keys %files)&lt;br /&gt;
      {&lt;br /&gt;
        my $filepath = $subdirpath.$filename;&lt;br /&gt;
        my $file = $doc-&amp;gt;local_path.'/'.$filename;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to read the contents of the file and add it to a file in the zip. First we'll create another in-memory file to hold the contents.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $data = '';&lt;br /&gt;
        open (my $datafh ,'&amp;gt;', \$data);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We open our file and print it straight out to our in-memory file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        open (INFH, &amp;quot;&amp;lt;$file&amp;quot;) or die (&amp;quot;Could not open file $file&amp;quot;);&lt;br /&gt;
        while (&amp;lt;INFH&amp;gt;)&lt;br /&gt;
        {&lt;br /&gt;
          print {$datafh} $_;&lt;br /&gt;
        }&lt;br /&gt;
        close INFH;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we add the file data to our file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $zip-&amp;gt;add_file($filepath, $data);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we add the DOM object for our eprint to the index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $index .= EPrints::XML::to_string($div);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Finishing Off ===&lt;br /&gt;
After finishing off our index file we add it to the zip file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $index .= '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
  $zip-&amp;gt;add_file('eprints-search/index.htm',$index);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a file handle has been provided we write to it, otherwise we write to the scalar file handle created earlier. We then return in the usual fashion.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $zip-&amp;gt;write_filehandle($opts{fh},'zip');&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
  $zip-&amp;gt;write_filehandle($FH,'zip');&lt;br /&gt;
  return $archive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Expzipv2.png]]&lt;br /&gt;
&lt;br /&gt;
The accompanying HTML index.&lt;br /&gt;
&lt;br /&gt;
[[Image:Expzip2.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsExcel&amp;diff=5752</id>
		<title>Contribute: Plugins/ExportPluginsExcel</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsExcel&amp;diff=5752"/>
		<updated>2007-09-28T19:00:02Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Constructor */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 4: Excel =&lt;br /&gt;
&lt;br /&gt;
In this tutorial and the next one we'll look at exporting files in non-text formats. Here we will explore exporting metadata in Excel format (pre Microsoft Office 2007 which uses an XML based format).&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/dist/Spreadsheet-WriteExcel/lib/Spreadsheet/WriteExcel.pm Spreadsheet::Excel] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Spreadsheet::Excel&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Excel.pm =&lt;br /&gt;
The code in the section below should be placed in a file called Excel.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::Excel;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
  my ($class, %opts) = @_;&lt;br /&gt;
  my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
  $self-&amp;gt;{name} = 'Excel';&lt;br /&gt;
  $self-&amp;gt;{accept} = ['list/eprint'];&lt;br /&gt;
  $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.xls';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/vnd.ms-excel';&lt;br /&gt;
&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Spreadsheet::WriteExcel');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Spreadsheet::WriteExcel';&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
  my ($plugin, %opts) = @_;&lt;br /&gt;
  my $workbook;&lt;br /&gt;
&lt;br /&gt;
  my $output;&lt;br /&gt;
  open(my $FH,'&amp;gt;',\$output);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new(\*{$opts{fh}});&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new($FH);&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  my $worksheet = $workbook-&amp;gt;add_worksheet();&lt;br /&gt;
&lt;br /&gt;
  my $i = 0;&lt;br /&gt;
  my @fields =&lt;br /&gt;
  $plugin-&amp;gt;{session}-&amp;gt;get_repository-&amp;gt;get_dataset('archive')-&amp;gt;get_fields;&lt;br /&gt;
&lt;br /&gt;
  foreach my $field (@fields)&lt;br /&gt;
  {&lt;br /&gt;
    $worksheet-&amp;gt;write(0, $i, $field-&amp;gt;get_name);&lt;br /&gt;
    $i++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $i = 1;&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $j = 0;&lt;br /&gt;
    foreach my $field (@fields)&lt;br /&gt;
    {&lt;br /&gt;
      if ($dataobj-&amp;gt;exists_and_set($field-&amp;gt;get_name))&lt;br /&gt;
      {&lt;br /&gt;
        if ($field-&amp;gt;get_property('multiple'))&lt;br /&gt;
        {&lt;br /&gt;
          if ($field-&amp;gt;{type} eq 'name')&lt;br /&gt;
          {&lt;br /&gt;
            my $namelist = '';&lt;br /&gt;
            foreach my $name (@{$dataobj-&amp;gt;get_value_raw($field-&amp;gt;get_name)})&lt;br /&gt;
            {&lt;br /&gt;
              $namelist .= $name-&amp;gt;{family} . ',' . $name-&amp;gt;{given} . ';';&lt;br /&gt;
            }&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, $namelist);&lt;br /&gt;
          }&lt;br /&gt;
          elsif ($field-&amp;gt;{type} eq 'compound')&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, 'COMPOUND');&lt;br /&gt;
          }&lt;br /&gt;
          else&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j,&lt;br /&gt;
                        join(';',@{$dataobj-&amp;gt;get_value($field-&amp;gt;get_name)}));&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
          $worksheet-&amp;gt;write($i, $j, $dataobj-&amp;gt;get_value($field-&amp;gt;get_name));&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      $j++;&lt;br /&gt;
    }&lt;br /&gt;
    $i++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $workbook-&amp;gt;close;&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For the sake of simplicity this plugin will only deal with lists of eprints. This avoids some code duplication, and it would be fairly easy to modify the plugin to deal with both individual eprints and lists of eprints sensibly.&lt;br /&gt;
&amp;lt;pre&amp;gt;  &lt;br /&gt;
  $self-&amp;gt;{accept} = ['list/eprint'];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The file extension and [http://en.wikipedia.org/wiki/MIME MIME] type are set to values appropriate for Excel files.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.xls';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/vnd.ms-excel';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to import a module that is not included with EPrints for creating zip files. We use the EPrints::Utils::require_if_exists function to check if the module exists, and load it if it does. We then check the value returned from that function, and make the plugin invisible if it failed.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Spreadsheet::WriteExcel');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Spreadsheet::WriteExcel';&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List Handling ==&lt;br /&gt;
=== Setting Up a Workbook ===&lt;br /&gt;
Here we create a new Excel workbook. We start by creating a file handle using a scalar rather than a filename, this creates an in-memory file. Then depending on if a file handle has been supplied or not we create a workbook object that will be written to that file handle or the scalar file handle we just created.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $workbook;&lt;br /&gt;
&lt;br /&gt;
  my $output;&lt;br /&gt;
  open(my $FH,'&amp;gt;',\$output);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new(\*{$opts{fh}});&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new($FH);&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handling DataObjs ===&lt;br /&gt;
To start adding data to the Excel file we have to create a worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $worksheet = $workbook-&amp;gt;add_worksheet();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To the first row of our worksheet we add the names of all the metadata fields that can be associated with eprints. We get the session associated with the plugin, and then the repository associated with that session. We then get the DataSet &amp;quot;archive&amp;quot; from that repository and call the get_fields method. That method returns an array of MetaField objects.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my @fields =&lt;br /&gt;
  $plugin-&amp;gt;{session}-&amp;gt;get_repository-&amp;gt;get_dataset('archive')-&amp;gt;get_fields;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we loop over each field and write it's name to our worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  foreach my $field (@fields)&lt;br /&gt;
  {&lt;br /&gt;
    $worksheet-&amp;gt;write(0, $i, $field-&amp;gt;get_name);&lt;br /&gt;
    $i++;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now loop over each DataObj in our list, and over each MetaField we found earlier.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $j = 0;&lt;br /&gt;
    foreach my $field (@fields)&lt;br /&gt;
    {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We only write something to the worksheet if the field can apply to the DataObj and is set. Scalar values are simply written to the worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      if ($dataobj-&amp;gt;exists_and_set($field-&amp;gt;get_name))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The plugin handles fields which can take multiple values in a number of ways.   &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        if ($field-&amp;gt;get_property('multiple'))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Names are handled specially and are formatted with the family name followed by a comma, the given name or initial and then a semi-colon.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
          if ($field-&amp;gt;{type} eq 'name')&lt;br /&gt;
          {&lt;br /&gt;
            my $namelist = '';&lt;br /&gt;
            foreach my $name (@{$dataobj-&amp;gt;get_value_raw($field-&amp;gt;get_name)})&lt;br /&gt;
            {&lt;br /&gt;
              $namelist .= $name-&amp;gt;{family} . ',' . $name-&amp;gt;{given} . ';';&lt;br /&gt;
            }&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, $namelist);&lt;br /&gt;
          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Fields which have a compound type are not handled, and 'COMPOUND' is written to the worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
          elsif ($field-&amp;gt;{type} eq 'compound')&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, 'COMPOUND');&lt;br /&gt;
          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For most multiple fields each value is taken and concatenated, separated by semi-colons.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
          else&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j,&lt;br /&gt;
                        join(';',@{$dataobj-&amp;gt;get_value($field-&amp;gt;get_name)}));&lt;br /&gt;
          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Finishing Up ===&lt;br /&gt;
&lt;br /&gt;
We first need to close the workbook to ensure that the data is written to the file handles, and then we can return in the usual fashion. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $workbook-&amp;gt;close;&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Expexcel.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5751</id>
		<title>Contribute: Plugins/ExportPluginsHTML</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5751"/>
		<updated>2007-09-28T18:59:03Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* xml_dataobj */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 3: HTML =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we'll look at creating an export plugin with slightly more complex output than unformatted plain text. Although the plugin below produces XHTML the same principles apply to producing any XML document.&lt;br /&gt;
&lt;br /&gt;
= HTML.pm =&lt;br /&gt;
The code in the section below should be placed in a file called HTML.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HTML;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, HTML';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
        return EPrints::XML::to_string($xml);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $footer;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $footer;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub xml_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
        my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&lt;br /&gt;
        my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
        $title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
        $div-&amp;gt;appendChild($title);&lt;br /&gt;
&lt;br /&gt;
        my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
        $abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
        $div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&lt;br /&gt;
        return $div;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
Here we change the file extension to '.htm' and change the [http://en.wikipedia.org/wiki/MIME MIME] type to &amp;quot;text/html&amp;quot;. For general XML documents you should change the file extension to '.xml' and the [http://en.wikipedia.org/wiki/MIME MIME] type to 'text/xml';&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
$self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_dataobj ==&lt;br /&gt;
Here the output_dataobj method does very little. It calls the xml_dataobj method to obtain a [http://en.wikipedia.org/wiki/Document_Object_Model DOM] tree which is converted to plain text before being returned. The xml_dataobj method is described later.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
return EPrints::XML::to_string($xml);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_list ==&lt;br /&gt;
The only changes to the output_list method are the additions of a header and a footer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== xml_dataobj ==&lt;br /&gt;
This method has a similar signature to the output_dataobj method: it takes an implicit reference to the plugin object and a reference to a DataObj, however instead of returning a string it returns a [http://en.wikipedia.org/wiki/Document_Object_Model DOM] tree.&lt;br /&gt;
&lt;br /&gt;
Before we can start creating and manipulating [http://en.wikipedia.org/wiki/Document_Object_Model DOM] objects we need to get a reference to the Session object from the plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create elements using the make_element method of our session object. The first argument to the make_element method is the tag name of the element we want to create, for example 'a' or 'div'. The second parameter is a hash of attributes and values associated with the element.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we add a second-level header. The make_text method is used to create text nodes. The appendChild method is used to add child nodes. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
$title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
$div-&amp;gt;appendChild($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A similar pattern is followed to add a paragraph containing the eprint's abstract.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
$abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
$div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a [http://en.wikipedia.org/wiki/Document_Object_Model DOM] object representing a 'div' element containing our header and paragraph.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return $div;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphtml.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5750</id>
		<title>Contribute: Plugins/ExportPluginsHTML</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5750"/>
		<updated>2007-09-28T18:57:47Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* output_dataobj */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 3: HTML =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we'll look at creating an export plugin with slightly more complex output than unformatted plain text. Although the plugin below produces XHTML the same principles apply to producing any XML document.&lt;br /&gt;
&lt;br /&gt;
= HTML.pm =&lt;br /&gt;
The code in the section below should be placed in a file called HTML.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HTML;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, HTML';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
        return EPrints::XML::to_string($xml);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $footer;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $footer;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub xml_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
        my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&lt;br /&gt;
        my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
        $title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
        $div-&amp;gt;appendChild($title);&lt;br /&gt;
&lt;br /&gt;
        my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
        $abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
        $div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&lt;br /&gt;
        return $div;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
Here we change the file extension to '.htm' and change the [http://en.wikipedia.org/wiki/MIME MIME] type to &amp;quot;text/html&amp;quot;. For general XML documents you should change the file extension to '.xml' and the [http://en.wikipedia.org/wiki/MIME MIME] type to 'text/xml';&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
$self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_dataobj ==&lt;br /&gt;
Here the output_dataobj method does very little. It calls the xml_dataobj method to obtain a [http://en.wikipedia.org/wiki/Document_Object_Model DOM] tree which is converted to plain text before being returned. The xml_dataobj method is described later.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
return EPrints::XML::to_string($xml);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_list ==&lt;br /&gt;
The only changes to the output_list method are the additions of a header and a footer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== xml_dataobj ==&lt;br /&gt;
This method has a similar signature to the output_dataobj method: it takes an implicit reference to the plugin object and a reference to a DataObj, however instead of returning a string it returns a DOM object.&lt;br /&gt;
&lt;br /&gt;
Before we can start creating and manipulating DOM objects we need to get a reference to the Session object from the plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create elements using the make_element method of our session object. The first argument to the make_element method is the tag name of the element we want to create, for example 'a' or 'div'. The second parameter is a hash of attributes and values associated with the element.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we add a second-level header. The make_text method is used to create text nodes. The appendChild method is used to add child nodes. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
$title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
$div-&amp;gt;appendChild($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A similar pattern is followed to add a paragraph containing the eprint's abstract.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
$abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
$div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a DOM object representing a 'div' element containing our header and paragraph.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return $div;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphtml.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5749</id>
		<title>Contribute: Plugins/ExportPluginsHTML</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5749"/>
		<updated>2007-09-28T18:55:44Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Constructor */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 3: HTML =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we'll look at creating an export plugin with slightly more complex output than unformatted plain text. Although the plugin below produces XHTML the same principles apply to producing any XML document.&lt;br /&gt;
&lt;br /&gt;
= HTML.pm =&lt;br /&gt;
The code in the section below should be placed in a file called HTML.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HTML;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, HTML';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
        return EPrints::XML::to_string($xml);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $footer;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $footer;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub xml_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
        my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&lt;br /&gt;
        my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
        $title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
        $div-&amp;gt;appendChild($title);&lt;br /&gt;
&lt;br /&gt;
        my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
        $abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
        $div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&lt;br /&gt;
        return $div;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
Here we change the file extension to '.htm' and change the [http://en.wikipedia.org/wiki/MIME MIME] type to &amp;quot;text/html&amp;quot;. For general XML documents you should change the file extension to '.xml' and the [http://en.wikipedia.org/wiki/MIME MIME] type to 'text/xml';&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
$self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_dataobj ==&lt;br /&gt;
Here the output_dataobj method does very little. It calls the xml_dataobj method to obtain a DOM object which is converted to plain text before being returned. The xml_dataobj method is described later.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
return EPrints::XML::to_string($xml);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_list ==&lt;br /&gt;
The only changes to the output_list method are the additions of a header and a footer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== xml_dataobj ==&lt;br /&gt;
This method has a similar signature to the output_dataobj method: it takes an implicit reference to the plugin object and a reference to a DataObj, however instead of returning a string it returns a DOM object.&lt;br /&gt;
&lt;br /&gt;
Before we can start creating and manipulating DOM objects we need to get a reference to the Session object from the plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create elements using the make_element method of our session object. The first argument to the make_element method is the tag name of the element we want to create, for example 'a' or 'div'. The second parameter is a hash of attributes and values associated with the element.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we add a second-level header. The make_text method is used to create text nodes. The appendChild method is used to add child nodes. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
$title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
$div-&amp;gt;appendChild($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A similar pattern is followed to add a paragraph containing the eprint's abstract.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
$abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
$div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a DOM object representing a 'div' element containing our header and paragraph.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return $div;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphtml.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsList&amp;diff=5748</id>
		<title>Contribute: Plugins/ExportPluginsList</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsList&amp;diff=5748"/>
		<updated>2007-09-28T18:55:15Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Filehandles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Export Plugin Tutorial 2: List Handling =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn to create a slightly more complex export plugin than the one created in [[Contribute:_Plugins/ExportPluginsHello| the previous tutorial]] that changes the way lists of eprints are exported.&lt;br /&gt;
&lt;br /&gt;
= HelloList.pm =&lt;br /&gt;
The code in the section below should be placed in a file called HelloList.pm in the directory created previously, and MyPlugins&lt;br /&gt;
should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloList;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, List!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        return $dataobj-&amp;gt;get_id().&amp;quot;\t&amp;quot;.$dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;quot;ID\tTitle\n\n&amp;quot;;&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
The above code is very similar to the HelloExport.pm file in [[Contribute:_Plugins/ExportPluginsHello| the previous tutorial]] so only the points where it deviates significantly from that file will be discussed below.&lt;br /&gt;
&lt;br /&gt;
== Housekeeping ==&lt;br /&gt;
The package name has been changed to reflect the filename.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloList;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
Make sure you give each plugin a unique name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, List!';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dealing With Lists ==&lt;br /&gt;
In this example the output_list method is overridden in the export plugin to provide column headers. The original method just concatenates the output from the output_dataobj subroutine called on every DataObj in the list.&lt;br /&gt;
&lt;br /&gt;
Note that the method is not provided with a bare array of DataObjs, but a List object is provided within the opts hash. To get an array of DataObjs to loop over you must then call that List object's get_record method.&lt;br /&gt;
&lt;br /&gt;
== Filehandles ==&lt;br /&gt;
The command line export tool provides the output list method with a filehandle for output in the opts hash, while the [http://en.wikipedia.org/wiki/Common_Gateway_Interface CGI] export uses a value returned from the method. You must deal with the filehandle or you will get no output from the command line tool. In most cases this won't matter, but it is good practice to deal with it.&lt;br /&gt;
&lt;br /&gt;
The best way to handle output is to check if a filehandle has been provided every time something needs to be output. If a filehandle is provided we print to it, otherwise we save the output for later. At the end of the method we either return undef if a filehandle was provided or we return the saved output otherwise.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;quot;ID\tTitle\n\n&amp;quot;;&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
&lt;br /&gt;
Restart your webserver and test the plugin as in [[Contribute:_Plugins/ExportPluginsHello| the previous tutorial]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Explist.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5746</id>
		<title>Contribute: Plugins/ExportPluginsHelloOld</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5746"/>
		<updated>2007-09-28T14:48:24Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Processing DataObjs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Export Plugin Tutorial 1: &amp;quot;Hello, World!&amp;quot; =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn how to create a simple export plugin for EPrints, which will generate a list of titles from the results of a search. A basic knowledge of Perl is needed, but the code will be explained fully.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
Create a directory for your export plugins in the main export plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/Export). The directory used for these examples is called &amp;quot;MyPlugins&amp;quot;&lt;br /&gt;
&lt;br /&gt;
= HelloExport.pm =&lt;br /&gt;
&lt;br /&gt;
Replace MyPlugins with the name of the directory you have decided to put your export plugins in and place the code below in a file called HelloExport.pm in that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloExport;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== In More Detail ==&lt;br /&gt;
== Housekeeping ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::Foo::HelloExport;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Export plugins need to inherit from the EPrints::Plugin::Export class.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
After the implicit class reference, a hash of options is given.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a new export plugin by calling the Eprints::Plugin::Export constructor&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we set a number of fields to register our new plugin.&lt;br /&gt;
&lt;br /&gt;
This is the name that will appear in the export dropdown menu. The name should therefore be short and descriptive.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The accept field is a list containing the types of objects this&lt;br /&gt;
plugin can deal with. In this case lists of eprints and individual&lt;br /&gt;
eprints.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The visible field denotes the class of user which will be able to see the plugin.&lt;br /&gt;
For most export plugins the value 'all' will be required, allowing&lt;br /&gt;
all users to see and use the plugin. A value of 'staff' would&lt;br /&gt;
make the plugin visible only to repository staff.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The suffix field contains the extension of files exported by the plugin.&lt;br /&gt;
&lt;br /&gt;
The mimetype field defines the [http://en.wikipedia.org/wiki/MIME MIME] type of the files exported by the plugin&lt;br /&gt;
You can also specify file encoding, for example 'text/plain; charset=utf-8' to specify plain text, encoded using UTF-8.[http://en.wikipedia.org/wiki/UTF-8 UTF-8] is a Unicode character encoding capable of expressing characters from a large number of character sets and so is usually preferable to [http://en.wikipedia.org/wiki/ASCII ASCII]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then return our plugin reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return $self;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Processing DataObjs ==&lt;br /&gt;
&lt;br /&gt;
This method handles the export of each DataObj. DataObjs make up most of the content of an EPrint repository. The three main types are EPrint defining individual eprints, Document defining collections of one or more files belonging to an EPrints, and User which defines users of the repository.&lt;br /&gt;
&lt;br /&gt;
Besides an implicit reference to the Plugin object, this method is also provided with a reference to an individual DataObj. It is called by several [http://en.wikipedia.org/wiki/Common_Gateway_Interface CGI] and command line scripts to export single DataObjs, for instance the item control screen for repository staff. It is also called by the list handling method on each DataObj in a list, for example the results of a search. That will be explained in [[Contribute:_Plugins/ExportPluginsList|the next tutorial]].&lt;br /&gt;
&lt;br /&gt;
In the example below we get the title of each DataObj, but there are large number of fields which you can extract from each DataObj. For example try changing &amp;quot;title&amp;quot; to &amp;quot;abstract&amp;quot; to print the abstract of each eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        # Return a scalar containing the title.&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Finishing Off ==&lt;br /&gt;
&lt;br /&gt;
Standard Perl package requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your web server and perform a search.&lt;br /&gt;
&lt;br /&gt;
If all is well your plugin should appear in the dropdown menu. Select it and click export. As long as the search provided some results, you should get a list of EPrint titles returned.&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphello.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5745</id>
		<title>Contribute: Plugins/ExportPluginsHelloOld</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5745"/>
		<updated>2007-09-28T14:47:47Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Processing DataObjs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Export Plugin Tutorial 1: &amp;quot;Hello, World!&amp;quot; =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn how to create a simple export plugin for EPrints, which will generate a list of titles from the results of a search. A basic knowledge of Perl is needed, but the code will be explained fully.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
Create a directory for your export plugins in the main export plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/Export). The directory used for these examples is called &amp;quot;MyPlugins&amp;quot;&lt;br /&gt;
&lt;br /&gt;
= HelloExport.pm =&lt;br /&gt;
&lt;br /&gt;
Replace MyPlugins with the name of the directory you have decided to put your export plugins in and place the code below in a file called HelloExport.pm in that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloExport;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== In More Detail ==&lt;br /&gt;
== Housekeeping ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::Foo::HelloExport;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Export plugins need to inherit from the EPrints::Plugin::Export class.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
After the implicit class reference, a hash of options is given.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a new export plugin by calling the Eprints::Plugin::Export constructor&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we set a number of fields to register our new plugin.&lt;br /&gt;
&lt;br /&gt;
This is the name that will appear in the export dropdown menu. The name should therefore be short and descriptive.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The accept field is a list containing the types of objects this&lt;br /&gt;
plugin can deal with. In this case lists of eprints and individual&lt;br /&gt;
eprints.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The visible field denotes the class of user which will be able to see the plugin.&lt;br /&gt;
For most export plugins the value 'all' will be required, allowing&lt;br /&gt;
all users to see and use the plugin. A value of 'staff' would&lt;br /&gt;
make the plugin visible only to repository staff.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The suffix field contains the extension of files exported by the plugin.&lt;br /&gt;
&lt;br /&gt;
The mimetype field defines the [http://en.wikipedia.org/wiki/MIME MIME] type of the files exported by the plugin&lt;br /&gt;
You can also specify file encoding, for example 'text/plain; charset=utf-8' to specify plain text, encoded using UTF-8.[http://en.wikipedia.org/wiki/UTF-8 UTF-8] is a Unicode character encoding capable of expressing characters from a large number of character sets and so is usually preferable to [http://en.wikipedia.org/wiki/ASCII ASCII]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then return our plugin reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return $self;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Processing DataObjs ==&lt;br /&gt;
&lt;br /&gt;
This method handles the export of each DataObj. DataObjs make up most of the content of an EPrint repository. The three main types are EPrint defining individual eprints, Document defining collections of one or more files belonging to an EPrints, and User which defines users of the repository.&lt;br /&gt;
&lt;br /&gt;
Besides an implicit reference to the Plugin object, this method is also provided with a reference to an individual DataObj. It is called by several [http://en.wikipedia.org/wiki/Common_Gateway_Interface CGI] and command line scripts to export single DataObjs, for instance the item control screen for repository staff. It is also called by the list handling method on each DataObj in a list, for example the results of a search. That will be explained in [[Contribute:_Plugins/ExportPluginList|the next tutorial]].&lt;br /&gt;
&lt;br /&gt;
In the example below we get the title of each DataObj, but there are large number of fields which you can extract from each DataObj. For example try changing &amp;quot;title&amp;quot; to &amp;quot;abstract&amp;quot; to print the abstract of each eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        # Return a scalar containing the title.&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Finishing Off ==&lt;br /&gt;
&lt;br /&gt;
Standard Perl package requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your web server and perform a search.&lt;br /&gt;
&lt;br /&gt;
If all is well your plugin should appear in the dropdown menu. Select it and click export. As long as the search provided some results, you should get a list of EPrint titles returned.&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphello.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5744</id>
		<title>Contribute: Plugins/ExportPluginsHelloOld</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5744"/>
		<updated>2007-09-28T14:41:12Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Processing DataObjs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Export Plugin Tutorial 1: &amp;quot;Hello, World!&amp;quot; =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn how to create a simple export plugin for EPrints, which will generate a list of titles from the results of a search. A basic knowledge of Perl is needed, but the code will be explained fully.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
Create a directory for your export plugins in the main export plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/Export). The directory used for these examples is called &amp;quot;MyPlugins&amp;quot;&lt;br /&gt;
&lt;br /&gt;
= HelloExport.pm =&lt;br /&gt;
&lt;br /&gt;
Replace MyPlugins with the name of the directory you have decided to put your export plugins in and place the code below in a file called HelloExport.pm in that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloExport;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== In More Detail ==&lt;br /&gt;
== Housekeeping ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::Foo::HelloExport;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Export plugins need to inherit from the EPrints::Plugin::Export class.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
After the implicit class reference, a hash of options is given.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a new export plugin by calling the Eprints::Plugin::Export constructor&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we set a number of fields to register our new plugin.&lt;br /&gt;
&lt;br /&gt;
This is the name that will appear in the export dropdown menu. The name should therefore be short and descriptive.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The accept field is a list containing the types of objects this&lt;br /&gt;
plugin can deal with. In this case lists of eprints and individual&lt;br /&gt;
eprints.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The visible field denotes the class of user which will be able to see the plugin.&lt;br /&gt;
For most export plugins the value 'all' will be required, allowing&lt;br /&gt;
all users to see and use the plugin. A value of 'staff' would&lt;br /&gt;
make the plugin visible only to repository staff.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The suffix field contains the extension of files exported by the plugin.&lt;br /&gt;
&lt;br /&gt;
The mimetype field defines the [http://en.wikipedia.org/wiki/MIME MIME] type of the files exported by the plugin&lt;br /&gt;
You can also specify file encoding, for example 'text/plain; charset=utf-8' to specify plain text, encoded using UTF-8.[http://en.wikipedia.org/wiki/UTF-8 UTF-8] is a Unicode character encoding capable of expressing characters from a large number of character sets and so is usually preferable to [http://en.wikipedia.org/wiki/ASCII ASCII]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then return our plugin reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return $self;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Processing DataObjs ==&lt;br /&gt;
&lt;br /&gt;
This method handles the export of each DataObj. DataObjs make up most of the content of an EPrint repository. The three main types are EPrint defining individual eprints, Document defining collections of one or more files belonging to an EPrints, and User which defines users of the repository.&lt;br /&gt;
&lt;br /&gt;
Besides an implicit reference to the Plugin object, this method is also provided with a reference to an individual DataObj. It is called by several [http://en.wikipedia.org/wiki/Common_Gateway_Interface CGI] and command line scripts to export single DataObjs, for instance the item control screen for repository staff. It is also called by the list handling method on each DataObj in a list, for example the results of a search. That will be explained in [[User:Tom/Export Plugins/Hello Lists|the next tutorial]].&lt;br /&gt;
&lt;br /&gt;
In the example below we get the title of each DataObj, but there are large number of fields which you can extract from each DataObj. For example try changing &amp;quot;title&amp;quot; to &amp;quot;abstract&amp;quot; to print the abstract of each eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        # Return a scalar containing the title.&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Finishing Off ==&lt;br /&gt;
&lt;br /&gt;
Standard Perl package requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your web server and perform a search.&lt;br /&gt;
&lt;br /&gt;
If all is well your plugin should appear in the dropdown menu. Select it and click export. As long as the search provided some results, you should get a list of EPrint titles returned.&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphello.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5743</id>
		<title>Contribute: Plugins/ExportPluginsHelloOld</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5743"/>
		<updated>2007-09-28T14:38:26Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Constructor */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Export Plugin Tutorial 1: &amp;quot;Hello, World!&amp;quot; =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn how to create a simple export plugin for EPrints, which will generate a list of titles from the results of a search. A basic knowledge of Perl is needed, but the code will be explained fully.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
Create a directory for your export plugins in the main export plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/Export). The directory used for these examples is called &amp;quot;MyPlugins&amp;quot;&lt;br /&gt;
&lt;br /&gt;
= HelloExport.pm =&lt;br /&gt;
&lt;br /&gt;
Replace MyPlugins with the name of the directory you have decided to put your export plugins in and place the code below in a file called HelloExport.pm in that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloExport;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== In More Detail ==&lt;br /&gt;
== Housekeeping ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::Foo::HelloExport;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Export plugins need to inherit from the EPrints::Plugin::Export class.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
After the implicit class reference, a hash of options is given.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a new export plugin by calling the Eprints::Plugin::Export constructor&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we set a number of fields to register our new plugin.&lt;br /&gt;
&lt;br /&gt;
This is the name that will appear in the export dropdown menu. The name should therefore be short and descriptive.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The accept field is a list containing the types of objects this&lt;br /&gt;
plugin can deal with. In this case lists of eprints and individual&lt;br /&gt;
eprints.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The visible field denotes the class of user which will be able to see the plugin.&lt;br /&gt;
For most export plugins the value 'all' will be required, allowing&lt;br /&gt;
all users to see and use the plugin. A value of 'staff' would&lt;br /&gt;
make the plugin visible only to repository staff.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The suffix field contains the extension of files exported by the plugin.&lt;br /&gt;
&lt;br /&gt;
The mimetype field defines the [http://en.wikipedia.org/wiki/MIME MIME] type of the files exported by the plugin&lt;br /&gt;
You can also specify file encoding, for example 'text/plain; charset=utf-8' to specify plain text, encoded using UTF-8.[http://en.wikipedia.org/wiki/UTF-8 UTF-8] is a Unicode character encoding capable of expressing characters from a large number of character sets and so is usually preferable to [http://en.wikipedia.org/wiki/ASCII ASCII]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then return our plugin reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return $self;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Processing DataObjs ==&lt;br /&gt;
&lt;br /&gt;
This method handles the export of each DataObj. DataObjs make up most of the content of an EPrint repository. The three main types are EPrint defining individual eprints, Document defining collections of one or more files belonging to an EPrints and User which defines users of the repository.&lt;br /&gt;
&lt;br /&gt;
Besides an implicit reference to the plugin object, this method is also provided with a reference to an individual DataObj. It is called by several cgi and command line scripts to export single DataObjs, for instance the item control screen for repository staff. It is also called by the list handling method on each DataObj in a list, for example the results of a search. That will be explained in [[User:Tom/Export Plugins/Hello Lists|the next tutorial]].&lt;br /&gt;
&lt;br /&gt;
In the example below we get the title of each DataObj, but there are large number of fields which you can extract from each DataObj. For example try changing &amp;quot;title&amp;quot; to &amp;quot;abstract&amp;quot; to print the abstract of each eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        # Return a scalar containing the title.&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Finishing Off ==&lt;br /&gt;
&lt;br /&gt;
Standard Perl package requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your web server and perform a search.&lt;br /&gt;
&lt;br /&gt;
If all is well your plugin should appear in the dropdown menu. Select it and click export. As long as the search provided some results, you should get a list of EPrint titles returned.&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphello.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5742</id>
		<title>Contribute: Plugins/ExportPluginsHelloOld</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5742"/>
		<updated>2007-09-28T14:35:55Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Constructor */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Export Plugin Tutorial 1: &amp;quot;Hello, World!&amp;quot; =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn how to create a simple export plugin for EPrints, which will generate a list of titles from the results of a search. A basic knowledge of Perl is needed, but the code will be explained fully.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
Create a directory for your export plugins in the main export plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/Export). The directory used for these examples is called &amp;quot;MyPlugins&amp;quot;&lt;br /&gt;
&lt;br /&gt;
= HelloExport.pm =&lt;br /&gt;
&lt;br /&gt;
Replace MyPlugins with the name of the directory you have decided to put your export plugins in and place the code below in a file called HelloExport.pm in that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloExport;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== In More Detail ==&lt;br /&gt;
== Housekeeping ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::Foo::HelloExport;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Export plugins need to inherit from the EPrints::Plugin::Export class.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
After the implicit class reference, a hash of options is given.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a new export plugin by calling the Eprints::Plugin::Export constructor&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we set a number of fields to register our new plugin.&lt;br /&gt;
&lt;br /&gt;
This is the name that will appear in the export dropdown menu. The name should therefore be short and descriptive.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The accept field is a list containing the types of objects this&lt;br /&gt;
plugin can deal with. In this case lists of eprints and individual&lt;br /&gt;
eprints.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The visible field denotes the class of user which will be able to see the plugin.&lt;br /&gt;
For most export plugins the value 'all' will be required, allowing&lt;br /&gt;
all users to see and use the plugin. A value of 'staff' would&lt;br /&gt;
make the plugin visible only to repository staff.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The suffix field contains the extension of files exported by the plugin.&lt;br /&gt;
&lt;br /&gt;
The mimetype field defines the [http://en.wikipedia.org/wiki/MIME|MIME] type of the files exported by the plugin&lt;br /&gt;
You can also specify file encoding, for example 'text/plain; charset=utf-8' to specify plain text, encoded using UTF-8. UTF-8[http://en.wikipedia.org/wiki/UTF-8] is a Unicode character encoding capable of expressing a large number of different characters and so is usually preferable to ASCII[http://en.wikipedia.org/wiki/ASCII]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
We then return our plugin reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Processing DataObjs ==&lt;br /&gt;
&lt;br /&gt;
This method handles the export of each DataObj. DataObjs make up most of the content of an EPrint repository. The three main types are EPrint defining individual eprints, Document defining collections of one or more files belonging to an EPrints and User which defines users of the repository.&lt;br /&gt;
&lt;br /&gt;
Besides an implicit reference to the plugin object, this method is also provided with a reference to an individual DataObj. It is called by several cgi and command line scripts to export single DataObjs, for instance the item control screen for repository staff. It is also called by the list handling method on each DataObj in a list, for example the results of a search. That will be explained in [[User:Tom/Export Plugins/Hello Lists|the next tutorial]].&lt;br /&gt;
&lt;br /&gt;
In the example below we get the title of each DataObj, but there are large number of fields which you can extract from each DataObj. For example try changing &amp;quot;title&amp;quot; to &amp;quot;abstract&amp;quot; to print the abstract of each eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        # Return a scalar containing the title.&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Finishing Off ==&lt;br /&gt;
&lt;br /&gt;
Standard Perl package requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your web server and perform a search.&lt;br /&gt;
&lt;br /&gt;
If all is well your plugin should appear in the dropdown menu. Select it and click export. As long as the search provided some results, you should get a list of EPrint titles returned.&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphello.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsZip&amp;diff=5741</id>
		<title>Contribute: Plugins/ExportPluginsZip</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsZip&amp;diff=5741"/>
		<updated>2007-09-28T14:02:10Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Zip.pm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 5: Zip =&lt;br /&gt;
In this tutorial we'll look at packaging the results of a search into a Zip file. We'll create a directory for each eprint, and a sub-directory for each document belonging to that eprint. We'll also add an HTML index file to the archive to make it easier to navigate.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~miyagawa/Archive-Any-Create-0.02/lib/Archive/Any/Create.pm Archive::Any::Create] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Archive::Any::Create&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zip.pm =&lt;br /&gt;
The code in the section below should be placed in a file called Zip.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::Zip;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use Archive::Any::Create;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
  my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
  my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
  $self-&amp;gt;{name} = 'Zip';&lt;br /&gt;
  $self-&amp;gt;{accept} = [ 'list/eprint' ];&lt;br /&gt;
  $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.zip';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/zip';&lt;br /&gt;
&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Archive::Any::Create');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Archive::Any::Create';&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
  my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
  my $archive = '';&lt;br /&gt;
  open (my $FH, '&amp;gt;', \$archive) or&lt;br /&gt;
    die(&amp;quot;Could not create filehandle: $!&amp;quot;);&lt;br /&gt;
  my $zip = Archive::Any::Create-&amp;gt;new;&lt;br /&gt;
&lt;br /&gt;
  my $index = &amp;lt;&amp;lt;END;&lt;br /&gt;
  &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
      &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;title&amp;gt;EPrints Search Results&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
  my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
    my $heading = $session-&amp;gt;make_element('h2');&lt;br /&gt;
    $heading-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
    $div-&amp;gt;appendChild($heading);&lt;br /&gt;
&lt;br /&gt;
    my $uldoc = $session-&amp;gt;make_element('ul');&lt;br /&gt;
    $div-&amp;gt;appendChild($uldoc);&lt;br /&gt;
&lt;br /&gt;
    my $dirpath = 'eprints-search/'.$dataobj-&amp;gt;get_id().'/';&lt;br /&gt;
&lt;br /&gt;
    my $i = 1;&lt;br /&gt;
    foreach my $doc ($dataobj-&amp;gt;get_all_documents)&lt;br /&gt;
    {&lt;br /&gt;
      my $subdirpath = $dirpath.&amp;quot;doc$i/&amp;quot;;&lt;br /&gt;
      my %files = $doc-&amp;gt;files;&lt;br /&gt;
&lt;br /&gt;
      my $lidoc = $session-&amp;gt;make_element('li');&lt;br /&gt;
      $uldoc-&amp;gt;appendChild($lidoc);&lt;br /&gt;
&lt;br /&gt;
      my $adoc = $session-&amp;gt;make_element('a', href=&amp;gt;$dataobj-&amp;gt;get_id.&amp;quot;/doc$i/&amp;quot;.$doc-&amp;gt;get_main);&lt;br /&gt;
      $lidoc-&amp;gt;appendChild($adoc);&lt;br /&gt;
&lt;br /&gt;
      if ($doc-&amp;gt;exists_and_set('formatdesc'))&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_value('formatdesc')));&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_main));&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      foreach my $filename (sort keys %files)&lt;br /&gt;
      {&lt;br /&gt;
       my $filepath = $subdirpath.$filename;&lt;br /&gt;
        my $file = $doc-&amp;gt;local_path.'/'.$filename;&lt;br /&gt;
&lt;br /&gt;
        if (-d $file)&lt;br /&gt;
        {&lt;br /&gt;
          next;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $data = '';&lt;br /&gt;
        open (my $datafh ,'&amp;gt;', \$data);&lt;br /&gt;
&lt;br /&gt;
        open (INFH, &amp;quot;&amp;lt;$file&amp;quot;) or die (&amp;quot;Could not open file $file&amp;quot;);&lt;br /&gt;
        while (&amp;lt;INFH&amp;gt;)&lt;br /&gt;
        {&lt;br /&gt;
          print {$datafh} $_;&lt;br /&gt;
        }&lt;br /&gt;
        close INFH;&lt;br /&gt;
&lt;br /&gt;
        $zip-&amp;gt;add_file($filepath, $data);&lt;br /&gt;
      }&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $index .= EPrints::XML::to_string($div);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $index .= '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
  $zip-&amp;gt;add_file('eprints-search/index.htm',$index);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $zip-&amp;gt;write_filehandle($opts{fh},'zip');&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
  $zip-&amp;gt;write_filehandle($FH,'zip');&lt;br /&gt;
  return $archive;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For the sake of simplicity this plugin will only deal with lists of eprints. This avoids some code duplication, and it would be fairly easy to modify the plugin to deal with both individual eprints and lists of eprints sensibly.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{accept} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The file extension and MIME type are set to values appropriate for Zip files.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.zip';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/zip';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to import a module that is not included with EPrints for creating zip files. We use the EPrints::Utils::require_if_exists function to check if the module exists, and load it if it does. We then check the value returned from that function, and make the plugin invisible if it failed.&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Archive::Any::Create');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Archive::Any::Create';&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List Handling ==&lt;br /&gt;
=== Setting Up ===&lt;br /&gt;
Here we setup an in-memory file for the Zip, and create an Archive object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $archive = '';&lt;br /&gt;
  open (my $FH, '&amp;gt;', \$archive) or&lt;br /&gt;
    die(&amp;quot;Could not create filehandle: $!&amp;quot;);&lt;br /&gt;
  my $zip = Archive::Any::Create-&amp;gt;new;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navigation ===&lt;br /&gt;
Here we begin to setup the HTML file that we'll add to our archive for navigation. First we setup a header.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $index = &amp;lt;&amp;lt;END;&lt;br /&gt;
  &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
      &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;title&amp;gt;EPrints Search Results&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we get the Session object, we'll be using it to manipulate DOM objects later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handling DataObjs ===&lt;br /&gt;
We loop over the DataObjs as we have done before.&lt;br /&gt;
&lt;br /&gt;
This time we setup some DOM objects to be added to our index. Each eprint will have it's title printed out followed by a list of documents.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
    my $heading = $session-&amp;gt;make_element('h2');&lt;br /&gt;
    $heading-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
    $div-&amp;gt;appendChild($heading);&lt;br /&gt;
&lt;br /&gt;
    my $uldoc = $session-&amp;gt;make_element('ul');&lt;br /&gt;
    $div-&amp;gt;appendChild($uldoc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a directory for each eprint. Note it is not necessary to explicitly create a directory, we simply have to set the appropriate file path. However this means that if you do not add files to a certain directory it will not be created, rather than having an empty directory for a given eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $dirpath = 'eprints-search/'.$dataobj-&amp;gt;get_id().'/';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dealing With Documents ====&lt;br /&gt;
We then loop over all the documents belonging to each DataObj. The get_all_documents method returns an array of Document objects.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $i = 1;&lt;br /&gt;
    foreach my $doc ($dataobj-&amp;gt;get_all_documents)&lt;br /&gt;
    {&lt;br /&gt;
      my $subdirpath = $dirpath.&amp;quot;doc$i/&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we create a list item for the document containing a link to the main file. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      my $lidoc = $session-&amp;gt;make_element('li');&lt;br /&gt;
      $uldoc-&amp;gt;appendChild($lidoc);&lt;br /&gt;
&lt;br /&gt;
      my $adoc = $session-&amp;gt;make_element('a', href=&amp;gt;$dataobj-&amp;gt;get_id.&amp;quot;/doc$i/&amp;quot;.$doc-&amp;gt;get_main);&lt;br /&gt;
      $lidoc-&amp;gt;appendChild($adoc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If a description of the main file has been set we use that as the link text, otherwise we use the filename.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      if ($doc-&amp;gt;exists_and_set('formatdesc'))&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_value('formatdesc')));&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_main));&lt;br /&gt;
      }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dealing With Files ====&lt;br /&gt;
The files method of the Document object returns a hash whose keys are file names and values are file sizes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      my %files = $doc-&amp;gt;files;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We loop over each file belonging to the document, in most cases there will only be one file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      foreach my $filename (sort keys %files)&lt;br /&gt;
      {&lt;br /&gt;
        my $filepath = $subdirpath.$filename;&lt;br /&gt;
        my $file = $doc-&amp;gt;local_path.'/'.$filename;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to read the contents of the file and add it to a file in the zip. First we'll create another in-memory file to hold the contents.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $data = '';&lt;br /&gt;
        open (my $datafh ,'&amp;gt;', \$data);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We open our file and print it straight out to our in-memory file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        open (INFH, &amp;quot;&amp;lt;$file&amp;quot;) or die (&amp;quot;Could not open file $file&amp;quot;);&lt;br /&gt;
        while (&amp;lt;INFH&amp;gt;)&lt;br /&gt;
        {&lt;br /&gt;
          print {$datafh} $_;&lt;br /&gt;
        }&lt;br /&gt;
        close INFH;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we add the file data to our file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $zip-&amp;gt;add_file($filepath, $data);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we add the DOM object for our eprint to the index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $index .= EPrints::XML::to_string($div);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Finishing Off ===&lt;br /&gt;
After finishing off our index file we add it to the zip file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $index .= '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
  $zip-&amp;gt;add_file('eprints-search/index.htm',$index);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a file handle has been provided we write to it, otherwise we write to the scalar file handle created earlier. We then return in the usual fashion.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $zip-&amp;gt;write_filehandle($opts{fh},'zip');&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
  $zip-&amp;gt;write_filehandle($FH,'zip');&lt;br /&gt;
  return $archive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Expzipv2.png]]&lt;br /&gt;
&lt;br /&gt;
The accompanying HTML index.&lt;br /&gt;
&lt;br /&gt;
[[Image:Expzip2.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsExcel&amp;diff=5740</id>
		<title>Contribute: Plugins/ExportPluginsExcel</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsExcel&amp;diff=5740"/>
		<updated>2007-09-28T13:48:12Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Excel.pm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 4: Excel =&lt;br /&gt;
&lt;br /&gt;
In this tutorial and the next one we'll look at exporting files in non-text formats. Here we will explore exporting metadata in Excel format (pre Microsoft Office 2007 which uses an XML based format).&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/dist/Spreadsheet-WriteExcel/lib/Spreadsheet/WriteExcel.pm Spreadsheet::Excel] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Spreadsheet::Excel&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Excel.pm =&lt;br /&gt;
The code in the section below should be placed in a file called Excel.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::Excel;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
  my ($class, %opts) = @_;&lt;br /&gt;
  my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
  $self-&amp;gt;{name} = 'Excel';&lt;br /&gt;
  $self-&amp;gt;{accept} = ['list/eprint'];&lt;br /&gt;
  $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.xls';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/vnd.ms-excel';&lt;br /&gt;
&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Spreadsheet::WriteExcel');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Spreadsheet::WriteExcel';&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
  my ($plugin, %opts) = @_;&lt;br /&gt;
  my $workbook;&lt;br /&gt;
&lt;br /&gt;
  my $output;&lt;br /&gt;
  open(my $FH,'&amp;gt;',\$output);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new(\*{$opts{fh}});&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new($FH);&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  my $worksheet = $workbook-&amp;gt;add_worksheet();&lt;br /&gt;
&lt;br /&gt;
  my $i = 0;&lt;br /&gt;
  my @fields =&lt;br /&gt;
  $plugin-&amp;gt;{session}-&amp;gt;get_repository-&amp;gt;get_dataset('archive')-&amp;gt;get_fields;&lt;br /&gt;
&lt;br /&gt;
  foreach my $field (@fields)&lt;br /&gt;
  {&lt;br /&gt;
    $worksheet-&amp;gt;write(0, $i, $field-&amp;gt;get_name);&lt;br /&gt;
    $i++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $i = 1;&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $j = 0;&lt;br /&gt;
    foreach my $field (@fields)&lt;br /&gt;
    {&lt;br /&gt;
      if ($dataobj-&amp;gt;exists_and_set($field-&amp;gt;get_name))&lt;br /&gt;
      {&lt;br /&gt;
        if ($field-&amp;gt;get_property('multiple'))&lt;br /&gt;
        {&lt;br /&gt;
          if ($field-&amp;gt;{type} eq 'name')&lt;br /&gt;
          {&lt;br /&gt;
            my $namelist = '';&lt;br /&gt;
            foreach my $name (@{$dataobj-&amp;gt;get_value_raw($field-&amp;gt;get_name)})&lt;br /&gt;
            {&lt;br /&gt;
              $namelist .= $name-&amp;gt;{family} . ',' . $name-&amp;gt;{given} . ';';&lt;br /&gt;
            }&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, $namelist);&lt;br /&gt;
          }&lt;br /&gt;
          elsif ($field-&amp;gt;{type} eq 'compound')&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, 'COMPOUND');&lt;br /&gt;
          }&lt;br /&gt;
          else&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j,&lt;br /&gt;
                        join(';',@{$dataobj-&amp;gt;get_value($field-&amp;gt;get_name)}));&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
          $worksheet-&amp;gt;write($i, $j, $dataobj-&amp;gt;get_value($field-&amp;gt;get_name));&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      $j++;&lt;br /&gt;
    }&lt;br /&gt;
    $i++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $workbook-&amp;gt;close;&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For the sake of simplicity this plugin will only deal with lists of eprints. This avoids some code duplication, and it would be fairly easy to modify the plugin to deal with both individual eprints and lists of eprints sensibly.&lt;br /&gt;
&amp;lt;pre&amp;gt;  &lt;br /&gt;
  $self-&amp;gt;{accept} = ['list/eprint'];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The file extension and MIME type are set to values appropriate for Excel files.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.xls';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/vnd.ms-excel';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to import a module that is not included with EPrints for creating zip files. We use the EPrints::Utils::require_if_exists function to check if the module exists, and load it if it does. We then check the value returned from that function, and make the plugin invisible if it failed.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Spreadsheet::WriteExcel');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Spreadsheet::WriteExcel';&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== List Handling ==&lt;br /&gt;
=== Setting Up a Workbook ===&lt;br /&gt;
Here we create a new Excel workbook. We start by creating a file handle using a scalar rather than a filename, this creates an in-memory file. Then depending on if a file handle has been supplied or not we create a workbook object that will be written to that file handle or the scalar file handle we just created.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $workbook;&lt;br /&gt;
&lt;br /&gt;
  my $output;&lt;br /&gt;
  open(my $FH,'&amp;gt;',\$output);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new(\*{$opts{fh}});&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new($FH);&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handling DataObjs ===&lt;br /&gt;
To start adding data to the Excel file we have to create a worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $worksheet = $workbook-&amp;gt;add_worksheet();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To the first row of our worksheet we add the names of all the metadata fields that can be associated with eprints. We get the session associated with the plugin, and then the repository associated with that session. We then get the DataSet &amp;quot;archive&amp;quot; from that repository and call the get_fields method. That method returns an array of MetaField objects.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my @fields =&lt;br /&gt;
  $plugin-&amp;gt;{session}-&amp;gt;get_repository-&amp;gt;get_dataset('archive')-&amp;gt;get_fields;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we loop over each field and write it's name to our worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  foreach my $field (@fields)&lt;br /&gt;
  {&lt;br /&gt;
    $worksheet-&amp;gt;write(0, $i, $field-&amp;gt;get_name);&lt;br /&gt;
    $i++;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now loop over each DataObj in our list, and over each MetaField we found earlier.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $j = 0;&lt;br /&gt;
    foreach my $field (@fields)&lt;br /&gt;
    {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We only write something to the worksheet if the field can apply to the DataObj and is set. Scalar values are simply written to the worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      if ($dataobj-&amp;gt;exists_and_set($field-&amp;gt;get_name))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The plugin handles fields which can take multiple values in a number of ways.   &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        if ($field-&amp;gt;get_property('multiple'))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Names are handled specially and are formatted with the family name followed by a comma, the given name or initial and then a semi-colon.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
          if ($field-&amp;gt;{type} eq 'name')&lt;br /&gt;
          {&lt;br /&gt;
            my $namelist = '';&lt;br /&gt;
            foreach my $name (@{$dataobj-&amp;gt;get_value_raw($field-&amp;gt;get_name)})&lt;br /&gt;
            {&lt;br /&gt;
              $namelist .= $name-&amp;gt;{family} . ',' . $name-&amp;gt;{given} . ';';&lt;br /&gt;
            }&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, $namelist);&lt;br /&gt;
          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Fields which have a compound type are not handled, and 'COMPOUND' is written to the worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
          elsif ($field-&amp;gt;{type} eq 'compound')&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, 'COMPOUND');&lt;br /&gt;
          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For most multiple fields each value is taken and concatenated, separated by semi-colons.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
          else&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j,&lt;br /&gt;
                        join(';',@{$dataobj-&amp;gt;get_value($field-&amp;gt;get_name)}));&lt;br /&gt;
          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Finishing Up ===&lt;br /&gt;
&lt;br /&gt;
We first need to close the workbook to ensure that the data is written to the file handles, and then we can return in the usual fashion. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $workbook-&amp;gt;close;&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Expexcel.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5739</id>
		<title>Contribute: Plugins/ExportPluginsHTML</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5739"/>
		<updated>2007-09-28T13:46:47Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* HTML.pm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 3: HTML =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we'll look at creating an export plugin with slightly more complex output than unformatted plain text. Although the plugin below produces XHTML the same principles apply to producing any XML document.&lt;br /&gt;
&lt;br /&gt;
= HTML.pm =&lt;br /&gt;
The code in the section below should be placed in a file called HTML.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HTML;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, HTML';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
        return EPrints::XML::to_string($xml);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $footer;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $footer;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub xml_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
        my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&lt;br /&gt;
        my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
        $title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
        $div-&amp;gt;appendChild($title);&lt;br /&gt;
&lt;br /&gt;
        my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
        $abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
        $div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&lt;br /&gt;
        return $div;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
Here we change the file extension to '.htm' and change the MIME type to &amp;quot;text/html&amp;quot;. For general XML documents you should change the file extension to '.xml' and the MIME type to 'text/xml';&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
$self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_dataobj ==&lt;br /&gt;
Here the output_dataobj method does very little. It calls the xml_dataobj method to obtain a DOM object which is converted to plain text before being returned. The xml_dataobj method is described later.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
return EPrints::XML::to_string($xml);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_list ==&lt;br /&gt;
The only changes to the output_list method are the additions of a header and a footer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== xml_dataobj ==&lt;br /&gt;
This method has a similar signature to the output_dataobj method: it takes an implicit reference to the plugin object and a reference to a DataObj, however instead of returning a string it returns a DOM object.&lt;br /&gt;
&lt;br /&gt;
Before we can start creating and manipulating DOM objects we need to get a reference to the Session object from the plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create elements using the make_element method of our session object. The first argument to the make_element method is the tag name of the element we want to create, for example 'a' or 'div'. The second parameter is a hash of attributes and values associated with the element.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we add a second-level header. The make_text method is used to create text nodes. The appendChild method is used to add child nodes. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
$title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
$div-&amp;gt;appendChild($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A similar pattern is followed to add a paragraph containing the eprint's abstract.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
$abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
$div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a DOM object representing a 'div' element containing our header and paragraph.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return $div;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphtml.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5738</id>
		<title>Contribute: Plugins/ExportPluginsHTML</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5738"/>
		<updated>2007-09-28T13:46:06Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* HelloHTML.pm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 3: HTML =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we'll look at creating an export plugin with slightly more complex output than unformatted plain text. Although the plugin below produces XHTML the same principles apply to producing any XML document.&lt;br /&gt;
&lt;br /&gt;
= HTML.pm =&lt;br /&gt;
The code in the section below should be placed in a file called HelloHTML.pm in the directory created previously, and MyPlugins should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HTML;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, HTML!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
        return EPrints::XML::to_string($xml);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $footer;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $footer;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub xml_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
        my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&lt;br /&gt;
        my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
        $title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
        $div-&amp;gt;appendChild($title);&lt;br /&gt;
&lt;br /&gt;
        my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
        $abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
        $div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&lt;br /&gt;
        return $div;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
Here we change the file extension to '.htm' and change the MIME type to &amp;quot;text/html&amp;quot;. For general XML documents you should change the file extension to '.xml' and the MIME type to 'text/xml';&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
$self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_dataobj ==&lt;br /&gt;
Here the output_dataobj method does very little. It calls the xml_dataobj method to obtain a DOM object which is converted to plain text before being returned. The xml_dataobj method is described later.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
return EPrints::XML::to_string($xml);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_list ==&lt;br /&gt;
The only changes to the output_list method are the additions of a header and a footer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== xml_dataobj ==&lt;br /&gt;
This method has a similar signature to the output_dataobj method: it takes an implicit reference to the plugin object and a reference to a DataObj, however instead of returning a string it returns a DOM object.&lt;br /&gt;
&lt;br /&gt;
Before we can start creating and manipulating DOM objects we need to get a reference to the Session object from the plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create elements using the make_element method of our session object. The first argument to the make_element method is the tag name of the element we want to create, for example 'a' or 'div'. The second parameter is a hash of attributes and values associated with the element.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we add a second-level header. The make_text method is used to create text nodes. The appendChild method is used to add child nodes. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
$title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
$div-&amp;gt;appendChild($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A similar pattern is followed to add a paragraph containing the eprint's abstract.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
$abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
$div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a DOM object representing a 'div' element containing our header and paragraph.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return $div;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphtml.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5737</id>
		<title>Contribute: Plugins/ExportPluginsHelloOld</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5737"/>
		<updated>2007-09-28T12:51:32Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Before You Start */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Export Plugin Tutorial 1: &amp;quot;Hello, World!&amp;quot; =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn how to create a simple export plugin for EPrints, which will generate a list of titles from the results of a search. A basic knowledge of Perl is needed, but the code will be explained fully.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
Create a directory for your export plugins in the main export plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/Export). The directory used for these examples is called &amp;quot;MyPlugins&amp;quot;&lt;br /&gt;
&lt;br /&gt;
= HelloExport.pm =&lt;br /&gt;
&lt;br /&gt;
Replace MyPlugins with the name of the directory you have decided to put your export plugins in and place the code below in a file called HelloExport.pm in that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloExport;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== In More Detail ==&lt;br /&gt;
== Housekeeping ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::Foo::HelloExport;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Export plugins need to inherit from the EPrints::Plugin::Export class.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
The Constructor for our Plugin. After the implicit class reference, a hash of options is given.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
We create a new export plugin by calling the Eprints::Plugin::Export constructor&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we set a number of fields to register our new plugin.&lt;br /&gt;
&lt;br /&gt;
This is the name that will appear in the export dropdown menu. The name should therefore be short and descriptive.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The accept field is set to an array containing the type of objects this&lt;br /&gt;
plugin can deal with. In this case lists of eprints and individual&lt;br /&gt;
eprints.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The visible field denoes the class of user which will be able to see the plugin.&lt;br /&gt;
For most export plugins the value 'all' will be required, allowing&lt;br /&gt;
all users to see and use the plugin. A value of 'staff' would&lt;br /&gt;
make the plugin only visible to repository staff.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The suffix field contains the extension of files exported by the plugin.&lt;br /&gt;
&lt;br /&gt;
The mimetype field defines the MIME type of the files exported by the plugin&lt;br /&gt;
You can also specify file encoding, for example 'text/plain; charset=utf-8' to specify plain text, encoded using UTF-8. UTF-8[http://en.wikipedia.org/wiki/UTF-8] is a Unicode character encoding capable of expressing a large number of different characters and so is usually preferable to ASCII[http://en.wikipedia.org/wiki/ASCII]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
We then return our plugin reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Processing DataObjs ==&lt;br /&gt;
&lt;br /&gt;
This method handles the export of each DataObj. DataObjs make up most of the content of an EPrint repository. The three main types are EPrint defining individual eprints, Document defining collections of one or more files belonging to an EPrints and User which defines users of the repository.&lt;br /&gt;
&lt;br /&gt;
Besides an implicit reference to the plugin object, this method is also provided with a reference to an individual DataObj. It is called by several cgi and command line scripts to export single DataObjs, for instance the item control screen for repository staff. It is also called by the list handling method on each DataObj in a list, for example the results of a search. That will be explained in [[User:Tom/Export Plugins/Hello Lists|the next tutorial]].&lt;br /&gt;
&lt;br /&gt;
In the example below we get the title of each DataObj, but there are large number of fields which you can extract from each DataObj. For example try changing &amp;quot;title&amp;quot; to &amp;quot;abstract&amp;quot; to print the abstract of each eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        # Return a scalar containing the title.&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Finishing Off ==&lt;br /&gt;
&lt;br /&gt;
Standard Perl package requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your web server and perform a search.&lt;br /&gt;
&lt;br /&gt;
If all is well your plugin should appear in the dropdown menu. Select it and click export. As long as the search provided some results, you should get a list of EPrint titles returned.&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphello.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5736</id>
		<title>Contribute: Plugins/ExportPluginsHelloOld</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHelloOld&amp;diff=5736"/>
		<updated>2007-09-28T12:49:59Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Export Plugin Tutorial 1: &amp;quot;Hello, World!&amp;quot; */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Export Plugin Tutorial 1: &amp;quot;Hello, World!&amp;quot; =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn how to create a simple export plugin for EPrints, which will generate a list of titles from the results of a search. A basic knowledge of Perl is needed, but the code will be explained fully.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
It is sensible to separate the plugins you create for EPrints from those included with it. Create a directory for your export plugins in the main plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/Export) for example /opt/eprints3/perl_lib/EPrints/Plugin/Export/MyPlugins.&lt;br /&gt;
&lt;br /&gt;
= HelloExport.pm =&lt;br /&gt;
&lt;br /&gt;
Replace MyPlugins with the name of the directory you have decided to put your export plugins in and place the code below in a file called HelloExport.pm in that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloExport;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== In More Detail ==&lt;br /&gt;
== Housekeeping ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::Foo::HelloExport;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Export plugins need to inherit from the EPrints::Plugin::Export class.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
The Constructor for our Plugin. After the implicit class reference, a hash of options is given.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
We create a new export plugin by calling the Eprints::Plugin::Export constructor&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we set a number of fields to register our new plugin.&lt;br /&gt;
&lt;br /&gt;
This is the name that will appear in the export dropdown menu. The name should therefore be short and descriptive.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, World!';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The accept field is set to an array containing the type of objects this&lt;br /&gt;
plugin can deal with. In this case lists of eprints and individual&lt;br /&gt;
eprints.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The visible field denoes the class of user which will be able to see the plugin.&lt;br /&gt;
For most export plugins the value 'all' will be required, allowing&lt;br /&gt;
all users to see and use the plugin. A value of 'staff' would&lt;br /&gt;
make the plugin only visible to repository staff.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The suffix field contains the extension of files exported by the plugin.&lt;br /&gt;
&lt;br /&gt;
The mimetype field defines the MIME type of the files exported by the plugin&lt;br /&gt;
You can also specify file encoding, for example 'text/plain; charset=utf-8' to specify plain text, encoded using UTF-8. UTF-8[http://en.wikipedia.org/wiki/UTF-8] is a Unicode character encoding capable of expressing a large number of different characters and so is usually preferable to ASCII[http://en.wikipedia.org/wiki/ASCII]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
We then return our plugin reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Processing DataObjs ==&lt;br /&gt;
&lt;br /&gt;
This method handles the export of each DataObj. DataObjs make up most of the content of an EPrint repository. The three main types are EPrint defining individual eprints, Document defining collections of one or more files belonging to an EPrints and User which defines users of the repository.&lt;br /&gt;
&lt;br /&gt;
Besides an implicit reference to the plugin object, this method is also provided with a reference to an individual DataObj. It is called by several cgi and command line scripts to export single DataObjs, for instance the item control screen for repository staff. It is also called by the list handling method on each DataObj in a list, for example the results of a search. That will be explained in [[User:Tom/Export Plugins/Hello Lists|the next tutorial]].&lt;br /&gt;
&lt;br /&gt;
In the example below we get the title of each DataObj, but there are large number of fields which you can extract from each DataObj. For example try changing &amp;quot;title&amp;quot; to &amp;quot;abstract&amp;quot; to print the abstract of each eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        # Return a scalar containing the title.&lt;br /&gt;
        return $dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Finishing Off ==&lt;br /&gt;
&lt;br /&gt;
Standard Perl package requirement.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your web server and perform a search.&lt;br /&gt;
&lt;br /&gt;
If all is well your plugin should appear in the dropdown menu. Select it and click export. As long as the search provided some results, you should get a list of EPrint titles returned.&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphello.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5735</id>
		<title>Contribute: Plugins/ImportPluginsAWS</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5735"/>
		<updated>2007-09-28T10:27:43Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Import Plugin Tutorial 2: Amazon Web Services */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 2: Amazon Web Services =&lt;br /&gt;
&lt;br /&gt;
In the [[Contribute:_Plugins/ImportPluginsCSV|last tutorial]] we created an import plugin that took data which needed very little modification to import into the respository. The column names in the CSV file matched the names of metadata fields present in the repository. In this tutorial we'll look at importing data that needs some modification to be imported, needs more error checking and is obtained in a different way.&lt;br /&gt;
&lt;br /&gt;
We will be using Amazon's E-Commerce Webservice to import books from their website into our respository given a list of ASINs (Amazon Standard Identification Numbers).&lt;br /&gt;
&lt;br /&gt;
We will be accessing the service using a REST approach, communicating with the server using URL parameters and retrieving an XML document in response to our request. It is also possible to access their services using SOAP, but that will not be discussed here.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
== Amazon Web Services ==&lt;br /&gt;
To use Amazon's web services you must first signup for an account [http://aws.amazon.com here]. Their site has extensive documentation on the services that they offer as well as example programs including some written in Perl.&lt;br /&gt;
&lt;br /&gt;
== Required Modules ==&lt;br /&gt;
To prepare for this tutorial you should make sure the [http://search.cpan.org/~gaas/libwww-perl-5.805/lib/LWP/UserAgent.pm LWP::UserAgent] module is installed. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan LWP::UserAgent&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= AWS.pm = &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::AWS;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'AWS';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $input) = @_;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        $input =~ m/([0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        #Get Item Object&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
We will use the URI::Escape module in this plugin. As it is included with EPrints we don't need to check if it exists first.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup a number of values for parameters that will be part of our web service requests. The endpoint variable determines which server will be sent the request. Here we have used the UK server, but by changing the TLD we can use the US, Canadian, German or French servers.&lt;br /&gt;
&lt;br /&gt;
The accesskey stores the access key you will have gained from signing up to Amazon earlier. You should use the normal access key and not the secret one.&lt;br /&gt;
&lt;br /&gt;
Here we use the ItemLookup operation of the AWSECommerceService with the 2007-07-16 version of the service API. Other operations allow searching for items, but here we want to look up specific products. Finally the variable responsegroup determines the amount and nature of the information returned, we select &amp;quot;Large&amp;quot; in this case, giving a lot of information about the item.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
The constructor is similar to the one used for the CSV plugin, except this one will import individual eprints, given an ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Like we imported Text::CSV in the last tutorial, here we import LWP::UserAgent which will be used for making requests to the web service.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
This method is similar to the one used in the CSV plugin, but doesn't have to do quite so much work.&lt;br /&gt;
&lt;br /&gt;
First we create the array to hold our imported eprint ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next we read all the lines in the supplied file handle into our records array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we iterate over each record, running convert_input on it, importing it into our repository and adding the id to our array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we return a List object of the items imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
&lt;br /&gt;
ASINs are strings of decimal digits which may have leading zeroes which identify a product. Here we remove any non-numerical characters which are surrounding the ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $input =~ m/([0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We form the request from the variables we created earlier and the ASIN we have just obtained.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now send the request, by creating a new LWP::UserAgent object, setting its timeout to 30 seconds and then performing the request using HTTP GET.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create a DOM object from the XML document returned.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each request contains an Items element within the root element of the document which contains a Request element. This element contains an element IsValid. This element will contain the value True or False depending on whether a valid request was made or not.&lt;br /&gt;
&lt;br /&gt;
Here we obtain the Request element and check that the IsValid element within it contains the value True. If it doesn't we  call the error method and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName(&amp;quot;Items&amp;quot;)-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The product found with the ItemLookup method is contained within an Item element within the Items element. Here we attempt to get that element and raise the error and return undef if we can't.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each item contains an ItemAttributes element which contains most of the metadata about an item.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For some specialised repositories it might make sense to import DVDs, computer games and electronic equipment, but we're just going to deal with books. The ProductGroup element within the ItemAttributes element tells you what sort of item we're dealing with. We're looking for the value 'Book'.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set a few fields without consulting the imported data. We know this is a book, so we set the type. We assume that it has not been refereed. We also assume it has been published, because we can buy it. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get and set the title.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set the official URL to the Amazon product page. We have to do a bit of extra work using the uri_unescape method from the URI::Escape package to convert URI escape codes into characters.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the ISBN. Note that the ISBN is often the same as the ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the number of pages.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publisher.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publication date and finally return our output hash.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
We'll start by collecting a few ASINS. Go to [http://www.amazon.co.uk Amazon] and pick a few books. The URL for each project page is in the form http://www.amazon.co.uk/Combination-of-title-an-author/dp/ASIN... Collect a few different ASINS.&lt;br /&gt;
&lt;br /&gt;
Now we'll demonstrate importing from Amazon with a few sample ASINs.&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0946719616&lt;br /&gt;
0297843877&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;AWS&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsZip&amp;diff=5734</id>
		<title>Contribute: Plugins/ExportPluginsZip</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsZip&amp;diff=5734"/>
		<updated>2007-09-28T10:26:37Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Testing Your Plugin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 5: Zip =&lt;br /&gt;
In this tutorial we'll look at packaging the results of a search into a Zip file. We'll create a directory for each eprint, and a sub-directory for each document belonging to that eprint. We'll also add an HTML index file to the archive to make it easier to navigate.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~miyagawa/Archive-Any-Create-0.02/lib/Archive/Any/Create.pm Archive::Any::Create] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Archive::Any::Create&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zip.pm =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::Zip;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use Archive::Any::Create;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
  my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
  my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
  $self-&amp;gt;{name} = 'Zip';&lt;br /&gt;
  $self-&amp;gt;{accept} = [ 'list/eprint' ];&lt;br /&gt;
  $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.zip';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/zip';&lt;br /&gt;
&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Archive::Any::Create');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Archive::Any::Create';&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
  my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
  my $archive = '';&lt;br /&gt;
  open (my $FH, '&amp;gt;', \$archive) or&lt;br /&gt;
    die(&amp;quot;Could not create filehandle: $!&amp;quot;);&lt;br /&gt;
  my $zip = Archive::Any::Create-&amp;gt;new;&lt;br /&gt;
&lt;br /&gt;
  my $index = &amp;lt;&amp;lt;END;&lt;br /&gt;
  &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
      &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;title&amp;gt;EPrints Search Results&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
  my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
    my $heading = $session-&amp;gt;make_element('h2');&lt;br /&gt;
    $heading-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
    $div-&amp;gt;appendChild($heading);&lt;br /&gt;
&lt;br /&gt;
    my $uldoc = $session-&amp;gt;make_element('ul');&lt;br /&gt;
    $div-&amp;gt;appendChild($uldoc);&lt;br /&gt;
&lt;br /&gt;
    my $dirpath = 'eprints-search/'.$dataobj-&amp;gt;get_id().'/';&lt;br /&gt;
&lt;br /&gt;
    my $i = 1;&lt;br /&gt;
    foreach my $doc ($dataobj-&amp;gt;get_all_documents)&lt;br /&gt;
    {&lt;br /&gt;
      my $subdirpath = $dirpath.&amp;quot;doc$i/&amp;quot;;&lt;br /&gt;
      my %files = $doc-&amp;gt;files;&lt;br /&gt;
&lt;br /&gt;
      my $lidoc = $session-&amp;gt;make_element('li');&lt;br /&gt;
      $uldoc-&amp;gt;appendChild($lidoc);&lt;br /&gt;
&lt;br /&gt;
      my $adoc = $session-&amp;gt;make_element('a', href=&amp;gt;$dataobj-&amp;gt;get_id.&amp;quot;/doc$i/&amp;quot;.$doc-&amp;gt;get_main);&lt;br /&gt;
      $lidoc-&amp;gt;appendChild($adoc);&lt;br /&gt;
&lt;br /&gt;
      if ($doc-&amp;gt;exists_and_set('formatdesc'))&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_value('formatdesc')));&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_main));&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      foreach my $filename (sort keys %files)&lt;br /&gt;
      {&lt;br /&gt;
       my $filepath = $subdirpath.$filename;&lt;br /&gt;
        my $file = $doc-&amp;gt;local_path.'/'.$filename;&lt;br /&gt;
&lt;br /&gt;
        if (-d $file)&lt;br /&gt;
        {&lt;br /&gt;
          next;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $data = '';&lt;br /&gt;
        open (my $datafh ,'&amp;gt;', \$data);&lt;br /&gt;
&lt;br /&gt;
        open (INFH, &amp;quot;&amp;lt;$file&amp;quot;) or die (&amp;quot;Could not open file $file&amp;quot;);&lt;br /&gt;
        while (&amp;lt;INFH&amp;gt;)&lt;br /&gt;
        {&lt;br /&gt;
          print {$datafh} $_;&lt;br /&gt;
        }&lt;br /&gt;
        close INFH;&lt;br /&gt;
&lt;br /&gt;
        $zip-&amp;gt;add_file($filepath, $data);&lt;br /&gt;
      }&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $index .= EPrints::XML::to_string($div);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $index .= '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
  $zip-&amp;gt;add_file('eprints-search/index.htm',$index);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $zip-&amp;gt;write_filehandle($opts{fh},'zip');&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
  $zip-&amp;gt;write_filehandle($FH,'zip');&lt;br /&gt;
  return $archive;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For the sake of simplicity this plugin will only deal with lists of eprints. This avoids some code duplication, and it would be fairly easy to modify the plugin to deal with both individual eprints and lists of eprints sensibly.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{accept} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The file extension and MIME type are set to values appropriate for Zip files.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.zip';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/zip';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to import a module that is not included with EPrints for creating zip files. We use the EPrints::Utils::require_if_exists function to check if the module exists, and load it if it does. We then check the value returned from that function, and make the plugin invisible if it failed.&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Archive::Any::Create');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Archive::Any::Create';&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List Handling ==&lt;br /&gt;
=== Setting Up ===&lt;br /&gt;
Here we setup an in-memory file for the Zip, and create an Archive object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $archive = '';&lt;br /&gt;
  open (my $FH, '&amp;gt;', \$archive) or&lt;br /&gt;
    die(&amp;quot;Could not create filehandle: $!&amp;quot;);&lt;br /&gt;
  my $zip = Archive::Any::Create-&amp;gt;new;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navigation ===&lt;br /&gt;
Here we begin to setup the HTML file that we'll add to our archive for navigation. First we setup a header.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $index = &amp;lt;&amp;lt;END;&lt;br /&gt;
  &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
      &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;title&amp;gt;EPrints Search Results&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we get the Session object, we'll be using it to manipulate DOM objects later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handling DataObjs ===&lt;br /&gt;
We loop over the DataObjs as we have done before.&lt;br /&gt;
&lt;br /&gt;
This time we setup some DOM objects to be added to our index. Each eprint will have it's title printed out followed by a list of documents.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
    my $heading = $session-&amp;gt;make_element('h2');&lt;br /&gt;
    $heading-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
    $div-&amp;gt;appendChild($heading);&lt;br /&gt;
&lt;br /&gt;
    my $uldoc = $session-&amp;gt;make_element('ul');&lt;br /&gt;
    $div-&amp;gt;appendChild($uldoc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a directory for each eprint. Note it is not necessary to explicitly create a directory, we simply have to set the appropriate file path. However this means that if you do not add files to a certain directory it will not be created, rather than having an empty directory for a given eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $dirpath = 'eprints-search/'.$dataobj-&amp;gt;get_id().'/';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dealing With Documents ====&lt;br /&gt;
We then loop over all the documents belonging to each DataObj. The get_all_documents method returns an array of Document objects.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $i = 1;&lt;br /&gt;
    foreach my $doc ($dataobj-&amp;gt;get_all_documents)&lt;br /&gt;
    {&lt;br /&gt;
      my $subdirpath = $dirpath.&amp;quot;doc$i/&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we create a list item for the document containing a link to the main file. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      my $lidoc = $session-&amp;gt;make_element('li');&lt;br /&gt;
      $uldoc-&amp;gt;appendChild($lidoc);&lt;br /&gt;
&lt;br /&gt;
      my $adoc = $session-&amp;gt;make_element('a', href=&amp;gt;$dataobj-&amp;gt;get_id.&amp;quot;/doc$i/&amp;quot;.$doc-&amp;gt;get_main);&lt;br /&gt;
      $lidoc-&amp;gt;appendChild($adoc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If a description of the main file has been set we use that as the link text, otherwise we use the filename.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      if ($doc-&amp;gt;exists_and_set('formatdesc'))&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_value('formatdesc')));&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_main));&lt;br /&gt;
      }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dealing With Files ====&lt;br /&gt;
The files method of the Document object returns a hash whose keys are file names and values are file sizes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      my %files = $doc-&amp;gt;files;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We loop over each file belonging to the document, in most cases there will only be one file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      foreach my $filename (sort keys %files)&lt;br /&gt;
      {&lt;br /&gt;
        my $filepath = $subdirpath.$filename;&lt;br /&gt;
        my $file = $doc-&amp;gt;local_path.'/'.$filename;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to read the contents of the file and add it to a file in the zip. First we'll create another in-memory file to hold the contents.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $data = '';&lt;br /&gt;
        open (my $datafh ,'&amp;gt;', \$data);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We open our file and print it straight out to our in-memory file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        open (INFH, &amp;quot;&amp;lt;$file&amp;quot;) or die (&amp;quot;Could not open file $file&amp;quot;);&lt;br /&gt;
        while (&amp;lt;INFH&amp;gt;)&lt;br /&gt;
        {&lt;br /&gt;
          print {$datafh} $_;&lt;br /&gt;
        }&lt;br /&gt;
        close INFH;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we add the file data to our file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $zip-&amp;gt;add_file($filepath, $data);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we add the DOM object for our eprint to the index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $index .= EPrints::XML::to_string($div);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Finishing Off ===&lt;br /&gt;
After finishing off our index file we add it to the zip file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $index .= '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
  $zip-&amp;gt;add_file('eprints-search/index.htm',$index);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a file handle has been provided we write to it, otherwise we write to the scalar file handle created earlier. We then return in the usual fashion.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $zip-&amp;gt;write_filehandle($opts{fh},'zip');&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
  $zip-&amp;gt;write_filehandle($FH,'zip');&lt;br /&gt;
  return $archive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Expzipv2.png]]&lt;br /&gt;
&lt;br /&gt;
The accompanying HTML index.&lt;br /&gt;
&lt;br /&gt;
[[Image:Expzip2.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsExcel&amp;diff=5732</id>
		<title>Contribute: Plugins/ExportPluginsExcel</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsExcel&amp;diff=5732"/>
		<updated>2007-09-28T10:25:51Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Testing Your Plugin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 4: Excel =&lt;br /&gt;
&lt;br /&gt;
In this tutorial and the next one we'll look at exporting files in non-text formats. Here we will explore exporting metadata in Excel format (pre Microsoft Office 2007 which uses an XML based format).&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/dist/Spreadsheet-WriteExcel/lib/Spreadsheet/WriteExcel.pm Spreadsheet::Excel] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Spreadsheet::Excel&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Excel.pm =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::Excel;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
  my ($class, %opts) = @_;&lt;br /&gt;
  my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
  $self-&amp;gt;{name} = 'Excel';&lt;br /&gt;
  $self-&amp;gt;{accept} = ['list/eprint'];&lt;br /&gt;
  $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.xls';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/vnd.ms-excel';&lt;br /&gt;
&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Spreadsheet::WriteExcel');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Spreadsheet::WriteExcel';&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
  my ($plugin, %opts) = @_;&lt;br /&gt;
  my $workbook;&lt;br /&gt;
&lt;br /&gt;
  my $output;&lt;br /&gt;
  open(my $FH,'&amp;gt;',\$output);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new(\*{$opts{fh}});&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new($FH);&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  my $worksheet = $workbook-&amp;gt;add_worksheet();&lt;br /&gt;
&lt;br /&gt;
  my $i = 0;&lt;br /&gt;
  my @fields =&lt;br /&gt;
  $plugin-&amp;gt;{session}-&amp;gt;get_repository-&amp;gt;get_dataset('archive')-&amp;gt;get_fields;&lt;br /&gt;
&lt;br /&gt;
  foreach my $field (@fields)&lt;br /&gt;
  {&lt;br /&gt;
    $worksheet-&amp;gt;write(0, $i, $field-&amp;gt;get_name);&lt;br /&gt;
    $i++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $i = 1;&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $j = 0;&lt;br /&gt;
    foreach my $field (@fields)&lt;br /&gt;
    {&lt;br /&gt;
      if ($dataobj-&amp;gt;exists_and_set($field-&amp;gt;get_name))&lt;br /&gt;
      {&lt;br /&gt;
        if ($field-&amp;gt;get_property('multiple'))&lt;br /&gt;
        {&lt;br /&gt;
          if ($field-&amp;gt;{type} eq 'name')&lt;br /&gt;
          {&lt;br /&gt;
            my $namelist = '';&lt;br /&gt;
            foreach my $name (@{$dataobj-&amp;gt;get_value_raw($field-&amp;gt;get_name)})&lt;br /&gt;
            {&lt;br /&gt;
              $namelist .= $name-&amp;gt;{family} . ',' . $name-&amp;gt;{given} . ';';&lt;br /&gt;
            }&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, $namelist);&lt;br /&gt;
          }&lt;br /&gt;
          elsif ($field-&amp;gt;{type} eq 'compound')&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, 'COMPOUND');&lt;br /&gt;
          }&lt;br /&gt;
          else&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j,&lt;br /&gt;
                        join(';',@{$dataobj-&amp;gt;get_value($field-&amp;gt;get_name)}));&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
          $worksheet-&amp;gt;write($i, $j, $dataobj-&amp;gt;get_value($field-&amp;gt;get_name));&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      $j++;&lt;br /&gt;
    }&lt;br /&gt;
    $i++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $workbook-&amp;gt;close;&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For the sake of simplicity this plugin will only deal with lists of eprints. This avoids some code duplication, and it would be fairly easy to modify the plugin to deal with both individual eprints and lists of eprints sensibly.&lt;br /&gt;
&amp;lt;pre&amp;gt;  &lt;br /&gt;
  $self-&amp;gt;{accept} = ['list/eprint'];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The file extension and MIME type are set to values appropriate for Excel files.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.xls';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/vnd.ms-excel';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to import a module that is not included with EPrints for creating zip files. We use the EPrints::Utils::require_if_exists function to check if the module exists, and load it if it does. We then check the value returned from that function, and make the plugin invisible if it failed.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Spreadsheet::WriteExcel');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Spreadsheet::WriteExcel';&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== List Handling ==&lt;br /&gt;
=== Setting Up a Workbook ===&lt;br /&gt;
Here we create a new Excel workbook. We start by creating a file handle using a scalar rather than a filename, this creates an in-memory file. Then depending on if a file handle has been supplied or not we create a workbook object that will be written to that file handle or the scalar file handle we just created.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $workbook;&lt;br /&gt;
&lt;br /&gt;
  my $output;&lt;br /&gt;
  open(my $FH,'&amp;gt;',\$output);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new(\*{$opts{fh}});&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new($FH);&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handling DataObjs ===&lt;br /&gt;
To start adding data to the Excel file we have to create a worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $worksheet = $workbook-&amp;gt;add_worksheet();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To the first row of our worksheet we add the names of all the metadata fields that can be associated with eprints. We get the session associated with the plugin, and then the repository associated with that session. We then get the DataSet &amp;quot;archive&amp;quot; from that repository and call the get_fields method. That method returns an array of MetaField objects.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my @fields =&lt;br /&gt;
  $plugin-&amp;gt;{session}-&amp;gt;get_repository-&amp;gt;get_dataset('archive')-&amp;gt;get_fields;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we loop over each field and write it's name to our worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  foreach my $field (@fields)&lt;br /&gt;
  {&lt;br /&gt;
    $worksheet-&amp;gt;write(0, $i, $field-&amp;gt;get_name);&lt;br /&gt;
    $i++;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now loop over each DataObj in our list, and over each MetaField we found earlier.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $j = 0;&lt;br /&gt;
    foreach my $field (@fields)&lt;br /&gt;
    {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We only write something to the worksheet if the field can apply to the DataObj and is set. Scalar values are simply written to the worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      if ($dataobj-&amp;gt;exists_and_set($field-&amp;gt;get_name))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The plugin handles fields which can take multiple values in a number of ways.   &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        if ($field-&amp;gt;get_property('multiple'))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Names are handled specially and are formatted with the family name followed by a comma, the given name or initial and then a semi-colon.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
          if ($field-&amp;gt;{type} eq 'name')&lt;br /&gt;
          {&lt;br /&gt;
            my $namelist = '';&lt;br /&gt;
            foreach my $name (@{$dataobj-&amp;gt;get_value_raw($field-&amp;gt;get_name)})&lt;br /&gt;
            {&lt;br /&gt;
              $namelist .= $name-&amp;gt;{family} . ',' . $name-&amp;gt;{given} . ';';&lt;br /&gt;
            }&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, $namelist);&lt;br /&gt;
          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Fields which have a compound type are not handled, and 'COMPOUND' is written to the worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
          elsif ($field-&amp;gt;{type} eq 'compound')&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, 'COMPOUND');&lt;br /&gt;
          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For most multiple fields each value is taken and concatenated, separated by semi-colons.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
          else&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j,&lt;br /&gt;
                        join(';',@{$dataobj-&amp;gt;get_value($field-&amp;gt;get_name)}));&lt;br /&gt;
          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Finishing Up ===&lt;br /&gt;
&lt;br /&gt;
We first need to close the workbook to ensure that the data is written to the file handles, and then we can return in the usual fashion. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $workbook-&amp;gt;close;&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Expexcel.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5731</id>
		<title>Contribute: Plugins/ExportPluginsHTML</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5731"/>
		<updated>2007-09-28T10:25:21Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Testing Your Plugin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 3: HTML =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we'll look at creating an export plugin with slightly more complex output than unformatted plain text. Although the plugin below produces XHTML the same principles apply to producing any XML document.&lt;br /&gt;
&lt;br /&gt;
= HelloHTML.pm =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloHTML;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, HTML!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
        return EPrints::XML::to_string($xml);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $footer;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $footer;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub xml_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
        my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&lt;br /&gt;
        my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
        $title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
        $div-&amp;gt;appendChild($title);&lt;br /&gt;
&lt;br /&gt;
        my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
        $abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
        $div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&lt;br /&gt;
        return $div;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
Here we change the file extension to '.htm' and change the MIME type to &amp;quot;text/html&amp;quot;. For general XML documents you should change the file extension to '.xml' and the MIME type to 'text/xml';&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
$self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_dataobj ==&lt;br /&gt;
Here the output_dataobj method does very little. It calls the xml_dataobj method to obtain a DOM object which is converted to plain text before being returned. The xml_dataobj method is described later.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
return EPrints::XML::to_string($xml);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_list ==&lt;br /&gt;
The only changes to the output_list method are the additions of a header and a footer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== xml_dataobj ==&lt;br /&gt;
This method has a similar signature to the output_dataobj method: it takes an implicit reference to the plugin object and a reference to a DataObj, however instead of returning a string it returns a DOM object.&lt;br /&gt;
&lt;br /&gt;
Before we can start creating and manipulating DOM objects we need to get a reference to the Session object from the plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create elements using the make_element method of our session object. The first argument to the make_element method is the tag name of the element we want to create, for example 'a' or 'div'. The second parameter is a hash of attributes and values associated with the element.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we add a second-level header. The make_text method is used to create text nodes. The appendChild method is used to add child nodes. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
$title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
$div-&amp;gt;appendChild($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A similar pattern is followed to add a paragraph containing the eprint's abstract.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
$abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
$div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a DOM object representing a 'div' element containing our header and paragraph.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return $div;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as [[Contribute:_Plugins/ExportPluginsHello| before]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphtml.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsList&amp;diff=5730</id>
		<title>Contribute: Plugins/ExportPluginsList</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsList&amp;diff=5730"/>
		<updated>2007-09-28T10:24:28Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Testing Your Plugin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Export Plugin Tutorial 2: List Handling =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn to create a slightly more complex export plugin than the one created in [[Contribute:_Plugins/ExportPluginsHello| the previous tutorial]] that changes the way lists of eprints are exported.&lt;br /&gt;
&lt;br /&gt;
= HelloList.pm =&lt;br /&gt;
The code in the section below should be placed in a file called HelloList.pm in the directory created previously, and MyPlugins&lt;br /&gt;
should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloList;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, List!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        return $dataobj-&amp;gt;get_id().&amp;quot;\t&amp;quot;.$dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;quot;ID\tTitle\n\n&amp;quot;;&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
The above code is very similar to the HelloExport.pm file in [[Contribute:_Plugins/ExportPluginsHello| the previous tutorial]] so only the points where it deviates significantly from that file will be discussed below.&lt;br /&gt;
&lt;br /&gt;
== Housekeeping ==&lt;br /&gt;
The package name has been changed to reflect the filename.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloList;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
Make sure you give each plugin a unique name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, List!';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dealing With Lists ==&lt;br /&gt;
In this example the output_list method is overridden in the export plugin to provide column headers. The original method just concatenates the output from the output_dataobj subroutine called on every DataObj in the list.&lt;br /&gt;
&lt;br /&gt;
Note that the method is not provided with a bare array of DataObjs, but a List object is provided within the opts hash. To get an array of DataObjs to loop over you must then call that List object's get_record method.&lt;br /&gt;
&lt;br /&gt;
== Filehandles ==&lt;br /&gt;
The command line export tool provides the output list method with a filehandle for output in the opts hash, while the cgi export uses a value returned from the method. You must deal with the filehandle or you will get no output from the command line tool. In most cases this won't matter, but it is good practice to deal with it.&lt;br /&gt;
&lt;br /&gt;
The best way to handle output is to check if a filehandle has been provided every time something needs to be output. If a filehandle is provided we print to it, otherwise we save the output for later. At the end of the method we either return undef if a filehandle was provided or we return the saved output otherwise.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;quot;ID\tTitle\n\n&amp;quot;;&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
&lt;br /&gt;
Restart your webserver and test the plugin as in [[Contribute:_Plugins/ExportPluginsHello| the previous tutorial]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Explist.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsList&amp;diff=5729</id>
		<title>Contribute: Plugins/ExportPluginsList</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsList&amp;diff=5729"/>
		<updated>2007-09-28T10:24:10Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* In More Detail */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Export Plugin Tutorial 2: List Handling =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn to create a slightly more complex export plugin than the one created in [[Contribute:_Plugins/ExportPluginsHello| the previous tutorial]] that changes the way lists of eprints are exported.&lt;br /&gt;
&lt;br /&gt;
= HelloList.pm =&lt;br /&gt;
The code in the section below should be placed in a file called HelloList.pm in the directory created previously, and MyPlugins&lt;br /&gt;
should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloList;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, List!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        return $dataobj-&amp;gt;get_id().&amp;quot;\t&amp;quot;.$dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;quot;ID\tTitle\n\n&amp;quot;;&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
The above code is very similar to the HelloExport.pm file in [[Contribute:_Plugins/ExportPluginsHello| the previous tutorial]] so only the points where it deviates significantly from that file will be discussed below.&lt;br /&gt;
&lt;br /&gt;
== Housekeeping ==&lt;br /&gt;
The package name has been changed to reflect the filename.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloList;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
Make sure you give each plugin a unique name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, List!';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dealing With Lists ==&lt;br /&gt;
In this example the output_list method is overridden in the export plugin to provide column headers. The original method just concatenates the output from the output_dataobj subroutine called on every DataObj in the list.&lt;br /&gt;
&lt;br /&gt;
Note that the method is not provided with a bare array of DataObjs, but a List object is provided within the opts hash. To get an array of DataObjs to loop over you must then call that List object's get_record method.&lt;br /&gt;
&lt;br /&gt;
== Filehandles ==&lt;br /&gt;
The command line export tool provides the output list method with a filehandle for output in the opts hash, while the cgi export uses a value returned from the method. You must deal with the filehandle or you will get no output from the command line tool. In most cases this won't matter, but it is good practice to deal with it.&lt;br /&gt;
&lt;br /&gt;
The best way to handle output is to check if a filehandle has been provided every time something needs to be output. If a filehandle is provided we print to it, otherwise we save the output for later. At the end of the method we either return undef if a filehandle was provided or we return the saved output otherwise.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;quot;ID\tTitle\n\n&amp;quot;;&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
&lt;br /&gt;
Restart your webserver and test the plugin as in [[User:Tom/Export_Plugins/Hello_World| the previous tutorial]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Explist.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsList&amp;diff=5728</id>
		<title>Contribute: Plugins/ExportPluginsList</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsList&amp;diff=5728"/>
		<updated>2007-09-28T10:23:29Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Export Plugin Tutorial 2: List Handling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Export Plugin Tutorial 2: List Handling =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn to create a slightly more complex export plugin than the one created in [[Contribute:_Plugins/ExportPluginsHello| the previous tutorial]] that changes the way lists of eprints are exported.&lt;br /&gt;
&lt;br /&gt;
= HelloList.pm =&lt;br /&gt;
The code in the section below should be placed in a file called HelloList.pm in the directory created previously, and MyPlugins&lt;br /&gt;
should be changed to the name of that directory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloList;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, List!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.txt';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/plain; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        return $dataobj-&amp;gt;get_id().&amp;quot;\t&amp;quot;.$dataobj-&amp;gt;get_value('title').&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;quot;ID\tTitle\n\n&amp;quot;;&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
The above code is very similar to the HelloExport.pm file in [[User:Tom/Export_Plugins/Hello_World| the previous tutorial]] so only the points where it deviates significantly from that file will be discussed below.&lt;br /&gt;
&lt;br /&gt;
== Housekeeping ==&lt;br /&gt;
The package name has been changed to reflect the filename.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloList;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
Make sure you give each plugin a unique name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, List!';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dealing With Lists ==&lt;br /&gt;
In this example the output_list method is overridden in the export plugin to provide column headers. The original method just concatenates the output from the output_dataobj subroutine called on every DataObj in the list.&lt;br /&gt;
&lt;br /&gt;
Note that the method is not provided with a bare array of DataObjs, but a List object is provided within the opts hash. To get an array of DataObjs to loop over you must then call that List object's get_record method.&lt;br /&gt;
&lt;br /&gt;
== Filehandles ==&lt;br /&gt;
The command line export tool provides the output list method with a filehandle for output in the opts hash, while the cgi export uses a value returned from the method. You must deal with the filehandle or you will get no output from the command line tool. In most cases this won't matter, but it is good practice to deal with it.&lt;br /&gt;
&lt;br /&gt;
The best way to handle output is to check if a filehandle has been provided every time something needs to be output. If a filehandle is provided we print to it, otherwise we save the output for later. At the end of the method we either return undef if a filehandle was provided or we return the saved output otherwise.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;quot;ID\tTitle\n\n&amp;quot;;&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
&lt;br /&gt;
Restart your webserver and test the plugin as in [[User:Tom/Export_Plugins/Hello_World| the previous tutorial]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Explist.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=User:Tom&amp;diff=5727</id>
		<title>User:Tom</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=User:Tom&amp;diff=5727"/>
		<updated>2007-09-27T21:09:07Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Works In Progress */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Tom =&lt;br /&gt;
&lt;br /&gt;
== Works In Progress ==&lt;br /&gt;
A place to store the things I'm working on, before they're ready for the wiki proper. &lt;br /&gt;
&lt;br /&gt;
* [[User:Tom/Export_Plugins | Export Plugins]]&lt;br /&gt;
* [[User:Tom/Import_Plugins | Import Plugins]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=How_to_write_plugins&amp;diff=5726</id>
		<title>How to write plugins</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=How_to_write_plugins&amp;diff=5726"/>
		<updated>2007-09-27T21:06:33Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Import Plugin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Write a Plugin! ==&lt;br /&gt;
&lt;br /&gt;
The plugin system for EPrints 3 has been developed to make it easy and share the most common extensions to the code without having to hack the core system (and causing yourself problems with upgrades etc.)&lt;br /&gt;
&lt;br /&gt;
When the system loads, it automatically loads all modules in the perl_lib/EPrints/Plugin/ directory, so for simple plugins you just drop them in that directory and you're done!&lt;br /&gt;
&lt;br /&gt;
When a plugin is loaded it has a registration method which is called which tells the core EPrints system what this plugin does. EPrints then makes it available as appropriate.&lt;br /&gt;
&lt;br /&gt;
Clever plugins can detect features they need and adapt to use the tools available, or disable themselves if they are missing required tools (rather than crash the system). Some specialised plugins are disabled in their default state and must be enabled in the repository configuration.&lt;br /&gt;
&lt;br /&gt;
Another cool thing is that plugins are Perl Objects, which means you can subclass them. Here's an real-world example: We have a research group which uses BibTeX but over the years standardised within the group on an extra field. This is not a valid bibtex field, but are essential to their working because they have ancient and essential scripts which depend on it. To handle this we can subclass the default BibTeX Export plugin and override a single method (the data mapping one). We then just call the original parent plugins mapping method to do all the heavy lifting, then just add our non-standard extra field. Total code required: less than one screen. Number of happy researchers: none (they are never satisfied and will just demand the moon on the stick because you've already given them this nice new feature), but number of researchers able to get their work done: lots. Don't believe me? [[BibTeX Extension Example|look here]]!&lt;br /&gt;
&lt;br /&gt;
* See also [[Extension Packages]] for how to add configuration and resource files to plugins.&lt;br /&gt;
&lt;br /&gt;
=== Types of Plugin ===&lt;br /&gt;
&lt;br /&gt;
There are a number of different kinds of plugin for EPrints... &lt;br /&gt;
&lt;br /&gt;
==== Export Plugin ====&lt;br /&gt;
&lt;br /&gt;
These are used to export the data in a variety of formats. A number of tutorials have been created to help you create your own export plugins:&lt;br /&gt;
&lt;br /&gt;
* [[Contribute:_Plugins/ExportPluginsHello| Export Plugin Tutorial 1: &amp;quot;Hello, World!&amp;quot;]]&lt;br /&gt;
* [[Contribute:_Plugins/ExportPluginsList| Export Plugin Tutorial 2: List handling]]&lt;br /&gt;
* [[Contribute:_Plugins/ExportPluginsHTML| Export Plugin Tutorial 3: HTML]]&lt;br /&gt;
* [[Contribute:_Plugins/ExportPluginsExcel| Export Plugin Tutorial 4: Excel]]&lt;br /&gt;
* [[Contribute:_Plugins/ExportPluginsZip| Export Plugin Tutorial 5: Zip]]&lt;br /&gt;
&lt;br /&gt;
==== Import Plugin ====&lt;br /&gt;
&lt;br /&gt;
These are used to import data into a repository. They can take datafiles directly, or they can take an ID of a record that can be retrieved in a known way, or a URL of a file, or... whatever.&lt;br /&gt;
&lt;br /&gt;
These are a bit trickier to write than export plugins as parsing data is harder than just &amp;quot;print&amp;quot;ing it, but they are still reasonably straight forward. To get you started a small selection of tutorials has been written:&lt;br /&gt;
&lt;br /&gt;
* [[Contribute:_Plugins/ImportPluginsCSV| Import Plugin Tutorial 1: CSV]]&lt;br /&gt;
* [[Contribute:_Plugins/ImportPluginsAWS| Import Plugin Tutorial 2: Amazon Web Services]]&lt;br /&gt;
&lt;br /&gt;
==== Screen Plugin ====&lt;br /&gt;
&lt;br /&gt;
These handle (almost) all the user interface screens. Pages like &amp;quot;Review&amp;quot; and &amp;quot;Profile&amp;quot; are just built-in plugins. You can add your own very easily. &lt;br /&gt;
&lt;br /&gt;
Examples you could create...&lt;br /&gt;
* Birds Eye View - a view of various statistics on the database, all in one page.&lt;br /&gt;
* Spellchecking Tab - an additional tab in the item control page which checks the spelling on certain fields.&lt;br /&gt;
* Bulk Delete tool - a tool which takes a list of eprintid's and deletes them all in a fell swoop.&lt;br /&gt;
&lt;br /&gt;
Look at the existing Screen Plugins for an idea of how they work. They can be very simple.&lt;br /&gt;
&lt;br /&gt;
==== Input Component Plugin ====&lt;br /&gt;
&lt;br /&gt;
These handle how the workflow components are rendered. Built in components include the default (one field) component, the multiple fields component, the upload component, the subject component (which does pretty things to a field of type &amp;quot;subject&amp;quot;) and the XHTML component. You can add your own or sub-class existing ones.&lt;br /&gt;
&lt;br /&gt;
===== Convert Plugin =====&lt;br /&gt;
&lt;br /&gt;
These are used for two things, currently.&lt;br /&gt;
&lt;br /&gt;
* Converting the full text of documents into utf-8 text for search indexing&lt;br /&gt;
* Converting images and pdfs into thumbnails and previews&lt;br /&gt;
&lt;br /&gt;
Some examples you could create:&lt;br /&gt;
&lt;br /&gt;
* RTF to utf-8 to allow rich text documents to be indexed.&lt;br /&gt;
* Powerpoint to Thumbnail to allow thumbnail and previews of powerpoint slides&lt;br /&gt;
* Video to Thumbnail/Preview to make a still preview of a video file.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5724</id>
		<title>Contribute: Plugins/ImportPluginsAWS</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsAWS&amp;diff=5724"/>
		<updated>2007-09-27T20:59:54Z</updated>

		<summary type="html">&lt;p&gt;Tom: User:Tom/Import Plugins/Web Services moved to Contribute: Plugins/ImportPluginsAWS&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 2: Amazon Web Services =&lt;br /&gt;
&lt;br /&gt;
In the last tutorial we created an import plugin that took data which needed very little modification to import into the respository. The column names in the CSV file matched the names of metadata fields present in the repository. In this tutorial we'll look at importing data that needs some modification to be imported, needs more error checking and is obtained in a different way.&lt;br /&gt;
&lt;br /&gt;
We will be using Amazon's E-Commerce Webservice to import books from their website into our respository given a list of ASINs (Amazon Standard Identification Numbers).&lt;br /&gt;
&lt;br /&gt;
We will be accessing the service using a REST approach, communicating with the server using URL parameters and retrieving an XML document in response to our request. It is also possible to access their services using SOAP, but that will not be discussed here.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
== Amazon Web Services ==&lt;br /&gt;
To use Amazon's web services you must first signup for an account [http://aws.amazon.com here]. Their site has extensive documentation on the services that they offer as well as example programs including some written in Perl.&lt;br /&gt;
&lt;br /&gt;
== Required Modules ==&lt;br /&gt;
To prepare for this tutorial you should make sure the [http://search.cpan.org/~gaas/libwww-perl-5.805/lib/LWP/UserAgent.pm LWP::UserAgent] module is installed. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan LWP::UserAgent&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= AWS.pm = &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::AWS;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'AWS';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $input) = @_;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        $input =~ m/([0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        #Get Item Object&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
We will use the URI::Escape module in this plugin. As it is included with EPrints we don't need to check if it exists first.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use URI::Escape;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup a number of values for parameters that will be part of our web service requests. The endpoint variable determines which server will be sent the request. Here we have used the UK server, but by changing the TLD we can use the US, Canadian, German or French servers.&lt;br /&gt;
&lt;br /&gt;
The accesskey stores the access key you will have gained from signing up to Amazon earlier. You should use the normal access key and not the secret one.&lt;br /&gt;
&lt;br /&gt;
Here we use the ItemLookup operation of the AWSECommerceService with the 2007-07-16 version of the service API. Other operations allow searching for items, but here we want to look up specific products. Finally the variable responsegroup determines the amount and nature of the information returned, we select &amp;quot;Large&amp;quot; in this case, giving a lot of information about the item.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $endpoint = 'http://ecs.amazonaws.co.uk/onca/xml';&lt;br /&gt;
my $accesskey = '&amp;lt;YOURAMAZONWSKEY&amp;gt;';&lt;br /&gt;
my $service = 'AWSECommerceService';&lt;br /&gt;
my $operation = 'ItemLookup';&lt;br /&gt;
my $version = '2007-07-16';&lt;br /&gt;
my $responsegroup = 'Large';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
The constructor is similar to the one used for the CSV plugin, except this one will import individual eprints, given an ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' , 'dataobj/eprint'];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Like we imported Text::CSV in the last tutorial, here we import LWP::UserAgent which will be used for making requests to the web service.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('LWP::UserAgent');&lt;br /&gt;
        unless ($rc)&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Unable to load required module LWP::UserAgent';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
This method is similar to the one used in the CSV plugin, but doesn't have to do quite so much work.&lt;br /&gt;
&lt;br /&gt;
First we create the array to hold our imported eprint ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next we read all the lines in the supplied file handle into our records array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we iterate over each record, running convert_input on it, importing it into our repository and adding the id to our array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $input_data (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input($input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we return a List object of the items imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
&lt;br /&gt;
ASINs are strings of decimal digits which may have leading zeroes which identify a product. Here we remove any non-numerical characters which are surrounding the ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $input =~ m/([0-9]+)/;&lt;br /&gt;
        $input = $1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We form the request from the variables we created earlier and the ASIN we have just obtained.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $request =&lt;br /&gt;
                &amp;quot;$endpoint?&amp;quot;.&lt;br /&gt;
                &amp;quot;Service=$service&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;AWSAccessKeyId=$accesskey&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Operation=$operation&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ItemId=$input&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;Version=$version&amp;amp;&amp;quot;.&lt;br /&gt;
                &amp;quot;ResponseGroup=$responsegroup&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now send the request, by creating a new LWP::UserAgent object, setting its timeout to 30 seconds and then performing the request using HTTP GET.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $ua = LWP::UserAgent-&amp;gt;new;&lt;br /&gt;
        $ua-&amp;gt;timeout(30);&lt;br /&gt;
        my $response = $ua-&amp;gt;get($request);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create a DOM object from the XML document returned.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dom = EPrints::XML::parse_xml_string($response-&amp;gt;content);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each request contains an Items element within the root element of the document which contains a Request element. This element contains an element IsValid. This element will contain the value True or False depending on whether a valid request was made or not.&lt;br /&gt;
&lt;br /&gt;
Here we obtain the Request element and check that the IsValid element within it contains the value True. If it doesn't we  call the error method and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rep =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName(&amp;quot;Items&amp;quot;)-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Request')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        my $reptext =&lt;br /&gt;
                EPrints::Utils::tree_to_utf8($rep-&amp;gt;getElementsByTagName('IsValid')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($reptext eq 'True') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Invalid AWS Request');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The product found with the ItemLookup method is contained within an Item element within the Items element. Here we attempt to get that element and raise the error and return undef if we can't.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $item =&lt;br /&gt;
                $dom-&amp;gt;getElementsByTagName('Items')-&amp;gt;item(0)-&amp;gt;&lt;br /&gt;
                getElementsByTagName('Item')-&amp;gt;item(0);&lt;br /&gt;
&lt;br /&gt;
        unless (defined $item) &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('No Item element found');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each item contains an ItemAttributes element which contains most of the metadata about an item.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $attr = $item-&amp;gt;getElementsByTagName('ItemAttributes')-&amp;gt;item(0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For some specialised repositories it might make sense to import DVDs, computer games and electronic equipment, but we're just going to deal with books. The ProductGroup element within the ItemAttributes element tells you what sort of item we're dealing with. We're looking for the value 'Book'.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pg = EPrints::Utils::tree_to_utf8($attr-&amp;gt;getElementsByTagName('ProductGroup')-&amp;gt;item(0));&lt;br /&gt;
&lt;br /&gt;
        unless ($pg eq 'Book') &lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error('Product is not a book.');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set a few fields without consulting the imported data. We know this is a book, so we set the type. We assume that it has not been refereed. We also assume it has been published, because we can buy it. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $output{type} = 'book';&lt;br /&gt;
        $output{refereed} = 'FALSE';&lt;br /&gt;
        $output{ispublished} = 'pub';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get and set the title.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $title = $attr-&amp;gt;getElementsByTagName('Title')-&amp;gt;item(0);&lt;br /&gt;
        $output{title} = EPrints::Utils::tree_to_utf8($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we set the official URL to the Amazon product page. We have to do a bit of extra work using the uri_unescape method from the URI::Escape package to convert URI escape codes into characters.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $url = $item-&amp;gt;getElementsByTagName('DetailPageURL')-&amp;gt;item(0);&lt;br /&gt;
        $output{official_url} = uri_unescape(EPrints::Utils::tree_to_utf8($url));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the ISBN. Note that the ISBN is often the same as the ASIN.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $isbn = $attr-&amp;gt;getElementsByTagName('ISBN')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $isbn)&lt;br /&gt;
        {&lt;br /&gt;
                $output{isbn} = EPrints::Utils::tree_to_utf8($isbn);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the number of pages.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pages = $attr-&amp;gt;getElementsByTagName('NumberOfPages')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pages)&lt;br /&gt;
        {&lt;br /&gt;
                $output{pages} = EPrints::Utils::tree_to_utf8($pages);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publisher.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $publisher = $attr-&amp;gt;getElementsByTagName('Publisher')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $publisher)&lt;br /&gt;
        {&lt;br /&gt;
                $output{publisher} = EPrints::Utils::tree_to_utf8($publisher);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can set the publication date and finally return our output hash.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $pubdate = $attr-&amp;gt;getElementsByTagName('PublicationDate')-&amp;gt;item(0);&lt;br /&gt;
        if (defined $pubdate)&lt;br /&gt;
        {&lt;br /&gt;
                $output{date} = EPrints::Utils::tree_to_utf8($pubdate);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
We'll start by collecting a few ASINS. Go to [http://www.amazon.co.uk Amazon] and pick a few books. The URL for each project page is in the form http://www.amazon.co.uk/Combination-of-title-an-author/dp/ASIN... Collect a few different ASINS.&lt;br /&gt;
&lt;br /&gt;
Now we'll demonstrate importing from Amazon with a few sample ASINs.&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0946719616&lt;br /&gt;
0297843877&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;AWS&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=User:Tom/Import_Plugins/Web_Services&amp;diff=5725</id>
		<title>User:Tom/Import Plugins/Web Services</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=User:Tom/Import_Plugins/Web_Services&amp;diff=5725"/>
		<updated>2007-09-27T20:59:54Z</updated>

		<summary type="html">&lt;p&gt;Tom: User:Tom/Import Plugins/Web Services moved to Contribute: Plugins/ImportPluginsAWS&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#redirect [[Contribute: Plugins/ImportPluginsAWS]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5722</id>
		<title>Contribute: Plugins/ImportPluginsCSV</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ImportPluginsCSV&amp;diff=5722"/>
		<updated>2007-09-27T20:59:32Z</updated>

		<summary type="html">&lt;p&gt;Tom: User:Tom/Import Plugins/CSV moved to Contribute: Plugins/ImportPluginsCSV&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Import Plugin Tutorial 1: CSV =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we will look at creating a relatively simple plugin to import eprints into our repository by reading files containing comma separated variables. We won't be dealing with documents and files, but will be focusing on importing eprint metadata.&lt;br /&gt;
&lt;br /&gt;
Import plugins are inherently more complicated than export plugins because of the error checking that must be done, however in this example error checking has been kept to a minimum to simplify the example. In a &amp;quot;real&amp;quot; plugin you should check that the appropriate metadata fields are set for a given type of eprint, and unfortunately there appears to be no quick way to do this.&lt;br /&gt;
&lt;br /&gt;
= Before You Start =&lt;br /&gt;
&lt;br /&gt;
It is sensible to separate the plugins you create for EPrints from those included with it. Create a directory for your import plugins in the main plugin directory (usually /opt/eprints3/perl_lib/EPrints/Plugin/import) for example /opt/eprints3/perl_lib/EPrints/Plugin/import/MyPlugins.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~erangel/Text-CSV/CSV.pm Text::CSV] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Text::CSV&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CSV.pm = &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Import::MyPlugins::CSV;&lt;br /&gt;
&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my( $class, %params ) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new( %params );&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'CSV';&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub input_fh&lt;br /&gt;
{&lt;br /&gt;
        my( $plugin, %opts ) = @_;&lt;br /&gt;
        my @ids;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
        my @fields;&lt;br /&gt;
&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub convert_input&lt;br /&gt;
{&lt;br /&gt;
        my $plugin = shift;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
                        else&lt;br /&gt;
                        {&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
                }&lt;br /&gt;
                $i++;&lt;br /&gt;
        }&lt;br /&gt;
        return \%output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Modules == &lt;br /&gt;
Here we import the superclass for our plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use EPrints::Plugin::Import::TextFile;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Inheritance ==&lt;br /&gt;
&lt;br /&gt;
Our plugin will not inherit from the Import class directly, but from the TextFile subclass. This contains some extra file handling code that means we can ignore certain differences in text file formats. If you are creating an import plugin which imports non-text files you should subclass the EPrints::Plugin::Import class directly.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
our @ISA = ('EPrints::Plugin::Import::TextFile');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For import plugins we must set a 'produce' property, to tell the repository what kinds of objects the plugin can import. This plugin only supports importing lists of eprints, but if it supported importing individual eprints we could add 'dataobj/eprint' to this property. We would then have to implement the &amp;quot;input_dataobj&amp;quot; method. Most plugins implement this method, but it is rarely used in practice. Most imports are done in lists (even if that list only contains one member), via the import items screen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $self-&amp;gt;{produce} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we use a module that is not included with EPrints, Text::CSV, so we import it in a different way. First we check that it is installed, and load it if it is with &amp;quot;EPrints::Utils::require_if_exists&amp;quot;.If it isn't we make the plugin invisible and produce an error message. It is good practice to import non-standard modules in this way rather than with &amp;quot;use&amp;quot;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $rc = EPrints::Utils::require_if_exists('Text::CSV');&lt;br /&gt;
        unless( $rc )&lt;br /&gt;
        {&lt;br /&gt;
                $self-&amp;gt;{visible} = '';&lt;br /&gt;
                $self-&amp;gt;{error} = 'Failed to load required module Text::CSV';&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Input ==&lt;br /&gt;
Import plugins have to implement a couple of methods to read data from a file or string, manipulate it and turn it into a form which can be imported into the repository. That process will be described below.&lt;br /&gt;
&lt;br /&gt;
=== input_fh ===&lt;br /&gt;
&lt;br /&gt;
This method takes a filehandle, processes it, tries to import DataObjs in to the repository and then returns a List of the DataObjs imported.&lt;br /&gt;
&lt;br /&gt;
This array will be used to create a List of DataObjs later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @ids;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we open the filehandle passed, and read the lines into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $fh = $opts{fh};&lt;br /&gt;
        my @records = &amp;lt;$fh&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a Text::CSV object to handle the input. Using a dedicated CSV handling package is preferable to using Perl's split function as it handles a number of more complicated scenarios such as commas within records using double quotes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After setting up an array for metadata field names, we attempt to parse the first line of our file. The parse method does not return an array of fields, but reports success or failure. In the event of success we use the fields method to return the last fields parsed. In the event of failure we use the error_input method to get the last error, and return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields;&lt;br /&gt;
        if ($csv-&amp;gt;parse(shift @records))&lt;br /&gt;
        {&lt;br /&gt;
                @fields = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that the row of column titles has been dealt with we move onto processing each record in the file.&lt;br /&gt;
&lt;br /&gt;
In import plugins the convert_input method converts individual records into a format that can be imported into the repository. That is a hash whose keys are metadata field names and values are the corresponding values. As a row on its own cannot be imported as we don't know to which field each value belongs we have to construct an array to pass to convert_input first. We pass an array whose first element is the fields row and whose second element is the row we want to import.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        foreach my $row (@records)&lt;br /&gt;
        {&lt;br /&gt;
                my @input_data = (join(',',@fields),$row);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we call convert_input on our constructed input_data. If the conversion fails we simply move to the next record.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $epdata = $plugin-&amp;gt;convert_input(\@input_data);&lt;br /&gt;
                next unless defined $epdata;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The epdata_to_dataobj method takes our epdata hash reference and turns it into a new DataObj in our repository. If it is successful it returns the new DataObj, whose id we add to our array of ids.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $dataobj = $plugin-&amp;gt;epdata_to_dataobj($opts{dataset},$epdata);&lt;br /&gt;
                if( defined $dataobj )&lt;br /&gt;
                {&lt;br /&gt;
                        push @ids, $dataobj-&amp;gt;get_id;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a List object containing the ids of the records we have successfully imported.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return EPrints::List-&amp;gt;new(&lt;br /&gt;
                        dataset =&amp;gt; $opts{dataset},&lt;br /&gt;
                        session =&amp;gt; $plugin-&amp;gt;{session},&lt;br /&gt;
                        ids=&amp;gt;\@ids );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== convert_input ===&lt;br /&gt;
This method takes data in a particular format, in this case CSV and transforms it into a hash of metadata field names and values.&lt;br /&gt;
&lt;br /&gt;
We take the second argument to the method and convert the array reference into an array.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @input = @{shift @_};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we setup another Text::CSV object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $csv = Text::CSV-&amp;gt;new();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the second element of our array and parse it. This is the record we wish to import. If anything goes wrong we return undef.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @record;&lt;br /&gt;
        if ($csv-&amp;gt;parse($input[1]))&lt;br /&gt;
        {&lt;br /&gt;
                @record = $csv-&amp;gt;fields();&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;error($csv-&amp;gt;error_input);&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We take the first element and get the field names. We then check that we have the same number of fields names as records.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my @fields = split(',',$input[0]);&lt;br /&gt;
&lt;br /&gt;
        if (scalar @fields != scalar @record)&lt;br /&gt;
        {&lt;br /&gt;
                $plugin-&amp;gt;warning('Row length mismatch');&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the hash that we'll return later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my %output = ();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For convenience we get the DataSet object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $dataset = $plugin-&amp;gt;{session}-&amp;gt;{repository}-&amp;gt;get_dataset('archive');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now iterate over the fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $i = 0;&lt;br /&gt;
        foreach my $field (@fields)&lt;br /&gt;
        {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the field does not exist we look at the next one, remembering to increment our index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                unless ($dataset-&amp;gt;has_field($field))&lt;br /&gt;
                {&lt;br /&gt;
                        $i++;&lt;br /&gt;
                        next;&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get the MetaField object corresponding to the current field.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                my $metafield = $dataset-&amp;gt;get_field($field);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
     &lt;br /&gt;
We deal with multiple field types by separating individual values with a semi-colon.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                if ($metafield-&amp;gt;get_property('multiple'))&lt;br /&gt;
                {&lt;br /&gt;
                        my @values = split(';',$record[$i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Name fields are dealt with by using regular expressions and constructing a hash from the parts matched. The plugin expects names to be of the form Surname, Forenames, Lineage (Sr, Jr, III etc).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        if ($metafield-&amp;gt;{type} eq 'name')&lt;br /&gt;
                        {&lt;br /&gt;
                                my @names = ();&lt;br /&gt;
&lt;br /&gt;
                                foreach my $value (@values)&lt;br /&gt;
                                {&lt;br /&gt;
                                        my $name = $value;&lt;br /&gt;
&lt;br /&gt;
                                        next unless ($value =~ /^(.*?),(.*?)(,(.*?))?$/);&lt;br /&gt;
                                        push @names, {family =&amp;gt; $1,given =&amp;gt; $2,lineage =&amp;gt; $4};&lt;br /&gt;
                                }&lt;br /&gt;
&lt;br /&gt;
                                $output{$field} = \@names;&lt;br /&gt;
                        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Multiple fields which are not names are just added to the hash as an array reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                                $output{$field} = \@values;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Non-multiple fields are just added to the hash from the array of fields.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                        $output{$field} = $record[$i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a hash reference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        return \%output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
&lt;br /&gt;
After restarting your webserver go to the Import Items screen from the Manage Deposits screen. If you can't find this, make sure you're logged in.&lt;br /&gt;
&lt;br /&gt;
Type this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
This is a test title,This is a test abstract&lt;br /&gt;
This is another test title,This is another test abstract&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select &amp;quot;CSV&amp;quot; from the Select import format drop down menu and click &amp;quot;Test Run + Import&amp;quot;. You should end up at the Manage Deposits screen with the following message being displayed &amp;quot;Import completed: 2 item(s) imported.&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Embedding commas ==&lt;br /&gt;
If you want to include commas in your imports, which is very likely you must enclose the field in double quotations. For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,abstract&lt;br /&gt;
An interesting article,&amp;quot;Damn it Jim, I'm a Doctor, not a Perl hacker.&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When doing this make sure not to leave any whitespace between a quotation mark and a comma or the import will fail.&lt;br /&gt;
&lt;br /&gt;
== Multiple fields ==&lt;br /&gt;
Multiple field types are handled by separating each individual value by a semi-colon, a simple example of this would be the subjects field.&lt;br /&gt;
&lt;br /&gt;
Go back to the import items and proceed as before, but typing this into the &amp;quot;Cut and Paste Records&amp;quot; box:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
abstract,title,subjects&lt;br /&gt;
Testing,Testing,AI;C;M;F;P&lt;br /&gt;
Testing,Testing,AC;DC&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After the records have been imported examine each one on the View Item screen. You will find that a list of subjects are given, and that a more descriptive name is given than the code we imported.&lt;br /&gt;
&lt;br /&gt;
== Compound fields ==&lt;br /&gt;
&lt;br /&gt;
Compound fields are fields that have subfields within them, each with their own name. You don't compound fields in one go, but set the components individually. Subfields have names of the form  mainfieldname_subfieldname.&lt;br /&gt;
&lt;br /&gt;
One of the most commonly used compound fields is the &amp;quot;Creators&amp;quot; field. It has a names subfield &amp;quot;creators_name&amp;quot; and an ID subfield &amp;quot;creators_id&amp;quot; which is most often used for email addresses.&lt;br /&gt;
&lt;br /&gt;
Here is an example of setting the creators field:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
title,creators_names,creators_ids,&lt;br /&gt;
Setting compound fields.,&amp;quot;Bloggs, Joe;Doe, John&amp;quot;,&amp;quot;joe@bloggs.com;john@doe.com&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you import this record and then examine the view items screen you will find that the &amp;quot;Creators&amp;quot; field has been setup with the values displayed in a table.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=User:Tom/Import_Plugins/CSV&amp;diff=5723</id>
		<title>User:Tom/Import Plugins/CSV</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=User:Tom/Import_Plugins/CSV&amp;diff=5723"/>
		<updated>2007-09-27T20:59:32Z</updated>

		<summary type="html">&lt;p&gt;Tom: User:Tom/Import Plugins/CSV moved to Contribute: Plugins/ImportPluginsCSV&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#redirect [[Contribute: Plugins/ImportPluginsCSV]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=How_to_write_plugins&amp;diff=5721</id>
		<title>How to write plugins</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=How_to_write_plugins&amp;diff=5721"/>
		<updated>2007-09-27T20:58:13Z</updated>

		<summary type="html">&lt;p&gt;Tom: /* Export Plugin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Write a Plugin! ==&lt;br /&gt;
&lt;br /&gt;
The plugin system for EPrints 3 has been developed to make it easy and share the most common extensions to the code without having to hack the core system (and causing yourself problems with upgrades etc.)&lt;br /&gt;
&lt;br /&gt;
When the system loads, it automatically loads all modules in the perl_lib/EPrints/Plugin/ directory, so for simple plugins you just drop them in that directory and you're done!&lt;br /&gt;
&lt;br /&gt;
When a plugin is loaded it has a registration method which is called which tells the core EPrints system what this plugin does. EPrints then makes it available as appropriate.&lt;br /&gt;
&lt;br /&gt;
Clever plugins can detect features they need and adapt to use the tools available, or disable themselves if they are missing required tools (rather than crash the system). Some specialised plugins are disabled in their default state and must be enabled in the repository configuration.&lt;br /&gt;
&lt;br /&gt;
Another cool thing is that plugins are Perl Objects, which means you can subclass them. Here's an real-world example: We have a research group which uses BibTeX but over the years standardised within the group on an extra field. This is not a valid bibtex field, but are essential to their working because they have ancient and essential scripts which depend on it. To handle this we can subclass the default BibTeX Export plugin and override a single method (the data mapping one). We then just call the original parent plugins mapping method to do all the heavy lifting, then just add our non-standard extra field. Total code required: less than one screen. Number of happy researchers: none (they are never satisfied and will just demand the moon on the stick because you've already given them this nice new feature), but number of researchers able to get their work done: lots. Don't believe me? [[BibTeX Extension Example|look here]]!&lt;br /&gt;
&lt;br /&gt;
* See also [[Extension Packages]] for how to add configuration and resource files to plugins.&lt;br /&gt;
&lt;br /&gt;
=== Types of Plugin ===&lt;br /&gt;
&lt;br /&gt;
There are a number of different kinds of plugin for EPrints... &lt;br /&gt;
&lt;br /&gt;
==== Export Plugin ====&lt;br /&gt;
&lt;br /&gt;
These are used to export the data in a variety of formats. A number of tutorials have been created to help you create your own export plugins:&lt;br /&gt;
&lt;br /&gt;
* [[Contribute:_Plugins/ExportPluginsHello| Export Plugin Tutorial 1: &amp;quot;Hello, World!&amp;quot;]]&lt;br /&gt;
* [[Contribute:_Plugins/ExportPluginsList| Export Plugin Tutorial 2: List handling]]&lt;br /&gt;
* [[Contribute:_Plugins/ExportPluginsHTML| Export Plugin Tutorial 3: HTML]]&lt;br /&gt;
* [[Contribute:_Plugins/ExportPluginsExcel| Export Plugin Tutorial 4: Excel]]&lt;br /&gt;
* [[Contribute:_Plugins/ExportPluginsZip| Export Plugin Tutorial 5: Zip]]&lt;br /&gt;
&lt;br /&gt;
==== Import Plugin ====&lt;br /&gt;
&lt;br /&gt;
These are used to import data into a repository. They can take datafiles directly, or they can take an ID of a record that can be retrieved in a known way, or a URL of a file, or... whatever.&lt;br /&gt;
&lt;br /&gt;
These are a bit trickier to write than export plugins as parsing data is harder than just &amp;quot;print&amp;quot;ing it, but they are still reasonably straight forward.&lt;br /&gt;
&lt;br /&gt;
==== Screen Plugin ====&lt;br /&gt;
&lt;br /&gt;
These handle (almost) all the user interface screens. Pages like &amp;quot;Review&amp;quot; and &amp;quot;Profile&amp;quot; are just built-in plugins. You can add your own very easily. &lt;br /&gt;
&lt;br /&gt;
Examples you could create...&lt;br /&gt;
* Birds Eye View - a view of various statistics on the database, all in one page.&lt;br /&gt;
* Spellchecking Tab - an additional tab in the item control page which checks the spelling on certain fields.&lt;br /&gt;
* Bulk Delete tool - a tool which takes a list of eprintid's and deletes them all in a fell swoop.&lt;br /&gt;
&lt;br /&gt;
Look at the existing Screen Plugins for an idea of how they work. They can be very simple.&lt;br /&gt;
&lt;br /&gt;
==== Input Component Plugin ====&lt;br /&gt;
&lt;br /&gt;
These handle how the workflow components are rendered. Built in components include the default (one field) component, the multiple fields component, the upload component, the subject component (which does pretty things to a field of type &amp;quot;subject&amp;quot;) and the XHTML component. You can add your own or sub-class existing ones.&lt;br /&gt;
&lt;br /&gt;
===== Convert Plugin =====&lt;br /&gt;
&lt;br /&gt;
These are used for two things, currently.&lt;br /&gt;
&lt;br /&gt;
* Converting the full text of documents into utf-8 text for search indexing&lt;br /&gt;
* Converting images and pdfs into thumbnails and previews&lt;br /&gt;
&lt;br /&gt;
Some examples you could create:&lt;br /&gt;
&lt;br /&gt;
* RTF to utf-8 to allow rich text documents to be indexed.&lt;br /&gt;
* Powerpoint to Thumbnail to allow thumbnail and previews of powerpoint slides&lt;br /&gt;
* Video to Thumbnail/Preview to make a still preview of a video file.&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsZip&amp;diff=5719</id>
		<title>Contribute: Plugins/ExportPluginsZip</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsZip&amp;diff=5719"/>
		<updated>2007-09-27T20:57:16Z</updated>

		<summary type="html">&lt;p&gt;Tom: User:Tom/Export Plugins/Zip moved to Contribute: Plugins/ExportPluginsZip&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 5: Zip =&lt;br /&gt;
In this tutorial we'll look at packaging the results of a search into a Zip file. We'll create a directory for each eprint, and a sub-directory for each document belonging to that eprint. We'll also add an HTML index file to the archive to make it easier to navigate.&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/~miyagawa/Archive-Any-Create-0.02/lib/Archive/Any/Create.pm Archive::Any::Create] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Archive::Any::Create&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zip.pm =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::Zip;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use Archive::Any::Create;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
  my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
  my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
  $self-&amp;gt;{name} = 'Zip';&lt;br /&gt;
  $self-&amp;gt;{accept} = [ 'list/eprint' ];&lt;br /&gt;
  $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.zip';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/zip';&lt;br /&gt;
&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Archive::Any::Create');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Archive::Any::Create';&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
  my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
  my $archive = '';&lt;br /&gt;
  open (my $FH, '&amp;gt;', \$archive) or&lt;br /&gt;
    die(&amp;quot;Could not create filehandle: $!&amp;quot;);&lt;br /&gt;
  my $zip = Archive::Any::Create-&amp;gt;new;&lt;br /&gt;
&lt;br /&gt;
  my $index = &amp;lt;&amp;lt;END;&lt;br /&gt;
  &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
      &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;title&amp;gt;EPrints Search Results&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
  my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
    my $heading = $session-&amp;gt;make_element('h2');&lt;br /&gt;
    $heading-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
    $div-&amp;gt;appendChild($heading);&lt;br /&gt;
&lt;br /&gt;
    my $uldoc = $session-&amp;gt;make_element('ul');&lt;br /&gt;
    $div-&amp;gt;appendChild($uldoc);&lt;br /&gt;
&lt;br /&gt;
    my $dirpath = 'eprints-search/'.$dataobj-&amp;gt;get_id().'/';&lt;br /&gt;
&lt;br /&gt;
    my $i = 1;&lt;br /&gt;
    foreach my $doc ($dataobj-&amp;gt;get_all_documents)&lt;br /&gt;
    {&lt;br /&gt;
      my $subdirpath = $dirpath.&amp;quot;doc$i/&amp;quot;;&lt;br /&gt;
      my %files = $doc-&amp;gt;files;&lt;br /&gt;
&lt;br /&gt;
      my $lidoc = $session-&amp;gt;make_element('li');&lt;br /&gt;
      $uldoc-&amp;gt;appendChild($lidoc);&lt;br /&gt;
&lt;br /&gt;
      my $adoc = $session-&amp;gt;make_element('a', href=&amp;gt;$dataobj-&amp;gt;get_id.&amp;quot;/doc$i/&amp;quot;.$doc-&amp;gt;get_main);&lt;br /&gt;
      $lidoc-&amp;gt;appendChild($adoc);&lt;br /&gt;
&lt;br /&gt;
      if ($doc-&amp;gt;exists_and_set('formatdesc'))&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_value('formatdesc')));&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_main));&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      foreach my $filename (sort keys %files)&lt;br /&gt;
      {&lt;br /&gt;
       my $filepath = $subdirpath.$filename;&lt;br /&gt;
        my $file = $doc-&amp;gt;local_path.'/'.$filename;&lt;br /&gt;
&lt;br /&gt;
        if (-d $file)&lt;br /&gt;
        {&lt;br /&gt;
          next;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $data = '';&lt;br /&gt;
        open (my $datafh ,'&amp;gt;', \$data);&lt;br /&gt;
&lt;br /&gt;
        open (INFH, &amp;quot;&amp;lt;$file&amp;quot;) or die (&amp;quot;Could not open file $file&amp;quot;);&lt;br /&gt;
        while (&amp;lt;INFH&amp;gt;)&lt;br /&gt;
        {&lt;br /&gt;
          print {$datafh} $_;&lt;br /&gt;
        }&lt;br /&gt;
        close INFH;&lt;br /&gt;
&lt;br /&gt;
        $zip-&amp;gt;add_file($filepath, $data);&lt;br /&gt;
      }&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $index .= EPrints::XML::to_string($div);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $index .= '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
  $zip-&amp;gt;add_file('eprints-search/index.htm',$index);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $zip-&amp;gt;write_filehandle($opts{fh},'zip');&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
  $zip-&amp;gt;write_filehandle($FH,'zip');&lt;br /&gt;
  return $archive;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For the sake of simplicity this plugin will only deal with lists of eprints. This avoids some code duplication, and it would be fairly easy to modify the plugin to deal with both individual eprints and lists of eprints sensibly.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{accept} = [ 'list/eprint' ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The file extension and MIME type are set to values appropriate for Zip files.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.zip';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/zip';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to import a module that is not included with EPrints for creating zip files. We use the EPrints::Utils::require_if_exists function to check if the module exists, and load it if it does. We then check the value returned from that function, and make the plugin invisible if it failed.&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Archive::Any::Create');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Archive::Any::Create';&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List Handling ==&lt;br /&gt;
=== Setting Up ===&lt;br /&gt;
Here we setup an in-memory file for the Zip, and create an Archive object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $archive = '';&lt;br /&gt;
  open (my $FH, '&amp;gt;', \$archive) or&lt;br /&gt;
    die(&amp;quot;Could not create filehandle: $!&amp;quot;);&lt;br /&gt;
  my $zip = Archive::Any::Create-&amp;gt;new;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navigation ===&lt;br /&gt;
Here we begin to setup the HTML file that we'll add to our archive for navigation. First we setup a header.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $index = &amp;lt;&amp;lt;END;&lt;br /&gt;
  &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
      &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
      &amp;lt;title&amp;gt;EPrints Search Results&amp;lt;/title&amp;gt;&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
  &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we get the Session object, we'll be using it to manipulate DOM objects later.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handling DataObjs ===&lt;br /&gt;
We loop over the DataObjs as we have done before.&lt;br /&gt;
&lt;br /&gt;
This time we setup some DOM objects to be added to our index. Each eprint will have it's title printed out followed by a list of documents.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
    my $heading = $session-&amp;gt;make_element('h2');&lt;br /&gt;
    $heading-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
    $div-&amp;gt;appendChild($heading);&lt;br /&gt;
&lt;br /&gt;
    my $uldoc = $session-&amp;gt;make_element('ul');&lt;br /&gt;
    $div-&amp;gt;appendChild($uldoc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create a directory for each eprint. Note it is not necessary to explicitly create a directory, we simply have to set the appropriate file path. However this means that if you do not add files to a certain directory it will not be created, rather than having an empty directory for a given eprint.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $dirpath = 'eprints-search/'.$dataobj-&amp;gt;get_id().'/';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dealing With Documents ====&lt;br /&gt;
We then loop over all the documents belonging to each DataObj. The get_all_documents method returns an array of Document objects.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    my $i = 1;&lt;br /&gt;
    foreach my $doc ($dataobj-&amp;gt;get_all_documents)&lt;br /&gt;
    {&lt;br /&gt;
      my $subdirpath = $dirpath.&amp;quot;doc$i/&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we create a list item for the document containing a link to the main file. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      my $lidoc = $session-&amp;gt;make_element('li');&lt;br /&gt;
      $uldoc-&amp;gt;appendChild($lidoc);&lt;br /&gt;
&lt;br /&gt;
      my $adoc = $session-&amp;gt;make_element('a', href=&amp;gt;$dataobj-&amp;gt;get_id.&amp;quot;/doc$i/&amp;quot;.$doc-&amp;gt;get_main);&lt;br /&gt;
      $lidoc-&amp;gt;appendChild($adoc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If a description of the main file has been set we use that as the link text, otherwise we use the filename.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      if ($doc-&amp;gt;exists_and_set('formatdesc'))&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_value('formatdesc')));&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        $adoc-&amp;gt;appendChild($session-&amp;gt;make_text($doc-&amp;gt;get_main));&lt;br /&gt;
      }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dealing With Files ====&lt;br /&gt;
The files method of the Document object returns a hash whose keys are file names and values are file sizes.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      my %files = $doc-&amp;gt;files;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We loop over each file belonging to the document, in most cases there will only be one file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      foreach my $filename (sort keys %files)&lt;br /&gt;
      {&lt;br /&gt;
        my $filepath = $subdirpath.$filename;&lt;br /&gt;
        my $file = $doc-&amp;gt;local_path.'/'.$filename;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to read the contents of the file and add it to a file in the zip. First we'll create another in-memory file to hold the contents.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        my $data = '';&lt;br /&gt;
        open (my $datafh ,'&amp;gt;', \$data);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We open our file and print it straight out to our in-memory file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        open (INFH, &amp;quot;&amp;lt;$file&amp;quot;) or die (&amp;quot;Could not open file $file&amp;quot;);&lt;br /&gt;
        while (&amp;lt;INFH&amp;gt;)&lt;br /&gt;
        {&lt;br /&gt;
          print {$datafh} $_;&lt;br /&gt;
        }&lt;br /&gt;
        close INFH;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we add the file data to our file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $zip-&amp;gt;add_file($filepath, $data);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we add the DOM object for our eprint to the index.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $index .= EPrints::XML::to_string($div);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Finishing Off ===&lt;br /&gt;
After finishing off our index file we add it to the zip file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $index .= '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
  $zip-&amp;gt;add_file('eprints-search/index.htm',$index);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a file handle has been provided we write to it, otherwise we write to the scalar file handle created earlier. We then return in the usual fashion.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $zip-&amp;gt;write_filehandle($opts{fh},'zip');&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
  $zip-&amp;gt;write_filehandle($FH,'zip');&lt;br /&gt;
  return $archive;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as in [[User:Tom/Export_Plugins/Excel | the previous tutorial]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Expzipv2.png]]&lt;br /&gt;
&lt;br /&gt;
The accompanying HTML index.&lt;br /&gt;
&lt;br /&gt;
[[Image:Expzip2.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=User:Tom/Export_Plugins/Zip&amp;diff=5720</id>
		<title>User:Tom/Export Plugins/Zip</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=User:Tom/Export_Plugins/Zip&amp;diff=5720"/>
		<updated>2007-09-27T20:57:16Z</updated>

		<summary type="html">&lt;p&gt;Tom: User:Tom/Export Plugins/Zip moved to Contribute: Plugins/ExportPluginsZip&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#redirect [[Contribute: Plugins/ExportPluginsZip]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsExcel&amp;diff=5717</id>
		<title>Contribute: Plugins/ExportPluginsExcel</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsExcel&amp;diff=5717"/>
		<updated>2007-09-27T20:56:05Z</updated>

		<summary type="html">&lt;p&gt;Tom: User:Tom/Export Plugins/Excel moved to Contribute: Plugins/ExportPluginsExcel&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 4: Excel =&lt;br /&gt;
&lt;br /&gt;
In this tutorial and the next one we'll look at exporting files in non-text formats. Here we will explore exporting metadata in Excel format (pre Microsoft Office 2007 which uses an XML based format).&lt;br /&gt;
&lt;br /&gt;
To prepare for this tutorial you should install the [http://search.cpan.org/dist/Spreadsheet-WriteExcel/lib/Spreadsheet/WriteExcel.pm Spreadsheet::Excel] module. The following command as root, or using sudo should work.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cpan Spreadsheet::Excel&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Excel.pm =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::Excel;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
  my ($class, %opts) = @_;&lt;br /&gt;
  my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
  $self-&amp;gt;{name} = 'Excel';&lt;br /&gt;
  $self-&amp;gt;{accept} = ['list/eprint'];&lt;br /&gt;
  $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.xls';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/vnd.ms-excel';&lt;br /&gt;
&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Spreadsheet::WriteExcel');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Spreadsheet::WriteExcel';&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
  my ($plugin, %opts) = @_;&lt;br /&gt;
  my $workbook;&lt;br /&gt;
&lt;br /&gt;
  my $output;&lt;br /&gt;
  open(my $FH,'&amp;gt;',\$output);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new(\*{$opts{fh}});&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new($FH);&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  my $worksheet = $workbook-&amp;gt;add_worksheet();&lt;br /&gt;
&lt;br /&gt;
  my $i = 0;&lt;br /&gt;
  my @fields =&lt;br /&gt;
  $plugin-&amp;gt;{session}-&amp;gt;get_repository-&amp;gt;get_dataset('archive')-&amp;gt;get_fields;&lt;br /&gt;
&lt;br /&gt;
  foreach my $field (@fields)&lt;br /&gt;
  {&lt;br /&gt;
    $worksheet-&amp;gt;write(0, $i, $field-&amp;gt;get_name);&lt;br /&gt;
    $i++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $i = 1;&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $j = 0;&lt;br /&gt;
    foreach my $field (@fields)&lt;br /&gt;
    {&lt;br /&gt;
      if ($dataobj-&amp;gt;exists_and_set($field-&amp;gt;get_name))&lt;br /&gt;
      {&lt;br /&gt;
        if ($field-&amp;gt;get_property('multiple'))&lt;br /&gt;
        {&lt;br /&gt;
          if ($field-&amp;gt;{type} eq 'name')&lt;br /&gt;
          {&lt;br /&gt;
            my $namelist = '';&lt;br /&gt;
            foreach my $name (@{$dataobj-&amp;gt;get_value_raw($field-&amp;gt;get_name)})&lt;br /&gt;
            {&lt;br /&gt;
              $namelist .= $name-&amp;gt;{family} . ',' . $name-&amp;gt;{given} . ';';&lt;br /&gt;
            }&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, $namelist);&lt;br /&gt;
          }&lt;br /&gt;
          elsif ($field-&amp;gt;{type} eq 'compound')&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, 'COMPOUND');&lt;br /&gt;
          }&lt;br /&gt;
          else&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j,&lt;br /&gt;
                        join(';',@{$dataobj-&amp;gt;get_value($field-&amp;gt;get_name)}));&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
          $worksheet-&amp;gt;write($i, $j, $dataobj-&amp;gt;get_value($field-&amp;gt;get_name));&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      $j++;&lt;br /&gt;
    }&lt;br /&gt;
    $i++;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $workbook-&amp;gt;close;&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $output;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
For the sake of simplicity this plugin will only deal with lists of eprints. This avoids some code duplication, and it would be fairly easy to modify the plugin to deal with both individual eprints and lists of eprints sensibly.&lt;br /&gt;
&amp;lt;pre&amp;gt;  &lt;br /&gt;
  $self-&amp;gt;{accept} = ['list/eprint'];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The file extension and MIME type are set to values appropriate for Excel files.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $self-&amp;gt;{suffix} = '.xls';&lt;br /&gt;
  $self-&amp;gt;{mimetype} = 'application/vnd.ms-excel';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We need to import a module that is not included with EPrints for creating zip files. We use the EPrints::Utils::require_if_exists function to check if the module exists, and load it if it does. We then check the value returned from that function, and make the plugin invisible if it failed.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $rc = EPrints::Utils::require_if_exists('Spreadsheet::WriteExcel');&lt;br /&gt;
  unless ($rc)&lt;br /&gt;
  {&lt;br /&gt;
    $self-&amp;gt;{visible} = '';&lt;br /&gt;
    $self-&amp;gt;{error} = 'Unable to load required module Spreadsheet::WriteExcel';&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== List Handling ==&lt;br /&gt;
=== Setting Up a Workbook ===&lt;br /&gt;
Here we create a new Excel workbook. We start by creating a file handle using a scalar rather than a filename, this creates an in-memory file. Then depending on if a file handle has been supplied or not we create a workbook object that will be written to that file handle or the scalar file handle we just created.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $workbook;&lt;br /&gt;
&lt;br /&gt;
  my $output;&lt;br /&gt;
  open(my $FH,'&amp;gt;',\$output);&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new(\*{$opts{fh}});&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    $workbook = Spreadsheet::WriteExcel-&amp;gt;new($FH);&lt;br /&gt;
    die(&amp;quot;Unable to create spreadsheet: $!&amp;quot;)unless defined $workbook;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handling DataObjs ===&lt;br /&gt;
To start adding data to the Excel file we have to create a worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my $worksheet = $workbook-&amp;gt;add_worksheet();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To the first row of our worksheet we add the names of all the metadata fields that can be associated with eprints. We get the session associated with the plugin, and then the repository associated with that session. We then get the DataSet &amp;quot;archive&amp;quot; from that repository and call the get_fields method. That method returns an array of MetaField objects.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  my @fields =&lt;br /&gt;
  $plugin-&amp;gt;{session}-&amp;gt;get_repository-&amp;gt;get_dataset('archive')-&amp;gt;get_fields;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we loop over each field and write it's name to our worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  foreach my $field (@fields)&lt;br /&gt;
  {&lt;br /&gt;
    $worksheet-&amp;gt;write(0, $i, $field-&amp;gt;get_name);&lt;br /&gt;
    $i++;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now loop over each DataObj in our list, and over each MetaField we found earlier.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
  {&lt;br /&gt;
    my $j = 0;&lt;br /&gt;
    foreach my $field (@fields)&lt;br /&gt;
    {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We only write something to the worksheet if the field can apply to the DataObj and is set. Scalar values are simply written to the worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      if ($dataobj-&amp;gt;exists_and_set($field-&amp;gt;get_name))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The plugin handles fields which can take multiple values in a number of ways.   &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        if ($field-&amp;gt;get_property('multiple'))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Names are handled specially and are formatted with the family name followed by a comma, the given name or initial and then a semi-colon.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
          if ($field-&amp;gt;{type} eq 'name')&lt;br /&gt;
          {&lt;br /&gt;
            my $namelist = '';&lt;br /&gt;
            foreach my $name (@{$dataobj-&amp;gt;get_value_raw($field-&amp;gt;get_name)})&lt;br /&gt;
            {&lt;br /&gt;
              $namelist .= $name-&amp;gt;{family} . ',' . $name-&amp;gt;{given} . ';';&lt;br /&gt;
            }&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, $namelist);&lt;br /&gt;
          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Fields which have a compound type are not handled, and 'COMPOUND' is written to the worksheet.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
          elsif ($field-&amp;gt;{type} eq 'compound')&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j, 'COMPOUND');&lt;br /&gt;
          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For most multiple fields each value is taken and concatenated, separated by semi-colons.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
          else&lt;br /&gt;
          {&lt;br /&gt;
            $worksheet-&amp;gt;write($i, $j,&lt;br /&gt;
                        join(';',@{$dataobj-&amp;gt;get_value($field-&amp;gt;get_name)}));&lt;br /&gt;
          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Finishing Up ===&lt;br /&gt;
&lt;br /&gt;
We first need to close the workbook to ensure that the data is written to the file handles, and then we can return in the usual fashion. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $workbook-&amp;gt;close;&lt;br /&gt;
&lt;br /&gt;
  if (defined $opts{fh})&lt;br /&gt;
  {&lt;br /&gt;
    return undef;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return $output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as in [[User:Tom/Export_Plugins/HTML | the previous tutorial]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Expexcel.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=User:Tom/Export_Plugins/Excel&amp;diff=5718</id>
		<title>User:Tom/Export Plugins/Excel</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=User:Tom/Export_Plugins/Excel&amp;diff=5718"/>
		<updated>2007-09-27T20:56:05Z</updated>

		<summary type="html">&lt;p&gt;Tom: User:Tom/Export Plugins/Excel moved to Contribute: Plugins/ExportPluginsExcel&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#redirect [[Contribute: Plugins/ExportPluginsExcel]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5715</id>
		<title>Contribute: Plugins/ExportPluginsHTML</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Contribute:_Plugins/ExportPluginsHTML&amp;diff=5715"/>
		<updated>2007-09-27T20:55:43Z</updated>

		<summary type="html">&lt;p&gt;Tom: User:Tom/Export Plugins/HTML moved to Contribute: Plugins/ExportPluginsHTML&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=  Export Plugin Tutorial 3: HTML =&lt;br /&gt;
&lt;br /&gt;
In this tutorial we'll look at creating an export plugin with slightly more complex output than unformatted plain text. Although the plugin below produces XHTML the same principles apply to producing any XML document.&lt;br /&gt;
&lt;br /&gt;
= HelloHTML.pm =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package EPrints::Plugin::Export::MyPlugins::HelloHTML;&lt;br /&gt;
&lt;br /&gt;
@ISA = ('EPrints::Plugin::Export');&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
&lt;br /&gt;
sub new&lt;br /&gt;
{&lt;br /&gt;
        my ($class, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new(%opts);&lt;br /&gt;
&lt;br /&gt;
        $self-&amp;gt;{name} = 'Hello, HTML!';&lt;br /&gt;
        $self-&amp;gt;{accept} = [ 'dataobj/eprint', 'list/eprint' ];&lt;br /&gt;
        $self-&amp;gt;{visible} = 'all';&lt;br /&gt;
        $self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
        $self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&lt;br /&gt;
        return $self;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
        return EPrints::XML::to_string($xml);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub output_list&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, %opts) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $r = [];&lt;br /&gt;
&lt;br /&gt;
        my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $header;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $header;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        foreach my $dataobj ($opts{list}-&amp;gt;get_records)&lt;br /&gt;
        {&lt;br /&gt;
                my $part = $plugin-&amp;gt;output_dataobj($dataobj, %opts);&lt;br /&gt;
                if (defined $opts{fh})&lt;br /&gt;
                {&lt;br /&gt;
                        print {$opts{fh}} $part;&lt;br /&gt;
                }&lt;br /&gt;
                else&lt;br /&gt;
                {&lt;br /&gt;
                        push @{$r}, $part;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                print {$opts{fh}} $footer;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
                push @{$r}, $footer;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (defined $opts{fh})&lt;br /&gt;
        {&lt;br /&gt;
                return undef;&lt;br /&gt;
        }&lt;br /&gt;
        return join('', @{$r});&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub xml_dataobj&lt;br /&gt;
{&lt;br /&gt;
        my ($plugin, $dataobj) = @_;&lt;br /&gt;
&lt;br /&gt;
        my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&lt;br /&gt;
        my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&lt;br /&gt;
        my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
        $title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
        $div-&amp;gt;appendChild($title);&lt;br /&gt;
&lt;br /&gt;
        my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
        $abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
        $div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&lt;br /&gt;
        return $div;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= In More Detail =&lt;br /&gt;
== Constructor ==&lt;br /&gt;
Here we change the file extension to '.htm' and change the MIME type to &amp;quot;text/html&amp;quot;. For general XML documents you should change the file extension to '.xml' and the MIME type to 'text/xml';&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$self-&amp;gt;{suffix} = '.htm';&lt;br /&gt;
$self-&amp;gt;{mimetype} = 'text/html; charset=utf-8';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_dataobj ==&lt;br /&gt;
Here the output_dataobj method does very little. It calls the xml_dataobj method to obtain a DOM object which is converted to plain text before being returned. The xml_dataobj method is described later.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $xml = $plugin-&amp;gt;xml_dataobj($dataobj);&lt;br /&gt;
&lt;br /&gt;
return EPrints::XML::to_string($xml);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== output_list ==&lt;br /&gt;
The only changes to the output_list method are the additions of a header and a footer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $header = &amp;lt;&amp;lt;END;&lt;br /&gt;
        &amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;br /&gt;
        &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot; lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
                &amp;lt;head&amp;gt;&lt;br /&gt;
                        &amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html;charset=utf-8&amp;quot;/&amp;gt;&lt;br /&gt;
                        &amp;lt;title&amp;gt;XHTML Export Plugin&amp;lt;/title&amp;gt;&lt;br /&gt;
                &amp;lt;/head&amp;gt;&lt;br /&gt;
        &amp;lt;body&amp;gt;&lt;br /&gt;
END&lt;br /&gt;
&lt;br /&gt;
my $footer = '&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== xml_dataobj ==&lt;br /&gt;
This method has a similar signature to the output_dataobj method: it takes an implicit reference to the plugin object and a reference to a DataObj, however instead of returning a string it returns a DOM object.&lt;br /&gt;
&lt;br /&gt;
Before we can start creating and manipulating DOM objects we need to get a reference to the Session object from the plugin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $session = $plugin-&amp;gt;{session};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We then create elements using the make_element method of our session object. The first argument to the make_element method is the tag name of the element we want to create, for example 'a' or 'div'. The second parameter is a hash of attributes and values associated with the element.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $div = $session-&amp;gt;make_element('div');&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we add a second-level header. The make_text method is used to create text nodes. The appendChild method is used to add child nodes. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $title = $session-&amp;gt;make_element('h2');&lt;br /&gt;
$title-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('title')));&lt;br /&gt;
$div-&amp;gt;appendChild($title);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A similar pattern is followed to add a paragraph containing the eprint's abstract.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $abstract = $session-&amp;gt;make_element('p');&lt;br /&gt;
$abstract-&amp;gt;appendChild($session-&amp;gt;make_text($dataobj-&amp;gt;get_value('abstract')));&lt;br /&gt;
$div-&amp;gt;appendChild($abstract);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally we return a DOM object representing a 'div' element containing our header and paragraph.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return $div;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Testing Your Plugin =&lt;br /&gt;
Restart your webserver and test the plugin as in [[User:Tom/Export_Plugins/Hello_Lists| the previous tutorial]].&lt;br /&gt;
&lt;br /&gt;
== Sample Output ==&lt;br /&gt;
[[Image:Exphtml.png]]&lt;/div&gt;</summary>
		<author><name>Tom</name></author>
		
	</entry>
</feed>