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

(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/patron-attr-types.tt (-1 / +1 lines)
Lines 319-325 function CheckAttributeTypeForm(f) { Link Here
319
            <td>[% item.code |html %]</td>
319
            <td>[% item.code |html %]</td>
320
            <td>[% item.description %]</td>
320
            <td>[% item.description %]</td>
321
            <td>
321
            <td>
322
                [% IF item.branches.size > 0 %]
322
                [% IF ( item.branches ) %]
323
                    [% branches_str = "" %]
323
                    [% branches_str = "" %]
324
                    [% FOREACH branch IN item.branches %]
324
                    [% FOREACH branch IN item.branches %]
325
                        [% branches_str = branches_str _ " " _ branch.branchname _ "(" _ branch.branchcode _ ")" %]
325
                        [% branches_str = branches_str _ " " _ branch.branchname _ "(" _ branch.branchcode _ ")" %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt (-18 / +91 lines)
Lines 1-5 Link Here
1
[% USE AuthorisedValues %]
1
[% USE Koha %]
2
[% USE Koha %]
2
[% USE KohaDates %]
3
[% USE KohaDates %]
4
[% USE Math %]
3
[% SET userupdateview = 1 %]
5
[% SET userupdateview = 1 %]
4
6
5
[% INCLUDE 'doc-head-open.inc' %]
7
[% INCLUDE 'doc-head-open.inc' %]
Lines 95-100 Link Here
95
                        </ol>
97
                        </ol>
96
                    </fieldset>
98
                    </fieldset>
97
                [% END %]
99
                [% END %]
100
                [% IF ( extended_unique_id_failed_code ) %]
101
                    <div class="alert" id="extended_unique_id_failed"><a href="#patron-attr-start-[% extended_unique_id_failed_code %]">[% extended_unique_id_failed_description %]</a> value <i>[% extended_unique_id_failed_value %]</i> is already in use.</div>
102
                [% END %]
98
103
99
                <form method="post" action="/cgi-bin/koha/opac-memberentry.pl" id="memberentry-form">
104
                <form method="post" action="/cgi-bin/koha/opac-memberentry.pl" id="memberentry-form">
100
105
Lines 776-781 Link Here
776
                    </fieldset>
781
                    </fieldset>
777
                [% END %]
782
                [% END %]
778
783
784
                [% IF ( Koha.Preference('ExtendedPatronAttributes') && patron_attribute_classes.size ) %]
785
                    [% FOREACH pa_class IN patron_attribute_classes %]
786
                        [% IF pa_class.class %]
787
                            <fieldset id="aai_[% pa_loo.class %]" class="rows patron-attributes">
788
                            <legend>[% pa_loo.lib %]</legend>
789
                        [% ELSE %]
790
                            <fieldset class="rows patron-attributes">
791
                            <legend>Additional information</legend>
792
                        [% END %]
793
                        <ol class="attributes_table">
794
                            [% FOREACH pa IN pa_class.items %]
795
                                [% FOREACH pa_value IN pa.values %]
796
                                    [% IF loop.first %]<a name="patron-attr-start-[% pa.type.code %]"></a>[% END %]
797
                                    [% form_id = 'patron-attr-' _ Math.int( Math.rand(1000000) ) %]
798
                                    <li data-category_code="[% pa.type.category_code %]">
799
                                        <label for="[% form_id %]">[% pa.type.description %]: </label>
800
                                        [% IF pa.type.opac_editable %]
801
                                            <input type="hidden" name="patron_attribute_code" value="[% pa.type.code |html %]" />
802
                                            [% IF ( pa.type.authorised_value_category ) %]
803
                                                <select id="[% form_id %]" name="patron_attribute_value">
804
                                                    <option value=""></option>
805
                                                    [% FOREACH auth_val IN AuthorisedValues.Get( pa.type.authorised_value_category, pa_value.value || '', 1 ) %]
806
                                                        [% IF ( auth_val.selected ) %]
807
                                                            <option value="[% auth_val.authorised_value %]" selected="selected">
808
                                                                [%# Yes, lib; GetAuthorisedValues takes care of intelligently setting this from lib_opac %]
809
                                                                [% auth_val.lib %]
810
                                                            </option>
811
                                                        [% ELSE %]
812
                                                            <option value="[% auth_val.authorised_value %]" >
813
                                                                [% auth_val.lib %]
814
                                                            </option>
815
                                                        [% END %]
816
                                                    [% END %]
817
                                                </select>
818
                                            [% ELSE %]
819
                                                <textarea rows="2" cols="30" id="[% form_id %]" name="patron_attribute_value">[% pa_value.value %]</textarea>
820
                                            [% END %]
821
                                            [% IF ( pa.type.password_allowed ) %]
822
                                                (<label class="lradio" for="[% form_id %]_password">Password:</label>
823
                                                <input type="password" maxlength="64" value="[% pa_value.password %]" id="[% form_id %]_password" name="patron_attribute_password" />)
824
                                            [% ELSE %]
825
                                                [%# To keep the form inputs lined up in the POST %]
826
                                                <input type="hidden" name="patron_attribute_password" value="" />
827
                                            [% END %]
828
                                            <a href="#" class="clear-attribute">Clear</a>
829
                                            [% IF ( pa.type.repeatable ) %]
830
                                            <a href="#" class="clone-attribute">New</a>
831
                                            [% END %]
832
                                        [% ELSE %]
833
                                            [% IF ( pa.type.authorised_value_category ) %]
834
                                                [% AuthorisedValues.GetByCode( pa.type.authorised_value, pa_value.value, 1 ) %]
835
                                            [% ELSE %]
836
                                                [% pa_value.value | html_line_break %]
837
                                            [% END %]
838
                                            [% IF ( pa_value.password ) %]
839
                                                (Password: *******)
840
                                            [% END %]
841
                                        [% END %]
842
                                    </li>
843
                                [% END %]
844
                            [% END %]
845
                        </ol>
846
                        </fieldset>
847
                    [% END %]
848
                [% END %]
849
779
                [% UNLESS action == 'edit' %]
850
                [% UNLESS action == 'edit' %]
780
                    <fieldset class="rows" id="memberentry_captcha">
851
                    <fieldset class="rows" id="memberentry_captcha">
781
                        <ol>
852
                        <ol>
Lines 792-815 Link Here
792
                    </fieldset>
863
                    </fieldset>
793
                [% END %]
864
                [% END %]
794
865
795
                [% IF ( borrower.ExtendedPatronAttributes ) %]
796
                    <fieldset class="rows">
797
                        <legend>Additional information</legend>
798
                        <ol>
799
                            [% FOREACH patron_attribute IN borrower.patron_attributes %]
800
                                <li>
801
                                    <label>[% patron_attribute.description %]:</label>
802
                                    [% IF ( patron_attribute.value_description ) %]
803
                                        [% patron_attribute.value_description %]
804
                                    [% ELSE %]
805
                                        [% patron_attribute.value |html_line_break %]
806
                                    [% END %]
807
                                </li>
808
                            [% END %]
809
                        </ol>
810
                    </fieldset>
811
                [% END %]
812
813
                [% IF action == 'edit' %]
866
                [% IF action == 'edit' %]
814
                    [% IF OPACPatronDetails %]
867
                    [% IF OPACPatronDetails %]
815
                        <fieldset class="action">
868
                        <fieldset class="action">
Lines 881-886 Link Here
881
                }
934
                }
882
            });
935
            });
883
936
937
<<<<<<< HEAD
884
            [% IF borrower.guarantorid && !Koha.Preference('OPACPrivacy') && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %]
938
            [% IF borrower.guarantorid && !Koha.Preference('OPACPrivacy') && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %]
885
                $('#update_privacy_guarantor_checkouts').click( function() {
939
                $('#update_privacy_guarantor_checkouts').click( function() {
886
                    $.post( "/cgi-bin/koha/svc/patron/show_checkouts_to_relatives", { privacy_guarantor_checkouts: $('#privacy_guarantor_checkouts').val() }, null, 'json')
940
                    $.post( "/cgi-bin/koha/svc/patron/show_checkouts_to_relatives", { privacy_guarantor_checkouts: $('#privacy_guarantor_checkouts').val() }, null, 'json')
Lines 896-901 Link Here
896
                     });
950
                     });
897
                });
951
                });
898
            [% END %]
952
            [% END %]
953
=======
954
            $(".patron-attributes").on( 'click', '.clear-attribute', function() {
955
                $(this).parent()
956
                    .find('textarea').attr('value', '').end()
957
                    .find('select').attr('value', '').end();
958
959
                return false;
960
            } );
961
962
            $(".patron-attributes").on( 'click', '.clone-attribute', function() {
963
                var clone = $(this).parent().clone().insertAfter( $(this).parent() );
964
965
                var newId = 50 + parseInt(Math.random() * 100000);
966
                $('input[type!="hidden"],select,textarea', clone).attr('id', 'patron-attr-' + newId).attr('value', '');
967
                $("label", clone).attr('for', 'patron-attr-' + newId).attr('value', '');
968
969
                return false;
970
            } );
971
>>>>>>> Bug 13757 - Make patron attributes editable in the OPAC (2/2)
899
        });
972
        });
900
    //]]>
973
    //]]>
901
    </script>
974
    </script>
(-)a/opac/opac-memberentry.pl (-11 / +115 lines)
Lines 19-27 use Modern::Perl; Link Here
19
19
20
use CGI qw ( -utf8 );
20
use CGI qw ( -utf8 );
21
use Digest::MD5 qw( md5_base64 md5_hex );
21
use Digest::MD5 qw( md5_base64 md5_hex );
22
use List::MoreUtils qw( each_array uniq );
22
use String::Random qw( random_string );
23
use String::Random qw( random_string );
23
24
24
use C4::Auth;
25
use C4::Auth;
26
use C4::Koha qw( GetAuthorisedValueByCode );
25
use C4::Output;
27
use C4::Output;
26
use C4::Members;
28
use C4::Members;
27
use C4::Form::MessagingPreferences;
29
use C4::Form::MessagingPreferences;
Lines 72-77 $template->param( Link Here
72
    OPACPatronDetails => C4::Context->preference('OPACPatronDetails'),
74
    OPACPatronDetails => C4::Context->preference('OPACPatronDetails'),
73
);
75
);
74
76
77
my $attributes = ParsePatronAttributes($cgi);
78
my $conflicting_attribute = 0;
79
80
foreach my $attr (@$attributes) {
81
    unless ( C4::Members::Attributes::CheckUniqueness($attr->{code}, $attr->{value}, $borrowernumber) ) {
82
        my $attr_info = C4::Members::AttributeTypes->fetch($attr->{code});
83
        $template->param(
84
            extended_unique_id_failed_code => $attr->{code},
85
            extended_unique_id_failed_value => $attr->{value},
86
            extended_unique_id_failed_description => $attr_info->description()
87
        );
88
        $conflicting_attribute = 1;
89
    }
90
}
91
75
if ( $action eq 'create' ) {
92
if ( $action eq 'create' ) {
76
93
77
    my %borrower = ParseCgiForBorrower($cgi);
94
    my %borrower = ParseCgiForBorrower($cgi);
Lines 81-92 if ( $action eq 'create' ) { Link Here
81
    my @empty_mandatory_fields = CheckMandatoryFields( \%borrower, $action );
98
    my @empty_mandatory_fields = CheckMandatoryFields( \%borrower, $action );
82
    my $invalidformfields = CheckForInvalidFields(\%borrower);
99
    my $invalidformfields = CheckForInvalidFields(\%borrower);
83
100
84
    if (@empty_mandatory_fields || @$invalidformfields) {
101
    if (@empty_mandatory_fields || @$invalidformfields || $conflicting_attribute) {
85
        $template->param(
102
        $template->param(
86
            empty_mandatory_fields => \@empty_mandatory_fields,
103
            empty_mandatory_fields => \@empty_mandatory_fields,
87
            invalid_form_fields    => $invalidformfields,
104
            invalid_form_fields    => $invalidformfields,
88
            borrower               => \%borrower
105
            borrower               => \%borrower
89
        );
106
        );
107
        $template->param( patron_attribute_classes => GeneratePatronAttributesForm( undef, $attributes ) );
90
    }
108
    }
91
    elsif (
109
    elsif (
92
        md5_base64( $cgi->param('captcha') ) ne $cgi->param('captcha_digest') )
110
        md5_base64( $cgi->param('captcha') ) ne $cgi->param('captcha_digest') )
Lines 95-100 if ( $action eq 'create' ) { Link Here
95
            failed_captcha => 1,
113
            failed_captcha => 1,
96
            borrower       => \%borrower
114
            borrower       => \%borrower
97
        );
115
        );
116
        $template->param( patron_attribute_classes => GeneratePatronAttributesForm( undef, $attributes ) );
98
    }
117
    }
99
    else {
118
    else {
100
        if (
119
        if (
Lines 152-157 if ( $action eq 'create' ) { Link Here
152
                  C4::Context->preference('OpacPasswordChange') );
171
                  C4::Context->preference('OpacPasswordChange') );
153
172
154
            my ( $borrowernumber, $password ) = AddMember_Opac(%borrower);
173
            my ( $borrowernumber, $password ) = AddMember_Opac(%borrower);
174
            C4::Members::Attributes::SetBorrowerAttributes( $borrowernumber, $attributes );
155
            C4::Form::MessagingPreferences::handle_form_action($cgi, { borrowernumber => $borrowernumber }, $template, 1, C4::Context->preference('PatronSelfRegistrationDefaultCategory') ) if $borrowernumber && C4::Context->preference('EnhancedMessagingPreferences');
175
            C4::Form::MessagingPreferences::handle_form_action($cgi, { borrowernumber => $borrowernumber }, $template, 1, C4::Context->preference('PatronSelfRegistrationDefaultCategory') ) if $borrowernumber && C4::Context->preference('EnhancedMessagingPreferences');
156
176
157
            $template->param( password_cleartext => $password );
177
            $template->param( password_cleartext => $password );
Lines 174-185 elsif ( $action eq 'update' ) { Link Here
174
      CheckMandatoryFields( \%borrower_changes, $action );
194
      CheckMandatoryFields( \%borrower_changes, $action );
175
    my $invalidformfields = CheckForInvalidFields(\%borrower);
195
    my $invalidformfields = CheckForInvalidFields(\%borrower);
176
196
177
    if (@empty_mandatory_fields || @$invalidformfields) {
197
    if (@empty_mandatory_fields || @$invalidformfields || $conflicting_attribute) {
178
        $template->param(
198
        $template->param(
179
            empty_mandatory_fields => \@empty_mandatory_fields,
199
            empty_mandatory_fields => \@empty_mandatory_fields,
180
            invalid_form_fields    => $invalidformfields,
200
            invalid_form_fields    => $invalidformfields,
181
            borrower               => \%borrower
201
            borrower               => \%borrower
182
        );
202
        );
203
        $template->param( patron_attribute_classes => GeneratePatronAttributesForm( undef, $attributes ) );
183
204
184
        $template->param( action => 'edit' );
205
        $template->param( action => 'edit' );
185
    }
206
    }
Lines 217-230 elsif ( $action eq 'update' ) { Link Here
217
elsif ( $action eq 'edit' ) {    #Display logged in borrower's data
238
elsif ( $action eq 'edit' ) {    #Display logged in borrower's data
218
    my $borrower = GetMember( borrowernumber => $borrowernumber );
239
    my $borrower = GetMember( borrowernumber => $borrowernumber );
219
240
220
    if (C4::Context->preference('ExtendedPatronAttributes')) {
221
        my $attributes = C4::Members::Attributes::GetBorrowerAttributes($borrowernumber, 'opac');
222
        if (scalar(@$attributes) > 0) {
223
            $borrower->{ExtendedPatronAttributes} = 1;
224
            $borrower->{patron_attributes} = $attributes;
225
        }
226
    }
227
228
    $template->param(
241
    $template->param(
229
        borrower  => $borrower,
242
        borrower  => $borrower,
230
        guarantor => Koha::Borrowers->find($borrowernumber)->guarantor(),
243
        guarantor => Koha::Borrowers->find($borrowernumber)->guarantor(),
Lines 239-244 elsif ( $action eq 'edit' ) { #Display logged in borrower's data Link Here
239
        }
252
        }
240
    }
253
    }
241
254
255
    $template->param( patron_attribute_classes => GeneratePatronAttributesForm( $borrower ) );
256
} else {
257
    $template->param( patron_attribute_classes => GeneratePatronAttributesForm() );
242
}
258
}
243
259
244
my $captcha = random_string("CCCCC");
260
my $captcha = random_string("CCCCC");
Lines 373-375 sub DelEmptyFields { Link Here
373
389
374
    return %borrower;
390
    return %borrower;
375
}
391
}
376
- 
392
393
sub GeneratePatronAttributesForm {
394
    my ( $borrower, $entered_attributes ) = @_;
395
396
    use Data::Dumper; warn Dumper( $entered_attributes );
397
398
    # Get all attribute types and the values for this patron (if applicable)
399
    my @types = C4::Members::AttributeTypes::GetAttributeTypes();
400
401
    if (scalar(@types) == 0) {
402
        return [];
403
    }
404
405
    my %attr_values = ();
406
407
    if ( $borrower ) {
408
        my $attributes = C4::Members::Attributes::GetBorrowerAttributes($borrowernumber);
409
410
        # Remap the patron's attributes into a hash of arrayrefs per attribute (depends on
411
        # autovivification)
412
        foreach my $attr (@$attributes) {
413
            push @{ $attr_values{ $attr->{code} } }, $attr;
414
        }
415
    }
416
417
    if ( $entered_attributes ) {
418
        foreach my $attr (@$entered_attributes) {
419
            push @{ $attr_values{ $attr->{code} } }, $attr;
420
        }
421
    }
422
423
    # Find all existing classes
424
    my @classes = uniq( map { $_->{class} } @types );
425
    @classes = sort @classes;
426
    my %items_by_class;
427
428
    foreach my $attr_type_desc (@types) {
429
        my $attr_type = C4::Members::AttributeTypes->fetch( $attr_type_desc->{code} );
430
        # Make sure this attribute should be displayed in the OPAC
431
        next unless ( $attr_type->opac_display() );
432
        # Then, make sure it either has values or is editable
433
        next unless ( $attr_values{ $attr_type->code() } || $attr_type->opac_editable() );
434
435
        push @{ $items_by_class{ $attr_type->class() } }, {
436
            type => $attr_type,
437
            # If editable, make sure there's at least one empty entry, to make the template's job easier
438
            values => $attr_values{ $attr_type->code() } || [{}]
439
        };
440
    }
441
442
    use Data::Dumper; warn Dumper( \%items_by_class );
443
444
    # Finally, build a list of containing classes
445
    my @class_loop;
446
    foreach my $class (@classes) {
447
        next unless ( $items_by_class{$class} );
448
449
        my $lib = GetAuthorisedValueByCode( 'PA_CLASS', $class, 1 ) || $class;
450
        push @class_loop, {
451
            class => $class,
452
            items => $items_by_class{$class},
453
            lib   => $lib,
454
        };
455
    }
456
457
    return \@class_loop;
458
}
459
460
sub ParsePatronAttributes {
461
    my ( $cgi ) = @_;
462
463
    my @codes = $cgi->param('patron_attribute_code');
464
    my @values = $cgi->param('patron_attribute_value');
465
    my @passwords = $cgi->param('patron_attribute_password');
466
467
    my $ea = each_array( @codes, @values, @passwords );
468
    my @attributes;
469
    my %dups = ();
470
471
    while ( my ( $code, $value, $password ) = $ea->() ) {
472
        next unless defined($value) and $value ne '';
473
        next if exists $dups{$code}->{$value};
474
        $dups{$code}->{$value} = 1;
475
476
        push @attributes, { code => $code, value => $value, password => $password };
477
    }
478
479
    return \@attributes;
480
}

Return to bug 13757