@@ -, +, @@ --- C4/Biblio.pm | 43 +++- C4/Installer/PerlDependencies.pm | 5 + C4/UploadedFiles.pm | 230 ++++++++++++++++++++ basket/basket.pl | 2 +- catalogue/detail.pl | 2 +- cataloguing/value_builder/upload.pl | 187 ++++++++++++++++ etc/koha-conf.xml | 1 + installer/data/mysql/kohastructure.sql | 11 + installer/data/mysql/updatedatabase.pl | 16 ++ .../prog/en/modules/catalogue/MARCdetail.tt | 80 ++++++- .../en/modules/cataloguing/value_builder/upload.tt | 77 +++++++ .../value_builder/upload_delete_file.tt | 60 +++++ misc/strip_value_from_tag.pl | 60 +++++ opac/opac-basket.pl | 2 +- opac/opac-detail.pl | 2 +- opac/opac-retrieve-file.pl | 50 +++++ t/db_dependent/UploadedFiles.t | 42 ++++ 17 files changed, 845 insertions(+), 25 deletions(-) create mode 100644 C4/UploadedFiles.pm create mode 100755 cataloguing/value_builder/upload.pl create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/value_builder/upload.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/value_builder/upload_delete_file.tt create mode 100755 misc/strip_value_from_tag.pl create mode 100755 opac/opac-retrieve-file.pl create mode 100755 t/db_dependent/UploadedFiles.t --- a/C4/Biblio.pm +++ a/C4/Biblio.pm @@ -37,6 +37,7 @@ use C4::ClassSource; use C4::Charset; use C4::Linker; use C4::OAI::Sets; +use C4::UploadedFiles; use vars qw($VERSION @ISA @EXPORT); @@ -1445,13 +1446,14 @@ sub GetCOinSBiblio { =head2 GetMarcPrice return the prices in accordance with the Marc format. + =cut sub GetMarcPrice { my ( $record, $marcflavour ) = @_; my @listtags; my $subfield; - + if ( $marcflavour eq "MARC21" ) { @listtags = ('345', '020'); $subfield="c"; @@ -1461,7 +1463,7 @@ sub GetMarcPrice { } else { return; } - + for my $field ( $record->field(@listtags) ) { for my $subfield_value ($field->subfield($subfield)){ #check value @@ -1907,7 +1909,7 @@ sub GetMarcAuthors { =head2 GetMarcUrls - $marcurls = GetMarcUrls($record,$marcflavour); + $marcurls = GetMarcUrls($record,$marcflavour,$frameworkcode); Returns arrayref of URLs from MARC data, suitable to pass to tmpl loop. Assumes web resources (not uncommon in MARC21 to omit resource type ind) @@ -1915,15 +1917,19 @@ Assumes web resources (not uncommon in MARC21 to omit resource type ind) =cut sub GetMarcUrls { - my ( $record, $marcflavour ) = @_; + my ( $record, $marcflavour, $frameworkcode ) = @_; + + my $tagslib = &GetMarcStructure(1, $frameworkcode); + my $urltag = '856'; + my $urlsubtag = 'u'; my @marcurls; - for my $field ( $record->field('856') ) { + for my $field ( $record->field($urltag) ) { my @notes; for my $note ( $field->subfield('z') ) { push @notes, { note => $note }; } - my @urls = $field->subfield('u'); + my @urls = $field->subfield($urlsubtag); foreach my $url (@urls) { my $marcurl; if ( $marcflavour eq 'MARC21' ) { @@ -1951,8 +1957,20 @@ sub GetMarcUrls { $marcurl->{'part'} = $s3 if ($link); $marcurl->{'toc'} = 1 if ( defined($s3) && $s3 =~ /^[Tt]able/ ); } else { - $marcurl->{'linktext'} = $field->subfield('2') || C4::Context->preference('URLLinkText') || $url; - $marcurl->{'MARCURL'} = $url; + if ($tagslib->{ $urltag }->{ $urlsubtag }->{value_builder} eq "upload.pl" + and $url =~ /id=([0-9a-f]+)/) { + my $file = C4::UploadedFiles::GetUploadedFile($1); + my $text = $file ? $file->{filename} : $url; + $marcurl->{'linktext'} = $field->subfield('2') + || C4::Context->preference('URLLinkText') + || $file->{filename}; + $marcurl->{'MARCURL'} = $url; + } else { + $marcurl->{'linktext'} = $field->subfield('2') + || C4::Context->preference('URLLinkText') + || $url; + $marcurl->{'MARCURL'} = $url; + } } push @marcurls, $marcurl; } @@ -3377,7 +3395,8 @@ sub get_biblio_authorised_values { =head2 CountBiblioInOrders =over 4 -$count = &CountBiblioInOrders( $biblionumber); + +=item $count = &CountBiblioInOrders( $biblionumber); =back @@ -3400,7 +3419,8 @@ sub CountBiblioInOrders { =head2 GetSubscriptionsId =over 4 -$subscriptions = &GetSubscriptionsId($biblionumber); + +=item $subscriptions = &GetSubscriptionsId($biblionumber); =back @@ -3423,7 +3443,8 @@ sub GetSubscriptionsId { =head2 GetHolds =over 4 -$holds = &GetHolds($biblionumber); + +=item $holds = &GetHolds($biblionumber); =back --- a/C4/Installer/PerlDependencies.pm +++ a/C4/Installer/PerlDependencies.pm @@ -689,6 +689,11 @@ our $PERL_DEPS = { 'required' => '1', 'min_ver' => '0.22', }, + 'Test::CGI::Multipart' => { + 'usage' => 'Tests', + 'required' => '0', + 'min_ver' => '0.0.3', + }, }; 1; --- a/C4/UploadedFiles.pm +++ a/C4/UploadedFiles.pm @@ -0,0 +1,230 @@ +package C4::UploadedFiles; + +# Copyright 2011-2012 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 3 +# 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. +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301 USA. + +=head1 NAME + +C4::UploadedFiles - Functions to deal with files uploaded with + cataloging plugin upload.pl + +=head1 SYNOPSIS + + use C4::UploadedFiles; + + my $filename = $cgi->param('uploaded_file'); + my $file = $cgi->upload('uploaded_file'); + my $dir = $input->param('dir'); + + # upload file + my $id = C4::UploadedFiles::UploadFile($filename, $dir, $file->handle); + + # retrieve file infos + my $uploaded_file = C4::UploadedFiles::GetUploadedFile($id); + + # delete file + C4::UploadedFiles::DelUploadedFile($id); + +=head1 DESCRIPTION + +This module provides basic functions for adding, retrieving +and deleting files related to cataloging plugin upload.pl. + +It uses uploaded_files table. + +It is not related to C4::UploadedFile + +=head1 FUNCTIONS + +=cut + +use Modern::Perl; +use Digest::SHA; +use Fcntl; +use Encode; + +use C4::Context; + +sub _get_file_path { + my ($id, $dirname, $filename) = @_; + + my $upload_path = C4::Context->config('upload_path'); + my $filepath = "$upload_path/$dirname/${id}_$filename"; + $filepath =~ s|/+|/|g; + + return $filepath; +} + +=head2 GetUploadedFile + + my $file = C4::UploadedFiles::GetUploadedFile($id); + +Returns a hashref containing infos on uploaded files. +Hash keys are: + +=over 2 + +=item * id: id of the file (same as given in argument) + +=item * filename: name of the file + +=item * dir: directory where file is stored (relative to config variable 'upload_path') + +=back + +It returns undef if file is not found + +=cut + +sub GetUploadedFile { + my ($id) = @_; + + return unless $id; + + my $dbh = C4::Context->dbh; + my $query = qq{ + SELECT id, filename, dir + FROM uploaded_files + WHERE id = ? + }; + my $sth = $dbh->prepare($query); + $sth->execute($id); + my $file = $sth->fetchrow_hashref; + if ($file) { + $file->{filepath} = _get_file_path($file->{id}, $file->{dir}, + $file->{filename}); + } + + return $file; +} + +=head2 UploadFile + + my $id = C4::UploadedFiles::UploadFile($filename, $dir, $io_handle); + +Upload a new file and returns its id (its SHA-1 sum, actually). + +Parameters: + +=over 2 + +=item * $filename: name of the file + +=item * $dir: directory where to store the file (path relative to config variable 'upload_path') + +=item * $io_handle: valid IO::Handle object, can be retrieved with +$cgi->upload('uploaded_file')->handle; + +=back + +=cut + +sub UploadFile { + my ($filename, $dir, $handle) = @_; + + $filename = decode_utf8($filename); + if($filename =~ m#(^|/)\.\.(/|$)# or $dir =~ m#(^|/)\.\.(/|$)#) { + warn "Filename or dirname contains '..'. Aborting upload"; + return; + } + + my $buffer; + my $data = ''; + while($handle->read($buffer, 1024)) { + $data .= $buffer; + } + $handle->close; + + my $sha = new Digest::SHA; + $sha->add($data); + my $id = $sha->hexdigest; + + # Test if this id already exist + my $file = GetUploadedFile($id); + if ($file) { + return $file->{id}; + } + + my $file_path = _get_file_path($id, $dir, $filename); + + my $out_fh; + # Create the file only if it doesn't exist + unless( sysopen($out_fh, $file_path, O_WRONLY|O_CREAT|O_EXCL) ) { + warn "Failed to open file '$file_path': $!"; + return; + } + + print $out_fh $data; + close $out_fh; + + my $dbh = C4::Context->dbh; + my $query = qq{ + INSERT INTO uploaded_files (id, filename, dir) + VALUES (?,?, ?); + }; + my $sth = $dbh->prepare($query); + if($sth->execute($id, $filename, $dir)) { + return $id; + } + + return; +} + +=head2 DelUploadedFile + + C4::UploadedFiles::DelUploadedFile($id); + +Remove a previously uploaded file, given its id. + +Returns a false value if an error occurs. + +=cut + +sub DelUploadedFile { + my ($id) = @_; + + my $file = GetUploadedFile($id); + if($file) { + my $file_path = $file->{filepath}; + my $file_deleted = 0; + unless( -f $file_path ) { + warn "Id $file->{id} is in database but not in filesystem, removing id from database"; + $file_deleted = 1; + } else { + if(unlink $file_path) { + $file_deleted = 1; + } + } + + unless($file_deleted) { + warn "File $file_path cannot be deleted: $!"; + } + + my $dbh = C4::Context->dbh; + my $query = qq{ + DELETE FROM uploaded_files + WHERE id = ? + }; + my $sth = $dbh->prepare($query); + return $sth->execute($id); + } +} + +1; --- a/basket/basket.pl +++ a/basket/basket.pl @@ -66,7 +66,7 @@ foreach my $biblionumber ( @bibs ) { my $marcauthorsarray = GetMarcAuthors( $record, $marcflavour ); my $marcsubjctsarray = GetMarcSubjects( $record, $marcflavour ); my $marcseriesarray = GetMarcSeries ($record,$marcflavour); - my $marcurlsarray = GetMarcUrls ($record,$marcflavour); + my $marcurlsarray = GetMarcUrls ($record,$marcflavour, GetFrameworkCode($biblionumber)); my @items = GetItemsInfo( $biblionumber ); my $hasauthors = 0; --- a/catalogue/detail.pl +++ a/catalogue/detail.pl @@ -113,7 +113,7 @@ my $marcisbnsarray = GetMarcISBN( $record, $marcflavour ); my $marcauthorsarray = GetMarcAuthors( $record, $marcflavour ); my $marcsubjctsarray = GetMarcSubjects( $record, $marcflavour ); my $marcseriesarray = GetMarcSeries($record,$marcflavour); -my $marcurlsarray = GetMarcUrls ($record,$marcflavour); +my $marcurlsarray = GetMarcUrls ($record,$marcflavour, $fw); my $marchostsarray = GetMarcHosts($record,$marcflavour); my $subtitle = GetRecordValue('subtitle', $record, $fw); --- a/cataloguing/value_builder/upload.pl +++ a/cataloguing/value_builder/upload.pl @@ -0,0 +1,187 @@ +#!/usr/bin/perl + +# Copyright 2011-2012 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 3 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. +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301 USA. + +use Modern::Perl; +use CGI qw/-utf8/; +use File::Basename; + +use C4::Auth; +use C4::Context; +use C4::Output; +use C4::UploadedFiles; + +sub plugin_parameters { + my ( $dbh, $record, $tagslib, $i, $tabloop ) = @_; + return ""; +} + +sub plugin_javascript { + my ( $dbh, $record, $tagslib, $field_number, $tabloop ) = @_; + my $function_name = $field_number; + my $res = " + +"; + + return ( $function_name, $res ); +} + +sub plugin { + my ($input) = @_; + my $index = $input->param('index'); + my $id = $input->param('id'); + my $delete = $input->param('delete'); + my $uploaded_file = $input->param('uploaded_file'); + + my $template_name = ($id || $delete) + ? "upload_delete_file.tt" + : "upload.tt"; + + my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { template_name => "cataloguing/value_builder/$template_name", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { editcatalogue => '*' }, + debug => 1, + } + ); + + # Dealing with the uploaded file + if ($uploaded_file) { + my $fh = $input->upload('uploaded_file'); + my $dir = $input->param('dir'); + + $id = C4::UploadedFiles::UploadFile($uploaded_file, $dir, $fh->handle); + if($id) { + my $OPACBaseURL = C4::Context->preference('OPACBaseURL'); + $OPACBaseURL =~ s#/$##; + my $return = "$OPACBaseURL/cgi-bin/koha/opac-retrieve-file.pl?id=$id"; + $template->param( + success => 1, + return => $return, + uploaded_file => $uploaded_file, + ); + } else { + $template->param(error => 1); + } + } elsif ($delete || $id) { + # If there's already a file uploaded for this field, + # We handle its deletion + if ($delete) { + if(C4::UploadedFiles::DelUploadedFile($id)) {; + $template->param(success => 1); + } else { + $template->param(error => 1); + } + } + } else { + my $upload_path = C4::Context->config('upload_path'); + if ($upload_path) { + my $filefield = CGI::filefield( + -name => 'uploaded_file', + -size => 50, + ); + + my $dirs_tree = [ { + name => '/', + value => '/', + dirs => finddirs($upload_path) + } ]; + + $template->param( + dirs_tree => $dirs_tree, + filefield => $filefield + ); + } else { + $template->param( error_upload_path_not_configured => 1 ); + } + } + + $template->param( + index => $index, + id => $id + ); + + output_html_with_http_headers $input, $cookie, $template->output; +} + +# Build a hierarchy of directories +sub finddirs { + my $base = shift; + my $upload_path = C4::Context->config('upload_path'); + my $found = 0; + my @dirs; + my @files = glob("$base/*"); + foreach (@files) { + if (-d $_ and -w $_) { + my $lastdirname = basename($_); + my $dirname = $_; + $dirname =~ s/^$upload_path//g; + push @dirs, { + value => $dirname, + name => $lastdirname, + dirs => finddirs($_) + }; + $found = 1; + }; + } + return \@dirs; +} + +1; + + +__END__ + +=head1 upload.pl + +This plugin allow to upload files on the server and reference it in a marc +field. + +Two system settings are used: + +=over 4 + +=item * upload_path: the real absolute path where files will be stored + +=item * OPACBaseURL: for building URLs to be stored in MARC + +=back + +upload_path is in koha-conf.xml, and OPACBaseURL is a system preference. --- a/etc/koha-conf.xml +++ a/etc/koha-conf.xml @@ -278,6 +278,7 @@ __PAZPAR2_TOGGLE_XML_POST__ 1 __PLUGINS_DIR__ 0 + __INTRANET_CGI_DIR__ __OPAC_CGI_DIR__/opac __OPAC_TMPL_DIR__ --- a/installer/data/mysql/kohastructure.sql +++ a/installer/data/mysql/kohastructure.sql @@ -3184,6 +3184,17 @@ CREATE TABLE IF NOT EXISTS `borrower_modifications` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- +-- Table structure for table uploaded_files +-- + +DROP TABLE IF EXISTS uploaded_files; +CREATE TABLE uploaded_files ( + id CHAR(40) NOT NULL PRIMARY KEY, + filename TEXT NOT NULL, + dir TEXT NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -- Table structure for table linktracker -- This stores clicks to external links -- --- a/installer/data/mysql/updatedatabase.pl +++ a/installer/data/mysql/updatedatabase.pl @@ -7067,6 +7067,22 @@ if ( CheckVersion($DBversion) ) { SetVersion($DBversion); } +$DBversion = "XXX"; +if ( CheckVersion($DBversion) ) { + $dbh->do(" + CREATE TABLE uploaded_files ( + id CHAR(40) NOT NULL PRIMARY KEY, + filename TEXT NOT NULL, + dir TEXT NOT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + "); + + print "Upgrade to $DBversion done (Bug 6874: New cataloging plugin upload.pl)\n"; + print "This plugin comes with a new config variable (upload_path) and a new table (uploaded_files)\n"; + print "To use it, set the 'upload_path' variable and 'OPACBaseURL' system preference and link this plugin to a subfield (856\$u for example)\n"; + SetVersion($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) --- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/MARCdetail.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/MARCdetail.tt @@ -120,7 +120,13 @@ function Changefwk(FwkList) {   [% UNLESS ( subfiel.hide_marc ) %][% subfiel.marc_subfield %][% END %] [% subfiel.short_desc %] - [% IF ( subfiel.is_url ) %][% subfiel.marc_value |html %][% ELSE %][% subfiel.marc_value |html %][% END %] + [% IF ( subfiel.is_url ) %] + [% subfiel.marc_value |html %] + [% ELSIF ( subfiel.is_file ) %] + [% subfiel.marc_value %] + [% ELSE %] + [% subfiel.marc_value |html %] + [% END %] [% IF ( subfiel.link ) %] Search on [% subfiel.marc_value |html %] @@ -150,7 +156,13 @@ function Changefwk(FwkList) {   [% UNLESS ( subfiel.hide_marc ) %][% subfiel.marc_subfield %][% END %] [% subfiel.short_desc %] - [% IF ( subfiel.is_url ) %][% subfiel.marc_value |html %][% ELSE %][% subfiel.marc_value |html %][% END %] + [% IF ( subfiel.is_url ) %] + [% subfiel.marc_value |html %] + [% ELSIF ( subfiel.is_file ) %] + [% subfiel.marc_value %] + [% ELSE %] + [% subfiel.marc_value |html %] + [% END %] [% IF ( subfiel.link ) %] Search on [% subfiel.marc_value |html %] @@ -180,7 +192,13 @@ function Changefwk(FwkList) {   [% UNLESS ( subfiel.hide_marc ) %][% subfiel.marc_subfield %][% END %] [% subfiel.short_desc %] - [% IF ( subfiel.is_url ) %][% subfiel.marc_value |html %][% ELSE %][% subfiel.marc_value |html %][% END %] + [% IF ( subfiel.is_url ) %] + [% subfiel.marc_value |html %] + [% ELSIF ( subfiel.is_file ) %] + [% subfiel.marc_value %] + [% ELSE %] + [% subfiel.marc_value |html %] + [% END %] [% IF ( subfiel.link ) %] Search on [% subfiel.marc_value |html %] @@ -210,7 +228,13 @@ function Changefwk(FwkList) {   [% UNLESS ( subfiel.hide_marc ) %][% subfiel.marc_subfield %][% END %] [% subfiel.short_desc %] - [% IF ( subfiel.is_url ) %][% subfiel.marc_value |html %][% ELSE %][% subfiel.marc_value |html %][% END %] + [% IF ( subfiel.is_url ) %] + [% subfiel.marc_value |html %] + [% ELSIF ( subfiel.is_file ) %] + [% subfiel.marc_value %] + [% ELSE %] + [% subfiel.marc_value |html %] + [% END %] [% IF ( subfiel.link ) %] Search on [% subfiel.marc_value |html %] @@ -240,7 +264,13 @@ function Changefwk(FwkList) {   [% UNLESS ( subfiel.hide_marc ) %][% subfiel.marc_subfield %][% END %] [% subfiel.short_desc %] - [% IF ( subfiel.is_url ) %][% subfiel.marc_value |html %][% ELSE %][% subfiel.marc_value |html %][% END %] + [% IF ( subfiel.is_url ) %] + [% subfiel.marc_value |html %] + [% ELSIF ( subfiel.is_file ) %] + [% subfiel.marc_value %] + [% ELSE %] + [% subfiel.marc_value |html %] + [% END %] [% IF ( subfiel.link ) %] Search on [% subfiel.marc_value |html %] @@ -270,7 +300,13 @@ function Changefwk(FwkList) {   [% UNLESS ( subfiel.hide_marc ) %][% subfiel.marc_subfield %][% END %] [% subfiel.short_desc %] - [% IF ( subfiel.is_url ) %][% subfiel.marc_value |html %][% ELSE %][% subfiel.marc_value |html %][% END %] + [% IF ( subfiel.is_url ) %] + [% subfiel.marc_value |html %] + [% ELSIF ( subfiel.is_file ) %] + [% subfiel.marc_value %] + [% ELSE %] + [% subfiel.marc_value |html %] + [% END %] [% IF ( subfiel.link ) %] Search on [% subfiel.marc_value |html %] @@ -300,7 +336,13 @@ function Changefwk(FwkList) {   [% UNLESS ( subfiel.hide_marc ) %][% subfiel.marc_subfield %][% END %] [% subfiel.short_desc %] - [% IF ( subfiel.is_url ) %][% subfiel.marc_value |html %][% ELSE %][% subfiel.marc_value |html %][% END %] + [% IF ( subfiel.is_url ) %] + [% subfiel.marc_value |html %] + [% ELSIF ( subfiel.is_file ) %] + [% subfiel.marc_value %] + [% ELSE %] + [% subfiel.marc_value |html %] + [% END %] [% IF ( subfiel.link ) %] Search on [% subfiel.marc_value |html %] @@ -330,7 +372,13 @@ function Changefwk(FwkList) {   [% UNLESS ( subfiel.hide_marc ) %][% subfiel.marc_subfield %][% END %] [% subfiel.short_desc %] - [% IF ( subfiel.is_url ) %][% subfiel.marc_value |html %][% ELSE %][% subfiel.marc_value |html %][% END %] + [% IF ( subfiel.is_url ) %] + [% subfiel.marc_value |html %] + [% ELSIF ( subfiel.is_file ) %] + [% subfiel.marc_value %] + [% ELSE %] + [% subfiel.marc_value |html %] + [% END %] [% IF ( subfiel.link ) %] Search on [% subfiel.marc_value |html %] @@ -360,7 +408,13 @@ function Changefwk(FwkList) {   [% UNLESS ( subfiel.hide_marc ) %][% subfiel.marc_subfield %][% END %] [% subfiel.short_desc %] - [% IF ( subfiel.is_url ) %][% subfiel.marc_value |html %][% ELSE %][% subfiel.marc_value |html %][% END %] + [% IF ( subfiel.is_url ) %] + [% subfiel.marc_value |html %] + [% ELSIF ( subfiel.is_file ) %] + [% subfiel.marc_value |html %] + [% ELSE %] + [% subfiel.marc_value |html %] + [% END %] [% IF ( subfiel.link ) %] Search on [% subfiel.marc_value |html %] @@ -390,7 +444,13 @@ function Changefwk(FwkList) {   [% UNLESS ( subfiel.hide_marc ) %][% subfiel.marc_subfield %][% END %] [% subfiel.short_desc %] - [% IF ( subfiel.is_url ) %][% subfiel.marc_value |html %][% ELSE %][% subfiel.marc_value |html %][% END %] + [% IF ( subfiel.is_url ) %] + [% subfiel.marc_value |html %] + [% ELSIF ( subfiel.is_file ) %] + [% subfiel.marc_value %] + [% ELSE %] + [% subfiel.marc_value |html %] + [% END %] [% IF ( subfiel.link ) %] Search on [% subfiel.marc_value |html %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/value_builder/upload.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/value_builder/upload.tt @@ -0,0 +1,77 @@ + + + + Upload plugin + + + + + + +[% IF ( success ) %] + + + + + The file [% uploaded_file | html %] has been successfully uploaded. +

+ +[% ELSE %] + + [% IF ( error ) %] +

Error: Failed to upload file. See logs for details.

+ + [% ELSE %] + [%# This block display recursively a directory tree in variable 'dirs' %] + [% BLOCK list_dirs %] + [% IF dirs.size %] + + [% END %] + [% END %] + + [% IF (error_upload_path_not_configured) %] +

Configuration error

+

Configuration variable 'upload_path' is not configured.

+

Please configure it in your koha-conf.xml

+ [% ELSE %] +

Please select the file to upload :

+
+ [% filefield %] +

Choose where to upload file

+ [% INCLUDE list_dirs dirs = dirs_tree %] + + + +
+ [% END %] + [% END %] + +[% END %] + + + --- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/value_builder/upload_delete_file.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/value_builder/upload_delete_file.tt @@ -0,0 +1,60 @@ + + + + Upload plugin + + + + + + + +[% IF ( success ) %] + + + +

The file has been successfully deleted.

+ + + + +[% ELSE %] + + [% IF ( error ) %] + Error: Unable to delete the file. +

+ [% ELSE %] +

File deletion

+

A file has already been uploaded for this field. Do you want to delete it?

+
+ + + + + + +
+ [% END %] + +[% END %] + + + --- a/misc/strip_value_from_tag.pl +++ a/misc/strip_value_from_tag.pl @@ -0,0 +1,60 @@ +#!/usr/bin/perl + +# This script takes every biblio record in the database, +# looks for a value beginning with $find in $fieldtag$$subfieldtag +# and strips $find from this value. +# This was originally made for the file upload plugin, but might +# be used for other purposes. + +use Modern::Perl; + +use Getopt::Long; +use C4::Context; +use C4::Biblio; + +my $debug = 0; + +# Which field are we processing? +my $fieldtag = "857"; +my $subfieldtag = "u"; + +# Whichi beginning pattern are we looking to delete? +my $find = "http://myurl.tld/"; +my $length = length($find); +my $pattern = qr|^$find|; + +print "Field $fieldtag\$$subfieldtag\n"; + +# Getting db connection +my $dbh = C4::Context->dbh; + +# Getting max bibnumber +my $query = "SELECT MAX(biblionumber) from biblio"; +my $sth = $dbh->prepare($query); +$sth->execute(); +my $bibliocount = $sth->fetchrow; + +warn "unable to get biblio count" and exit -1 unless $bibliocount; + +print "Biblio count : $bibliocount\n"; + +# Foreach each biblio +foreach (1..$bibliocount) { + my $found = 0; + my $record = GetMarcBiblio($_); + $debug and warn "unable to get marc for record $_" unless $record; + next unless $record; + foreach my $field ($record->field($fieldtag)) { + my $newfield = $field->clone(); + my $subfield = $newfield->subfield($subfieldtag); + if ($subfield and $subfield =~ $pattern) { + my $newsubfield = substr $subfield, $length; + $newsubfield =~ s/\s+$//; + $newfield->update($subfieldtag, $newsubfield); + $field->replace_with($newfield); + $found = 1; + } + } + print "processing $_\n" if ($found == 1); + ModBiblioMarc($record, $_, GetFrameworkCode($_)) if ($found == 1); +} --- a/opac/opac-basket.pl +++ a/opac/opac-basket.pl @@ -69,7 +69,7 @@ foreach my $biblionumber ( @bibs ) { my $marcauthorsarray = GetMarcAuthors( $record, $marcflavour ); my $marcsubjctsarray = GetMarcSubjects( $record, $marcflavour ); my $marcseriesarray = GetMarcSeries ($record,$marcflavour); - my $marcurlsarray = GetMarcUrls ($record,$marcflavour); + my $marcurlsarray = GetMarcUrls ($record,$marcflavour, GetFrameworkCode($biblionumber)); my @items = &GetItemsInfo( $biblionumber ); my $subtitle = GetRecordValue('subtitle', $record, GetFrameworkCode($biblionumber)); --- a/opac/opac-detail.pl +++ a/opac/opac-detail.pl @@ -634,7 +634,7 @@ my $marcisbnsarray = GetMarcISBN ($record,$marcflavour); my $marcauthorsarray = GetMarcAuthors ($record,$marcflavour); my $marcsubjctsarray = GetMarcSubjects($record,$marcflavour); my $marcseriesarray = GetMarcSeries ($record,$marcflavour); -my $marcurlsarray = GetMarcUrls ($record,$marcflavour); +my $marcurlsarray = GetMarcUrls ($record,$marcflavour, $dat->{'frameworkcode'}); my $marchostsarray = GetMarcHosts($record,$marcflavour); my $subtitle = GetRecordValue('subtitle', $record, GetFrameworkCode($biblionumber)); --- a/opac/opac-retrieve-file.pl +++ a/opac/opac-retrieve-file.pl @@ -0,0 +1,50 @@ +#!/usr/bin/perl + +# Copyright 2011-2012 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. +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301 USA. + +use Modern::Perl; +use CGI; + +use C4::Context; +use C4::UploadedFiles; + +my $input = new CGI; + +my $id = $input->param('id'); +my $file = C4::UploadedFiles::GetUploadedFile($id); +exit 1 if not $file; + +my $file_path = $file->{filepath}; + +if( -f $file_path ) { + open my $fh, '<', $file_path or die "Can't open file: $!"; + print $input->header( + -type => "application/octet-stream", + -attachment => $file->{filename} + ); + while(<$fh>) { + print $_; + } +} else { + exit 1; +} + +exit 0; --- a/t/db_dependent/UploadedFiles.t +++ a/t/db_dependent/UploadedFiles.t @@ -0,0 +1,42 @@ +#!/usr/bin/perl + +use Modern::Perl; +use File::Temp qw/ tempdir /; +use Test::CGI::Multipart; +use Test::More tests => 11; + +use t::lib::Mocks; + +use C4::Context; +use C4::UploadedFiles; + +# This simulates a multipart POST request with a file upload. +my $tcm = new Test::CGI::Multipart; +$tcm->upload_file( + name => 'testfile', + file => 'testfilename.txt', + value => "This is the content of testfilename.txt", +); +my $cgi = $tcm->create_cgi; + +my $tempdir = tempdir(CLEANUP => 1); +t::lib::Mocks::mock_config('upload_path', $tempdir); + +my $testfilename = $cgi->param('testfile'); +my $testfile_fh = $cgi->upload('testfile'); +my $id = C4::UploadedFiles::UploadFile($testfilename, '', $testfile_fh->handle); +ok($id, "File uploaded, id is $id"); + +my $file = C4::UploadedFiles::GetUploadedFile($id); +isa_ok($file, 'HASH', "GetUploadedFiles($id)"); +foreach my $key (qw(id filename filepath dir)) { + ok(exists $file->{$key}, "GetUploadedFile($id)->{$key} exists"); +} + +ok(-e $file->{filepath}, "File $file->{filepath} exists"); + +ok(C4::UploadedFiles::DelUploadedFile($id), "DelUploadedFile($id) returned true"); +ok(! -e $file->{filepath}, "File $file->{filepath} does not exist anymore"); + +is(C4::UploadedFiles::UploadFile($testfilename, '../', $testfile_fh->handle), undef, 'UploadFile with $dir containing ".." return undef'); +is(C4::UploadedFiles::GetUploadedFile(), undef, 'GetUploadedFile without parameters returns undef'); --