MT 1587 : CSV export for cart and shelves, with the ability to define different expor...
authorHenri-Damien LAURENT <henridamien.laurent@biblibre.com>
Wed, 16 Sep 2009 19:31:30 +0000 (21:31 +0200)
committerHenri-Damien LAURENT <henridamien.laurent@biblibre.com>
Wed, 30 Sep 2009 09:30:35 +0000 (11:30 +0200)
Signed-off-by: Henri-Damien LAURENT <henridamien.laurent@biblibre.com>
12 files changed:
C4/Csv.pm [new file with mode: 0644]
C4/Record.pm
installer/data/mysql/en/mandatory/userpermissions.sql
installer/data/mysql/fr-FR/1-Obligatoire/userpermissions.sql
installer/data/mysql/updatedatabase.pl
koha-tmpl/intranet-tmpl/prog/en/modules/tools/csv-profiles.tmpl [new file with mode: 0644]
koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tmpl
koha-tmpl/opac-tmpl/prog/en/modules/opac-downloadcart.tmpl
koha-tmpl/opac-tmpl/prog/en/modules/opac-downloadshelf.tmpl
opac/opac-downloadcart.pl
opac/opac-downloadshelf.pl
tools/csv-profiles.pl [new file with mode: 0755]

diff --git a/C4/Csv.pm b/C4/Csv.pm
new file mode 100644 (file)
index 0000000..3c1c56d
--- /dev/null
+++ b/C4/Csv.pm
@@ -0,0 +1,80 @@
+package C4::Csv;
+
+# Copyright 2008 BibLibre
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+# Suite 330, Boston, MA  02111-1307 USA
+#
+#
+
+use C4::Context;
+use vars qw($VERSION @ISA @EXPORT);
+
+# set the version for version checking
+$VERSION = 3.00;
+
+@ISA = qw(Exporter);
+
+# only export API methods
+
+@EXPORT = qw(
+  &GetCsvProfiles
+  &GetCsvProfilesLoop
+  &GetMarcFieldsForCsv
+);
+
+my $dbh = C4::Context->dbh;
+
+# Returns all informations about csv profiles
+sub GetCsvProfiles {
+
+    my $query = "SELECT * FROM export_format";
+
+    $sth = $dbh->prepare($query);
+    $sth->execute;
+
+    $sth->fetchall_arrayref({});
+
+}
+
+# Returns fields to extract for the given csv profile
+sub GetMarcFieldsForCsv {
+
+    my ($id) = @_;
+
+    my $query = "SELECT marcfields FROM export_format WHERE export_format_id=?";
+
+    $sth = $dbh->prepare($query);
+    $sth->execute($id);
+
+    return ($sth->fetchrow_hashref)->{marcfields};
+    
+}
+
+# Returns informations aboout csv profiles suitable for html templates
+sub GetCsvProfilesLoop {
+   # List of existing profiles
+    my $sth;
+    my $query = "SELECT export_format_id, profile FROM export_format";
+    $sth = $dbh->prepare($query);
+    $sth->execute();
+    return $sth->fetchall_arrayref({});
+
+}
+
+
+
+1;
index 2c96756..a892c5c 100644 (file)
@@ -29,6 +29,9 @@ use Biblio::EndnoteStyle;
 use Unicode::Normalize; # _entity_encode
 use XML::LibXSLT;
 use XML::LibXML;
+use C4::Biblio; #marc2bibtex
+use C4::Csv; #marc2csv
+use Text::CSV; #marc2csv
 
 use vars qw($VERSION @ISA @EXPORT);
 
@@ -46,6 +49,8 @@ $VERSION = 3.00;
   &marcxml2marc
   &marc2dcxml
   &marc2modsxml
+  &marc2bibtex
+  &marc2csv
 
   &html2marcxml
   &html2marc
@@ -322,6 +327,99 @@ sub marc2endnote {
        
 }
 
+=head2 marc2csv - Convert from UNIMARC to CSV
+
+=over 4
+
+my ($csv) = marc2csv($record, $csvprofileid);
+
+Returns a CSV scalar
+
+=over 2
+
+C<$record> - a MARC::Record object
+
+C<$csvprofileid> - the id of the CSV profile to use for the export (see export_format.export_format_id and the GetCsvProfiles function in C4::Csv)
+
+=back
+
+=back
+
+=cut
+
+
+sub marc2csv {
+    my ($record, $id, $header) = @_;
+    my $output;
+    my $csv = Text::CSV->new();
+
+    # Get the information about the csv profile
+    my $marcfieldslist = GetMarcFieldsForCsv($id);
+
+    # Getting the marcfields as an array
+    my @marcfields = split('\|', $marcfieldslist);
+
+    # If we have to insert the headers
+    if ($header) {
+       my @marcfieldsheaders;
+
+       my $dbh   = C4::Context->dbh;
+
+       # For each field or subfield
+       foreach (@marcfields) {
+           # We get the matching tag name
+           if (index($_, '$') > 0) {
+               my ($fieldtag, $subfieldtag) = split('\$', $_);
+               my $query = "SELECT liblibrarian FROM marc_subfield_structure WHERE tagfield=? AND tagsubfield=?";
+               my $sth = $dbh->prepare($query);
+               $sth->execute($fieldtag, $subfieldtag);
+               my @results = $sth->fetchrow_array();
+               push @marcfieldsheaders, @results[0];
+           } else {
+               my $query = "SELECT liblibrarian FROM marc_tag_structure WHERE tagfield=?";
+               my $sth = $dbh->prepare($query);
+               $sth->execute($_);
+               my @results = $sth->fetchrow_array();
+               push @marcfieldsheaders, @results[0];
+           }
+       }
+       $csv->combine(@marcfieldsheaders);
+       $output = $csv->string() . "\n";        
+    }
+
+    # For each marcfield to export
+    my @fieldstab;
+    foreach my $marcfield (@marcfields) {
+       # If it is a subfield
+       if (index($marcfield, '$') > 0) {
+           my ($fieldtag, $subfieldtag) = split('\$', $marcfield);
+           my @fields = $record->field($fieldtag);
+           my @tmpfields;
+
+           # For each field
+           foreach my $field (@fields) {
+
+               # We take every matching subfield
+               my @subfields = $field->subfield($subfieldtag);
+               foreach my $subfield (@subfields) {
+                   push @tmpfields, $subfield;
+               }
+           }
+           push (@fieldstab, join(',', @tmpfields));           
+       # Or a field
+       } else {
+           my @fields = ($record->field($marcfield));
+           push (@fieldstab, join(',', map($_->as_string(), @fields)));                
+        }
+    };
+
+    $csv->combine(@fieldstab);
+    $output .= $csv->string() . "\n";
+   
+    return $output;
+
+}
+
 
 =head2 html2marcxml
 
index 80f060d..6f4c230 100644 (file)
@@ -18,6 +18,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES
    (13, 'schedule_tasks', 'Schedule tasks to run'),
    (13, 'batchmod', 'Perform batch modification of items'),
    (13, 'batchdel', 'Perform batch deletion of items'),
+   (13, 'manage_csv_profiles', 'Manage CSV export profiles')
    (11, 'vendors_manage', 'Manage vendors'),
    (11, 'contracts_manage', 'Manage contracts'),
    (11, 'period_manage', 'Manage periods'),
@@ -28,4 +29,4 @@ INSERT INTO permissions (module_bit, code, description) VALUES
    (11, 'group_manage', 'Manage orders & basketgroups'),
    (11, 'order_receive', 'Manage orders & basket'),
    (11, 'budget_add_del', 'Add and delete budgets (but cant modify budgets)')
-;
\ No newline at end of file
+;
index 96f2577..e54dc5d 100644 (file)
@@ -26,4 +26,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES
    (11, 'group_manage', 'Gérer les commandes et les bons de commande'),
    (11, 'order_receive', 'Gérer les réceptions')
    (11, 'budget_add_del', 'Ajouter et supprimer les budgets (mais pas modifier)')
+   (13, 'manage_csv_profiles', 'Manage CSV export profiles')
+   (13, 'batch_mod', 'Modification Par lot des exemplaires')
+   (13, 'batch_del', 'Suppression par lot des exemplaires')
 ;
index 5009cb9..7dba9d5 100755 (executable)
@@ -2688,6 +2688,13 @@ if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
        print "Upgrade to $DBversion done ( Added ShowPatronImageInWebBasedSelfCheck system preference )\n";
     SetVersion ($DBversion);
 }
+
+$DBversion = "3.01.00.062";
+if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
+    $dbh->do("INSERT INTO permissions (module_bit, code, description) VALUES ( 13, 'manage_csv_profiles', 'Manage CSV export profiles')");
+    print "Upgrade to $DBversion done (added permissions for csv export profiles)\n";
+}
+
 =item
 
 Acquisitions update
@@ -3032,6 +3039,7 @@ if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
 }
 
 
+
 =item DropAllForeignKeys($table)
 
   Drop all foreign keys of the table $table
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/csv-profiles.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/csv-profiles.tmpl
new file mode 100644 (file)
index 0000000..f0a072e
--- /dev/null
@@ -0,0 +1,98 @@
+<!-- TMPL_INCLUDE NAME="doc-head-open.inc" -->
+
+       <title>Koha &rsaquo; Catalog &rsaquo; Profile for CSV export</title>
+
+       <!-- TMPL_INCLUDE NAME="doc-head-close.inc" -->
+
+<script language="Javascript" type="text/javascript">
+function reloadPage(p) {
+       var id = p.value;
+       if (id != 0) { document.location = "/cgi-bin/koha/tools/csv-profiles.pl?id=" + id; }
+}
+</script>
+
+</head>
+
+<body>
+       <!-- TMPL_INCLUDE NAME="header.inc" -->
+       <!-- TMPL_INCLUDE NAME="cat-search.inc" -->
+
+       <div id="breadcrumbs">
+               <a href="/cgi-bin/koha/mainpage.pl">Home</a>
+               &rsaquo; <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a>
+               &rsaquo; New profile for CSV export
+       </div>
+
+       <div id="doc3" class="yui-t2">
+               <div id="bd">
+                       <div id="yui-main">
+                               <div class="yui-b">
+                                   <!-- TMPL_IF EXPR="success || error" -->
+                                           <!-- TMPL_IF NAME="success" -->
+               <!-- TMPL_IF EXPR="action eq 'create'" --><p>The new CSV profile "<!-- TMPL_VAR NAME="profile_name" -->" has been successfully created.</p><!-- /TMPL_IF -->
+               <!-- TMPL_IF EXPR="action eq 'edit'"   --><p>The CSV profile has been successfully modified.</p><!-- /TMPL_IF -->
+               <!-- TMPL_IF EXPR="action eq 'delete'" --><p>The CSV profile has been successfully deleted.</p><!-- /TMPL_IF -->
+                                           <!-- TMPL_ELSE -->
+               <!-- TMPL_IF EXPR="action eq 'create'" --><p class="error">The new CSV profile "<!-- TMPL_VAR NAME="profile_name" -->" has not been created.</p><!-- /TMPL_IF -->
+               <!-- TMPL_IF EXPR="action eq 'edit'"   --><p class="error">The CSV profile has not been modified.</p><!-- /TMPL_IF -->
+               <!-- TMPL_IF EXPR="action eq 'delete'" --><p class="error">The CSV profile has not been deleted.</p><!-- /TMPL_IF -->
+                                           <!-- /TMPL_IF -->
+                                   <!-- /TMPL_IF -->
+
+                                       <h1>New profile for CSV export</h1>
+
+                                            <form action="/cgi-bin/koha/tools/csv-profiles.pl" method="post">
+                                               <fieldset class="brief">
+                                                   <label for="profile_name">Profile name :</label>
+                                                   <input type="text" id="profile_name" name="profile_name" /><br /><br />
+
+                                                   <label for="profile_description">Profile description :</label>
+                                                   <textarea cols="50" name="profile_description" id="profile_description"></textarea><br /><br />
+
+                                                   <label for="profile_content">Profile marcfields :</label>
+                                                   <textarea cols="50" name="profile_content" id="profile_content"></textarea>
+                                               </fieldset>
+                                               <input type="hidden" name="action" value="create" />
+                                               <input type="submit" />
+                                           </form>
+                                       <!-- /TMPL_IF -->
+
+                                       <!-- TMPL_IF NAME="existing_profiles" -->
+                                       <br /><br />
+                                       <h1>Modify or delete an existing profile</h1>
+                                               
+                                           <form action="/cgi-bin/koha/tools/csv-profiles.pl" method="post">
+                                               <fieldset class="brief">
+                                                   <label for="modify_profile_name">Profile name :</label>
+                                                   <select id="modify_profile_name" name="profile_name" onchange="javascript:reloadPage(this)">
+                                                       <option value="0">-- Choose One --</option>
+                                                       <!-- TMPL_LOOP NAME="existing_profiles" -->
+                                                       <option value="<!-- TMPL_VAR NAME="export_format_id" -->"<!-- TMPL_IF EXPR="export_format_id eq selected_profile_id" --> selected="selected"<!-- /TMPL_IF-->><!-- TMPL_VAR NAME="profile" --></option>
+                                                       <!-- /TMPL_LOOP -->
+                                                   </select><br /><br />
+
+                                                   <label for="modify_profile_description">Profile description :</label>
+                                                   <textarea cols="50" name="profile_description" id="modify_profile_description"><!-- TMPL_VAR NAME="selected_profile_description" --></textarea><br /><br />
+
+                                                   <label for="modify_profile_content">Profile marcfields :</label>
+                                                   <textarea cols="50" name="profile_content" id="modify_profile_content"><!-- TMPL_VAR NAME="selected_profile_marcfields" --></textarea><br /><br />
+                                                   
+                                                   <label for="delete">Delete selected profile ?</label>
+                                                   <input type="checkbox" name="delete" id="delete" />
+
+                                               </fieldset>
+
+                                               <input type="hidden" name="modify_profile_id" value="<!-- TMPL_VAR NAME="selected_profile_id" -->" />
+                                               <input type="hidden" name="action" value="edit" />
+                                               <input type="submit" />
+                                           </form>
+                                       <!-- /TMPL_IF -->
+
+                               </div>
+                       </div>
+                       <div class="yui-b">
+
+
+               </div>
+       </div>
+       <!-- TMPL_INCLUDE NAME="intranet-bottom.inc" -->
index 54e9ce4..090e172 100644 (file)
     <dt><a href="/cgi-bin/koha/tags/review.pl">Tags</a></dt>
        <dd>Moderate patron tags</dd>
     <!-- /TMPL_IF -->
+
+    <!-- TMPL_IF NAME="CAN_user_tools_manage_csv_profiles" -->
+    <dt><a href="/cgi-bin/koha/tools/csv-profiles.pl">CSV Profiles</a></dt>
+       <dd>Manage CSV export profiles</dd>
+    <!-- /TMPL_IF -->
+
        </dl>
 </div>
 <div class="yui-u">
index aa71a3d..85a0136 100644 (file)
            <option value="iso2709">iso2709</option>
            <option value="ris">RIS</option>
            <option value="bibtex">BibTex</option>
+           <!-- TMPL_LOOP NAME="csv_profiles" -->
+           <option value="<!-- TMPL_VAR NAME="export_format_id" -->">CSV - <!-- TMPL_VAR NAME="profile" --></option>
+           <!-- /TMPL_LOOP -->
+
        </select>
        <input type="hidden" name="bib_list" value="<!-- TMPL_VAR NAME="bib_list" -->" />
        <input type="submit" name="save" value="Go" />
index a36caf0..52203f5 100644 (file)
            <option value="iso2709">iso2709</option>
            <option value="ris">RIS</option>
            <option value="bibtex">BibTex</option>
+           <!-- TMPL_LOOP NAME="csv_profiles" -->
+           <option value="<!-- TMPL_VAR NAME="export_format_id" -->">CSV - <!-- TMPL_VAR NAME="profile" --></option>
+           <!-- /TMPL_LOOP -->
+
        </select>
        <input type="hidden" name="shelfid" value="<!-- TMPL_VAR NAME="shelfid" -->" />
        <input type="submit" name="save" value="Go" />
index 25fe7ce..fe80f30 100755 (executable)
@@ -31,6 +31,7 @@ use C4::Output;
 use C4::VirtualShelves;
 use C4::Record;
 use C4::Ris;
+use C4::Csv;
 use utf8;
 use open qw( :std :utf8);
 my $query = new CGI;
@@ -57,6 +58,7 @@ if ($bib_list && $format) {
     my $output;
 
     # retrieve biblios from shelf
+    my $firstpass = 1;
     foreach my $biblio (@bibs) {
 
        my $record = GetMarcBiblio($biblio);
@@ -65,9 +67,16 @@ if ($bib_list && $format) {
            case "iso2709" { $output .= $record->as_usmarc(); }
            case "ris"     { $output .= marc2ris($record); }
            case "bibtex"  { $output .= marc2bibtex($record, $biblio); }
+           # We're in the case of a csv profile (firstpass is used for headers printing) :
+            case /^\d+$/   { $output .= marc2csv($record, $format, $firstpass); }
        }
+        $firstpass = 0;
+
     }
 
+    # If it was a CSV export we change the format after the export so the file extension is fine
+    $format = "csv" if ($format =~ m/^\d+$/);
+
     print $query->header(
        -type => 'application/octet-stream',
        -'Content-Transfer-Encoding' => 'binary',
@@ -75,6 +84,7 @@ if ($bib_list && $format) {
     print $output;
 
 } else { 
+    $template->param(csv_profiles => GetCsvProfilesLoop());
     $template->param(bib_list => $bib_list); 
     output_html_with_http_headers $query, $cookie, $template->output;
 }
index b377109..976412b 100755 (executable)
@@ -31,6 +31,7 @@ use C4::Output;
 use C4::VirtualShelves;
 use C4::Record;
 use C4::Ris;
+use C4::Csv;
 use utf8;
 use open qw( :std :utf8);
 my $query = new CGI;
@@ -57,6 +58,7 @@ if ($shelfid && $format) {
     my $output;
 
     # retrieve biblios from shelf
+    my $firstpass = 1;
     foreach my $biblio (@$items) {
        my $biblionumber = $biblio->{biblionumber};
 
@@ -66,16 +68,23 @@ if ($shelfid && $format) {
            case "iso2709" { $output .= $record->as_usmarc(); }
            case "ris"     { $output .= marc2ris($record); }
            case "bibtex"  { $output .= marc2bibtex($record, $biblionumber); }
+           # We're in the case of a csv profile (firstpass is used for headers printing) :
+           case /^\d+$/   { $output .= marc2csv($record, $format, $firstpass); }
        }
+       $firstpass = 0;
     }
 
+    # If it was a CSV export we change the format after the export so the file extension is fine
+    $format = "csv" if ($format =~ m/^\d+$/);
+
     print $query->header(
        -type => 'application/octet-stream',
        -'Content-Transfer-Encoding' => 'binary',
        -attachment=>"shelf.$format");
     print $output;
 
-} else { 
+} else {
+    $template->param(csv_profiles => GetCsvProfilesLoop());
     $template->param(shelfid => $shelfid); 
     output_html_with_http_headers $query, $cookie, $template->output;
 }
diff --git a/tools/csv-profiles.pl b/tools/csv-profiles.pl
new file mode 100755 (executable)
index 0000000..1e037f3
--- /dev/null
@@ -0,0 +1,121 @@
+#!/usr/bin/perl
+
+# Copyright 2009 BibLibre
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+# Suite 330, Boston, MA  02111-1307 USA
+
+=head1 NAME
+
+csv-profile.pl : Defines a CSV export profile
+
+=head1 SYNOPSIS
+
+
+=head1 DESCRIPTION
+
+This script allow the user to define a new profile for CSV export
+
+=head1 FUNCTIONS
+
+=over 2
+
+=cut
+
+use strict;
+use Data::Dumper;
+
+use C4::Auth;
+use C4::Context;
+use C4::Output;
+use CGI;
+use C4::Koha;
+use C4::Csv;
+
+my $input        = new CGI;
+my $dbh          = C4::Context->dbh;
+
+# open template
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {
+        template_name   => "tools/csv-profiles.tmpl",
+        query           => $input,
+        type            => "intranet",
+        authnotrequired => 0,
+        flagsrequired   => { tools => 'manage_csv_profiles' },
+        debug           => 1,
+    }
+);
+
+
+my $profile_name        = $input->param("profile_name");
+my $profile_description = $input->param("profile_description");
+my $profile_content     = $input->param("profile_content");
+my $action              = $input->param("action");
+my $delete              = $input->param("delete");
+my $id                  = $input->param("id");
+if ($delete) { $action = "delete"; }
+
+if ($profile_name && $profile_content && $profile_description && $action) {
+    my $rows;
+
+    if ($action eq "create") {
+       my $query = "INSERT INTO export_format(export_format_id, profile, description, marcfields) VALUES (NULL, ?, ?, ?)";
+       my $sth   = $dbh->prepare($query);
+       $rows  = $sth->execute($profile_name, $profile_description, $profile_content);
+    
+    }
+
+    if ($action eq "edit") {
+       my $query = "UPDATE export_format SET description=?, marcfields=? WHERE export_format_id=? LIMIT 1";
+       my $sth   = $dbh->prepare($query);
+       $rows  = $sth->execute($profile_description, $profile_content, $profile_name);
+
+    }
+
+    if ($action eq "delete") {
+       my $query = "DELETE FROM export_format WHERE export_format_id=? LIMIT 1";
+       my $sth   = $dbh->prepare($query);
+       $rows  = $sth->execute($profile_name);
+
+    }
+
+    $rows ? $template->param(success => 1) : $template->param(error => 1);
+    $template->param(profile_name => $profile_name);
+    $template->param(action => $action);
+
+}
+
+    # If a profile has been selected for modification
+    if ($id) {
+       my $query = "SELECT export_format_id, profile, description, marcfields FROM export_format WHERE export_format_id = ?";
+       my $sth;
+       $sth = $dbh->prepare($query);
+
+       $sth->execute($id);
+       my $selected_profile = $sth->fetchrow_arrayref();
+       $template->param(
+           selected_profile_id          => $selected_profile->[0],
+           selected_profile_name        => $selected_profile->[1],
+           selected_profile_description => $selected_profile->[2],
+           selected_profile_marcfields  => $selected_profile->[3]
+       );
+
+    }
+
+    # List of existing profiles
+    $template->param(existing_profiles => GetCsvProfilesLoop());
+
+output_html_with_http_headers $input, $cookie, $template->output;