[Patches] [PATCH] [Bug 1633] Patch to add local cover images support for

koha-patchbot at kohaaloha.com koha-patchbot at kohaaloha.com
Sun Nov 6 22:40:03 NZDT 2011


From: Koustubha Kale <kmkale at anantcorp.com>
Date: Sun, 6 Nov 2011 11:13:23 +0530
Subject: [PATCH] [Bug 1633] Patch to add local cover images support for
 display in OPAC.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

This patch adds support for local cover images for display in OPAC.

How to use/test :
Assign user permission to the user Tools > (upload_local_cover_images Upload local cover images.
In order to upload local  images, login to the staff client. Go to Home › Tools › Upload Cover Images.
Here you can upload cover images either singly or in bulk in the form of a zip file.
If uploading singly, click on image file, browse the image from your local disk, type in the biblio number of the catalouge entry and press upload.
If uploading in bulk as a zip file, the zip file must contain ( in addition to cover images ) one text file named either datalink.txt  OR
idlink.txt. This file should have mapping of biblionumber to image file name in the zip one per line with comma or tab as delimiters. For example

1, scanned_cover_image_of_bib_no_1.jpg
2, scanned_cover_image_of_bib_no_1.jpg

Cover Image Files greater than 100K will be refused. Images should be 140x200 pixels. If they are larger they will be auto-resized to comply.
If a cover image file is uploaded with a biblionumber which already has matching image in the database it will be overwritten.
1. The patch  adds a menu link in Tools from where you can upload local cover images
2. It adds a user permission to enable access control to this menu item under Tools
3. It adds a system preference OPACLocalCoverImages under Enhanced Content. This needs to be turned on to show local cover images in OPAC.

Once you have uploaded local images, if you search for the biblio, the local cover should show up in search as well as search detail pages.

I am working on another patch which will allow us to set a cover image source priority in system preferences,
and which will then gracefully fail over to the next source if image is not available from the first choice source.
---
 .../data/mysql/en/mandatory/userpermissions.sql    |    1 +
 installer/data/mysql/kohastructure.sql             |   13 +
 installer/data/mysql/sysprefs.sql                  |    2 +-
 installer/data/mysql/updatedatabase.pl             |   16 +
 .../intranet-tmpl/prog/en/includes/tools-menu.inc  |    3 +
 .../admin/preferences/enhanced_content.pref        |    7 +
 .../prog/en/modules/tools/tools-home.tt            |    5 +
 .../prog/en/modules/tools/upload-cover-image.tt    |  126 +++++++
 koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt |    1 +
 .../opac-tmpl/prog/en/modules/opac-results.tt      |    1 +
 opac/opac-coverimage.pl                            |  105 ++++++
 opac/opac-detail.pl                                |    5 +
 opac/opac-search.pl                                |    4 +
 tools/upload-cover-image.pl                        |  341 ++++++++++++++++++++
 14 files changed, 629 insertions(+), 1 deletions(-)
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/upload-cover-image.tt
 create mode 100755 opac/opac-coverimage.pl
 create mode 100755 tools/upload-cover-image.pl

diff --git a/installer/data/mysql/en/mandatory/userpermissions.sql b/installer/data/mysql/en/mandatory/userpermissions.sql
index ec61ea0..873089a 100644
--- a/installer/data/mysql/en/mandatory/userpermissions.sql
+++ b/installer/data/mysql/en/mandatory/userpermissions.sql
@@ -36,6 +36,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES
    (13, 'manage_csv_profiles', 'Manage CSV export profiles'),
    (13, 'moderate_tags', 'Moderate patron tags'),
    (13, 'rotating_collections', 'Manage rotating collections'),
+   (13, 'upload_local_cover_images', 'Upload local cover images'),
    (15, 'check_expiration', 'Check the expiration of a serial'),
    (15, 'claim_serials', 'Claim missing serials'),
    (15, 'create_subscription', 'Create a new subscription'),
diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql
index e4388a4..97bd04e 100644
--- a/installer/data/mysql/kohastructure.sql
+++ b/installer/data/mysql/kohastructure.sql
@@ -2665,6 +2665,19 @@ CREATE TABLE `fieldmapping` ( -- koha to keyword mapping
   PRIMARY KEY  (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
+--
+-- Table structure for table `bibliocoverimage`
+--
+
+DROP TABLE IF EXISTS `bibliocoverimage`;
+
+CREATE TABLE `bibliocoverimage` (
+ `biblionumber` int(11) NOT NULL,
+ `mimetype` varchar(15) NOT NULL,
+ `imagefile` mediumblob NOT NULL,
+ PRIMARY KEY (`biblionumber`),
+ CONSTRAINT `bibliocoverimage_fk1` FOREIGN KEY (`biblionumber`) REFERENCES `biblio` (`biblionumber`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8
 
 /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
 /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql
index ae2c1cb..254fcc8 100755
--- a/installer/data/mysql/sysprefs.sql
+++ b/installer/data/mysql/sysprefs.sql
@@ -328,4 +328,4 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('
 INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('OpacKohaUrl','1',"Show 'Powered by Koha' text on OPAC footer.",NULL,NULL);
 INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('EasyAnalyticalRecords','0','If on, display in the catalogue screens tools to easily setup analytical record relationships','','YesNo');
 INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpacShowRecentComments',0,'If ON a link to recent comments will appear in the OPAC masthead',NULL,'YesNo');
-
+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('OPACLocalCoverImages','0','Display local cover images on OPAC search and details pages.','1','YesNo');
diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl
index a8cd166..b796f26 100755
--- a/installer/data/mysql/updatedatabase.pl
+++ b/installer/data/mysql/updatedatabase.pl
@@ -4550,6 +4550,22 @@ if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
     SetVersion ($DBversion);
 }
 
+$DBversion = '3.06.00.XXX';
+if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
+    $dbh->do( q|CREATE TABLE `bibliocoverimage` (
+      `biblionumber` int(11) NOT NULL,
+      `mimetype` varchar(15) NOT NULL,
+      `imagefile` mediumblob NOT NULL,
+      PRIMARY KEY (`biblionumber`),
+      CONSTRAINT `bibliocoverimage_fk1` FOREIGN KEY (`biblionumber`) REFERENCES `biblio` (`biblionumber`) ON DELETE CASCADE ON UPDATE CASCADE
+      ) ENGINE=InnoDB DEFAULT CHARSET=utf8|);
+   $dbh->do( q|INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('OPACLocalCoverImages','0','Display local cover mages on OPAC search and details pages.','1','YesNo')|);
+   $dbh->do( q|INSERT INTO permissions (module_bit, code, description) VALUES (13, 'upload_local_cover_images', 'Upload local cover images')|);
+    print "Upgrade to $DBversion done (Added bibliocoverimage table to stover local cover images)\n";
+    SetVersion ($DBversion);
+}
+
+
 =head1 FUNCTIONS
 
 =head2 DropAllForeignKeys($table)
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc
index e3896cc..8a739d5 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc
@@ -70,6 +70,9 @@
     [% IF ( CAN_user_tools_manage_staged_marc ) %]
 	<li><a href="/cgi-bin/koha/tools/manage-marc-import.pl">Staged MARC management</a></li>
     [% END %]
+    [% IF ( CAN_user_tools_upload_local_cover_images ) %]
+	<li><a href="/cgi-bin/koha/tools/upload-cover-image.pl">Upload Local Cover Image</a></li>
+    [% END %]
 </ul>
 <h5>Additional Tools</h5>
 <ul>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref
index 171b5f4..1db76a2 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref
@@ -311,3 +311,10 @@ Enhanced Content:
             - pref: TagsExternalDictionary
               class: file
             - on the server to be approved without moderation.
+    Local Cover Images:
+        -
+            - pref: OPACLocalCoverImages
+              choices:
+                  yes: Display
+                  no: "Don't display"
+            - local cover images on OPAC search and details pages.
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
index 10f1d6d..0c3077a 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
@@ -140,6 +140,11 @@
     <dd>Managed staged MARC records, including completing and reversing imports</dd>
     [% END %]
 
+    [% IF ( CAN_user_tools_upload_local_cover_images ) %]
+    <dt><a href="/cgi-bin/koha/tools/upload-cover-image.pl">Upload Local Cover Image</a></dt>
+    <dd>Utility to upload scanned cover images for display in OPAC</dd>
+    [% END %]
+
 </dl>
 </div>
 
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/upload-cover-image.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/upload-cover-image.tt
new file mode 100644
index 0000000..2f1642d
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/upload-cover-image.tt
@@ -0,0 +1,126 @@
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha › Tools › Upload Cover Images</title>
+[% INCLUDE 'doc-head-close.inc' %]
+<script type="text/javascript">
+ $(document).ready(function() {
+	$("#zipfile").click(function(){
+		$("#bibnum").hide();
+	});
+	$("#image").click(function(){
+		$("#bibnum").show();
+	});
+});
+</script>
+</head>
+<body>
+
+[% INCLUDE 'header.inc' %]
+[%# [% INCLUDE 'cover-search.inc' %] %]
+
+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> › <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> › Upload Cover Images </div>
+
+<div id="doc3" class="yui-t2">
+    <div id="bd">
+        <div id="yui-main">
+[% IF ( TOTAL ) %]
+    <div class="yui-b">
+        <div class="yui-g">
+            <div class="yui-u first">
+            [% IF ( ERRORS ) %]
+				[% IF ( TCOUNTS ) %]
+					<div class="dialog alert">
+					<h3>Cover Image(s) Uploaded With Some Errors</h3>
+				[% ELSE %]
+					<div class="dialog alert">
+					<h3>Cover Image Failed to Upload</h3>
+				[% END %]
+            [% ELSE %]
+                <div class="dialog message">
+                <h3>Cover Image(s) Successfully Uploaded</h3>
+            [% END %]
+	        <ul class="data">
+	            <li>Unpacking completed</li>
+                  <li>[% TOTAL %] directorie(s) scanned.</li>
+                    <li>[% HANDLED %] directorie(s) processed.</li>
+                </ul>
+                [% FOREACH COUNT IN COUNTS %]
+	            <ul class="data">
+                        [% IF ( COUNT.TCOUNTS ) %]<li>[% COUNT.TCOUNTS %] image(s) moved into the database:</li>[% END %]
+                        [% FOREACH filename IN COUNT.filenames %]
+                            <li>[% filename.source %] - Biblionumber: [% filename.biblionumber %]
+                                    [% IF ( filename.filerrors ) %]<br /> <b>WARNING:</b> This image <i>not</i> imported because 
+                                    [% FOREACH filerror IN filename.filerrors %]
+                                        [% IF ( filerror.DBERR ) %]the database returned an error. Please refer to the error log for more details.</li>
+                                        [% ELSIF ( filerror.IMGEXISTS ) %]this cover does not exist in the database.</li>
+                                        [% ELSIF ( filerror.MIMERR ) %]the image format is unrecognized.</li>
+                                        [% ELSIF ( filerror.CORERR ) %]the image file is corrupted.</li>
+                                        [% ELSIF ( filerror.OPNERR ) %]Koha was unable to open the image for reading.</li>
+                                        [% ELSIF ( filerror.OVRSIZ ) %]the image file is too big.</li>
+                                        [% ELSIF ( filerror.CRDFIL ) %]the [% filerror.CRDFIL %] is missing.</li>
+                                        [% ELSE %]of an unknown error. Please refer to the error log for more details.</li>[% END %]
+                                    [% END %]
+                                        [% ELSE %] imported successfully.</li>
+                                    [% END %]
+                        [% END %]
+                        </ul>
+                [% END %]
+
+				</div>
+				
+					<a id="uploadmore" href="/cgi-bin/koha/tools/upload-cover-image.pl">Upload More Images</a>
+					<a id="doneupload" href="/cgi-bin/koha/tools/tools-home.pl">Return to Tools</a>
+            </div>
+[% ELSE %]
+    <div class="yui-b">
+        <div class="yui-g">
+            <div class="yui-u first">
+                <h1>Upload Cover Images</h1>
+                [% IF ( ERRORS ) %]
+                <div class="dialog alert">
+                    [% FOREACH ERROR IN ERRORS %]
+                        [% IF ( ERROR.NOTZIP ) %]<li><b>The upload file does not appear to be a zip file.  The extension is not '.zip'.</b></li>
+                        [% ELSIF ( ERROR.NOWRITETEMP ) %]<li><b>This script is not able to create/write to the necessary temporary directory.</b></li>
+                        [% ELSIF ( ERROR.EMPTYUPLOAD ) %]<li><b>The upload file appears to be empty.</b></li>
+                        [% ELSIF ( ERROR.OPNLINK ) %]<li><b>Cannot open [% ERROR.OPNLINK %] to read.<br />Please verify that it exists.</b></li>
+                        [% ELSIF ( ERROR.OPNIMG ) %]<li><b>Cannot open [% ERROR.OPNIMG %] to read.<br />Please verify that it exists.</b></li>
+                        [% ELSIF ( ERROR.DELERR ) %]<li><b>Unrecognized or missing field delimiter.<br />Please verify that you are using either a single quote or a tab.</b></li>
+                        [% ELSIF ( ERROR.UZIPFAIL ) %]<li><b>[% ERROR.UZIPFAIL %] failed to unpack.<br />Please verify the integrity of the zip file and retry.</b></li>
+                        [% ELSE %]<li><b>[% ERROR.CORERR %] An unknown error has occurred.<br />Please review the error log for more details.</b></li>[% END %]
+                    [% END %]
+                </div>
+                [% END %]
+	        <form method="post" action="/cgi-bin/koha/tools/upload-cover-image.pl" enctype="multipart/form-data">
+	            <fieldset class="brief">
+			 <div class="hint"><b>NOTE:</b> Only PNG, GIF, JPEG, XPM formats are supported.</div>
+                        <ol class="radio">
+                            <li class="radio">
+                                <label for="zipfile"><input type="radio" id="zipfile" name="filetype" value="zip" checked="checked" /> zip file</label></li>
+                                <li>
+                                <label for="image">
+								[% IF ( filetype == 'image' ) %]<input type="radio" id="image" name="filetype" value="image" checked="checked" />[% ELSE %]<input type="radio" id="image" name="filetype" value="image" />[% END %]
+								 image file</label>
+	                <ol>
+                            <li>
+                                <label for="uploadfile">Select the file to upload: </label><input type="file" id="uploadfile" name="uploadfile" />
+                                [% IF ( filetype == 'image' ) %]<span id="bibnum">[% ELSE %]<span id="bibnum" style="display: none">[% END %]<label for="biblionumber">Enter cover biblionumber: </label><input type="text" id="biblionumber" name="biblionumber" value="[% biblionumber %]" size="15" /></span>
+                            </li>
+    	                </ol>
+					</li></ol>
+	            </fieldset>
+                    <fieldset class="action">
+                        <input type="hidden" name="op" value="Upload" />
+                        <input type="submit" value="Upload" class="submit" />
+						<a href="/cgi-bin/koha/tools/tools-home.pl" class="cancel">Cancel</a>
+                    </fieldset>
+                </form>
+	
+            </div>	
+[% END %]
+        </div>
+    </div>
+</div>
+<div class="yui-b noprint">
+    [% INCLUDE 'tools-menu.inc' %]
+</div>
+</div>
+[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt b/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt
index 8cba98b..13a9102 100755
--- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt
+++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt
@@ -224,6 +224,7 @@ YAHOO.util.Event.onContentReady("furtherm", function () {
     <div id="catalogue_detail_biblio">
 
     <div id="bookcover">
+	 [% IF ( OPACLocalCoverImages ) %] <img src="/cgi-bin/koha/opac-coverimage.pl?bibnum=[% biblionumber %]" alt="No Local Cover Image" class="thumbnail" />[% ELSE %]<span class="no-image">No Local cover image available</span>[% END %]
     [% IF ( OPACAmazonEnabled ) %][% IF ( OPACAmazonCoverImages ) %][% IF ( OPACurlOpenInNewWindow ) %]<a href="http://www.amazon[% AmazonTld %]/gp/reader/[% normalized_isbn %]/ref=sib_dp_pt/002-7879865-0184864#reader-link" target="_blank"><img border="0" src="http://images.amazon.com/images/P/[% normalized_isbn %].01.MZZZZZZZ.jpg" alt="Cover Image" /></a>[% ELSE %]<a href="http://www.amazon[% AmazonTld %]/gp/reader/[% normalized_isbn %]/ref=sib_dp_pt/002-7879865-0184864#reader-link"><img border="0" src="http://images.amazon.com/images/P/[% normalized_isbn %].01.MZZZZZZZ.jpg" alt="Cover Image" /></a>[% END %][% END %][% END %]
 
     [% IF ( SyndeticsEnabled ) %][% IF ( SyndeticsCoverImages ) %][% IF ( content_identifier_exists ) %][% IF ( using_https ) %]
diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt b/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt
index b7fbcb3..7a22884 100755
--- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt
+++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt
@@ -528,6 +528,7 @@ $(document).ready(function(){
 				</span>
 				</td><td>
 					<a class="p1" href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% SEARCH_RESULT.biblionumber %]">
+			[% IF ( OPACLocalCoverImages ) %] <img src="/cgi-bin/koha/opac-coverimage.pl?bibnum=[% SEARCH_RESULT.biblionumber %] alt="No Local Cover Image" class="thumbnail" />[% ELSE %]<span class="no-image">No Local cover image available</span>[% END %]
                     [% IF ( OPACAmazonEnabled ) %][% IF ( OPACAmazonCoverImages ) %][% IF ( SEARCH_RESULT.normalized_isbn ) %]<img src="http://images.amazon.com/images/P/[% SEARCH_RESULT.normalized_isbn %].01.TZZZZZZZ.jpg" alt="" class="thumbnail" />[% ELSE %]<span class="no-image">No cover image available</span>[% END %][% END %][% END %]
 
 					[% IF ( SyndeticsEnabled ) %][% IF ( SyndeticsCoverImages ) %][% IF ( using_https ) %]
diff --git a/opac/opac-coverimage.pl b/opac/opac-coverimage.pl
new file mode 100755
index 0000000..5ce86f0
--- /dev/null
+++ b/opac/opac-coverimage.pl
@@ -0,0 +1,105 @@
+#!/usr/bin/perl
+#
+# based on patronimage.pl
+#
+# 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 strict;
+use warnings;
+
+use CGI; #qw(:standard escapeHTML);
+use C4::Context;
+
+$|=1;
+
+my $DEBUG = 0;
+my $data = new CGI;
+my $biblionumber;
+
+=head1 NAME
+
+opac-coverimage.pl - Script for retrieving and formatting local cover images for display
+
+=head1 SYNOPSIS
+
+<img src="opac-coverimage.pl?bibnum= />
+
+=head1 DESCRIPTION
+
+This script, when called from within HTML and passed a valid biblionumber, will retrieve the image data associated with that biblionumber if one exists, format it in proper HTML format and pass it back to be displayed.
+
+=cut
+
+if ($data->param('bibnum')) {
+    $biblionumber = $data->param('bibnum');
+} else {
+    $biblionumber = shift;
+}
+
+
+warn "biblionumber passed in: $biblionumber" if $DEBUG;
+
+my ($imagedata, $dberror) = GetCoverImage($biblionumber);
+
+if ($dberror) {
+    warn "Database Error!";
+    exit;
+}
+
+# NOTE: Never dump the contents of $imagedata->{'patronimage'} via a warn to a log or nasty
+# things will result... you have been warned!
+
+if ($imagedata) {
+    print $data->header (-type => $imagedata->{'mimetype'}, -'Cache-Control' => 'no-store', -Content_Length => length ($imagedata->{'imagefile'})), $imagedata->{'imagefile'};
+    exit;
+} else {
+    warn "No image exists for $biblionumber";
+    exit;
+}
+
+=head2 GetCoverImage
+
+    my ($imagedata, $dberror) = GetCoverImage($biblionumber);
+
+Returns the mimetype and binary image data of the local cover image for the biblio with the supplied biblionumber.
+
+=cut
+
+sub GetCoverImage {
+    my ($biblionumber) = @_;
+    warn "Biblionumber passed to GetCoverImage is $biblionumber" if $DEBUG;
+    my $dbh = C4::Context->dbh;
+    my $query = 'SELECT mimetype, imagefile FROM bibliocoverimage WHERE biblionumber = ?';
+    my $sth = $dbh->prepare($query);
+    $sth->execute($biblionumber);
+    my $imagedata = $sth->fetchrow_hashref;
+    warn "Database error!" if $sth->errstr;
+    return $imagedata, $sth->errstr;
+}
+
+
+exit;
+
+=head1 AUTHOR
+
+Chris Nighswonger cnighswonger <at> foundations <dot> edu
+
+modified for local cover images by Koustubha Kale kmkale <at> anantcorp <dot> com
+
+=cut
diff --git a/opac/opac-detail.pl b/opac/opac-detail.pl
index d3c7e2a..8be033e 100755
--- a/opac/opac-detail.pl
+++ b/opac/opac-detail.pl
@@ -681,6 +681,11 @@ if (scalar(@serialcollections) > 0) {
 	serialcollections => \@serialcollections);
 }
 
+# Local cover Images stuff
+if (C4::Context->preference("OPACLocalCoverImages")){
+		$template->param(OPACLocalCoverImages => 1);
+}
+
 # Amazon.com Stuff
 if ( C4::Context->preference("OPACAmazonEnabled") ) {
     $template->param( AmazonTld => get_amazon_tld() );
diff --git a/opac/opac-search.pl b/opac/opac-search.pl
index 716b164..83d0613 100755
--- a/opac/opac-search.pl
+++ b/opac/opac-search.pl
@@ -611,6 +611,10 @@ for (my $i=0;$i<@servers;$i++) {
             $template->param(SEARCH_RESULTS => \@newresults,
                                 OPACItemsResultsDisplay => (C4::Context->preference("OPACItemsResultsDisplay") eq "itemdetails"?1:0),
                             );
+	    if (C4::Context->preference("OPACLocalCoverImages")){
+		$template->param(OPACLocalCoverImages => 1);
+		$template->param(OPACLocalCoverImagesPriority => C4::Context->preference("OPACLocalCoverImagesPriority"));
+	    }
             ## Build the page numbers on the bottom of the page
             my @page_numbers;
             # total number of pages there will be
diff --git a/tools/upload-cover-image.pl b/tools/upload-cover-image.pl
new file mode 100755
index 0000000..75eefa4
--- /dev/null
+++ b/tools/upload-cover-image.pl
@@ -0,0 +1,341 @@
+#!/usr/bin/perl
+#
+#
+# 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 strict;
+#use warnings; FIXME - Bug 2505
+
+use File::Temp;
+use File::Copy;
+use CGI;
+use GD;
+use C4::Context;
+use C4::Auth;
+use C4::Output;
+use C4::Debug;
+
+my $input = new CGI;
+
+my ($template, $loggedinuser, $cookie)
+	= get_template_and_user({template_name => "tools/upload-cover-image.tmpl",
+					query => $input,
+					type => "intranet",
+					authnotrequired => 0,
+					flagsrequired => { tools => '*'},
+					debug => 0,
+					});
+
+my $filetype            = $input->param('filetype');
+my $biblionumber          = $input->param('biblionumber');
+my $uploadfilename      = $input->param('uploadfile');
+my $uploadfile          = $input->upload('uploadfile');
+my $op                  = $input->param('op');
+
+#FIXME: This code is really in the rough. The variables need to be re-scoped as the two subs depend on global vars to operate.
+#       Other parts of this code could be optimized as well, I think. Perhaps the file upload could be done with YUI's upload
+#       coded. -fbcit
+
+$debug and warn "Params are: filetype=$filetype, biblionumber=$biblionumber, uploadfile=$uploadfilename";
+
+=head1 NAME
+
+upload-cover-image.pl - Script for handling uploading of both single and bulk coverimages and importing them into the database.
+
+=head1 SYNOPSIS
+
+Copied from picture-upload.pl
+
+=head1 DESCRIPTION
+
+This script is called and presents the user with an interface allowing him/her to upload a single cover image or bulk cover images via a zip file.
+Files greater than 100K will be refused. Images should be 140x200 pixels. If they are larger they will be auto-resized to comply.
+
+=cut
+
+$debug and warn "Operation requested: $op";
+
+my ( $total, $handled, @counts, $tempfile, $tfh );
+
+if ( ($op eq 'Upload') && $uploadfile ) {       # Case is important in these operational values as the template must use case to be visually pleasing!
+    my $dirname = File::Temp::tempdir( CLEANUP => 1);
+    $debug and warn "dirname = $dirname";
+    my $filesuffix = $1 if $uploadfilename =~ m/(\..+)$/i;
+    ( $tfh, $tempfile ) = File::Temp::tempfile( SUFFIX => $filesuffix, UNLINK => 1 );
+    $debug and warn "tempfile = $tempfile";
+    my ( @directories, $errors );
+
+    $errors{'NOTZIP'} = 1 if ( $uploadfilename !~ /\.zip$/i && $filetype =~ m/zip/i );
+    $errors{'NOWRITETEMP'} = 1 unless ( -w $dirname );
+    $errors{'EMPTYUPLOAD'} = 1 unless ( length( $uploadfile ) > 0 );
+
+    if ( %errors ) {
+	$template->param( ERRORS => [ \%errors ] );
+    } else {
+	while ( <$uploadfile> ) {
+	    print $tfh $_;
+        }
+        close $tfh;
+        if ( $filetype eq 'zip' ) {
+            unless (system("unzip", $tempfile,  '-d', $dirname) == 0) {
+                $errors{'UZIPFAIL'} = $uploadfilename;
+	        $template->param( ERRORS => [ \%errors ] );
+                output_html_with_http_headers $input, $cookie, $template->output;   # This error is fatal to the import, so bail out here
+                exit;
+            }
+            push @directories, "$dirname";
+            foreach $recursive_dir ( @directories ) {
+                opendir $dir, $recursive_dir;
+                while ( my $entry = readdir $dir ) {
+	        push @directories, "$recursive_dir/$entry" if ( -d "$recursive_dir/$entry" and $entry !~ /^\./ );
+                $debug and warn "$recursive_dir/$entry";
+                }
+                closedir $dir;
+            }
+            my $results;
+            foreach my $dir ( @directories ) {
+                $results = handle_dir( $dir, $filesuffix );
+                $handled++ if $results == 1;
+            }
+            $total = scalar @directories;
+        } else {       #if ($filetype eq 'zip' )
+            $results = handle_dir( $dirname, $filesuffix );
+            $handled = 1;
+            $total = 1;
+        }
+
+        if ( %$results || %errors ) {
+            $template->param( ERRORS => [ \%$results ] );
+        } else {
+			my $filecount;
+			map {$filecount += $_->{count}} @counts;
+            $debug and warn "Total directories processed: $total";
+            $debug and warn "Total files processed: $filecount";
+            $template->param(
+		 	TOTAL => $total,
+		 	HANDLED => $handled,
+		 	COUNTS => \@counts,
+			TCOUNTS => ($filecount > 0 ? $filecount : undef),
+            );
+			$template->param( biblionumber => $biblionumber ) if $biblionumber;
+        }
+    }
+} elsif ( ($op eq 'Upload') && !$uploadfile ) {
+    warn "Problem uploading file or no file uploaded.";
+    $template->param(biblionumber => $biblionumber);
+    $template->param(filetype => $filetype);
+} elsif ( $op eq 'Delete' ) {
+    my $dberror = RmBiblioImage($biblionumber);
+	$debug and warn "Cover image deleted for $biblionumber";
+    warn "Database returned $dberror" if $dberror;
+}
+
+output_html_with_http_headers $input, $cookie, $template->output;
+
+
+sub handle_dir {
+    my ( $dir, $suffix ) = @_;
+    my $source;
+    $debug and warn "Entering sub handle_dir; passed \$dir=$dir, \$suffix=$suffix";
+    if ($suffix =~ m/zip/i) {     # If we were sent a zip file, process any included data/idlink.txt files
+        my ( $file, $filename, $biblionumber );
+        $debug and warn "Passed a zip file.";
+        opendir my $dirhandle, $dir;
+        while ( my $filename = readdir $dirhandle ) {
+            $file = "$dir/$filename" if ($filename =~ m/datalink\.txt/i || $filename =~ m/idlink\.txt/i);
+        }
+        unless (open (FILE, $file)) {
+		warn "Opening $dir/$file failed!";
+                $errors{'OPNLINK'} = $file;
+		return $errors; # This error is fatal to the import of this directory contents, so bail and return the error to the caller
+        };
+
+        while (my $line = <FILE>) {
+            $debug and warn "Reading contents of $file";
+	    chomp $line;
+            $debug and warn "Examining line: $line";
+	    my $delim = ($line =~ /\t/) ? "\t" : ($line =~ /,/) ? "," : "";
+            $debug and warn "Delimeter is \'$delim\'";
+            unless ( $delim eq "," || $delim eq "\t" ) {
+                warn "Unrecognized or missing field delimeter. Please verify that you are using either a ',' or a 'tab'";
+                $errors{'DELERR'} = 1;      # This error is fatal to the import of this directory contents, so bail and return the error to the caller
+                return $errors;
+            }
+	    ($biblionumber, $filename) = split $delim, $line;
+	    $biblionumber =~ s/[\"\r\n]//g;  # remove offensive characters
+	    $filename   =~ s/[\"\r\n\s]//g;
+            $debug and warn "Biblionumber: $biblionumber Filename: $filename";
+            $source = "$dir/$filename";
+            %counts = handle_file($biblionumber, $source, %counts);
+        }
+        close FILE;
+        closedir ($dirhandle);
+    } else {
+        $source = $tempfile;
+        %counts = handle_file($biblionumber, $source, %counts);
+    }
+push @counts, \%counts;
+return 1;
+}
+
+sub handle_file {
+    my ($biblionumber, $source, %count) = @_;
+    $debug and warn "Entering sub handle_file; passed \$biblionumber=$biblionumber, \$source=$source";
+    $count{filenames} = () if !$count{filenames};
+    $count{source} = $source if !$count{source};
+    if ($biblionumber && $source) {     # Now process any imagefiles
+        my %filerrors;
+        my $filename;
+        if ($filetype eq 'image') {
+            $filename = $uploadfilename;
+        } else {
+            $filename = $1 if ($source =~ /\/([^\/]+)$/);
+        }
+        $debug and warn "Source: $source";
+        my $size = (stat($source))[7];
+            if ($size > 550000) {    # This check is necessary even with image resizing to avoid possible security/performance issues...
+                $filerrors{'OVRSIZ'} = 1;
+                push my @filerrors, \%filerrors;
+                push @{ $count{filenames} }, { filerrors => \@filerrors, source => $filename, biblionumber => $biblionumber };
+                $template->param( ERRORS => 1 );
+                return %count;    # this one is fatal so bail here...
+            }
+        my ($srcimage, $image);
+        if (open (IMG, "$source")) {
+            $srcimage = GD::Image->new(*IMG);
+            close (IMG);
+			if (defined $srcimage) {
+				my $mimetype = 'image/png';	# GD autodetects three basic image formats: PNG, JPEG, XPM; we will convert all to PNG which is lossless...
+				# Check the pixel size of the image we are about to import...
+				my ($width, $height) = $srcimage->getBounds();
+				$debug and warn "$filename is $width pix X $height pix.";
+				if ($width > 200 || $height > 300) {    # MAX pixel dims are 200 X 300...
+					$debug and warn "$filename exceeds the maximum pixel dimensions of 200 X 300. Resizing...";
+					my $percent_reduce;    # Percent we will reduce the image dimensions by...
+					if ($width > 200) {
+						$percent_reduce = sprintf("%.5f",(140/$width));    # If the width is oversize, scale based on width overage...
+					} else {
+						$percent_reduce = sprintf("%.5f",(200/$height));    # otherwise scale based on height overage.
+					}
+					my $width_reduce = sprintf("%.0f", ($width * $percent_reduce));
+					my $height_reduce = sprintf("%.0f", ($height * $percent_reduce));
+					$debug and warn "Reducing $filename by " . ($percent_reduce * 100) . "\% or to $width_reduce pix X $height_reduce pix";
+					$image = GD::Image->new($width_reduce, $height_reduce, 1); #'1' creates true color image...
+					$image->copyResampled($srcimage,0,0,0,0,$width_reduce,$height_reduce,$width,$height);
+					$imgfile = $image->png();
+					$debug and warn "$filename is " . length($imgfile) . " bytes after resizing.";
+					undef $image;
+					undef $srcimage;    # This object can get big...
+				} else {
+					$image = $srcimage;
+					$imgfile = $image->png();
+					$debug and warn "$filename is " . length($imgfile) . " bytes.";
+					undef $image;
+					undef $srcimage;    # This object can get big...
+				}
+				$debug and warn "Image is of mimetype $mimetype";
+				my $dberror = PutCoverImage($biblionumber,$mimetype, $imgfile) if $mimetype;
+				if ( !$dberror && $mimetype ) { # Errors from here on are fatal only to the import of a particular image, so don't bail, just note the error and keep going
+					$count{count}++;
+					push @{ $count{filenames} }, { source => $filename, biblionumber => $biblionumber };
+				} elsif ( $dberror ) {
+						warn "Database returned error: $dberror";
+						($dberror =~ /coverimage_fk1/) ? $filerrors{'IMGEXISTS'} = 1 : $filerrors{'DBERR'} = 1;
+						push my @filerrors, \%filerrors;
+						push @{ $count{filenames} }, { filerrors => \@filerrors, source => $filename, biblionumber => $biblionumber };
+						$template->param( ERRORS => 1 );
+				} elsif ( !$mimetype ) {
+					warn "Unable to determine mime type of $filename. Please verify mimetype.";
+					$filerrors{'MIMERR'} = 1;
+					push my @filerrors, \%filerrors;
+					push @{ $count{filenames} }, { filerrors => \@filerrors, source => $filename, biblionumber => $biblionumber };
+					$template->param( ERRORS => 1 );
+				}
+			} else {
+				warn "Contents of $filename corrupted!";
+			#	$count{count}--;
+				$filerrors{'CORERR'} = 1;
+				push my @filerrors, \%filerrors;
+				push @{ $count{filenames} }, { filerrors => \@filerrors, source => $filename, biblionumber => $biblionumber };
+				$template->param( ERRORS => 1 );
+			}
+		} else {
+			warn "Opening $dir/$filename failed!";
+			$filerrors{'OPNERR'} = 1;
+			push my @filerrors, \%filerrors;
+			push @{ $count{filenames} }, { filerrors => \@filerrors, source => $filename, biblionumber => $biblionumber };
+			$template->param( ERRORS => 1 );
+		}
+    } else {    # The need for this seems a bit unlikely, however, to maximize error trapping it is included
+        warn "Missing " . ($biblionumber ? "filename" : ($filename ? "biblionumber" : "biblionumber and filename"));
+        $filerrors{'CRDFIL'} = ($biblionumber ? "filename" : ($filename ? "biblionumber" : "biblionumber and filename"));
+        push my @filerrors, \%filerrors;
+		push @{ $count{filenames} }, { filerrors => \@filerrors, source => $filename, biblionumber => $biblionumber };
+        $template->param( ERRORS => 1 );
+    }
+    return (%count);
+}
+=head2 PutCoverImage
+
+    PutCoverImage($biblionumber, $mimetype, $imgfile);
+
+Stores cover binary image data and mimetype in database.
+NOTE: This function is good for updating images as well as inserting new images in the database.
+
+=cut
+
+sub PutCoverImage {
+    my ($biblionumber, $mimetype, $imgfile) = @_;
+    warn "Parameters passed in: Biblionumber=$biblionumber, Mimetype=$mimetype, " . ($imgfile ? "Imagefile" : "No Imagefile") if $debug;
+    my $dbh = C4::Context->dbh;
+    my $query = "INSERT INTO bibliocoverimage (biblionumber, mimetype, imagefile) VALUES (?,?,?) ON DUPLICATE KEY UPDATE imagefile = ?;";
+    my $sth = $dbh->prepare($query);
+    $sth->execute($biblionumber,$mimetype,$imgfile,$imgfile);
+    warn "Error returned inserting $biblionumber.$mimetype." if $sth->errstr;
+    return $sth->errstr;
+}
+
+=head2 RmCoverImage
+
+    my ($dberror) = RmCoverImage($biblionumber);
+
+Removes the image for the cover with the supplied biblionumber.
+
+=cut
+
+sub RmCoverImage {
+    my ($biblionumber) = @_;
+    warn "Biblionumber passed to RmBiblioImage is $biblionumber" if $debug;
+    my $dbh = C4::Context->dbh;
+    my $query = "DELETE FROM bibliocoverimage WHERE biblionumber = ?;";
+    my $sth = $dbh->prepare($query);
+    $sth->execute($biblionumber);
+    my $dberror = $sth->errstr;
+    warn "Database error!" if $sth->errstr;
+    return $dberror;
+}
+
+=head1 AUTHORS
+
+Copied from picture-upload.pl
+
+Database storage, single coverimage upload option, and extensive error trapping contributed by Chris Nighswonger cnighswonger <at> foundations <dot> edu
+Image scaling/resizing contributed by the same.
+
+=cut
-- 
1.7.5.4




More information about the Patches mailing list