From 2270ecaa9000bac7d06267987b7826fe6d91c59f Mon Sep 17 00:00:00 2001 From: John Beppu Date: Tue, 3 Feb 2009 16:02:08 -0600 Subject: [PATCH] Brand New UI for Alert Configuration - grid layout (javascript required now) - makes distinction between checkin and checkout messages - no longer have to click on a button to submit the branch selection form Signed-off-by: Daniel Sweeney Signed-off-by: Galen Charlton --- C4/ItemCirculationAlertPreference.pm | 150 +++++++++++--- admin/item_circulation_alerts.pl | 142 ++++++------- .../admin/item_circulation_alerts.tmpl | 190 +++++++++++------- 3 files changed, 298 insertions(+), 184 deletions(-) diff --git a/C4/ItemCirculationAlertPreference.pm b/C4/ItemCirculationAlertPreference.pm index df231a4c26..7ade3dfa47 100644 --- a/C4/ItemCirculationAlertPreference.pm +++ b/C4/ItemCirculationAlertPreference.pm @@ -18,6 +18,8 @@ package C4::ItemCirculationAlertPreference; use strict; use warnings; use C4::Context; +use C4::Category; +use C4::ItemType; use Carp qw(carp croak); our $AUTOLOAD; @@ -25,7 +27,7 @@ our $AUTOLOAD; # helper function for validating \%opts our $valid = sub { my $opts = shift; - for (qw(branchcode categorycode item_type)) { + for (qw(branchcode categorycode item_type notification)) { exists($opts->{$_}) || croak("'$_' is a required parameter."); } }; @@ -46,20 +48,22 @@ Basics: # a short-cut to reduce typing the long package name over and over again my $preferences = 'C4::ItemCirculationAlertPreference'; -Creating Rules: +Creating a restriction on sending messages: my $pref = $preferences->create({ branchcode => 'CPL', categorycode => 'YA', item_type => 'BK', + notification => 'CHECKOUT', }); -Deleting Rules: +Removing a restriction on sending messages: $preferences->delete({ branchcode => 'CPL', categorycode => 'YA', item_type => 'BK', + notification => 'CHECKOUT', }); =head1 DESCRIPTION @@ -96,7 +100,28 @@ sub new { =head3 C4::ItemCirculationAlertPreference->create(\%opts) This will find or create an item circulation alert preference. You must pass -it a B, B, and B. +it a B, B, B, and B. Valid +values for these attributes are as follows: + +=over 4 + +=item branchcode + +branches.branchcode + +=item categorycode + +category.categorycode + +=item item_type + +itemtypes.itemtype + +=item notification + +This can be "CHECKIN" or "CHECKOUT" + +=back =cut @@ -109,22 +134,25 @@ sub create { FROM item_circulation_alert_preferences WHERE branchcode = ? AND categorycode = ? - AND item_type = ?", + AND item_type = ? + AND notification = ?", { Slice => {} }, $opts->{branchcode}, $opts->{categorycode}, $opts->{item_type}, + $opts->{notification}, ); if (@$prefs) { return $class->new($prefs->[0]); } else { my $success = $dbh->do( "INSERT INTO item_circulation_alert_preferences - (branchcode, categorycode, item_type) VALUES (?, ?, ?)", + (branchcode, categorycode, item_type, notification) VALUES (?, ?, ?, ?)", {}, $opts->{branchcode}, $opts->{categorycode}, $opts->{item_type}, + $opts->{notification}, ); if ($success) { my $self = { @@ -132,6 +160,7 @@ sub create { branchcode => $opts->{branchcode}, categorycode => $opts->{categorycode}, item_type => $opts->{item_type}, + notification => $opts->{notification}, }; return $class->new($self); } else { @@ -163,15 +192,19 @@ sub delete { ); } else { $valid->($opts); - $dbh->do( + my $sql = "DELETE FROM item_circulation_alert_preferences WHERE branchcode = ? AND categorycode = ? - AND item_type = ?", + AND item_type = ? + AND notification = ?"; + $dbh->do( + $sql, {}, $opts->{branchcode}, $opts->{categorycode}, - $opts->{item_type} + $opts->{item_type}, + $opts->{notification}, ); } } @@ -200,48 +233,52 @@ B: =cut -sub is_enabled_for { +sub is_disabled_for { my ($class, $opts) = @_; $valid->($opts); my $dbh = C4::Context->dbh; # Does a preference exist to block this alert? my $query = qq{ - SELECT id + SELECT id, branchcode, categorycode, item_type, notification FROM item_circulation_alert_preferences WHERE (branchcode = ? OR branchcode = '*') AND (categorycode = ? OR categorycode = '*') AND (item_type = ? OR item_type = '*') + AND (notification = ? OR notification = '*') }; my $preferences = $dbh->selectall_arrayref( $query, - { }, + { Slice => {} }, $opts->{branchcode}, $opts->{categorycode}, $opts->{item_type}, + $opts->{notification}, ); # If any preferences showed up, we are NOT enabled. - if (@$preferences) { - return undef; - } else { - return 1; - } + return @$preferences; +} + +sub is_enabled_for { + my ($class, $opts) = @_; + return not $class->is_disabled_for($opts); } -=head3 C4::ItemCirculationAlertPreference->find({ branchcode => $bc }) +=head3 C4::ItemCirculationAlertPreference->find({ branchcode => $bc, notification => $type }) This method returns all the item circulation alert preferences for a given -branch. +branch and notification. B: my @branch_prefs = C4::ItemCirculationAlertPreference->find({ - branchcode => 'CPL', + branchcode => 'CPL', + notification => 'CHECKIN', }); =cut @@ -250,21 +287,83 @@ sub find { my ($class, $where) = @_; my $dbh = C4::Context->dbh; my $query = qq{ - SELECT id, branchcode, categorycode, item_type + SELECT id, branchcode, categorycode, item_type, notification FROM item_circulation_alert_preferences - WHERE branchcode = ? + WHERE branchcode = ? AND notification = ? ORDER BY categorycode, item_type }; return map { $class->new($_) } @{$dbh->selectall_arrayref( $query, { Slice => {} }, - $where->{branchcode} + $where->{branchcode}, + $where->{notification}, )}; } +=head3 C4::ItemCirculationAlertPreference->grid({ branchcode => $c, notification => $type }) + +Return a 2D arrayref for the grid view in F. +Each row represents a category (like 'Patron' or 'Young Adult') and +each column represents an itemtype (like 'Book' or 'Music'). + +Each cell contains... + +B: + + use Data::Dump 'pp'; + my $grid = C4::ItemCirculationAlertPreference->grid({ + branchcode => 'CPL', + notification => 'CHECKOUT', + }); + warn pp($grid); + +See F to see how this method is used. + +=cut + +sub grid { + my ($class, $where) = @_; + my @branch_prefs = $class->find($where); + my @default_prefs = $class->find({ branchcode => '*', notification => $where->{notification} }); + my @cc = C4::Category->all; + my @it = C4::ItemType->all; + my $notification = $where->{notification}; + my %disabled = map { + my $key = $_->categorycode . "-" . $_->item_type . "-" . $notification; + $key =~ s/\*/_/g; + ($key => 1); + } @branch_prefs; + my %default = map { + my $key = $_->categorycode . "-" . $_->item_type . "-" . $notification; + $key =~ s/\*/_/g; + ($key => 1); + } @default_prefs; + my @grid; + for my $c (@cc) { + my $row = { description => $c->description, items => [] }; + push @grid, $row; + for my $i (@it) { + my $key = $c->categorycode . "-" . $i->itemtype . "-" . $notification; + $key =~ s/\*/_/g; + my @classes; + if ($disabled{$key}) { + push @classes, 'disabled'; + } + if ($default{$key}) { + push @classes, 'default'; + } + push @{$row->{items}}, { class => join(' ', @classes), id => $key }; + } + } + return \@grid; +} + + + + =head2 Object Methods These are read-only accessors for the various attributes of a preference. @@ -277,6 +376,8 @@ These are read-only accessors for the various attributes of a preference. =head3 $pref->item_type +=head3 $pref->notification + =cut sub AUTOLOAD { @@ -290,12 +391,13 @@ sub AUTOLOAD { } } +sub DESTROY { } =head1 SEE ALSO -L, C +L, F =head1 AUTHOR diff --git a/admin/item_circulation_alerts.pl b/admin/item_circulation_alerts.pl index f8c1227944..f2a9acea2d 100755 --- a/admin/item_circulation_alerts.pl +++ b/admin/item_circulation_alerts.pl @@ -22,6 +22,7 @@ use CGI; use File::Basename; use Encode; use URI::Escape 'uri_escape_utf8'; +use JSON; #use Data::Dump 'pp'; use C4::Auth; @@ -35,15 +36,6 @@ use C4::Output; # shortcut for long package name my $preferences = 'C4::ItemCirculationAlertPreference'; -# common redirect code -sub redirect { - my ($input) = @_; - my $path = defined($input->param('redirect_to')) - ? $input->param('redirect_to') - : basename($0); - print $input->redirect($path); -} - # utf8 filter sub utf8 { my ($data, @keys) = @_; @@ -53,15 +45,15 @@ sub utf8 { $data; } -# add long category and itemtype descriptions to preferences -sub category_and_itemtype { - my ($categories, $item_types, @prefs) = @_; - my %c = map { $_->{categorycode} => $_->{description} } @$categories; - my %i = map { $_->{itemtype} => $_->{description} } @$item_types; - for (@prefs) { - $_->{category_description} = $c{$_->{categorycode}} || 'Default'; - $_->{item_type_description} = $i{$_->{item_type}} || 'Default'; +# prepend "br_" to column name and replace spaces with "
" +sub br { + my ($data, @keys) = @_; + for (@keys) { + my $br = $data->{$_}; + $br =~ s{\s+}{
}g; + $data->{'br_'.$_} = $br; } + $data; } # display item circulation alerts @@ -94,61 +86,72 @@ sub show { my $branch_name = exists($br->{$branch}) && $br->{$branch}->{branchname}; my @categories = map { utf8($_, 'description') } ( - C4::Category->new({ categorycode => '*', description => 'Default' }), C4::Category->all ); - my @item_types = map { utf8($_, 'description') } ( - C4::ItemType->new({ itemtype => '*', description => 'Default' }), + my @item_types = map { utf8($_, 'description'); br($_, 'description') } ( C4::ItemType->all ); - my @default_prefs = $preferences->find({ branchcode => '*' }); - my @branch_prefs; - my $redirect_to = "?branch=$branch"; + my $grid_checkout = $preferences->grid({ branchcode => $branch, notification => 'CHECKOUT' }); + my $grid_checkin = $preferences->grid({ branchcode => $branch, notification => 'CHECKIN' }); - $template->param(redirect_to => $redirect_to); - $template->param(redirect_to_x => uri_escape_utf8($redirect_to)); $template->param(branch => $branch); - $template->param(branch_name => $branch_name); + $template->param(branch_name => $branch_name || 'Default'); $template->param(branches => \@branches); $template->param(categories => \@categories); $template->param(item_types => \@item_types); - $template->param(default_prefs => \@default_prefs); - if ($branch ne '*') { - @branch_prefs = $preferences->find({ branchcode => $branch }); - $template->param(branch_prefs => \@branch_prefs); - } - category_and_itemtype(\@categories, \@item_types, (@default_prefs, @branch_prefs)); + $template->param(grid_checkout => $grid_checkout); + $template->param(grid_checkin => $grid_checkin); + output_html_with_http_headers $input, $cookie, $template->output; } -# create item circulation alert preference and redirect -sub create { +# toggle a preference via ajax +sub toggle { my ($input) = @_; - my $branchcode = $input->param('branchcode'); - my $categorycode = $input->param('categorycode'); - my $item_type = $input->param('item_type'); - $preferences->create({ - branchcode => $branchcode, - categorycode => $categorycode, + my $id = $input->param('id'); + my $branch = $input->param('branch'); + my ($category, $item_type, $notification) = split('-', $id); + $category =~ s/_/*/; + $item_type =~ s/_/*/; + + my $settings = { + branchcode => $branch, + categorycode => $category, item_type => $item_type, - }); - redirect($input); -} + notification => $notification, + }; + + my $restrictions = $preferences; # all the same thing... + my $notifications = $preferences; # + if ($notifications->is_enabled_for($settings)) { + # toggle by adding a restriction + $restrictions->create($settings); + } else { + # toggle by removing the restriction + $restrictions->delete($settings); + } -# delete preference and redirect -sub delete { - my ($input) = @_; - my $id = $input->param('id'); - $preferences->delete({ id => $id }); - redirect($input); + my $response = { success => 1 }; + my @reasons = $notifications->is_disabled_for($settings); + if (@reasons == 0) { + $response->{class} = ''; + } else { + my $default_exists = grep { $_->{branchcode} eq '*' } @reasons; + my $non_default_also = grep { $_->{branchcode} ne '*' } @reasons; + my @classes; + push @classes, 'default' if $default_exists; + push @classes, 'disabled' if $non_default_also; + $response->{class} = join(' ', @classes); + } + print $input->header; + print encode_json($response); } # dispatch to various actions based on CGI parameter 'action' sub dispatch { my %handler = ( show => \&show, - create => \&create, - delete => \&delete, + toggle => \&toggle, ); my $input = new CGI; my $action = $input->param('action') || 'show'; @@ -203,35 +206,9 @@ branch '*' is used. +=head3 ?action=toggle -=head3 ?action=create - -Create an item circulation alert preference. - -Parameters: - -=over 4 - -=item branchcode - -Branch code - -=item categorycode - -Patron category - -=item item_type - -Item type - -=back - - - - -=head3 ?action=delete - -Delete an item circulation alert preference. +Toggle a preference via AJAX Parameters: @@ -239,12 +216,13 @@ Parameters: =item id -The id of the preference to delete. - -=back +"$categorycode-$item_type-$notification" +=item branch +Branch code to apply this preference to +=back =cut diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/item_circulation_alerts.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/item_circulation_alerts.tmpl index 26d27799b7..49bf6884a1 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/item_circulation_alerts.tmpl +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/item_circulation_alerts.tmpl @@ -8,7 +8,68 @@ div.circulation-alert h2 { div.circulation-alert h3 { margin-top: 1em; } + +table.grid thead th { + vertical-align: bottom; +} + +table.grid tbody th { + text-align: right; +} + +table.grid tbody td.info { + background: #fff; +} + +table.grid.active tbody td { + width: 10%; + cursor: pointer; +} + +table.grid tbody td { + background: #cfc; + color: #111; +} + +table.grid td.disabled { + background: #fcc; +} + +table.grid td.default { + background: #f88; +} + @@ -24,9 +85,9 @@ div.circulation-alert h3 {

Item Circulation Alerts

-

Pick a branch:

-
- @@ -35,105 +96,78 @@ div.circulation-alert h3 { - +
-

Circulation alerts are disabled for the following conditions:

-
- - -" /> -

Default (for all branches)

- +

Circulation Alerts for

+

Click on the grid to toggle the settings.

+ +

Checkout

+
- - - + + + + - + - - - + + + + - - - - - - -
Patron CategoryItem TypeAction 
&redirect_to=">delete" id=""> 
- - - - - -
-
- - -
- -" /> -" /> -

- +

Check-in

+
- - - + + + + - + - - - + + + + - - - - - - -
Patron CategoryItem TypeAction 
&redirect_to=">delete" id=""> 
- - - - - -
-
- + +

Legend

+ + + + + + + + + + + + + + + + + + + +
ColorMeaning
 These are disabled for ALL libraries. To change these settings, choose the "Default" library.
 These are disabled for the current library.
 These are enabled.
-- 2.39.2