use strict;
use warnings;
use C4::Context;
+use C4::Category;
+use C4::ItemType;
use Carp qw(carp croak);
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.");
}
};
# 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
=head3 C4::ItemCirculationAlertPreference->create(\%opts)
This will find or create an item circulation alert preference. You must pass
-it a B<branchcode>, B<categorycode>, and B<item_type>.
+it a B<branchcode>, B<categorycode>, B<item_type>, and B<notification>. 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
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 = {
branchcode => $opts->{branchcode},
categorycode => $opts->{categorycode},
item_type => $opts->{item_type},
+ notification => $opts->{notification},
};
return $class->new($self);
} else {
);
} 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},
);
}
}
=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<Example>:
my @branch_prefs = C4::ItemCirculationAlertPreference->find({
- branchcode => 'CPL',
+ branchcode => 'CPL',
+ notification => 'CHECKIN',
});
=cut
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<admin/item_circulation_alert_preferences.pl>.
+Each row represents a category (like 'Patron' or 'Young Adult') and
+each column represents an itemtype (like 'Book' or 'Music').
+
+Each cell contains...
+
+B<Example>:
+
+ use Data::Dump 'pp';
+ my $grid = C4::ItemCirculationAlertPreference->grid({
+ branchcode => 'CPL',
+ notification => 'CHECKOUT',
+ });
+ warn pp($grid);
+
+See F<admin/item_circulation_alerts.pl> 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.
=head3 $pref->item_type
+=head3 $pref->notification
+
=cut
sub AUTOLOAD {
}
}
+sub DESTROY { }
=head1 SEE ALSO
-L<C4::Circulation>, C<admin/item_circulation_alerts.pl>
+L<C4::Circulation>, F<admin/item_circulation_alerts.pl>
=head1 AUTHOR
use File::Basename;
use Encode;
use URI::Escape 'uri_escape_utf8';
+use JSON;
#use Data::Dump 'pp';
use C4::Auth;
# 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) = @_;
$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 "<br/>"
+sub br {
+ my ($data, @keys) = @_;
+ for (@keys) {
+ my $br = $data->{$_};
+ $br =~ s{\s+}{<br/>}g;
+ $data->{'br_'.$_} = $br;
}
+ $data;
}
# display item circulation alerts
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';
+=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:
=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
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;
+}
</style>
+<script>
+var $branch = "<!-- TMPL_VAR NAME="branch" -->";
+$(function(){
+
+ $('#branch_selector input:submit').hide();
+ $('#branch').change(function(){
+ $('#branch_selector').submit();
+ });
+
+ $('table.grid.active tbody td').click(function(ev){
+ var id = this.id;
+ var td = $(this);
+ if (td.hasClass('default') && $branch != '*') {
+ td.html(_('Blocked'));
+ } else {
+ td.html(_('Saving...'));
+ $.ajax({
+ url : '/cgi-bin/koha/admin/item_circulation_alerts.pl',
+ type : 'POST',
+ dataType : 'json',
+ data : { action: 'toggle', id: id, branch: $branch },
+ success : function(response){
+ td.html(' ');
+ td.attr('class', response.class);
+ }
+ });
+ }
+ });
+
+});
+</script>
</head>
<body>
<!-- TMPL_INCLUDE NAME="header.inc" -->
<h1>Item Circulation Alerts</h1>
-<h2>Pick a branch:</h2>
-<form method="GET">
-<select name="branch">
+<h2>Select a library:</h2>
+<form id="branch_selector" method="GET">
+<select id="branch" name="branch">
<!-- TMPL_LOOP NAME="branches" -->
<!-- TMPL_IF NAME="selected" -->
<option value="<!-- TMPL_VAR NAME="branchcode" -->" selected="selected"><!-- TMPL_VAR NAME="branchname" --></option>
<!-- /TMPL_IF -->
<!-- /TMPL_LOOP -->
</select>
-<input type="submit" name="submit" value="Pick" />
+<input type="submit" name="pick" value="Pick" />
</form>
-<h2>Circulation alerts are disabled for the following conditions:</h2>
-<form method="POST">
-<input type="hidden" name="action" value="create" />
-<input type="hidden" name="branchcode" value="*" />
-<input type="hidden" name="redirect_to" value="<!-- TMPL_VAR NAME="redirect_to" -->" />
-<h3>Default (for all branches)</h3>
-<table>
+<h2>Circulation Alerts for <!-- TMPL_VAR NAME="branch_name" --></h2>
+<p>Click on the grid to toggle the settings.</p>
+
+<h3>Checkout</h3>
+<table class="grid active" width="100%">
<thead>
<tr>
-<th>Patron Category</th>
-<th>Item Type</th>
-<th>Action</th>
+ <th> </th>
+ <!-- TMPL_LOOP NAME="item_types" -->
+ <th><!-- TMPL_VAR NAME="br_description" --></th>
+ <!-- /TMPL_LOOP -->
</tr>
</thead>
<tbody>
-<!-- TMPL_LOOP NAME="default_prefs" -->
+<!-- TMPL_LOOP NAME="grid_checkout" -->
<tr>
-<td><!-- TMPL_VAR NAME="category_description" --></td>
-<td><!-- TMPL_VAR NAME="item_type_description" --></td>
-<td><a href="?action=delete&id=<!-- TMPL_VAR NAME="id" -->&redirect_to=<!-- TMPL_VAR NAME="redirect_to_x" -->">delete</a></td>
+ <th><!-- TMPL_VAR NAME="description" --></th>
+ <!-- TMPL_LOOP NAME="items" -->
+ <td class="<!-- TMPL_VAR NAME="class" -->" id="<!-- TMPL_VAR NAME="id" -->"> </td>
+ <!-- /TMPL_LOOP -->
</tr>
<!-- /TMPL_LOOP -->
</tbody>
-<tfoot>
-<tr>
-<td>
-<select name="categorycode">
-<!-- TMPL_LOOP NAME="categories" -->
-<option value="<!-- TMPL_VAR NAME="categorycode" -->"><!-- TMPL_VAR NAME="description" --></option>
-<!-- /TMPL_LOOP -->
-</select>
-</td>
-<td>
-<select name="item_type">
-<!-- TMPL_LOOP NAME="item_types" -->
-<option value="<!-- TMPL_VAR NAME="itemtype" -->"><!-- TMPL_VAR NAME="description" --></option>
-<!-- /TMPL_LOOP -->
-</select>
-</td>
-<td>
-<input type="submit" name="submit" value="Disable Alert" />
-</td>
-</tr>
-</tfoot>
</table>
-</form>
-
-<!-- TMPL_IF NAME="branch_name" -->
-<form method="POST">
-<input type="hidden" name="action" value="create" />
-<input type="hidden" name="branchcode" value="<!-- TMPL_VAR NAME="branch" -->" />
-<input type="hidden" name="redirect_to" value="<!-- TMPL_VAR NAME="redirect_to" -->" />
-<h3><!-- TMPL_VAR NAME="branch_name" --></h3>
-<table>
+<h3>Check-in</h3>
+<table class="grid active" width="100%">
<thead>
<tr>
-<th>Patron Category</th>
-<th>Item Type</th>
-<th>Action</th>
+ <th> </th>
+ <!-- TMPL_LOOP NAME="item_types" -->
+ <th><!-- TMPL_VAR NAME="br_description" --></th>
+ <!-- /TMPL_LOOP -->
</tr>
</thead>
<tbody>
-<!-- TMPL_LOOP NAME="branch_prefs" -->
+<!-- TMPL_LOOP NAME="grid_checkin" -->
<tr>
-<td><!-- TMPL_VAR NAME="category_description" --></td>
-<td><!-- TMPL_VAR NAME="item_type_description" --></td>
-<td><a href="?action=delete&id=<!-- TMPL_VAR NAME="id" -->&redirect_to=<!-- TMPL_VAR NAME="redirect_to_x" -->">delete</a></td>
+ <th><!-- TMPL_VAR NAME="description" --></th>
+ <!-- TMPL_LOOP NAME="items" -->
+ <td class="<!-- TMPL_VAR NAME="class" -->" id="<!-- TMPL_VAR NAME="id" -->"> </td>
+ <!-- /TMPL_LOOP -->
</tr>
<!-- /TMPL_LOOP -->
</tbody>
-<tfoot>
-<tr>
-<td>
-<select name="categorycode">
-<!-- TMPL_LOOP NAME="categories" -->
-<option value="<!-- TMPL_VAR NAME="categorycode" -->"><!-- TMPL_VAR NAME="description" --></option>
-<!-- /TMPL_LOOP -->
-</select>
-</td>
-<td>
-<select name="item_type">
-<!-- TMPL_LOOP NAME="item_types" -->
-<option value="<!-- TMPL_VAR NAME="itemtype" -->"><!-- TMPL_VAR NAME="description" --></option>
-<!-- /TMPL_LOOP -->
-</select>
-</td>
-<td>
-<input type="submit" name="submit" value="Disable Alert" />
-</td>
-</tr>
-</tfoot>
</table>
-</form>
-<!-- /TMPL_IF -->
+
+<h3>Legend</h3>
+<table class="grid">
+<thead>
+ <th>Color</th>
+ <th>Meaning</th>
+</thead>
+<tbody>
+ <tr>
+ <td width="100" class="default"> </td>
+ <td class="info">These are disabled for ALL libraries. To change these settings, choose the "Default" library.</td>
+ </tr>
+ <tr>
+ <td class="disabled"> </td>
+ <td class="info">These are disabled for the current library.</td>
+ </tr>
+ <tr>
+ <td class=""> </td>
+ <td class="info">These are enabled.</td>
+ </tr>
+</tbody>
+</table>
</div>