From 492628bd6876e3c8c75ce28c2e8d41eb61839d37 Mon Sep 17 00:00:00 2001 From: Jesse Weaver Date: Wed, 2 Sep 2015 17:54:04 -0600 Subject: [PATCH] Bug 13757 - Make patron attributes editable in the OPAC (2/2) This patch makes attributes marked as OPAC-editable editable from the OPAC. NOTE: This needs to be extended to work with the modifications system, and thus only works for new patrons. Test plan: 1) Create attributes that cover the spectrum of possible attribute types; repeatable, unique, dropdown, etc. 2) Verify that all of these attributes are correctly validated and saved. Aleisha's update: Fixed a merge conflict and a warn "Argument "" isn't numeric in numeric gt (>) at /usr/share/kohaclone/koha-tmpl/intranet-tmpl/prog/en/modules/admin/patron-attr-types.tt line 336." --- .../prog/en/modules/admin/patron-attr-types.tt | 2 +- .../bootstrap/en/modules/opac-memberentry.tt | 109 ++++++++++++++--- opac/opac-memberentry.pl | 125 ++++++++++++++++++-- 3 files changed, 207 insertions(+), 29 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/patron-attr-types.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/patron-attr-types.tt index 3cf1418..3c2f7a6 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/patron-attr-types.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/patron-attr-types.tt @@ -319,7 +319,7 @@ function CheckAttributeTypeForm(f) { [% item.code %] [% item.description %] - [% IF item.branches.size > 0 %] + [% IF ( item.branches ) %] [% branches_str = "" %] [% FOREACH branch IN item.branches %] [% branches_str = branches_str _ " " _ branch.branchname _ "(" _ branch.branchcode _ ")" %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt index 3f3428d..0849f0c 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt @@ -1,5 +1,7 @@ +[% USE AuthorisedValues %] [% USE Koha %] [% USE KohaDates %] +[% USE Math %] [% SET userupdateview = 1 %] [% INCLUDE 'doc-head-open.inc' %] @@ -95,6 +97,9 @@ [% END %] + [% IF ( extended_unique_id_failed_code ) %] +
[% extended_unique_id_failed_description %] value [% extended_unique_id_failed_value %] is already in use.
+ [% END %]
@@ -776,6 +781,72 @@ [% END %] + [% IF ( Koha.Preference('ExtendedPatronAttributes') && patron_attribute_classes.size ) %] + [% FOREACH pa_class IN patron_attribute_classes %] + [% IF pa_class.class %] +
+ [% pa_loo.lib %] + [% ELSE %] +
+ Additional information + [% END %] +
    + [% FOREACH pa IN pa_class.items %] + [% FOREACH pa_value IN pa.values %] + [% IF loop.first %][% END %] + [% form_id = 'patron-attr-' _ Math.int( Math.rand(1000000) ) %] +
  1. + + [% IF pa.type.opac_editable %] + + [% IF ( pa.type.authorised_value_category ) %] + + [% ELSE %] + + [% END %] + [% IF ( pa.type.password_allowed ) %] + ( + ) + [% ELSE %] + [%# To keep the form inputs lined up in the POST %] + + [% END %] + Clear + [% IF ( pa.type.repeatable ) %] + New + [% END %] + [% ELSE %] + [% IF ( pa.type.authorised_value_category ) %] + [% AuthorisedValues.GetByCode( pa.type.authorised_value, pa_value.value, 1 ) %] + [% ELSE %] + [% pa_value.value | html_line_break %] + [% END %] + [% IF ( pa_value.password ) %] + (Password: *******) + [% END %] + [% END %] +
  2. + [% END %] + [% END %] +
+
+ [% END %] + [% END %] + [% UNLESS action == 'edit' %]
    @@ -792,24 +863,6 @@
[% END %] - [% IF ( borrower.ExtendedPatronAttributes ) %] -
- Additional information -
    - [% FOREACH patron_attribute IN borrower.patron_attributes %] -
  1. - - [% IF ( patron_attribute.value_description ) %] - [% patron_attribute.value_description %] - [% ELSE %] - [% patron_attribute.value |html_line_break %] - [% END %] -
  2. - [% END %] -
-
- [% END %] - [% IF action == 'edit' %] [% IF OPACPatronDetails %]
@@ -881,6 +934,7 @@ } }); +<<<<<<< HEAD [% IF borrower.guarantorid && !Koha.Preference('OPACPrivacy') && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %] $('#update_privacy_guarantor_checkouts').click( function() { $.post( "/cgi-bin/koha/svc/patron/show_checkouts_to_relatives", { privacy_guarantor_checkouts: $('#privacy_guarantor_checkouts').val() }, null, 'json') @@ -896,6 +950,25 @@ }); }); [% END %] +======= + $(".patron-attributes").on( 'click', '.clear-attribute', function() { + $(this).parent() + .find('textarea').attr('value', '').end() + .find('select').attr('value', '').end(); + + return false; + } ); + + $(".patron-attributes").on( 'click', '.clone-attribute', function() { + var clone = $(this).parent().clone().insertAfter( $(this).parent() ); + + var newId = 50 + parseInt(Math.random() * 100000); + $('input[type!="hidden"],select,textarea', clone).attr('id', 'patron-attr-' + newId).attr('value', ''); + $("label", clone).attr('for', 'patron-attr-' + newId).attr('value', ''); + + return false; + } ); +>>>>>>> Bug 13757 - Make patron attributes editable in the OPAC (2/2) }); //]]> diff --git a/opac/opac-memberentry.pl b/opac/opac-memberentry.pl index 667f201..b140e1b 100755 --- a/opac/opac-memberentry.pl +++ b/opac/opac-memberentry.pl @@ -19,9 +19,11 @@ use Modern::Perl; use CGI qw ( -utf8 ); use Digest::MD5 qw( md5_base64 md5_hex ); +use List::MoreUtils qw( each_array uniq ); use String::Random qw( random_string ); use C4::Auth; +use C4::Koha qw( GetAuthorisedValueByCode ); use C4::Output; use C4::Members; use C4::Form::MessagingPreferences; @@ -72,6 +74,21 @@ $template->param( OPACPatronDetails => C4::Context->preference('OPACPatronDetails'), ); +my $attributes = ParsePatronAttributes($cgi); +my $conflicting_attribute = 0; + +foreach my $attr (@$attributes) { + unless ( C4::Members::Attributes::CheckUniqueness($attr->{code}, $attr->{value}, $borrowernumber) ) { + my $attr_info = C4::Members::AttributeTypes->fetch($attr->{code}); + $template->param( + extended_unique_id_failed_code => $attr->{code}, + extended_unique_id_failed_value => $attr->{value}, + extended_unique_id_failed_description => $attr_info->description() + ); + $conflicting_attribute = 1; + } +} + if ( $action eq 'create' ) { my %borrower = ParseCgiForBorrower($cgi); @@ -81,12 +98,13 @@ if ( $action eq 'create' ) { my @empty_mandatory_fields = CheckMandatoryFields( \%borrower, $action ); my $invalidformfields = CheckForInvalidFields(\%borrower); - if (@empty_mandatory_fields || @$invalidformfields) { + if (@empty_mandatory_fields || @$invalidformfields || $conflicting_attribute) { $template->param( empty_mandatory_fields => \@empty_mandatory_fields, invalid_form_fields => $invalidformfields, borrower => \%borrower ); + $template->param( patron_attribute_classes => GeneratePatronAttributesForm( undef, $attributes ) ); } elsif ( md5_base64( $cgi->param('captcha') ) ne $cgi->param('captcha_digest') ) @@ -95,6 +113,7 @@ if ( $action eq 'create' ) { failed_captcha => 1, borrower => \%borrower ); + $template->param( patron_attribute_classes => GeneratePatronAttributesForm( undef, $attributes ) ); } else { if ( @@ -152,6 +171,7 @@ if ( $action eq 'create' ) { C4::Context->preference('OpacPasswordChange') ); my ( $borrowernumber, $password ) = AddMember_Opac(%borrower); + C4::Members::Attributes::SetBorrowerAttributes( $borrowernumber, $attributes ); C4::Form::MessagingPreferences::handle_form_action($cgi, { borrowernumber => $borrowernumber }, $template, 1, C4::Context->preference('PatronSelfRegistrationDefaultCategory') ) if $borrowernumber && C4::Context->preference('EnhancedMessagingPreferences'); $template->param( password_cleartext => $password ); @@ -174,12 +194,13 @@ elsif ( $action eq 'update' ) { CheckMandatoryFields( \%borrower_changes, $action ); my $invalidformfields = CheckForInvalidFields(\%borrower); - if (@empty_mandatory_fields || @$invalidformfields) { + if (@empty_mandatory_fields || @$invalidformfields || $conflicting_attribute) { $template->param( empty_mandatory_fields => \@empty_mandatory_fields, invalid_form_fields => $invalidformfields, borrower => \%borrower ); + $template->param( patron_attribute_classes => GeneratePatronAttributesForm( undef, $attributes ) ); $template->param( action => 'edit' ); } @@ -217,14 +238,6 @@ elsif ( $action eq 'update' ) { elsif ( $action eq 'edit' ) { #Display logged in borrower's data my $borrower = GetMember( borrowernumber => $borrowernumber ); - if (C4::Context->preference('ExtendedPatronAttributes')) { - my $attributes = C4::Members::Attributes::GetBorrowerAttributes($borrowernumber, 'opac'); - if (scalar(@$attributes) > 0) { - $borrower->{ExtendedPatronAttributes} = 1; - $borrower->{patron_attributes} = $attributes; - } - } - $template->param( borrower => $borrower, guarantor => Koha::Borrowers->find($borrowernumber)->guarantor(), @@ -239,6 +252,9 @@ elsif ( $action eq 'edit' ) { #Display logged in borrower's data } } + $template->param( patron_attribute_classes => GeneratePatronAttributesForm( $borrower ) ); +} else { + $template->param( patron_attribute_classes => GeneratePatronAttributesForm() ); } my $captcha = random_string("CCCCC"); @@ -373,3 +389,92 @@ sub DelEmptyFields { return %borrower; } + +sub GeneratePatronAttributesForm { + my ( $borrower, $entered_attributes ) = @_; + + use Data::Dumper; warn Dumper( $entered_attributes ); + + # Get all attribute types and the values for this patron (if applicable) + my @types = C4::Members::AttributeTypes::GetAttributeTypes(); + + if (scalar(@types) == 0) { + return []; + } + + my %attr_values = (); + + if ( $borrower ) { + my $attributes = C4::Members::Attributes::GetBorrowerAttributes($borrowernumber); + + # Remap the patron's attributes into a hash of arrayrefs per attribute (depends on + # autovivification) + foreach my $attr (@$attributes) { + push @{ $attr_values{ $attr->{code} } }, $attr; + } + } + + if ( $entered_attributes ) { + foreach my $attr (@$entered_attributes) { + push @{ $attr_values{ $attr->{code} } }, $attr; + } + } + + # Find all existing classes + my @classes = uniq( map { $_->{class} } @types ); + @classes = sort @classes; + my %items_by_class; + + foreach my $attr_type_desc (@types) { + my $attr_type = C4::Members::AttributeTypes->fetch( $attr_type_desc->{code} ); + # Make sure this attribute should be displayed in the OPAC + next unless ( $attr_type->opac_display() ); + # Then, make sure it either has values or is editable + next unless ( $attr_values{ $attr_type->code() } || $attr_type->opac_editable() ); + + push @{ $items_by_class{ $attr_type->class() } }, { + type => $attr_type, + # If editable, make sure there's at least one empty entry, to make the template's job easier + values => $attr_values{ $attr_type->code() } || [{}] + }; + } + + use Data::Dumper; warn Dumper( \%items_by_class ); + + # Finally, build a list of containing classes + my @class_loop; + foreach my $class (@classes) { + next unless ( $items_by_class{$class} ); + + my $lib = GetAuthorisedValueByCode( 'PA_CLASS', $class, 1 ) || $class; + push @class_loop, { + class => $class, + items => $items_by_class{$class}, + lib => $lib, + }; + } + + return \@class_loop; +} + +sub ParsePatronAttributes { + my ( $cgi ) = @_; + + my @codes = $cgi->param('patron_attribute_code'); + my @values = $cgi->param('patron_attribute_value'); + my @passwords = $cgi->param('patron_attribute_password'); + + my $ea = each_array( @codes, @values, @passwords ); + my @attributes; + my %dups = (); + + while ( my ( $code, $value, $password ) = $ea->() ) { + next unless defined($value) and $value ne ''; + next if exists $dups{$code}->{$value}; + $dups{$code}->{$value} = 1; + + push @attributes, { code => $code, value => $value, password => $password }; + } + + return \@attributes; +} -- 1.7.10.4