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

(-)a/C4/Suggestions.pm (+42 lines)
Lines 27-32 use C4::Context; Link Here
27
use C4::Output;
27
use C4::Output;
28
use C4::Debug;
28
use C4::Debug;
29
use C4::Letters;
29
use C4::Letters;
30
use C4::Biblio qw( GetMarcFromKohaField );
30
use Koha::DateUtils;
31
use Koha::DateUtils;
31
32
32
use List::MoreUtils qw(any);
33
use List::MoreUtils qw(any);
Lines 48-53 our @EXPORT = qw( Link Here
48
  SearchSuggestion
49
  SearchSuggestion
49
  DelSuggestionsOlderThan
50
  DelSuggestionsOlderThan
50
  GetUnprocessedSuggestions
51
  GetUnprocessedSuggestions
52
  MarcRecordFromSuggestion
51
);
53
);
52
54
53
=head1 NAME
55
=head1 NAME
Lines 613-618 sub GetUnprocessedSuggestions { Link Here
613
    return $s;
615
    return $s;
614
}
616
}
615
617
618
=head2 MarcRecordFromSuggestion
619
620
    $record = MarcRecordFromSuggestion ( $suggestion )
621
622
This function build a marc record object from a suggestion
623
624
=cut
625
626
sub MarcRecordFromSuggestion {
627
    my ($suggestion) = @_;
628
    my $record = MARC::Record->new();
629
630
    my ($title_tag, $title_subfield) = GetMarcFromKohaField('biblio.title', '');
631
    $record->append_fields(
632
        MARC::Field->new($title_tag, ' ', ' ', $title_subfield => $suggestion->{title})
633
    );
634
635
    my ($author_tag, $author_subfield) = GetMarcFromKohaField('biblio.author', '');
636
    if ($record->field( $author_tag )) {
637
        $record->field( $author_tag )->add_subfields( $author_subfield => $suggestion->{author} );
638
    }
639
    else {
640
        $record->append_fields(
641
            MARC::Field->new($author_tag, ' ', ' ', $author_subfield => $suggestion->{author})
642
        );
643
    }
644
645
    my ($it_tag, $it_subfield) = GetMarcFromKohaField('biblioitems.itemtype', '');
646
    if ($record->field( $it_tag )) {
647
        $record->field( $it_tag )->add_subfields( $it_subfield => $suggestion->{itemtype} );
648
    }
649
    else {
650
        $record->append_fields(
651
            MARC::Field->new($it_tag, ' ', ' ', $it_subfield => $suggestion->{author})
652
        );
653
    }
654
655
    return $record;
656
}
657
616
1;
658
1;
617
__END__
659
__END__
618
660
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-suggestions.tt (-12 / +28 lines)
Lines 32-52 Link Here
32
                            <p>Please fill out this form to make a purchase suggestion. You will receive an email when the library processes your suggestion</p>
32
                            <p>Please fill out this form to make a purchase suggestion. You will receive an email when the library processes your suggestion</p>
33
                            <p>Only the title is required, but the more information you enter the easier it will be for the librarians to find the title you're requesting. The "Notes" field can be used to provide any additional information.</p>
33
                            <p>Only the title is required, but the more information you enter the easier it will be for the librarians to find the title you're requesting. The "Notes" field can be used to provide any additional information.</p>
34
34
35
                            [% FOR m IN messages %]
36
                                <div class="alert alert-[% m.type %]">
37
                                    [% SWITCH m.code %]
38
                                    [% CASE 'biblio_exists' %]
39
                                        A similar document already exists: <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% m.id %]">[% m.title %]</a>. Click on "Confirm your suggestion" to ignore this message.
40
                                    [% CASE %]
41
                                        [% m.code %]
42
                                    [% END %]
43
                                </div>
44
                            [% END %]
45
35
                            <form action="/cgi-bin/koha/opac-suggestions.pl" method="post">
46
                            <form action="/cgi-bin/koha/opac-suggestions.pl" method="post">
36
                                <fieldset class="rows">
47
                                <fieldset class="rows">
37
                                    <ol>
48
                                    <ol>
38
                                        <li><label class="required" for="title">Title:</label><input type="text" id="title" name="title" class="span6" maxlength="255" /></li>
49
                                        <li><label class="required" for="title">Title:</label><input type="text" id="title" name="title" class="span6" maxlength="255" value="[% title %]" /></li>
39
                                        <li><label for="author">Author:</label><input type="text" id="author" name="author" class="span6" maxlength="80" /></li>
50
                                        <li><label for="author">Author:</label><input type="text" id="author" name="author" class="span6" maxlength="80" value="[% author %]" /></li>
40
                                        <li><label for="copyrightdate">Copyright date:</label><input type="text" id="copyrightdate" name="copyrightdate" size="4" maxlength="4" /></li>
51
                                        <li><label for="copyrightdate">Copyright date:</label><input type="text" id="copyrightdate" name="copyrightdate" size="4" maxlength="4" value="[% copyrightdate %]" /></li>
41
                                        <li><label for="isbn">Standard number (ISBN, ISSN or other):</label><input type="text" id="isbn" name="isbn"  maxlength="80" /></li>
52
                                        <li><label for="isbn">Standard number (ISBN, ISSN or other):</label><input type="text" id="isbn" name="isbn"  maxlength="80" value="[% isbn %]" /></li>
42
                                        <li><label for="publishercode">Publisher:</label><input type="text" id="publishercode" name="publishercode" class="span6" maxlength="80" /></li>
53
                                        <li><label for="publishercode">Publisher:</label><input type="text" id="publishercode" name="publishercode" class="span6" maxlength="80" value="[% publishercode %]"/></li>
43
                                        <li><label for="collectiontitle">Collection title:</label><input type="text" id="collectiontitle" name="collectiontitle" class="span6" maxlength="80" /></li>
54
                                        <li><label for="collectiontitle">Collection title:</label><input type="text" id="collectiontitle" name="collectiontitle" class="span6" maxlength="80" value="[% collectiontitle %]"/></li>
44
                                        <li><label for="place">Publication place:</label><input type="text" id="place" name="place"  maxlength="80" /></li>
55
                                        <li><label for="place">Publication place:</label><input type="text" id="place" name="place"  maxlength="80" valu="[% place %]" /></li>
45
                                        <li><label for="itemtype">Item type:</label>
56
                                        <li><label for="itemtype">Item type:</label>
46
                                            <select name="itemtype" id="itemtype">
57
                                            <select name="itemtype" id="itemtype">
47
                                                <option value="">Default</option>
58
                                                <option value="">Default</option>
48
                                                [% FOREACH itemtypeloo IN itemtypeloop %]
59
                                                [% FOREACH itemtypeloo IN itemtypeloop %]
49
                                                    [% IF ( itemtypeloo.selected ) %]
60
                                                    [% IF ( itemtypeloo.itemtype == itemtype ) %]
50
                                                        <option value="[% itemtypeloo.itemtype %]" selected="selected">
61
                                                        <option value="[% itemtypeloo.itemtype %]" selected="selected">
51
                                                    [% ELSE %]
62
                                                    [% ELSE %]
52
                                                        <option value="[% itemtypeloo.itemtype %]">
63
                                                        <option value="[% itemtypeloo.itemtype %]">
Lines 60-66 Link Here
60
                                            <li><label for="branch">Library:</label>
71
                                            <li><label for="branch">Library:</label>
61
                                                <select name="branchcode" id="branch">
72
                                                <select name="branchcode" id="branch">
62
                                                    [% FOREACH branchloo IN branchloop %]
73
                                                    [% FOREACH branchloo IN branchloop %]
63
                                                        [% IF ( branchloo.selected ) %]
74
                                                        [% IF ( branchloo.branchcode == branchcode ) %]
64
                                                            <option value="[% branchloo.branchcode %]" selected="selected">[% branchloo.branchname %]</option>
75
                                                            <option value="[% branchloo.branchcode %]" selected="selected">[% branchloo.branchname %]</option>
65
                                                        [% ELSE %]
76
                                                        [% ELSE %]
66
                                                            <option value="[% branchloo.branchcode %]">[% branchloo.branchname %]</option>
77
                                                            <option value="[% branchloo.branchcode %]">[% branchloo.branchname %]</option>
Lines 82-88 Link Here
82
                                        [% END %]
93
                                        [% END %]
83
                                        <li>
94
                                        <li>
84
                                            <label for="note">Notes:</label>
95
                                            <label for="note">Notes:</label>
85
                                            <textarea name="note" id="note" rows="5" cols="40"></textarea>
96
                                            <textarea name="note" id="note" rows="5" cols="40">[% note %]</textarea>
86
                                        </li>
97
                                        </li>
87
98
88
                                        <!--  Add a hidden 'negcap' field -->
99
                                        <!--  Add a hidden 'negcap' field -->
Lines 94-101 Link Here
94
                                </fieldset>
105
                                </fieldset>
95
                                <fieldset class="action">
106
                                <fieldset class="action">
96
                                    <input type="hidden" name="suggested_by_anyone" value="[% suggested_by_anyone %]" />
107
                                    <input type="hidden" name="suggested_by_anyone" value="[% suggested_by_anyone %]" />
97
                                    <input type="hidden" name="op" value="add_confirm" />
108
                                    <input type="hidden" name="op" value="add_validate" />
98
                                    <input type="submit" onclick="Check(this.form); return false;" class="btn" value="Submit your suggestion" /> <a class="action" href="/cgi-bin/koha/opac-suggestions.pl">Cancel</a>
109
                                    [% IF ( need_confirm ) %]
110
                                    <input type="submit" onclick="this.form.op.value = 'add_confirm'; this.form.submit();" class="btn" value="Confirm your suggestion" />
111
                                    [% ELSE %]
112
                                    <input type="submit" onclick="Check(this.form); return false;" class="btn" value="Submit your suggestion" />
113
                                    [% END %]
114
                                    <a class="action" href="/cgi-bin/koha/opac-suggestions.pl">Cancel</a>
99
                                </fieldset>
115
                                </fieldset>
100
                            </form>
116
                            </form>
101
                        [% END #  IF op_add %]
117
                        [% END #  IF op_add %]
(-)a/opac/opac-suggestions.pl (-23 / +31 lines)
Lines 29-34 use C4::Suggestions; Link Here
29
use C4::Koha;
29
use C4::Koha;
30
use C4::Dates;
30
use C4::Dates;
31
use C4::Scrubber;
31
use C4::Scrubber;
32
use C4::Search qw( FindDuplicate );
32
33
33
use Koha::DateUtils qw( dt_from_string );
34
use Koha::DateUtils qw( dt_from_string );
34
35
Lines 38-43 my $suggestion = $input->Vars; Link Here
38
delete $suggestion->{negcap};
39
delete $suggestion->{negcap};
39
my $negcaptcha      = $input->param('negcap');
40
my $negcaptcha      = $input->param('negcap');
40
my $suggested_by_anyone = $input->param('suggested_by_anyone') || 0;
41
my $suggested_by_anyone = $input->param('suggested_by_anyone') || 0;
42
my $need_confirm    = 0;
41
43
42
# If a spambot accidentally populates the 'negcap' field in the sugesstions form, then silently skip and return.
44
# If a spambot accidentally populates the 'negcap' field in the sugesstions form, then silently skip and return.
43
if ($negcaptcha ) {
45
if ($negcaptcha ) {
Lines 51-57 if ( ! C4::Context->preference('suggestion') ) { Link Here
51
    exit;
53
    exit;
52
}
54
}
53
55
54
delete $suggestion->{$_} foreach qw<op suggested_by_anyone>;
56
delete $suggestion->{$_} foreach qw<op suggested_by_anyone confirm>;
55
$op = 'else' unless $op;
57
$op = 'else' unless $op;
56
58
57
my ( $template, $borrowernumber, $cookie, @messages );
59
my ( $template, $borrowernumber, $cookie, @messages );
Lines 109-143 if ( $op eq 'else' ) { Link Here
109
    }
111
    }
110
}
112
}
111
113
112
my $suggestions_loop =
114
if ( $op eq "add_validate" ) {
113
  &SearchSuggestion( $suggestion);
115
    $op = 'add_confirm';
114
if ( $op eq "add_confirm" ) {
116
    my $suggestions = &SearchSuggestion($suggestion);
115
	if (@$suggestions_loop>=1){
117
    my $biblio = MarcRecordFromSuggestion($suggestion);
118
	if (@$suggestions >=1 ){
116
		#some suggestion are answering the request Donot Add
119
		#some suggestion are answering the request Donot Add
117
        for my $suggestion ( @$suggestions_loop ) {
120
        for my $suggestion ( @$suggestions ) {
118
            push @messages, { type => 'error', code => 'already_exists', id => $suggestion->{suggestionid} };
121
            push @messages, { type => 'error', code => 'already_exists', id => $suggestion->{suggestionid} };
119
            last;
122
            last;
120
        }
123
        }
124
        $op = 'else';
121
	}
125
	}
122
	else {
126
    elsif (my ($duplicatebiblionumber, $duplicatetitle) = FindDuplicate($biblio)) {
123
		my $scrubber = C4::Scrubber->new();
127
        push @messages, { type => 'error', code => 'biblio_exists', id => $duplicatebiblionumber, title => $duplicatetitle };
124
		foreach my $suggest (keys %$suggestion){
128
        $need_confirm = 1;
125
            # Don't know why the encode is needed for Perl v5.10 here
129
        $op = 'add';
126
            $suggestion->{$suggest} = Encode::encode("utf8", $scrubber->scrub($suggestion->{$suggest}) );
130
    }
127
		}
131
}
128
        $suggestion->{suggesteddate} = dt_from_string;
132
129
        $suggestion->{branchcode} = $input->param('branchcode') || C4::Context->userenv->{"branch"};
133
if ($op eq "add_confirm") {
130
134
    my $scrubber = C4::Scrubber->new();
131
		&NewSuggestion($suggestion);
135
    foreach my $suggest (keys %$suggestion) {
132
		# empty fields, to avoid filter in "SearchSuggestion"
136
        # Don't know why the encode is needed for Perl v5.10 here
133
		$$suggestion{$_}='' foreach qw<title author publishercode copyrightdate place collectiontitle isbn STATUS>;
137
        $suggestion->{$suggest} = Encode::encode("utf8", $scrubber->scrub($suggestion->{$suggest}) );
134
		$suggestions_loop =
138
    }
135
		   &SearchSuggestion( $suggestion );
139
    $suggestion->{suggesteddate} = dt_from_string;
136
        push @messages, { type => 'info', code => 'success_on_inserted' };
140
    $suggestion->{branchcode} = $input->param('branchcode') || C4::Context->userenv->{"branch"};
137
	}
141
142
    &NewSuggestion($suggestion);
143
    push @messages, { type => 'info', code => 'success_on_inserted' };
138
    $op = 'else';
144
    $op = 'else';
139
}
145
}
140
146
147
my $suggestions_loop = &SearchSuggestion({suggestedby => $suggestion->{suggestedby}});
141
if ( $op eq "delete_confirm" ) {
148
if ( $op eq "delete_confirm" ) {
142
    my @delete_field = $input->param("delete_field");
149
    my @delete_field = $input->param("delete_field");
143
    foreach my $delete_field (@delete_field) {
150
    foreach my $delete_field (@delete_field) {
Lines 189-195 if ( C4::Context->preference("AllowPurchaseSuggestionBranchChoice") ) { Link Here
189
196
190
$template->param(
197
$template->param(
191
	%$suggestion,
198
	%$suggestion,
192
	itemtypeloop=> $supportlist,
199
	 placeitemtypeloop=> $supportlist,
193
    suggestions_loop => $suggestions_loop,
200
    suggestions_loop => $suggestions_loop,
194
    patron_reason_loop => $patron_reason_loop,
201
    patron_reason_loop => $patron_reason_loop,
195
    "op_$op"         => 1,
202
    "op_$op"         => 1,
Lines 197-202 $template->param( Link Here
197
    messages => \@messages,
204
    messages => \@messages,
198
    suggestionsview => 1,
205
    suggestionsview => 1,
199
    suggested_by_anyone => $suggested_by_anyone,
206
    suggested_by_anyone => $suggested_by_anyone,
207
    need_confirm => $need_confirm,
200
);
208
);
201
209
202
output_html_with_http_headers $input, $cookie, $template->output;
210
output_html_with_http_headers $input, $cookie, $template->output;
(-)a/t/db_dependent/Suggestions.t (-2 / +19 lines)
Lines 26-32 use C4::Budgets qw( AddBudgetPeriod AddBudget ); Link Here
26
use Koha::DateUtils qw( dt_from_string );
26
use Koha::DateUtils qw( dt_from_string );
27
27
28
use DateTime::Duration;
28
use DateTime::Duration;
29
use Test::More tests => 105;
29
use Test::More tests => 108;
30
use Test::Warn;
30
use Test::Warn;
31
31
32
BEGIN {
32
BEGIN {
Lines 364-369 ModSuggestion( $my_suggestion ); Link Here
364
$suggestion = GetSuggestion($my_suggestionid_test_budgetid);
364
$suggestion = GetSuggestion($my_suggestionid_test_budgetid);
365
is( $suggestion->{budgetid}, undef, 'NewSuggestion Should set budgetid to NULL if equals an empty string' );
365
is( $suggestion->{budgetid}, undef, 'NewSuggestion Should set budgetid to NULL if equals an empty string' );
366
366
367
my $suggestion2 = {
368
    title => "Cuisine d'automne",
369
    author => "Catherine",
370
    itemtype => "LIV"
371
};
372
373
my $record = MarcRecordFromSuggestion($suggestion2);
374
375
is("MARC::Record", ref($record), "MarcRecordFromSuggestion should return a MARC::Record object");
376
377
my ($title_tag, $title_subfield) = C4::Biblio::GetMarcFromKohaField('biblio.title', '');
378
379
is($record->field( $title_tag )->subfield( $title_subfield ), "Cuisine d'automne", "Record from suggestion title should be 'Cuisine d'automne'");
380
381
my ($author_tag, $author_subfield) = C4::Biblio::GetMarcFromKohaField('biblio.author', '');
382
383
is($record->field( $author_tag )->subfield( $author_subfield ), "Catherine", "Record from suggestion author should be 'Catherine'");
384
367
subtest 'GetUnprocessedSuggestions' => sub {
385
subtest 'GetUnprocessedSuggestions' => sub {
368
    plan tests => 11;
386
    plan tests => 11;
369
    $dbh->do(q|DELETE FROM suggestions|);
387
    $dbh->do(q|DELETE FROM suggestions|);
370
- 

Return to bug 14973