Sorted the same way the user is currently viewing his/her bookbags.
Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
return $attrs;
}
+sub bib_container_items_via_search {
+ my ($class, $container_id, $search_query, $search_args) = @_;
+
+ # First, Use search API to get container items sorted in any way that crad
+ # sorters support.
+ my $search_result = $class->simplereq(
+ "open-ils.search", "open-ils.search.biblio.multiclass.query",
+ $search_args, $search_query
+ );
+ unless ($search_result) {
+ # empty result sets won't cause this, but actual errors should.
+ $logger->warn("bib_container_items_via_search() got nothing from search");
+ return;
+ }
+
+ # Throw away other junk from search, keeping only bib IDs.
+ my $id_list = [ map { pop @$_ } @{$search_result->{ids}} ];
+
+ return [] unless @$id_list;
+
+ # Now get the bib container items themselves...
+ my $e = new OpenILS::Utils::CStoreEditor;
+ unless ($e) {
+ $logger->warn("bib_container_items_via_search() couldn't get cstoreeditor");
+ return;
+ }
+
+ my $items = $e->search_container_biblio_record_entry_bucket_item([
+ {
+ "target_biblio_record_entry" => $id_list,
+ "bucket" => $container_id
+ }, {
+ flesh => 1,
+ flesh_fields => {"cbrebi" => [qw/notes target_biblio_record_entry/]}
+ }
+ ]);
+ unless ($items) {
+ $logger->warn(
+ "bib_container_items_via_search() couldn't get bucket items: " .
+ $e->die_event->{textcode}
+ );
+ return;
+ }
+
+ $e->disconnect;
+
+ # ... and put them in the same order that the search API said they
+ # should be in.
+ my %ordering_hash = map { $_->target_biblio_record_entry->id, $_ } @$items;
+ return [map { $ordering_hash{$_} } @$id_list];
+}
+
1;
use DateTime;
use DateTime::Format::ISO8601;
use Unicode::Normalize;
+use XML::LibXML;
use OpenSRF::Utils qw/:datetime/;
use OpenSRF::Utils::Logger qw(:logger);
use OpenILS::Application::AppUtils;
return;
},
+ csv_datum => sub {
+ my ($str) = @_;
+
+ if ($str =~ /\,/ || $str =~ /"/) {
+ $str =~ s/"/""/g;
+ $str = '"' . $str . '"';
+ }
+
+ return $str;
+ },
+
+ xml_doc => sub {
+ my ($str) = @_;
+ return $str ? (new XML::LibXML)->parse_string($str) : undef;
+ }
+
};
--- /dev/null
+package OpenILS::Application::Trigger::Reactor::ContainerCSV;
+use base "OpenILS::Application::Trigger::Reactor";
+use strict;
+use warnings;
+use OpenSRF::Utils::Logger qw/:logger/;
+use Data::Dumper;
+$Data::Dumper::Indent = 0;
+my $U = "OpenILS::Application::AppUtils";
+
+sub ABOUT {
+ return q|
+
+The ContainerCSV Reactor Module processes the configured template after
+fetching the items from the bookbag refererred to in $env->{target}
+by using the search api with the query in $env->{params}{search}. It's
+the event-creator's responsibility to build a correct search query and check
+permissions and do that sort of thing.
+
+open-ils.trigger is not a public service, so that should be ok.
+
+The output, like all processed templates, is stored in the event_output table.
+
+|;
+}
+
+sub handler {
+ my ($self, $env) = @_;
+
+ # get items for bookbags (bib containers of btype bookbag)
+ if ($env->{user_data}{item_search}) {
+ # use the search api for bib container items
+ my $items = $U->bib_container_items_via_search(
+ $env->{target}->id, $env->{user_data}{item_search}
+ ) or return 0; # TODO build error output for db?
+
+ $env->{items} = $items;
+ } else {
+ # XXX TODO If we're going to support other types of containers here,
+ # we'll probably just want to flesh those containers' items directly,
+ # not involve the search API.
+
+ $logger->warn("ContainerCSV reactor used without item_search, doesn't know what to do."); # XXX
+ }
+
+ return 1 if $self->run_TT($env);
+ return 0;
+}
+
+1;
return $self->load_myopac_update_password if $path =~ m|opac/myopac/update_password|;
return $self->load_myopac_update_username if $path =~ m|opac/myopac/update_username|;
return $self->load_myopac_bookbags if $path =~ m|opac/myopac/lists|;
+ return $self->load_myopac_bookbag_print if $path =~ m|opac/myopac/list/print|;
return $self->load_myopac_bookbag_update if $path =~ m|opac/myopac/list/update|;
return $self->load_myopac_circ_history if $path =~ m|opac/myopac/circ_history|;
return $self->load_myopac_hold_history if $path =~ m|opac/myopac/hold_history|;
my $sorter = $self->cgi->param("sort") || "";
my $modifier = ($sorter =~ /\.(.+$)/) ? $1 : undef;
+ $sorter =~ s/\..+$// if $sorter;
$e->xact_begin; # replication...
return 1; # success
}
-1
+sub load_myopac_bookbag_print {
+ my ($self) = @_;
+
+ $self->apache->content_type("text/plain; encoding=utf8");
+
+ my $id = int($self->cgi->param("list"));
+
+ # Prepare bib search query for bookbag's items, but don't use it yet.
+ my $sorter = $self->cgi->param("sort") || "";
+ my $modifier = ($sorter =~ /\.(.+$)/) ? $1 : undef;
+ $sorter =~ s/\..+$// if $sorter;
+
+ my $item_search = sprintf(
+ "container(bre,bookbag,%d,%s)%s%s",
+ $id, $self->editor->authtoken,
+ ($sorter ? " sort($sorter)" : ""),
+ ($modifier ? "#$modifier" : "")
+ );
+
+ my $bbag;
+
+ # Get the bookbag object itself, assuming we're allowed to.
+ if ($self->editor->allowed("VIEW_CONTAINER")) {
+
+ $bbag = $self->editor->retrieve_container_biblio_record_entry_bucket($id) or return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
+ } else {
+ my $bookbags = $self->editor->search_container_biblio_record_entry_bucket(
+ {
+ "id" => $id,
+ "-or" => {
+ "owner" => $self->editor->requestor->id,
+ "pub" => "t"
+ }
+ }
+ ) or return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
+
+ $bbag = pop @$bookbags;
+ }
+
+ # If we have a bookbag we're allowed to look at, issue the A/T event
+ # to get CSV, passing as a user param that search query we built before.
+ if ($bbag) {
+ $self->ctx->{csv} = $U->fire_object_event(
+ undef, "container.biblio_record_entry_bucket.csv",
+ $bbag, $self->editor->requestor->home_ou,
+ undef, {"item_search" => $item_search}
+ );
+ }
+
+ return Apache2::Const::OK;
+}
+
+1;
ALTER TABLE container.user_bucket
ADD COLUMN description TEXT;
+INSERT INTO action_trigger.hook (key, core_type, description, passive)
+VALUES (
+ 'container.biblio_record_entry_bucket.csv',
+ 'cbreb',
+ oils_i18n_gettext(
+ 'container.biblio_record_entry_bucket.csv',
+ 'Produce a CSV file representing a bookbag',
+ 'ath',
+ 'description'
+ ),
+ FALSE
+);
+
+INSERT INTO action_trigger.reactor (module, description)
+VALUES (
+ 'ContainerCSV',
+ oils_i18n_gettext(
+ 'ContainerCSV',
+ 'Facilitates produce a CSV file representing a bookbag by introducing an "items" variable into the TT environment, sorted as dictated according to user params',
+ 'atr',
+ 'description'
+ )
+);
+
+INSERT INTO action_trigger.event_definition (
+ id, active, owner,
+ name, hook, reactor,
+ validator, template
+) VALUES (
+ 48, TRUE, 1,
+ 'Bookbag CSV', 'container.biblio_record_entry_bucket.csv', 'ContainerCSV',
+ 'NOOP_True',
+$$
+[%-
+# target is the bookbag itself. The 'items' variable does not need to be in
+# the environment because a special reactor will take care of filling it in.
+
+FOR item IN items;
+ bibxml = helpers.xml_doc(item.target_biblio_record_entry.marc);
+ title = "";
+ FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
+ title = title _ part.textContent;
+ END;
+ author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
+
+ helpers.csv_datum(title) %],[% helpers.csv_datum(author) %],[% FOR note IN item.notes; helpers.csv_datum(note.note); ","; END; "\n";
+END -%]
+$$
+);
+
COMMIT;
--- /dev/null
+[%- ctx.csv.template_output.data -%]
<input type="submit" value="[% l('Delete List') %]" />
</div>
</form>
+ <form action="[% ctx.opac_root %]/myopac/list/print" method="POST" target="_blank">
+ <div class="bookbag-controls">
+ <input type="hidden" name="list" value="[% bbag.id %]" />
+ <input type="hidden" name="sort" value="[% CGI.param('sort') | html %]" />
+ <input type="submit" value="[% l('Download CSV') %]" />
+ </div>
+ </form>
<div class="bookbag-controls">
<big><strong>
[% IF bbag.pub == 't' %]