View | Details | Raw Unified | Return to bug 9055
Collapse All | Expand All

(-)a/C4/Auth.pm (-2 / +2 lines)
Lines 383-389 sub get_template_and_user { Link Here
383
        my $opac_name = '';
383
        my $opac_name = '';
384
        if (($opac_search_limit && $opac_search_limit =~ /branch:(\w+)/ && $opac_limit_override) || ($in->{'query'}->param('limit') && $in->{'query'}->param('limit') =~ /branch:(\w+)/)){
384
        if (($opac_search_limit && $opac_search_limit =~ /branch:(\w+)/ && $opac_limit_override) || ($in->{'query'}->param('limit') && $in->{'query'}->param('limit') =~ /branch:(\w+)/)){
385
            $opac_name = $1;   # opac_search_limit is a branch, so we use it.
385
            $opac_name = $1;   # opac_search_limit is a branch, so we use it.
386
        } elsif (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv && C4::Context->userenv->{'branch'}) {
386
        } elsif (C4::Context->preference("SearchableBranches") ne "all" && C4::Context->userenv && C4::Context->userenv->{'branch'}) {
387
            $opac_name = C4::Context->userenv->{'branch'};
387
            $opac_name = C4::Context->userenv->{'branch'};
388
        }
388
        }
389
        $template->param(
389
        $template->param(
Lines 429-435 sub get_template_and_user { Link Here
429
            RequestOnOpac             => C4::Context->preference("RequestOnOpac"),
429
            RequestOnOpac             => C4::Context->preference("RequestOnOpac"),
430
            'Version'                 => C4::Context->preference('Version'),
430
            'Version'                 => C4::Context->preference('Version'),
431
            hidelostitems             => C4::Context->preference("hidelostitems"),
431
            hidelostitems             => C4::Context->preference("hidelostitems"),
432
            mylibraryfirst            => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
432
            mylibraryfirst            => (C4::Context->preference("SearchableBranches") ne 'all' && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '',
433
            opaclayoutstylesheet      => "" . C4::Context->preference("opaclayoutstylesheet"),
433
            opaclayoutstylesheet      => "" . C4::Context->preference("opaclayoutstylesheet"),
434
            opacbookbag               => "" . C4::Context->preference("opacbookbag"),
434
            opacbookbag               => "" . C4::Context->preference("opacbookbag"),
435
            opaccredits               => "" . C4::Context->preference("opaccredits"),
435
            opaccredits               => "" . C4::Context->preference("opaccredits"),
(-)a/C4/Branch.pm (-1 / +23 lines)
Lines 20-25 use strict; Link Here
20
#use warnings; FIXME - Bug 2505
20
#use warnings; FIXME - Bug 2505
21
require Exporter;
21
require Exporter;
22
use C4::Context;
22
use C4::Context;
23
use C4::Members::Attributes;
24
use C4::Members;
23
25
24
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
26
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
25
27
Lines 109-119 sub GetBranches { Link Here
109
    my $sth;
111
    my $sth;
110
    my $query="SELECT * FROM branches";
112
    my $query="SELECT * FROM branches";
111
    my @bind_parameters;
113
    my @bind_parameters;
114
    my $borrowernumber;
115
    if(C4::Context->userenv){$borrowernumber = C4::Context->userenv->{number}};
116
112
    if ($onlymine && C4::Context->userenv && C4::Context->userenv->{branch}){
117
    if ($onlymine && C4::Context->userenv && C4::Context->userenv->{branch}){
113
      $query .= ' WHERE branchcode = ? ';
118
      $query .= ' WHERE branchcode = ? ';
114
      push @bind_parameters, C4::Context->userenv->{branch};
119
      push @bind_parameters, C4::Context->userenv->{branch};
120
    } elsif(C4::Context->preference("SearchableBranches") eq 'securehome' && C4::Context->userenv && $borrowernumber !=0) { # Preference is set to search only the branches specified in patron record.
121
      my $value = C4::Members::Attributes::GetBorrowerAttributeValue($borrowernumber, "branchcode",1); #calling for an arrayref with all values.
122
      my %borrower_branches;
123
      foreach my $branchcode (@{$value}) {
124
        $borrower_branches{$$branchcode[0]} = 1;
125
      }
126
     my $members = GetMember( 'borrowernumber' => $borrowernumber );
127
     my $homebranch = $members->{'branchcode'};
128
     $borrower_branches{$homebranch} = 1 if $homebranch;
129
     if($ENV{'OPAC_SEARCH_LIMIT'} =~ m/^branch:(.*)/){
130
        $borrower_branches{$1} = 1;
131
     }
132
     my @allowedbranches;
133
     foreach my $branch (keys %borrower_branches) {
134
       push @allowedbranches, $branch;
135
     }
136
     $query .= ' WHERE branchcode in (\''.join('\',\'',@allowedbranches).'\')';;
115
    }
137
    }
116
        $query.=" ORDER BY branchname";
138
    $query.=" ORDER BY branchname";
117
    $sth = $dbh->prepare($query);
139
    $sth = $dbh->prepare($query);
118
    $sth->execute( @bind_parameters );
140
    $sth->execute( @bind_parameters );
119
141
(-)a/C4/Koha.pm (-2 / +2 lines)
Lines 717-723 sub getFacets { Link Here
717
            ];
717
            ];
718
718
719
            my $library_facet;
719
            my $library_facet;
720
            unless ( C4::Context->preference("singleBranchMode") || GetBranchesCount() == 1 ) {
720
            unless ( C4::Context->preference("singleBranchMode") || C4::Branch::GetBranchesCount() == 1 ) {
721
                $library_facet = {
721
                $library_facet = {
722
                    idx  => 'branch',
722
                    idx  => 'branch',
723
                    label => 'Libraries',
723
                    label => 'Libraries',
Lines 779-785 sub getFacets { Link Here
779
            ];
779
            ];
780
780
781
            my $library_facet;
781
            my $library_facet;
782
            unless ( C4::Context->preference("singleBranchMode") || GetBranchesCount() == 1 ) {
782
            unless ( C4::Context->preference("singleBranchMode") || C4::Branch::GetBranchesCount() == 1 ) {
783
                $library_facet = {
783
                $library_facet = {
784
                    idx  => 'branch',
784
                    idx  => 'branch',
785
                    label => 'Libraries',
785
                    label => 'Libraries',
(-)a/C4/Members/Attributes.pm (-5 / +9 lines)
Lines 118-141 sub GetAttributes { Link Here
118
118
119
=head2 GetBorrowerAttributeValue
119
=head2 GetBorrowerAttributeValue
120
120
121
  my $value = C4::Members::Attributes::GetBorrowerAttributeValue($borrowernumber, $attribute_code);
121
  my $value = C4::Members::Attributes::GetBorrowerAttributeValue($borrowernumber, $attribute_code, $as_array);
122
122
123
Retrieve the value of an extended attribute C<$attribute_code> associated with the
123
Retrieve the value of an extended attribute C<$attribute_code> associated with the
124
patron specified by C<$borrowernumber>.
124
patron specified by C<$borrowernumber>. If $as_array is true, gets all available values, and returns it as an arrayref.
125
125
126
=cut
126
=cut
127
127
128
sub GetBorrowerAttributeValue {
128
sub GetBorrowerAttributeValue {
129
    my $borrowernumber = shift;
129
    my $borrowernumber = shift;
130
    my $code = shift;
130
    my $code = shift;
131
131
    my $as_array = shift;
132
    my $dbh = C4::Context->dbh();
132
    my $dbh = C4::Context->dbh();
133
    my $query = "SELECT attribute
133
    my $query = "SELECT attribute
134
                 FROM borrower_attributes
134
                 FROM borrower_attributes
135
                 WHERE borrowernumber = ?
135
                 WHERE borrowernumber = ?
136
                 AND code = ?";
136
                 AND code = ?";
137
    my $value = $dbh->selectrow_array($query, undef, $borrowernumber, $code);
137
    if ($as_array){
138
    return $value;
138
      return $dbh->selectall_arrayref($query,undef, $borrowernumber, $code);
139
    } else {
140
      my $value = $dbh->selectrow_array($query, undef, $borrowernumber, $code);
141
      return $value;
142
    }
139
}
143
}
140
144
141
=head2 SearchIdMatchingAttribute
145
=head2 SearchIdMatchingAttribute
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref (-5 / +7 lines)
Lines 389-399 OPAC: Link Here
389
                  no: Allow
389
                  no: Allow
390
            - patrons to select their branch on the OPAC or show branch names with callnumbers.
390
            - patrons to select their branch on the OPAC or show branch names with callnumbers.
391
        -
391
        -
392
            - pref: SearchMyLibraryFirst
392
             - Search
393
              choices:
393
             - pref: SearchableBranches
394
                  yes: Limit
394
               choices:
395
                  no: "Don't limit"
395
                  all: All libraries
396
            - "patrons' searches to the library they are registered at."
396
                  preferhome: Prefers patrons' home library (others may be searched)
397
                  securehome:  Only patrons' home library and other assigned libraries (if any) can be searched
398
             - Note, Other assigned libraries are only accessable by logged-in users. Otherwise, in Only mode, Home library is set via virtualhost option, and no other libraries are available. Multiple libraries can only be assigned when Multibranch is enabled, found in the Patron syspref page.
397
#        -
399
#        -
398
#            This system preference does not actually affect anything
400
#            This system preference does not actually affect anything
399
#            - pref: OpacBrowser
401
#            - pref: OpacBrowser
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/patrons.pref (+6 lines)
Lines 91-96 Patrons: Link Here
91
           class: integer
91
           class: integer
92
         - characters long.
92
         - characters long.
93
     -
93
     -
94
         - pref: multibranch
95
           choices:
96
                yes: Enable
97
                no: Disable
98
         - assigning multiple branches to a patron.
99
     -
94
         - Show a notice that a patron is about to expire
100
         - Show a notice that a patron is about to expire
95
         - pref: NotifyBorrowerDeparture
101
         - pref: NotifyBorrowerDeparture
96
           class: integer
102
           class: integer
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref (+7 lines)
Lines 78-83 Searching: Link Here
78
            - "fields (separate values with |). Tabs appear in the order listed.<br/>"
78
            - "fields (separate values with |). Tabs appear in the order listed.<br/>"
79
            - "<em>Currently supported values</em>: Item types (<strong>itemtypes</strong>), Collection Codes (<strong>ccode</strong>) and Shelving Location (<strong>loc</strong>)."
79
            - "<em>Currently supported values</em>: Item types (<strong>itemtypes</strong>), Collection Codes (<strong>ccode</strong>) and Shelving Location (<strong>loc</strong>)."
80
        -
80
        -
81
            - pref: MultiBranchSelect
82
              type: boolean
83
              choices:
84
                  yes: Show
85
                  no: "Don't show"
86
            - 'a multibranch dropdown instead of single branch.'
87
        -
81
            - By default,
88
            - By default,
82
            - pref: expandedSearchOption
89
            - pref: expandedSearchOption
83
              type: boolean
90
              type: boolean
(-)a/koha-tmpl/opac-tmpl/prog/en/css/jquery.multiSelect.css (+85 lines)
Line 0 Link Here
1
a.multiSelect {
2
	background: #FFF url(../../images/dropdown.blue.png) right center no-repeat;
3
	border: solid 1px #BBB;
4
	padding-right: 20px;
5
	position: relative;
6
	cursor: default;
7
	text-decoration: none;
8
	color: black;
9
	display: -moz-inline-stack;
10
	display: inline-block;
11
	vertical-align: top;
12
}
13
14
a.multiSelect:link, a.multiSelect:visited, a.multiSelect:hover, a.multiSelect:active {
15
	color: black;
16
	text-decoration: none;
17
}
18
19
a.multiSelect span
20
{
21
	margin: 1px 0px 1px 3px;
22
	overflow: hidden;
23
	display: -moz-inline-stack;
24
	display: inline-block;
25
	white-space: nowrap;
26
}
27
28
a.multiSelect.hover {
29
	background-image: url(../../images/dropdown.blue.hover.png);
30
}
31
32
a.multiSelect.active, 
33
a.multiSelect.focus {
34
	border: inset 1px #000;
35
}
36
37
a.multiSelect.active {
38
	background-image: url(../../images/dropdown.blue.active.png);
39
}
40
41
.multiSelectOptions {
42
	margin-top: -1px;
43
	overflow-y: auto;
44
	overflow-x: hidden;
45
	border: solid 1px #B2B2B2;
46
	background: #FFF;
47
}
48
49
.multiSelectOptions LABEL {
50
	padding: 0px 2px;
51
	display: block;
52
	white-space: nowrap;
53
}
54
55
.multiSelectOptions LABEL.optGroup
56
{
57
	font-weight: bold;
58
}
59
60
.multiSelectOptions .optGroupContainer LABEL
61
{
62
	padding-left: 10px;
63
}
64
65
.multiSelectOptions.optGroupHasCheckboxes .optGroupContainer LABEL
66
{
67
	padding-left: 18px;
68
}
69
70
.multiSelectOptions input{
71
	vertical-align: middle;
72
}
73
74
.multiSelectOptions LABEL.checked {
75
	background-color: #dce5f8;
76
}
77
78
.multiSelectOptions LABEL.selectAll {
79
	border-bottom: dotted 1px #CCC;
80
}
81
82
.multiSelectOptions LABEL.hover {
83
	background-color: #3399ff;
84
	color: white;
85
}
(-)a/koha-tmpl/opac-tmpl/prog/en/js/jquery.bgiframe.min.js (+7 lines)
Line 0 Link Here
1
/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
2
 * Licensed under the MIT License (LICENSE.txt).
3
 *
4
 * Version 2.1.3-pre
5
 */
6
7
(function($){$.fn.bgiframe=($.browser.msie&&/msie 6\.0/i.test(navigator.userAgent)?function(s){s=$.extend({top:'auto',left:'auto',width:'auto',height:'auto',opacity:true,src:'javascript:false;'},s);var html='<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+s.src+'"'+'style="display:block;position:absolute;z-index:-1;'+(s.opacity!==false?'filter:Alpha(Opacity=\'0\');':'')+'top:'+(s.top=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+\'px\')':prop(s.top))+';'+'left:'+(s.left=='auto'?'expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+\'px\')':prop(s.left))+';'+'width:'+(s.width=='auto'?'expression(this.parentNode.offsetWidth+\'px\')':prop(s.width))+';'+'height:'+(s.height=='auto'?'expression(this.parentNode.offsetHeight+\'px\')':prop(s.height))+';'+'"/>';return this.each(function(){if($(this).children('iframe.bgiframe').length===0)this.insertBefore(document.createElement(html),this.firstChild)})}:function(){return this});$.fn.bgIframe=$.fn.bgiframe;function prop(n){return n&&n.constructor===Number?n+'px':n}})(jQuery);
(-)a/koha-tmpl/opac-tmpl/prog/en/js/jquery.multiSelect.js (+554 lines)
Line 0 Link Here
1
/*
2
// jQuery multiSelect
3
//
4
// Version 1.2.2 beta
5
//
6
// Cory S.N. LaViska
7
// A Beautiful Site (http://abeautifulsite.net/)
8
// 09 September 2009
9
//
10
// Visit http://abeautifulsite.net/notebook/62 for more information
11
//
12
// (Amended by Andy Richmond, Letters & Science Deans' Office, University of California, Davis)
13
//
14
// Usage: $('#control_id').multiSelect( options, callback )
15
//
16
// Options:  selectAll          - whether or not to display the Select All option; true/false, default = true
17
//           selectAllText      - text to display for selecting/unselecting all options simultaneously
18
//           noneSelected       - text to display when there are no selected items in the list
19
//           oneOrMoreSelected  - text to display when there are one or more selected items in the list
20
//                                (note: you can use % as a placeholder for the number of items selected).
21
//                                Use * to show a comma separated list of all selected; default = '% selected'
22
//           optGroupSelectable - whether or not optgroups are selectable if you use them; true/false, default = false
23
//           listHeight         - the max height of the droptdown options
24
//
25
// Dependencies:  jQuery 1.2.6 or higher (http://jquery.com/)
26
//
27
// Change Log:
28
//
29
//		1.0.1	- Updated to work with jQuery 1.2.6+ (no longer requires the dimensions plugin)
30
//				- Changed $(this).offset() to $(this).position(), per James' and Jono's suggestions
31
//
32
//		1.0.2	- Fixed issue where dropdown doesn't scroll up/down with keyboard shortcuts
33
//				- Changed '$' in setTimeout to use 'jQuery' to support jQuery.noConflict
34
//				- Renamed from jqueryMultiSelect.* to jquery.multiSelect.* per the standard recommended at
35
//				  http://docs.jquery.com/Plugins/Authoring (does not affect API methods)
36
//
37
//		1.0.3	- Now uses the bgiframe plugin (if it exists) to fix the IE6 layering bug.
38
//              - Forces IE6 to use a min-height of 200px (this needs to be added to the options)
39
//
40
//		1.1.0	- Added the ability to update the options dynamically via javascript: multiSelectOptionsUpdate(JSON)
41
//              - Added a title that displays the whole comma delimited list when using oneOrMoreSelected = *
42
//              - Moved some of the functions to be closured to make them private
43
//              - Changed the way the keyboard navigation worked to more closely match how a standard dropdown works
44
//              - ** by Andy Richmond **
45
//
46
//		1.2.0	- Added support for optgroups
47
//              - Added the ability for selectable optgroups (i.e. select all for an optgroup)
48
//              - ** by Andy Richmond **
49
//
50
//		1.2.1	- Fixed bug where input text overlapped dropdown arrow in IE (i.e. when using oneOrMoreSelected = *)
51
//              - Added option "listHeight" for min-height of the dropdown
52
//              - Fixed bug where bgiframe was causing a horizontal scrollbar and on short lists extra whitespace below the options
53
//              - ** by Andy Richmond **
54
//
55
//		1.2.2	- Fixed bug where the keypress stopped showing the dropdown because in jQuery 1.3.2 they changed the way ':visible' works
56
//              - Fixed some other bugs in the way the keyboard interface worked
57
//              - Changed the main textbox to an <a> tag (with 'display: inline-block') to prevent the display text from being selected/highlighted
58
//              - Added the ability to jump to an option by typing the first character of that option (simular to a normal drop down)
59
//              - ** by Andy Richmond **
60
//				- Added [] to make each control submit an HTML array so $.serialize() works properly
61
//
62
// Licensing & Terms of Use
63
// 
64
// This plugin is dual-licensed under the GNU General Public License and the MIT License and
65
// is copyright 2008 A Beautiful Site, LLC. 
66
//	
67
*/
68
if(jQuery) (function($){
69
	
70
	// render the html for a single option
71
	function renderOption(id, option)
72
	{
73
		var html = '<label><input type="checkbox" name="' + id + '[]" value="' + option.value + '"';
74
		if( option.selected ){
75
			html += ' checked="checked"';
76
		}
77
		html += ' />' + option.text + '</label>';
78
		
79
		return html;
80
	}
81
	
82
	// render the html for the options/optgroups
83
	function renderOptions(id, options, o)
84
	{
85
		var html = "";
86
		
87
		for(var i = 0; i < options.length; i++) {
88
			if(options[i].optgroup) {
89
				html += '<label class="optGroup">';
90
				
91
				if(o.optGroupSelectable) {
92
					html += '<input type="checkbox" class="optGroup" />' + options[i].optgroup;
93
				}
94
				else {
95
					html += options[i].optgroup;
96
				}
97
				
98
				html += '</label><div class="optGroupContainer">';
99
				
100
				html += renderOptions(id, options[i].options, o);
101
				
102
				html += '</div>';
103
			}
104
			else {
105
				html += renderOption(id, options[i]);
106
			}
107
		}
108
		
109
		return html;
110
	}
111
	
112
	// Building the actual options
113
	function buildOptions(options)
114
	{
115
		var multiSelect = $(this);
116
		var multiSelectOptions = multiSelect.next('.multiSelectOptions');
117
		var o = multiSelect.data("config");
118
		var callback = multiSelect.data("callback");
119
120
		// clear the existing options
121
		multiSelectOptions.html("");
122
		var html = "";
123
124
		// if we should have a select all option then add it
125
		if( o.selectAll ) {
126
			html += '<label class="selectAll"><input type="checkbox" class="selectAll" />' + o.selectAllText + '</label>';
127
		}
128
129
		// generate the html for the new options
130
		html += renderOptions(multiSelect.attr('id'), options, o);
131
		
132
		multiSelectOptions.html(html);
133
		
134
		// variables needed to account for width changes due to a scrollbar
135
		var initialWidth = multiSelectOptions.width();
136
		var hasScrollbar = false;
137
		
138
		// set the height of the dropdown options
139
		if(multiSelectOptions.height() > o.listHeight) {
140
			multiSelectOptions.css("height", o.listHeight + 'px');
141
			hasScrollbar = true;
142
		} else {
143
			multiSelectOptions.css("height", '');
144
		}
145
		
146
		// if the there is a scrollbar and the browser did not already handle adjusting the width (i.e. Firefox) then we will need to manaually add the scrollbar width
147
		var scrollbarWidth = hasScrollbar && (initialWidth == multiSelectOptions.width()) ? 17 : 0;
148
149
		// set the width of the dropdown options
150
		if((multiSelectOptions.width() + scrollbarWidth) < multiSelect.outerWidth()) {
151
			multiSelectOptions.css("width", multiSelect.outerWidth() - 2/*border*/ + 'px');
152
		} else {
153
			multiSelectOptions.css("width", (multiSelectOptions.width() + scrollbarWidth) + 'px');
154
		}
155
		
156
		// Apply bgiframe if available on IE6
157
		if( $.fn.bgiframe ) multiSelect.next('.multiSelectOptions').bgiframe( { width: multiSelectOptions.width(), height: multiSelectOptions.height() });
158
159
		// Handle selectAll oncheck
160
		if(o.selectAll) {
161
			multiSelectOptions.find('INPUT.selectAll').click( function() {
162
				// update all the child checkboxes
163
				multiSelectOptions.find('INPUT:checkbox').attr('checked', $(this).attr('checked')).parent("LABEL").toggleClass('checked', $(this).attr('checked'));
164
			});
165
		}
166
		
167
		// Handle OptGroup oncheck
168
		if(o.optGroupSelectable) {
169
			multiSelectOptions.addClass('optGroupHasCheckboxes');
170
		
171
			multiSelectOptions.find('INPUT.optGroup').click( function() {
172
				// update all the child checkboxes
173
				$(this).parent().next().find('INPUT:checkbox').attr('checked', $(this).attr('checked')).parent("LABEL").toggleClass('checked', $(this).attr('checked'));
174
			});
175
		}
176
		
177
		// Handle all checkboxes
178
		multiSelectOptions.find('INPUT:checkbox').click( function() {
179
			// set the label checked class
180
			$(this).parent("LABEL").toggleClass('checked', $(this).attr('checked'));
181
			
182
			updateSelected.call(multiSelect);
183
			multiSelect.focus();
184
			if($(this).parent().parent().hasClass('optGroupContainer')) {
185
				updateOptGroup.call(multiSelect, $(this).parent().parent().prev());
186
			}
187
			if( callback ) {
188
				callback($(this));
189
			}
190
		});
191
		
192
		// Initial display
193
		multiSelectOptions.each( function() {
194
			$(this).find('INPUT:checked').parent().addClass('checked');
195
		});
196
		
197
		// Initialize selected and select all 
198
		updateSelected.call(multiSelect);
199
		
200
		// Initialize optgroups
201
		if(o.optGroupSelectable) {
202
			multiSelectOptions.find('LABEL.optGroup').each( function() {
203
				updateOptGroup.call(multiSelect, $(this));
204
			});
205
		}
206
		
207
		// Handle hovers
208
		multiSelectOptions.find('LABEL:has(INPUT)').hover( function() {
209
			$(this).parent().find('LABEL').removeClass('hover');
210
			$(this).addClass('hover');
211
		}, function() {
212
			$(this).parent().find('LABEL').removeClass('hover');
213
		});
214
		
215
		// Keyboard
216
		multiSelect.keydown( function(e) {
217
		
218
			var multiSelectOptions = $(this).next('.multiSelectOptions');
219
220
			// Is dropdown visible?
221
			if( multiSelectOptions.css('visibility') != 'hidden' ) {
222
				// Dropdown is visible
223
				// Tab
224
				if( e.keyCode == 9 ) {
225
					$(this).addClass('focus').trigger('click'); // esc, left, right - hide
226
					$(this).focus().next(':input').focus();
227
					return true;
228
				}
229
				
230
				// ESC, Left, Right
231
				if( e.keyCode == 27 || e.keyCode == 37 || e.keyCode == 39 ) {
232
					// Hide dropdown
233
					$(this).addClass('focus').trigger('click');
234
				}
235
				// Down || Up
236
				if( e.keyCode == 40 || e.keyCode == 38) {
237
					var allOptions = multiSelectOptions.find('LABEL');
238
					var oldHoverIndex = allOptions.index(allOptions.filter('.hover'));
239
					var newHoverIndex = -1;
240
					
241
					// if there is no current highlighted item then highlight the first item
242
					if(oldHoverIndex < 0) {
243
						// Default to first item
244
						multiSelectOptions.find('LABEL:first').addClass('hover');
245
					}
246
					// else if we are moving down and there is a next item then move
247
					else if(e.keyCode == 40 && oldHoverIndex < allOptions.length - 1)
248
					{
249
						newHoverIndex = oldHoverIndex + 1;
250
					}
251
					// else if we are moving up and there is a prev item then move
252
					else if(e.keyCode == 38 && oldHoverIndex > 0)
253
					{
254
						newHoverIndex = oldHoverIndex - 1;
255
					}
256
257
					if(newHoverIndex >= 0) {
258
						$(allOptions.get(oldHoverIndex)).removeClass('hover'); // remove the current highlight
259
						$(allOptions.get(newHoverIndex)).addClass('hover'); // add the new highlight
260
						
261
						// Adjust the viewport if necessary
262
						adjustViewPort(multiSelectOptions);
263
					}
264
					
265
					return false;
266
				}
267
268
				// Enter, Space
269
				if( e.keyCode == 13 || e.keyCode == 32 ) {
270
					var selectedCheckbox = multiSelectOptions.find('LABEL.hover INPUT:checkbox');
271
					
272
					// Set the checkbox (and label class)
273
					selectedCheckbox.attr('checked', !selectedCheckbox.attr('checked')).parent("LABEL").toggleClass('checked', selectedCheckbox.attr('checked'));
274
					
275
					// if the checkbox was the select all then set all the checkboxes
276
					if(selectedCheckbox.hasClass("selectAll")) {
277
						multiSelectOptions.find('INPUT:checkbox').attr('checked', selectedCheckbox.attr('checked')).parent("LABEL").addClass('checked').toggleClass('checked', selectedCheckbox.attr('checked')); 
278
					}
279
280
					updateSelected.call(multiSelect);
281
					
282
					if( callback ) callback($(this));
283
					return false;
284
				}
285
286
				// Any other standard keyboard character (try and match the first character of an option)
287
				if( e.keyCode >= 33 && e.keyCode <= 126 ) {
288
					// find the next matching item after the current hovered item
289
					var match = multiSelectOptions.find('LABEL:startsWith(' + String.fromCharCode(e.keyCode) + ')');
290
					
291
					var currentHoverIndex = match.index(match.filter('LABEL.hover'));
292
					
293
					// filter the set to any items after the current hovered item
294
					var afterHoverMatch = match.filter(function (index) {
295
						return index > currentHoverIndex;
296
					});
297
298
					// if there were no item after the current hovered item then try using the full search results (filtered to the first one)
299
					match = (afterHoverMatch.length >= 1 ? afterHoverMatch : match).filter("LABEL:first");
300
301
					if(match.length == 1) {
302
						// if we found a match then move the hover
303
						multiSelectOptions.find('LABEL.hover').removeClass('hover');								
304
						match.addClass('hover');
305
						
306
						adjustViewPort(multiSelectOptions);
307
					}
308
				}
309
			} else {
310
				// Dropdown is not visible
311
				if( e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13 || e.keyCode == 32 ) { //up, down, enter, space - show
312
					// Show dropdown
313
					$(this).removeClass('focus').trigger('click');
314
					multiSelectOptions.find('LABEL:first').addClass('hover');
315
					return false;
316
				}
317
				//  Tab key
318
				if( e.keyCode == 9 ) {
319
					// Shift focus to next INPUT element on page
320
					multiSelectOptions.next(':input').focus();
321
					return true;
322
				}
323
			}
324
			// Prevent enter key from submitting form
325
			if( e.keyCode == 13 ) return false;
326
		});
327
	}
328
	
329
	// Adjust the viewport if necessary
330
	function adjustViewPort(multiSelectOptions)
331
	{
332
		// check for and move down
333
		var selectionBottom = multiSelectOptions.find('LABEL.hover').position().top + multiSelectOptions.find('LABEL.hover').outerHeight();
334
		
335
		if(selectionBottom > multiSelectOptions.innerHeight()){		
336
			multiSelectOptions.scrollTop(multiSelectOptions.scrollTop() + selectionBottom - multiSelectOptions.innerHeight());
337
		}
338
		
339
		// check for and move up						
340
		if(multiSelectOptions.find('LABEL.hover').position().top < 0){		
341
			multiSelectOptions.scrollTop(multiSelectOptions.scrollTop() + multiSelectOptions.find('LABEL.hover').position().top);
342
		}
343
	}
344
	
345
	// Update the optgroup checked status
346
	function updateOptGroup(optGroup)
347
	{
348
		var multiSelect = $(this);
349
		var o = multiSelect.data("config");
350
		
351
		// Determine if the optgroup should be checked
352
		if(o.optGroupSelectable) {
353
			var optGroupSelected = true;
354
			$(optGroup).next().find('INPUT:checkbox').each( function() {
355
				if( !$(this).attr('checked') ) {
356
					optGroupSelected = false;
357
					return false;
358
				}
359
			});
360
			
361
			$(optGroup).find('INPUT.optGroup').attr('checked', optGroupSelected).parent("LABEL").toggleClass('checked', optGroupSelected);
362
		}
363
	}
364
	
365
	// Update the textbox with the total number of selected items, and determine select all
366
	function updateSelected() {
367
		var multiSelect = $(this);
368
		var multiSelectOptions = multiSelect.next('.multiSelectOptions');
369
		var o = multiSelect.data("config");
370
		
371
		var i = 0;
372
		var selectAll = true;
373
		var display = '';
374
		multiSelectOptions.find('INPUT:checkbox').not('.selectAll, .optGroup').each( function() {
375
			if( $(this).attr('checked') ) {
376
				i++;
377
				display = display + $(this).parent().text() + ', ';
378
			}
379
			else selectAll = false;
380
		});
381
		
382
		// trim any end comma and surounding whitespace
383
		display = display.replace(/\s*\,\s*$/,'');
384
		
385
		if( i == 0 ) {
386
			multiSelect.find("span").html( o.noneSelected );
387
		} else {
388
                  if( o.TwoOrMoreSelected != undefined )  {
389
                    if( i == 1 ) {
390
                       multiSelect.find("span").html( display );
391
                       multiSelect.attr( "title", display );
392
                    }
393
                    else {
394
                       multiSelect.find("span").html( o.TwoOrMoreSelected.replace('%', i) );
395
                    }
396
                }
397
                    if( o.oneOrMoreSelected == '*' ) {
398
                       multiSelect.find("span").html( display );
399
                       multiSelect.attr( "title", display );
400
                    } else {
401
                       if ( o.oneOrMoreSelected != "^"){
402
                          multiSelect.find("span").html( o.oneOrMoreSelected.replace('%', i) );
403
                       }
404
                    }
405
                }
406
407
		// Determine if Select All should be checked
408
		if(o.selectAll) {
409
			multiSelectOptions.find('INPUT.selectAll').attr('checked', selectAll).parent("LABEL").toggleClass('checked', selectAll);
410
		}
411
	}
412
	
413
	$.extend($.fn, {
414
		multiSelect: function(o, callback) {
415
			// Default options
416
			if( !o ) o = {};
417
			if( o.selectAll == undefined ) o.selectAll = true;
418
			if( o.selectAllText == undefined ) o.selectAllText = "Select All";
419
			if( o.noneSelected == undefined ) o.noneSelected = 'Select options';
420
			if( o.oneOrMoreSelected == undefined ) o.oneOrMoreSelected = '% selected';
421
			if( o.optGroupSelectable == undefined ) o.optGroupSelectable = false;
422
			if( o.listHeight == undefined ) o.listHeight = 150;
423
424
			// Initialize each multiSelect
425
			$(this).each( function() {
426
				var select = $(this);
427
				var html = '<a href="javascript:;" class="multiSelect"><span></span></a>';
428
				html += '<div class="multiSelectOptions" style="position: absolute; z-index: 99999; visibility: hidden;"></div>';
429
				$(select).after(html);
430
				
431
				var multiSelect = $(select).next('.multiSelect');
432
				var multiSelectOptions = multiSelect.next('.multiSelectOptions');
433
				
434
				// if the select object had a width defined then match the new multilsect to it
435
				multiSelect.find("span").css("width", $(select).width() + 'px');
436
				
437
				// Attach the config options to the multiselect
438
				multiSelect.data("config", o);
439
				
440
				// Attach the callback to the multiselect
441
				multiSelect.data("callback", callback);
442
				
443
				// Serialize the select options into json options
444
				var options = [];
445
				$(select).children().each( function() {
446
					if(this.tagName.toUpperCase() == 'OPTGROUP')
447
					{
448
						var suboptions = [];
449
						options.push({ optgroup: $(this).attr('label'), options: suboptions });
450
						
451
						$(this).children('OPTION').each( function() {
452
							if( $(this).val() != '' ) {
453
								suboptions.push({ text: $(this).html(), value: $(this).val(), selected: $(this).attr('selected') });
454
							}
455
						});
456
					}
457
					else if(this.tagName.toUpperCase() == 'OPTION')
458
					{
459
						if( $(this).val() != '' ) {
460
							options.push({ text: $(this).html(), value: $(this).val(), selected: $(this).attr('selected') });
461
						}
462
					}
463
				});
464
				
465
				// Eliminate the original form element
466
				$(select).remove();
467
				
468
				// Add the id that was on the original select element to the new input
469
				multiSelect.attr("id", $(select).attr("id"));
470
				
471
				// Build the dropdown options
472
				buildOptions.call(multiSelect, options);
473
474
				// Events
475
				multiSelect.hover( function() {
476
					$(this).addClass('hover');
477
				}, function() {
478
					$(this).removeClass('hover');
479
				}).click( function() {
480
					// Show/hide on click
481
					if( $(this).hasClass('active') ) {
482
						$(this).multiSelectOptionsHide();
483
					} else {
484
						$(this).multiSelectOptionsShow();
485
					}
486
					return false;
487
				}).focus( function() {
488
					// So it can be styled with CSS
489
					$(this).addClass('focus');
490
				}).blur( function() {
491
					// So it can be styled with CSS
492
					$(this).removeClass('focus');
493
				});
494
				
495
				// Add an event listener to the window to close the multiselect if the user clicks off
496
				$(document).click( function(event) {
497
					// If somewhere outside of the multiselect was clicked then hide the multiselect
498
					if(!($(event.target).parents().andSelf().is('.multiSelectOptions'))){
499
						multiSelect.multiSelectOptionsHide();
500
					}
501
				});
502
			});
503
		},
504
		
505
		// Update the dropdown options
506
		multiSelectOptionsUpdate: function(options) {
507
			buildOptions.call($(this), options);
508
		},
509
		
510
		// Hide the dropdown
511
		multiSelectOptionsHide: function() {
512
			$(this).removeClass('active').removeClass('hover').next('.multiSelectOptions').css('visibility', 'hidden');
513
		},
514
		
515
		// Show the dropdown
516
		multiSelectOptionsShow: function() {
517
			var multiSelect = $(this);
518
			var multiSelectOptions = multiSelect.next('.multiSelectOptions');
519
			var o = multiSelect.data("config");
520
		
521
			// Hide any open option boxes
522
			$('.multiSelect').multiSelectOptionsHide();
523
			multiSelectOptions.find('LABEL').removeClass('hover');
524
			multiSelect.addClass('active').next('.multiSelectOptions').css('visibility', 'visible');
525
			multiSelect.focus();
526
			
527
			// reset the scroll to the top
528
			multiSelect.next('.multiSelectOptions').scrollTop(0);
529
530
			// Position it
531
			var offset = multiSelect.position();
532
			multiSelect.next('.multiSelectOptions').css({ top:  offset.top + $(this).outerHeight() + 'px' });
533
			multiSelect.next('.multiSelectOptions').css({ left: offset.left + 'px' });
534
		},
535
		
536
		// get a coma-delimited list of selected values
537
		selectedValuesString: function() {
538
			var selectedValues = "";
539
			$(this).next('.multiSelectOptions').find('INPUT:checkbox:checked').not('.optGroup, .selectAll').each(function() {
540
				selectedValues += $(this).attr('value') + ",";
541
			});
542
			// trim any end comma and surounding whitespace
543
			return selectedValues.replace(/\s*\,\s*$/,'');
544
		}		
545
	});
546
	
547
	// add a new ":startsWith" search filter
548
	$.expr[":"].startsWith = function(el, i, m) {
549
		var search = m[3];        
550
		if (!search) return false;
551
		return eval("/^[/s]*" + search + "/i").test($(el).text());
552
	};
553
	
554
})(jQuery);
(-)a/koha-tmpl/opac-tmpl/prog/en/modules/opac-advsearch.tt (+12 lines)
Lines 2-10 Link Here
2
[% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %]
2
[% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %]
3
    catalog &rsaquo; Advanced search
3
    catalog &rsaquo; Advanced search
4
[% INCLUDE 'doc-head-close.inc' %]
4
[% INCLUDE 'doc-head-close.inc' %]
5
[% IF (MultiBranchSelect) %]
6
<script type="text/javascript" language="javascript" src="[% themelang %]/js/jquery.bgiframe.min.js"></script>
7
<script type="text/javascript" language="javascript" src="[% themelang %]/js/jquery.multiSelect.js"></script>
8
<link href="[% themelang %]/css/jquery.multiSelect.css" rel="stylesheet" type="text/css" />
9
[% END %]
5
<script type="text/javascript" language="javascript">//<![CDATA[
10
<script type="text/javascript" language="javascript">//<![CDATA[
6
            $(document).ready(function() {
11
            $(document).ready(function() {
7
            $('#advsearches').tabs();
12
            $('#advsearches').tabs();
13
            [% IF (MultiBranchSelect) %]
14
            $('#branchloop').multiSelect({ TwoOrMoreSelected: 'Multiple Libraries', oneOrMoreSelected: '^' });
15
            [% END %]
8
});
16
});
9
    //]]>
17
    //]]>
10
</script>
18
</script>
Lines 268-274 Link Here
268
    <div id="location" class="container"><fieldset><legend>Location and availability: </legend>
276
    <div id="location" class="container"><fieldset><legend>Location and availability: </legend>
269
277
270
        <label for="branchloop">Library:</label>
278
        <label for="branchloop">Library:</label>
279
        [% IF ( MultiBranchSelect ) %]
280
        <select name="limit" id="branchloop" multiple="multiple"> 
281
        [% ELSE %]
271
        <select name="limit" id="branchloop">
282
        <select name="limit" id="branchloop">
283
        [% END %]
272
        <option value="">All libraries</option>
284
        <option value="">All libraries</option>
273
        [% FOREACH BranchesLoo IN BranchesLoop %]
285
        [% FOREACH BranchesLoo IN BranchesLoop %]
274
        [% IF ( BranchesLoo.selected ) %]
286
        [% IF ( BranchesLoo.selected ) %]
(-)a/opac/opac-search.pl (-4 / +57 lines)
Lines 51-57 use C4::Tags qw(get_tags); Link Here
51
use C4::Branch; # GetBranches
51
use C4::Branch; # GetBranches
52
use C4::SocialData;
52
use C4::SocialData;
53
use C4::Ratings;
53
use C4::Ratings;
54
54
use C4::Members;
55
use C4::Members::Attributes;
55
use POSIX qw(ceil floor strftime);
56
use POSIX qw(ceil floor strftime);
56
use URI::Escape;
57
use URI::Escape;
57
use Storable qw(thaw freeze);
58
use Storable qw(thaw freeze);
Lines 293-298 if ( $template_type && $template_type eq 'advsearch' ) { Link Here
293
            $template->param( expanded_options => $cgi->param('expanded_options'));
294
            $template->param( expanded_options => $cgi->param('expanded_options'));
294
        }
295
        }
295
    }
296
    }
297
    
298
    # If Multiple Branch Select is enabled, tell the template.
299
    if (C4::Context->preference("MultiBranchSelect")){
300
       $template->param( MultiBranchSelect => 1 )
301
    }
296
    output_html_with_http_headers $cgi, $cookie, $template->output;
302
    output_html_with_http_headers $cgi, $cookie, $template->output;
297
    exit;
303
    exit;
298
}
304
}
Lines 374-391 if ($operands[0] && !$operands[1]) { Link Here
374
380
375
# limits are use to limit to results to a pre-defined category such as branch or language
381
# limits are use to limit to results to a pre-defined category such as branch or language
376
my @limits = $cgi->param('limit');
382
my @limits = $cgi->param('limit');
377
383
my @finalbranches;
384
my @branches;
378
if($params->{'multibranchlimit'}) {
385
if($params->{'multibranchlimit'}) {
379
    push @limits, '('.join( " or ", map { "branch: $_ " } @{ GetBranchesInCategory( $params->{'multibranchlimit'} ) } ).')';
386
  @branches = @{GetBranchesInCategory($params->{'multibranchlimit'})};
380
}
387
}
381
388
382
my $available;
389
my $available;
390
my @newlimit;
383
foreach my $limit(@limits) {
391
foreach my $limit(@limits) {
384
    if ($limit =~/available/) {
392
    if ($limit =~/available/) {
385
        $available = 1;
393
        $available = 1;
386
    }
394
    }
395
    if ($limit =~ m/^branch:(.*)/){
396
      push @branches, $1;
397
    } else {
398
      push @newlimit, $limit;
399
    }
387
}
400
}
401
@limits = @newlimit;
402
403
my @branchloop = $params->{'branchloop[]'};
404
foreach my $line(@branchloop){
405
 if ($line =~ m/^branch:(.*)/){
406
   push @branches, $1;
407
 }
408
}
409
388
$template->param(available => $available);
410
$template->param(available => $available);
411
my @finalbranches;
412
if(C4::Context->preference("SearchableBranches") ne 'all') {
413
if(C4::Context->preference("SearchableBranches") eq 'securehome') { # Preference is set to search only the branches specified in patron record.
414
   my $value = C4::Members::Attributes::GetBorrowerAttributeValue($borrowernumber, "branchcode",1); #calling for an arrayref with all values.
415
   my %borrower_branches;
416
     foreach my $branchcode (@{$value}) {
417
       $borrower_branches{$$branchcode[0]} = 1;
418
     }
419
   my $members = GetMember( 'borrowernumber' => $borrowernumber );
420
   my $homebranch = $members->{'branchcode'};
421
   $borrower_branches{$homebranch} = 1 if $homebranch;
422
   if($ENV{'OPAC_SEARCH_LIMIT'} =~ m/^branch:(.*)/){
423
      $borrower_branches{$1} = 1;
424
   }
425
426
   foreach my $branchcode (@branches) {
427
      if ($borrower_branches{$branchcode}) {push @finalbranches, $branchcode};
428
  }
429
430
  if (!@finalbranches) {
431
    foreach my $branchcode (keys %borrower_branches) {
432
      warn $branchcode;
433
      push @finalbranches, $branchcode;
434
    }
435
  } 
436
}
437
 else { 
438
  push (@finalbranches, @branches); 
439
  }
440
} else {push (@finalbranches, @branches);}
441
push @limits, "(". join(" or ", map { "branch: $_ "}  @finalbranches) .")" if @finalbranches;
442
389
443
390
# append year limits if they exist
444
# append year limits if they exist
391
if ($params->{'limit-yr'}) {
445
if ($params->{'limit-yr'}) {
392
- 

Return to bug 9055