From 0512b03d4ff18216df78e75599cf2714afac4166 Mon Sep 17 00:00:00 2001
From: Blou <philippe.blouin@inlibro.com>
Date: Mon, 27 Jan 2014 13:16:05 -0500
Subject: [PATCH] Bug 10937 - Option to hide and group itemtypes from advanced
 search

It's possible to hide the records and the items, but the
advanced search form is a bit trickier. It would be nice if
we could specify which itemtypes are offered as search options
in the OPAC advanced search form.

To solve this, two new columns will be added in the item type.

1) Display in OPAC
Default : Yes
Set to NO to hide and itemtype in the opac's advanced search.

2) Search category
The search category allows library to group some similar
itemtypes in a single checkbox in the opac.  The categories are
defined in an Authorized value.

For Example, itemtypes DVD, VHS could use the Video category.
Searching for Video in the OPAC would returns the DVD and VHS
items.

Initial coding by philippe.blouin@inlibro.com with
mtompset@hotmail.com providing extra testing, patching,
squashing, and rebasing.

CURRENT TEST PLAN
------------------
0) Back up database, so you can reset and retest easily. ;)
1) Run Koha QA tool.
2) Apply the patch

SETUP
3) run ./installer/data/mysql/updatedatabase.pl to add the
    two columns to itemtypes
   -- Does a meaningful message get printed?
      Are the columns added?
       "DESCRIBE itemtypes;" should list hideinopac and
       searchcategory.
4) You need to add a category to group your item types:
   a) In Intranet/Koha Admin/Authorized values,
       select DOCTYPECAT in the 'Show category:' dropdown
       i) If you do not have a DOCTYPECAT category, create one.
   b) Click button "New authorized value for DOCTYPECAT"
   c) Enter
         Authorized value: HARDWARE
         Description : Hardware
         Description (OPAC): Hardware

NORMAL USAGE
2) Group your items under that new category
   a) In Intranet/Koha Admin/Item types, choose (at least)
      two item types and for each:
      - Click action/Edit on the right column
      - Third row (below Description) is the Search category
        list box, select Hardware
      - click Save changes at the bottom
3) Select at least one item to be hidden in the OPAC search
   a) In Intranet/Koha Admin/Item types (again), choose a
      different item type:
      -  Click action/Edit
      -  Click the checkbox "Hide in OPAC" below the list of
         icons.

FINAL TESTING
4) Go test your modifications
   a) Go to OPAC/Adv search.
   b) Validate that all items modified above (hidden or grouped)
       do not appear in Item type list
   c) Validate that new item type Hardware does appear instead.
   d) Select item Hardware, start Search.
   e) Validate returned items are the of the two types that were
       grouped into the Hardware category in step 4.

Sponsored-by: Vanier college
---
 C4/Koha.pm                                         |   62 ++++++++++++++++++++
 admin/authorised_values.pl                         |    2 +-
 admin/itemtypes.pl                                 |   14 ++++-
 installer/data/mysql/kohastructure.sql             |    2 +
 installer/data/mysql/updatedatabase.pl             |   13 ++++
 .../prog/en/modules/admin/itemtypes.tt             |   46 +++++++++++++++
 .../bootstrap/en/modules/opac-advsearch.tt         |    4 +-
 .../opac-tmpl/prog/en/modules/opac-advsearch.tt    |    6 +-
 opac/opac-search.pl                                |   28 +++++++--
 9 files changed, 166 insertions(+), 11 deletions(-)

diff --git a/C4/Koha.pm b/C4/Koha.pm
index 6f98ac6..edc4a85 100644
--- a/C4/Koha.pm
+++ b/C4/Koha.pm
@@ -43,6 +43,7 @@ BEGIN {
 		&subfield_is_koha_internal_p
 		&GetPrinters &GetPrinter
 		&GetItemTypes &getitemtypeinfo
+                &GetItemTypesCategorized &GetItemTypesByCategory
 		&GetSupportName &GetSupportList
 		&get_itemtypeinfos_of
 		&getframeworks &getframeworkinfo
@@ -275,6 +276,67 @@ sub GetItemTypes {
     }
 }
 
+=head2 GetItemTypesCategorized
+
+Returns item types but grouped by category if available.
+The categories must be part of Authorized Values (DOCTYPECAT)
+
+=cut
+
+sub GetItemTypesCategorized {
+    my $dbh   = C4::Context->dbh;
+    my $query = qq|
+        SELECT itemtype, description, imageurl, hideinopac, 0 as 'iscat' FROM itemtypes WHERE ISNULL(searchcategory) or length(searchcategory) = 0
+        UNION
+        SELECT DISTINCT searchcategory AS `itemtype`,
+                        authorised_values.lib_opac AS description,
+                        authorised_values.imageurl AS imageurl,
+                        hideinopac, 1 as 'iscat'
+        FROM itemtypes
+        LEFT JOIN authorised_values ON searchcategory = authorised_value
+        WHERE searchcategory > '' and hideinopac=1
+        UNION
+        SELECT DISTINCT searchcategory AS `itemtype`,
+                        authorised_values.lib_opac AS description,
+                        authorised_values.imageurl AS imageurl,
+                        hideinopac, 1 as 'iscat'
+        FROM itemtypes
+        LEFT JOIN authorised_values ON searchcategory = authorised_value
+        WHERE searchcategory > '' and hideinopac=0
+        |;
+    my $sth = $dbh->prepare($query);
+    $sth->execute;
+
+    my %itemtypes;
+    while ( my $IT = $sth->fetchrow_hashref ) {
+        $itemtypes{ $IT->{'itemtype'} } = $IT;
+    }
+
+    return ( \%itemtypes );
+}
+
+=head2 GetItemTypesByCategory
+
+    $category = category filter
+
+Returns the itemtypes that are grouped into the category.
+
+=cut
+
+sub GetItemTypesByCategory {
+    my ($category) = @_;
+    my $count = 0;
+    my @results;
+    my $dbh = C4::Context->dbh;
+    my $sth = $dbh->prepare("SELECT itemtype FROM itemtypes WHERE searchcategory=?");
+    $sth->execute($category);
+
+    while ( my $data = $sth->fetchrow ) {
+        push ( @results, $data );
+    }
+    return @results;
+}
+
 sub get_itemtypeinfos_of {
     my @itemtypes = @_;
 
diff --git a/admin/authorised_values.pl b/admin/authorised_values.pl
index 332f09a..47847f9 100755
--- a/admin/authorised_values.pl
+++ b/admin/authorised_values.pl
@@ -247,7 +247,7 @@ sub default_form {
     }
 
     # push koha system categories
-    foreach (qw(Asort1 Asort2 Bsort1 Bsort2 SUGGEST DAMAGED LOST REPORT_GROUP REPORT_SUBGROUP DEPARTMENT TERM SUGGEST_STATUS)) {
+    foreach (qw(Asort1 Asort2 Bsort1 Bsort2 SUGGEST DAMAGED LOST REPORT_GROUP REPORT_SUBGROUP DEPARTMENT TERM SUGGEST_STATUS DOCTYPECAT)) {
         push @category_list, $_ unless $categories{$_};
     }
 
diff --git a/admin/itemtypes.pl b/admin/itemtypes.pl
index 87af801..c8ec47c 100755
--- a/admin/itemtypes.pl
+++ b/admin/itemtypes.pl
@@ -112,6 +112,8 @@ if ( $op eq 'add_form' ) {
         $remote_image = $data->{imageurl};
     }
 
+    my $searchcategory = GetAuthorisedValues("DOCTYPECAT", $data->{'searchcategory'});
+
     $template->param(
         itemtype        => $itemtype,
         description     => $data->{'description'},
@@ -125,6 +127,8 @@ if ( $op eq 'add_form' ) {
         imagesets       => $imagesets,
         remote_image    => $remote_image,
         sip_media_type  => $data->{sip_media_type},
+        hideinopac      => $data->{'hideinopac'},
+        searchcategory  => $searchcategory,
     );
 
     # END $OP eq ADD_FORM
@@ -150,6 +154,8 @@ elsif ( $op eq 'add_validate' ) {
                  , checkinmsg = ?
                  , checkinmsgtype = ?
                  , sip_media_type = ?
+                 , hideinopac = ?
+                 , searchcategory = ?
             WHERE itemtype = ?
         ';
         $sth = $dbh->prepare($query2);
@@ -168,15 +174,17 @@ elsif ( $op eq 'add_validate' ) {
             $input->param('checkinmsg'),
             $input->param('checkinmsgtype'),
             $sip_media_type,
+            $input->param('hideinopac') ? 1 : 0,
+            $input->param('searchcategory'),
             $input->param('itemtype')
         );
     }
     else {    # add a new itemtype & not modif an old
         my $query = "
             INSERT INTO itemtypes
-                (itemtype,description,rentalcharge, notforloan, imageurl, summary, checkinmsg, checkinmsgtype, sip_media_type)
+                (itemtype,description,rentalcharge, notforloan, imageurl, summary, checkinmsg, checkinmsgtype, sip_media_type, hideinopac, searchcategory)
             VALUES
-                (?,?,?,?,?,?,?,?,?);
+                (?,?,?,?,?,?,?,?,?,?,?);
             ";
         my $sth = $dbh->prepare($query);
 		my $image = $input->param('image');
@@ -192,6 +200,8 @@ elsif ( $op eq 'add_validate' ) {
             $input->param('checkinmsg'),
             $input->param('checkinmsgtype'),
             $sip_media_type,
+            $input->param('hideinopac') ? 1 : 0,
+            $input->param('searchcategory'),
         );
     }
 
diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql
index b46dc6a..0f08f4c 100644
--- a/installer/data/mysql/kohastructure.sql
+++ b/installer/data/mysql/kohastructure.sql
@@ -1247,6 +1247,8 @@ CREATE TABLE `itemtypes` ( -- defines the item types
   checkinmsg VARCHAR(255), -- message that is displayed when an item with the given item type is checked in
   checkinmsgtype CHAR(16) DEFAULT 'message' NOT NULL, -- type (CSS class) for the checkinmsg, can be "alert" or "message"
   sip_media_type VARCHAR(3) DEFAULT NULL, -- SIP2 protocol media type for this itemtype
+  hideinopac tinyint(1) NOT NULL DEFAULT 0, -- Hide the item type from the search options in OPAC
+  searchcategory varchar(20) default NULL, -- Group this item type with others with the same value on OPAC search options
   PRIMARY KEY  (`itemtype`),
   UNIQUE KEY `itemtype` (`itemtype`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl
index 01472d7..97253c1 100755
--- a/installer/data/mysql/updatedatabase.pl
+++ b/installer/data/mysql/updatedatabase.pl
@@ -8551,6 +8551,19 @@ if (CheckVersion($DBversion)) {
     SetVersion($DBversion);
 }
 
+$DBversion = "3.17.00.XXX";
+if ( CheckVersion($DBversion) ) {
+    $dbh->do(q{
+        ALTER TABLE itemtypes
+            ADD hideinopac TINYINT(1) NOT NULL DEFAULT 0
+              AFTER sip_media_type,
+            ADD searchcategory VARCHAR(20) DEFAULT NULL
+              AFTER hideinopac;
+    });
+    print "Upgrade to $DBversion done (Bug 10937 - Option to hide and group itemtypes from advanced search)\n";
+    SetVersion($DBversion);
+}
+
 =head1 FUNCTIONS
 
 =head2 TableExists($table)
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt
index 522420b..2467902 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/itemtypes.tt
@@ -109,6 +109,39 @@ Item types administration
   [% END %]
       <li>
           <label for="description" class="required">Description: </label><input type="text" id="description" name="description" size="48" value="[% description |html %]" required="required" /> <span class="required">Required</span></li>
+      [% IF ( itemtype ) %]
+      <li>
+          <span class="label">Search category</span>
+          <select id="searchcategory" name="searchcategory">
+          <option value="">None</option>
+                [% FOREACH cat IN searchcategory %]
+                    [% IF ( cat.selected ) %]
+                        <option value="[% cat.authorised_value %]" selected="selected">
+                            [% cat.lib %]
+                        </option>
+                    [% ELSE %]
+                        <option value="[% cat.authorised_value %]" >
+                            [% cat.lib %]
+                        </option>
+                    [% END %]
+                [% END %]
+          </select>
+          (Options are defined as the authorized values for the DOCTYPECAT category)
+      </li>
+      [% ELSE %]
+      <li>
+          <span class="label">Search category</span>
+          <select id="searchcategory" name="searchcategory">
+          <option value="" />
+              [% FOREACH cat IN searchcategory %]
+                  <option value="[% cat.authorised_value %]" >
+                      [% cat.lib %]
+                  </option>
+              [% END %]
+          </select>
+      </li>
+      [% END %]
+
      [% IF ( noItemTypeImages ) %]
 	 <li><span class="label">Image: </span>Item type images are disabled. To enable them, turn off the <a href="/cgi-bin/koha/admin/preferences.pl?op=search&amp;searchfield=noItemTypeImages">noItemTypeImages system preference</a></li></ol>
 	 [% ELSE %]</ol>
@@ -164,6 +197,15 @@ Item types administration
 [% END %]
 <ol>
       <li>
+          <label for="hideinopac">Hide in OPAC: </label>
+          [% IF ( hideinopac ) %]
+              <input type="checkbox" id="hideinopac" name="hideinopac" checked="checked" value="1" />
+          [% ELSE %]
+              <input type="checkbox" id="hideinopac" name="hideinopac" value="1" />
+          [% END %]
+          (if checked, items of this type will be hidden as filters in OPAC's advanced search)
+      </li>
+      <li>
           <label for="notforloan">Not for loan: </label>   [% IF ( notforloan ) %]
                 <input type="checkbox" id="notforloan" name="notforloan" checked="checked" value="1" />
             [% ELSE %]
@@ -260,7 +302,9 @@ Item types administration
     [% UNLESS ( noItemTypeImages ) %]<th>Image</th>[% END %]
     <th>Code</th>
     <th>Description</th>
+    <th>Search category</th>
     <th>Not for loan</th>
+    <th>Hide in OPAC</th>
     <th>Charge</th>
     <th>Checkin message</th>
     <th>Actions</th>
@@ -278,7 +322,9 @@ Item types administration
       </a>
     </td>
     <td>[% loo.description %]</td>
+    <td>[% loo.searchcategory %]</td>
     <td>[% IF ( loo.notforloan ) %]Yes[% ELSE %]&nbsp;[% END %]</td>
+    <td>[% IF ( loo.hideinopac ) %]Yes[% ELSE %]&nbsp;[% END %]</td>
     <td>
     [% UNLESS ( loo.notforloan ) %]
       [% loo.rentalcharge %]
diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-advsearch.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-advsearch.tt
index d9b2d4b..f4f9fe0 100644
--- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-advsearch.tt
+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-advsearch.tt
@@ -141,9 +141,11 @@
                                     <legend>Limit to any of the following:</legend>
                                     <div class="row-fluid">
                                         [% FOREACH itemtypeloo IN advsearchloo.code_loop %]
-                                            <div class="span3"><input type="checkbox" id="[% itemtypeloo.ccl FILTER remove(',') %]-[% itemtypeloo.number %]" name="limit" value="mc-[% itemtypeloo.ccl %]:[% itemtypeloo.code %]"/><label for="[% itemtypeloo.ccl FILTER remove(',') %]-[% itemtypeloo.number %]">[% UNLESS ( noItemTypeImages ) %][% IF ( itemtypeloo.imageurl ) %]<img src="[% itemtypeloo.imageurl %]" alt="[% itemtypeloo.description %]" />[% END %]&nbsp;[% END %]
+                                            [% IF ((!itemtypeloo.searchcategory) AND (itemtypeloo.cat == 0)) OR (itemtypeloo.cat == 1) %]
+                                            <div class="span3"><input type="checkbox" id="[% itemtypeloo.ccl FILTER remove(',') %]-[% itemtypeloo.number %]" name="[% IF ( itemtypeloo.cat == 1 ) %]searchcat[% ELSE %]limit[% END %]" value="[% IF ( itemtypeloo.cat == 1 ) %][% itemtypeloo.code %][% ELSE %]mc-[% itemtypeloo.ccl %]:[% itemtypeloo.code %][% END %]"/><label for="[% itemtypeloo.ccl FILTER remove(',') %]-[% itemtypeloo.number %]">[% UNLESS ( noItemTypeImages ) %][% IF ( itemtypeloo.imageurl ) %]<img border="0" src="[% itemtypeloo.imageurl %]" alt="[% itemtypeloo.description %]" />[% END %]&nbsp;[% END %]
                                             [% itemtypeloo.description %]</label></div>
                                             [% IF ( loop.last ) %]</div>[% ELSE %][% UNLESS ( loop.count % 4 ) %]</div><div class="row-fluid">[% END %][% END %]
+                                            [% END %]
                                         [% END %]
                                 </fieldset>
                             </div> <!-- / #advsearch-[% advsearchloo.advanced_search_type %] -->
diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-advsearch.tt b/koha-tmpl/opac-tmpl/prog/en/modules/opac-advsearch.tt
index 04ab78c..8bf2dc0 100644
--- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-advsearch.tt
+++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-advsearch.tt
@@ -187,8 +187,10 @@
     <table>
         <tr>
     [% FOREACH itemtypeloo IN advsearchloo.code_loop %]
-        <td><input type="checkbox" id="[% itemtypeloo.ccl FILTER remove(',') %]-[% itemtypeloo.number %]" name="limit" value="mc-[% itemtypeloo.ccl %]:[% itemtypeloo.code %]"/><label for="[% itemtypeloo.ccl FILTER remove(',') %]-[% itemtypeloo.number %]">[% UNLESS ( noItemTypeImages ) %][% IF ( itemtypeloo.imageurl ) %]<img border="0" src="[% itemtypeloo.imageurl %]" alt="[% itemtypeloo.description %]" />[% END %]&nbsp;[% END %]
-        [% itemtypeloo.description %]</label></td>
+            [% IF ((!itemtypeloo.searchcategory) AND (itemtypeloo.cat == 0)) OR (itemtypeloo.cat == 1) %]
+                <td><input type="checkbox" id="[% itemtypeloo.ccl FILTER remove(',') %]-[% itemtypeloo.number %]" name="[% IF ( itemtypeloo.cat == 1 ) %]searchcat[% ELSE %]limit[% END %]" value="[% IF ( itemtypeloo.cat == 1 ) %][% itemtypeloo.code %][% ELSE %]mc-[% itemtypeloo.ccl %]:[% itemtypeloo.code %][% END %]"/><label for="[% itemtypeloo.ccl FILTER remove(',') %]-[% itemtypeloo.number %]">[% UNLESS ( noItemTypeImages ) %][% IF ( itemtypeloo.imageurl ) %]<img border="0" src="[% itemtypeloo.imageurl %]" alt="[% itemtypeloo.description %]" />[% END %]&nbsp;[% END %]
+                [% itemtypeloo.description %]</label></td>
+            [% END %]
         [% IF ( loop.last ) %]</tr>[% ELSE %][% UNLESS ( loop.count % 5 ) %]</tr><tr>[% END %][% END %]
     [% END %]
     </table>
diff --git a/opac/opac-search.pl b/opac/opac-search.pl
index 990cbd0..2a58366 100755
--- a/opac/opac-search.pl
+++ b/opac/opac-search.pl
@@ -95,17 +95,17 @@ my ($template,$borrowernumber,$cookie);
 my $template_name;
 my $template_type = 'basic';
 my @params = $cgi->param("limit");
-
+my @searchCategories = $cgi->param('searchcat');
 
 my $format = $cgi->param("format") || '';
 my $build_grouped_results = C4::Context->preference('OPACGroupResults');
 if ($format =~ /(rss|atom|opensearchdescription)/) {
     $template_name = 'opac-opensearch.tmpl';
 }
-elsif (@params && $build_grouped_results) {
+elsif ((@params || @searchCategories) && $build_grouped_results) {
     $template_name = 'opac-results-grouped.tmpl';
 }
-elsif ((@params>=1) || ($cgi->param("q")) || ($cgi->param('multibranchlimit')) || ($cgi->param('limit-yr')) ) {
+elsif (((@params>=1) || (@searchCategories>=1)) || ($cgi->param("q")) || ($cgi->param('multibranchlimit')) || ($cgi->param('limit-yr')) ) {
     $template_name = 'opac-results.tmpl';
 }
 else {
@@ -205,7 +205,7 @@ my $languages_limit_loop = getLanguages($lang, 1);
 $template->param(search_languages_loop => $languages_limit_loop,);
 
 # load the Type stuff
-my $itemtypes = GetItemTypes;
+my $itemtypes = GetItemTypesCategorized;
 # the index parameter is different for item-level itemtypes
 my $itype_or_itemtype = (C4::Context->preference("item-level_itypes"))?'itype':'itemtype';
 my @advancedsearchesloop;
@@ -223,8 +223,13 @@ foreach my $advanced_srch_type (@advanced_search_types) {
                 code => $thisitemtype,
                 description => $itemtypes->{$thisitemtype}->{'description'},
                 imageurl=> getitemtypeimagelocation( 'opac', $itemtypes->{$thisitemtype}->{'imageurl'} ),
+                cat => $itemtypes->{$thisitemtype}->{'iscat'},
+                hideinopac => $itemtypes->{$thisitemtype}->{'hideinopac'},
+                searchcategory => $itemtypes->{$thisitemtype}->{'searchcategory'},
             );
-	    push @itypesloop, \%row;
+            if ( !$itemtypes->{$thisitemtype}->{'hideinopac'} ) {
+                push @itypesloop, \%row;
+            }
 	}
         my %search_code = (  advanced_search_type => $advanced_srch_type,
                              code_loop => \@itypesloop );
@@ -239,6 +244,7 @@ foreach my $advanced_srch_type (@advanced_search_types) {
 				ccl => $advanced_srch_type,
                 code => $thisitemtype->{authorised_value},
                 description => $thisitemtype->{'lib_opac'} || $thisitemtype->{'lib'},
+                searchcategory => $itemtypes->{$thisitemtype}->{'searchcategory'},
                 imageurl => getitemtypeimagelocation( 'opac', $thisitemtype->{'imageurl'} ),
                 );
 		push @authvalueloop, \%row;
@@ -406,6 +412,18 @@ my @limits = $cgi->param('limit');
 my @nolimits = $cgi->param('nolimit');
 my %is_nolimit = map { $_ => 1 } @nolimits;
 @limits = grep { not $is_nolimit{$_} } @limits;
+
+if (@searchCategories > 0) {
+    my @tabcat;
+    foreach my $typecategory (@searchCategories) {
+        push (@tabcat, GetItemTypesByCategory($typecategory));
+    }
+
+    foreach my $itemtypeInCategory (@tabcat) {
+        push (@limits, "mc-$itype_or_itemtype,phr:".$itemtypeInCategory);
+    }
+}
+
 @limits = map { uri_unescape($_) } @limits;
 
 if($params->{'multibranchlimit'}) {
-- 
1.7.9.5