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

(-)a/C4/Search.pm (-2 / +2 lines)
Lines 1216-1222 sub buildQuery { Link Here
1216
    my $query_cgi;
1216
    my $query_cgi;
1217
    my $query_type;
1217
    my $query_type;
1218
1218
1219
    my $limit;
1219
    my $limit = q{};
1220
    my $limit_cgi;
1220
    my $limit_cgi;
1221
    my $limit_desc;
1221
    my $limit_desc;
1222
1222
Lines 1327-1333 sub buildQuery { Link Here
1327
        # This is needed otherwise ccl= and &limit won't work together, and
1327
        # This is needed otherwise ccl= and &limit won't work together, and
1328
        # this happens when selecting a subject on the opac-detail page
1328
        # this happens when selecting a subject on the opac-detail page
1329
        my $original_q = $q; # without available part
1329
        my $original_q = $q; # without available part
1330
        $q .= $limit;
1330
        $q .= $limit if $limit;
1331
        return ( undef, $q, $q, "q=ccl=".uri_escape_utf8($q), $original_q, '', '', '', 'ccl' );
1331
        return ( undef, $q, $q, "q=ccl=".uri_escape_utf8($q), $original_q, '', '', '', 'ccl' );
1332
    }
1332
    }
1333
    if ( $query =~ /^cql=/ ) {
1333
    if ( $query =~ /^cql=/ ) {
(-)a/Koha/Biblio.pm (-8 / +16 lines)
Lines 515-522 sub suggestions { Link Here
515
515
516
  my $components = $self->get_marc_components();
516
  my $components = $self->get_marc_components();
517
517
518
Returns an array of MARCXML data, which are component parts of
518
Returns an array of search results data, which are component parts of
519
this object (MARC21 773$w points to this)
519
this object (MARC21 773 points to this)
520
520
521
=cut
521
=cut
522
522
Lines 525-543 sub get_marc_components { Link Here
525
525
526
    return [] if (C4::Context->preference('marcflavour') ne 'MARC21');
526
    return [] if (C4::Context->preference('marcflavour') ne 'MARC21');
527
527
528
    my $searchstr = $self->get_components_query;
528
    my ( $searchstr, $sort ) = $self->get_components_query;
529
529
530
    my $components;
530
    my $components;
531
    if (defined($searchstr)) {
531
    if (defined($searchstr)) {
532
        my $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::BIBLIOS_INDEX});
532
        my $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::BIBLIOS_INDEX});
533
        my ( $error, $results, $total_hits );
533
        my ( $error, $results, $facets );
534
        eval {
534
        eval {
535
            ( $error, $results, $total_hits ) = $searcher->simple_search_compat( $searchstr, 0, $max_results );
535
            ( $error, $results, $facets ) = $searcher->search_compat( $searchstr, undef, [$sort], ['biblioserver'], $max_results, 0, undef, undef, 'ccl', 0 );
536
        };
536
        };
537
        if( $error || $@ ) {
537
        if( $error || $@ ) {
538
            $error //= q{};
538
            $error //= q{};
539
            $error .= $@ if $@;
539
            $error .= $@ if $@;
540
            warn "Warning from simple_search_compat: '$error'";
540
            warn "Warning from search_compat: '$error'";
541
            $self->add_message(
541
            $self->add_message(
542
                {
542
                {
543
                    type    => 'error',
543
                    type    => 'error',
Lines 546-552 sub get_marc_components { Link Here
546
                }
546
                }
547
            );
547
            );
548
        }
548
        }
549
        $components = $results if defined($results) && @$results;
549
        $components = $results->{biblioserver}->{RECORDS} if defined($results) && $results->{biblioserver}->{hits};
550
    }
550
    }
551
551
552
    return $components // [];
552
    return $components // [];
Lines 564-569 sub get_components_query { Link Here
564
    my $builder = Koha::SearchEngine::QueryBuilder->new(
564
    my $builder = Koha::SearchEngine::QueryBuilder->new(
565
        { index => $Koha::SearchEngine::BIBLIOS_INDEX } );
565
        { index => $Koha::SearchEngine::BIBLIOS_INDEX } );
566
    my $marc = $self->metadata->record;
566
    my $marc = $self->metadata->record;
567
    my $component_sort_field = C4::Context->preference('ComponentSortField') // "title";
568
    my $component_sort_order = C4::Context->preference('ComponentSortOrder') // "asc";
569
    my $sort = $component_sort_field . "_" . $component_sort_order;
567
570
568
    my $searchstr;
571
    my $searchstr;
569
    if ( C4::Context->preference('UseControlNumber') ) {
572
    if ( C4::Context->preference('UseControlNumber') ) {
Lines 596-603 sub get_components_query { Link Here
596
        $cleaned_title = $builder->clean_search_term($cleaned_title);
599
        $cleaned_title = $builder->clean_search_term($cleaned_title);
597
        $searchstr = qq#Host-item:("$cleaned_title")#;
600
        $searchstr = qq#Host-item:("$cleaned_title")#;
598
    }
601
    }
602
    my ($error, $query_str) = $builder->build_query_compat( undef, [$searchstr], undef, undef, [$sort], 0 );
603
    if( $error ){
604
        warn $error;
605
        return;
606
    }
599
607
600
    return $searchstr;
608
    return ($query_str, $sort);
601
}
609
}
602
610
603
=head3 subscriptions
611
=head3 subscriptions
(-)a/catalogue/detail.pl (-1 / +3 lines)
Lines 216-222 if ( $showcomp eq 'both' || $showcomp eq 'staff' ) { Link Here
216
              );
216
              );
217
        }
217
        }
218
        $template->param( ComponentParts => $parts );
218
        $template->param( ComponentParts => $parts );
219
        $template->param( ComponentPartsQuery => $biblio->get_components_query );
219
        my ( $comp_query, $comp_sort ) = $biblio->get_components_query;
220
        my $cpq = $comp_query . "&sort_by=" . $comp_sort;
221
        $template->param( ComponentPartsQuery => $cpq );
220
    }
222
    }
221
} else { # check if we should show analytics anyway
223
} else { # check if we should show analytics anyway
222
    $show_analytics = 1 if $marc_record && @{$biblio->get_marc_components(1)}; # count matters here, results does not
224
    $show_analytics = 1 if $marc_record && @{$biblio->get_marc_components(1)}; # count matters here, results does not
(-)a/installer/data/mysql/atomicupdate/bug_30327_add_sortComponents_syspref.pl (+16 lines)
Line 0 Link Here
1
use Modern::Perl;
2
3
return {
4
    bug_number => "30327",
5
    description => "Add ComponentSortField and ComponentSortOrder sysprefs",
6
    up => sub {
7
        my ($args) = @_;
8
        my ($dbh, $out) = @$args{qw(dbh out)};
9
        $dbh->do(q{
10
            INSERT IGNORE INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES
11
            ('ComponentSortField','title','call_number|pubdate|acqdate|title|author','Specify the default field used for sorting','Choice'),
12
            ('ComponentSortOrder','asc','asc|dsc|az|za','Specify the default sort order','Choice')
13
        });
14
        say $out "Added ComponentSortField and ComponentSortOrder sysprefs";
15
    },
16
};
(-)a/installer/data/mysql/mandatory/sysprefs.sql (+2 lines)
Lines 142-147 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
142
('CoceProviders', '', 'aws,gb,ol', 'Coce providers', 'multiple'),
142
('CoceProviders', '', 'aws,gb,ol', 'Coce providers', 'multiple'),
143
('COinSinOPACResults','1','','If ON, use COinS in OPAC search results page.  NOTE: this can slow down search response time significantly','YesNo'),
143
('COinSinOPACResults','1','','If ON, use COinS in OPAC search results page.  NOTE: this can slow down search response time significantly','YesNo'),
144
('CollapseFieldsPatronAddForm','',NULL,'Collapse these fields by default when adding a new patron. These fields can still be expanded.','Multiple'),
144
('CollapseFieldsPatronAddForm','',NULL,'Collapse these fields by default when adding a new patron. These fields can still be expanded.','Multiple'),
145
('ComponentSortField','title','call_number|pubdate|acqdate|title|author','Specify the default field used for sorting','Choice'),
146
('ComponentSortOrder','asc','asc|dsc|az|za','Specify the default sort order','Choice'),
145
('ConfirmFutureHolds','0','','Number of days for confirming future holds','Integer'),
147
('ConfirmFutureHolds','0','','Number of days for confirming future holds','Integer'),
146
('ConsiderOnSiteCheckoutsAsNormalCheckouts','1',NULL,'Consider on-site checkouts as normal checkouts','YesNo'),
148
('ConsiderOnSiteCheckoutsAsNormalCheckouts','1',NULL,'Consider on-site checkouts as normal checkouts','YesNo'),
147
('CreateAVFromCataloguing', '1', '', 'Ability to create authorized values from the cataloguing module', 'YesNo'),
149
('CreateAVFromCataloguing', '1', '', 'Ability to create authorized values from the cataloguing module', 'YesNo'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/cataloguing.pref (+16 lines)
Lines 280-285 Cataloging: Link Here
280
            - pref: MaxComponentRecords
280
            - pref: MaxComponentRecords
281
            - "records will be displayed."
281
            - "records will be displayed."
282
            - "<br/> UNIMARC is not supported."
282
            - "<br/> UNIMARC is not supported."
283
            - By default, sort component results in the staff interface by
284
            - pref: ComponentSortField
285
              default: title
286
              choices:
287
                  call_number: call number
288
                  pubdate: date of publication
289
                  acqdate: date added
290
                  title: title
291
                  author: author
292
            - ','
293
            - pref: ComponentSortOrder
294
              choices:
295
                  asc: ascending.
296
                  dsc: descending.
297
                  az: from A to Z.
298
                  za: from Z to A.
283
    Importing:
299
    Importing:
284
        -
300
        -
285
            - When matching on ISBN with the record import tool,
301
            - When matching on ISBN with the record import tool,
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt (-1 / +1 lines)
Lines 680-686 Note that permanent location is a code, and location may be an authval. Link Here
680
            [% END %]
680
            [% END %]
681
        </table>
681
        </table>
682
        [% IF ComponentParts.size == Koha.Preference('MaxComponentRecords')%]
682
        [% IF ComponentParts.size == Koha.Preference('MaxComponentRecords')%]
683
        <p>Only [% ComponentParts.size | html %] results are shown: <a href="/cgi-bin/koha/catalogue/search.pl?q=[% ComponentPartsQuery | uri %]"/>show all component parts</a></p>
683
        <p>Only [% ComponentParts.size | html %] results are shown: <a href="/cgi-bin/koha/catalogue/search.pl?q=[% ComponentPartsQuery | url %]"/>show all component parts</a></p>
684
        [% END %]
684
        [% END %]
685
    </div> <!-- /.content_set -->
685
    </div> <!-- /.content_set -->
686
</div> <!-- /#components -->
686
</div> <!-- /#components -->
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-detail.tt (-1 / +1 lines)
Lines 600-606 Link Here
600
                                [% END %]
600
                                [% END %]
601
                            </table>
601
                            </table>
602
                            [% IF ComponentParts.size == Koha.Preference('MaxComponentRecords')%]
602
                            [% IF ComponentParts.size == Koha.Preference('MaxComponentRecords')%]
603
                            <p>Only [% ComponentParts.size | html %] results are shown: <a href="/cgi-bin/koha/opac-search.pl?q=[% ComponentPartsQuery | uri %]"/>show all component parts</a></p>
603
                            <p>Only [% ComponentParts.size | html %] results are shown: <a href="/cgi-bin/koha/opac-search.pl?q=[% ComponentPartsQuery | url %]"/>show all component parts</a></p>
604
                            [% END %]
604
                            [% END %]
605
                        </div>
605
                        </div>
606
                    </div>
606
                    </div>
(-)a/opac/opac-detail.pl (-1 / +3 lines)
Lines 650-656 if ( $showcomp eq 'both' || $showcomp eq 'opac' ) { Link Here
650
              );
650
              );
651
        }
651
        }
652
        $template->param( ComponentParts => $parts );
652
        $template->param( ComponentParts => $parts );
653
        $template->param( ComponentPartsQuery => $biblio->get_components_query );
653
        my ( $comp_query, $comp_sort ) = $biblio->get_components_query;
654
        my $cpq = $comp_query . "&sort_by=" . $comp_sort;
655
        $template->param( ComponentPartsQuery => $cpq );
654
    }
656
    }
655
} else { # check if we should show analytics anyway
657
} else { # check if we should show analytics anyway
656
    $show_analytics = 1 if @{$biblio->get_marc_components(1)}; # count matters here, results does not
658
    $show_analytics = 1 if @{$biblio->get_marc_components(1)}; # count matters here, results does not
(-)a/t/db_dependent/Koha/Biblio.t (-14 / +24 lines)
Lines 521-527 subtest 'get_marc_components() tests' => sub { Link Here
521
    my $host_biblio = Koha::Biblios->find($host_bibnum);
521
    my $host_biblio = Koha::Biblios->find($host_bibnum);
522
    t::lib::Mocks::mock_preference( 'SearchEngine', 'Zebra' );
522
    t::lib::Mocks::mock_preference( 'SearchEngine', 'Zebra' );
523
    my $search_mod = Test::MockModule->new( 'Koha::SearchEngine::Zebra::Search' );
523
    my $search_mod = Test::MockModule->new( 'Koha::SearchEngine::Zebra::Search' );
524
    $search_mod->mock( 'simple_search_compat', \&search_component_record2 );
524
    $search_mod->mock( 'search_compat', \&search_component_record2 );
525
525
526
    my $components = $host_biblio->get_marc_components;
526
    my $components = $host_biblio->get_marc_components;
527
    is( ref($components), 'ARRAY', 'Return type is correct' );
527
    is( ref($components), 'ARRAY', 'Return type is correct' );
Lines 532-539 subtest 'get_marc_components() tests' => sub { Link Here
532
        '->get_marc_components returns an empty ARRAY'
532
        '->get_marc_components returns an empty ARRAY'
533
    );
533
    );
534
534
535
    $search_mod->unmock( 'simple_search_compat');
535
    $search_mod->unmock( 'search_compat');
536
    $search_mod->mock( 'simple_search_compat', \&search_component_record1 );
536
    $search_mod->mock( 'search_compat', \&search_component_record1 );
537
    my $component_record = component_record1()->as_xml();
537
    my $component_record = component_record1()->as_xml();
538
538
539
    is_deeply(
539
    is_deeply(
Lines 541-553 subtest 'get_marc_components() tests' => sub { Link Here
541
        [$component_record],
541
        [$component_record],
542
        '->get_marc_components returns the related component part record'
542
        '->get_marc_components returns the related component part record'
543
    );
543
    );
544
    $search_mod->unmock( 'simple_search_compat');
544
    $search_mod->unmock( 'search_compat');
545
545
546
    $search_mod->mock( 'simple_search_compat',
546
    $search_mod->mock( 'search_compat',
547
        sub { Koha::Exceptions::Exception->throw("error searching analytics") }
547
        sub { Koha::Exceptions::Exception->throw("error searching analytics") }
548
    );
548
    );
549
    warning_like { $components = $host_biblio->get_marc_components }
549
    warning_like { $components = $host_biblio->get_marc_components }
550
        qr{^Warning from simple_search_compat: 'error searching analytics'};
550
        qr{^Warning from search_compat: 'error searching analytics'};
551
551
552
    is_deeply(
552
    is_deeply(
553
        $host_biblio->object_messages,
553
        $host_biblio->object_messages,
Lines 559-593 subtest 'get_marc_components() tests' => sub { Link Here
559
            }
559
            }
560
        ]
560
        ]
561
    );
561
    );
562
    $search_mod->unmock( 'simple_search_compat');
562
    $search_mod->unmock( 'search_compat');
563
563
564
    $schema->storage->txn_rollback;
564
    $schema->storage->txn_rollback;
565
};
565
};
566
566
567
subtest 'get_components_query' => sub {
567
subtest 'get_components_query' => sub {
568
    plan tests => 3;
568
    plan tests => 6;
569
569
570
    my $biblio = $builder->build_sample_biblio();
570
    my $biblio = $builder->build_sample_biblio();
571
    my $biblionumber = $biblio->biblionumber;
571
    my $biblionumber = $biblio->biblionumber;
572
    my $record = $biblio->metadata->record;
572
    my $record = $biblio->metadata->record;
573
573
574
    t::lib::Mocks::mock_preference( 'UseControlNumber', '0' );
574
    t::lib::Mocks::mock_preference( 'UseControlNumber', '0' );
575
    is($biblio->get_components_query, 'Host-item:("Some boring read")', "UseControlNumber disabled");
575
    t::lib::Mocks::mock_preference( 'ComponentSortField', 'author' );
576
    t::lib::Mocks::mock_preference( 'ComponentSortOrder', 'za' );
577
    my ( $comp_q, $comp_s ) = $biblio->get_components_query;
578
    is($comp_q, 'Host-item:("Some boring read")', "UseControlNumber disabled");
579
    is($comp_s, "author_za", "UseControlNumber disabled sort is correct");
576
580
577
    t::lib::Mocks::mock_preference( 'UseControlNumber', '1' );
581
    t::lib::Mocks::mock_preference( 'UseControlNumber', '1' );
582
    t::lib::Mocks::mock_preference( 'ComponentSortOrder', 'az' );
578
    my $marc_001_field = MARC::Field->new('001', $biblionumber);
583
    my $marc_001_field = MARC::Field->new('001', $biblionumber);
579
    $record->append_fields($marc_001_field);
584
    $record->append_fields($marc_001_field);
580
    C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
585
    C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
581
    $biblio = Koha::Biblios->find( $biblio->biblionumber);
586
    $biblio = Koha::Biblios->find( $biblio->biblionumber);
582
587
583
    is($biblio->get_components_query, "(rcn:$biblionumber AND (bib-level:a OR bib-level:b))", "UseControlNumber enabled without MarcOrgCode");
588
    ( $comp_q, $comp_s ) = $biblio->get_components_query;
589
    is($comp_q, "(rcn:$biblionumber AND (bib-level:a OR bib-level:b))", "UseControlNumber enabled without MarcOrgCode");
590
    is($comp_s, "author_az", "UseControlNumber enabled without MarcOrgCode sort is correct");
584
591
585
    my $marc_003_field = MARC::Field->new('003', 'OSt');
592
    my $marc_003_field = MARC::Field->new('003', 'OSt');
586
    $record->append_fields($marc_003_field);
593
    $record->append_fields($marc_003_field);
587
    C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
594
    C4::Biblio::ModBiblio( $record, $biblio->biblionumber );
588
    $biblio = Koha::Biblios->find( $biblio->biblionumber);
595
    $biblio = Koha::Biblios->find( $biblio->biblionumber);
589
596
590
    is($biblio->get_components_query, "(((rcn:$biblionumber AND cni:OSt) OR rcn:\"OSt $biblionumber\") AND (bib-level:a OR bib-level:b))", "UseControlNumber enabled with MarcOrgCode");
597
    t::lib::Mocks::mock_preference( 'ComponentSortField', 'title' );
598
    t::lib::Mocks::mock_preference( 'ComponentSortOrder', 'asc' );
599
    ( $comp_q, $comp_s ) = $biblio->get_components_query;
600
    is($comp_q, "(((rcn:$biblionumber AND cni:OSt) OR rcn:\"OSt $biblionumber\") AND (bib-level:a OR bib-level:b))", "UseControlNumber enabled with MarcOrgCode");
601
    is($comp_s, "title_asc", "UseControlNumber enabled with MarcOrgCode sort if correct");
591
};
602
};
592
603
593
subtest 'orders() and active_orders() tests' => sub {
604
subtest 'orders() and active_orders() tests' => sub {
Lines 883-894 sub component_record1 { Link Here
883
}
894
}
884
sub search_component_record1 {
895
sub search_component_record1 {
885
    my @results = ( component_record1()->as_xml() );
896
    my @results = ( component_record1()->as_xml() );
886
    return ( undef, \@results, 1 );
897
    return ( undef, { biblioserver => { RECORDS => \@results, hits => 1 } }, 1 );
887
}
898
}
888
899
889
sub search_component_record2 {
900
sub search_component_record2 {
890
    my @results;
901
    my @results;
891
    return ( undef, \@results, 0 );
902
    return ( undef, { biblioserver => { RECORDS => \@results, hits => 0 } }, 0 );
892
}
903
}
893
904
894
sub host_record {
905
sub host_record {
895
- 

Return to bug 30327