From 6d54aa97f08c111862b107fef02bb30b14ec36b4 Mon Sep 17 00:00:00 2001 From: Stephen Edwards Date: Wed, 1 Apr 2009 13:19:35 -0400 Subject: [PATCH] bug 3034: Tag multiple items at once. Added a "Tag" button in the toolbar area of the results page. This activates an inline form that allows the user to enter a tag that can then be applied to all currently selected items. In addition, a "Tag" link has been added to both the List and Cart pages that provides the same functionality in those contexts. In these two cases, the a per-item states appears below the bibliographic information for each item. Status that applies to the overall operation, such as failing to provide a tag, will be shown in an alert dialog box. Signed-off-by: Galen Charlton --- koha-tmpl/opac-tmpl/prog/en/css/opac.css | 2 +- koha-tmpl/opac-tmpl/prog/en/js/tags.js | 86 +- .../prog/en/modules/opac-basket.tmpl | 88 +- .../prog/en/modules/opac-reserve.tmpl | 14 +- .../prog/en/modules/opac-results.tmpl | 85 +- .../prog/en/modules/opac-shelves.tmpl | 765 ++++++++++-------- opac/opac-basket.pl | 6 + opac/opac-tags.pl | 46 +- 8 files changed, 760 insertions(+), 332 deletions(-) diff --git a/koha-tmpl/opac-tmpl/prog/en/css/opac.css b/koha-tmpl/opac-tmpl/prog/en/css/opac.css index cb5697a777..6bf54e073a 100644 --- a/koha-tmpl/opac-tmpl/prog/en/css/opac.css +++ b/koha-tmpl/opac-tmpl/prog/en/css/opac.css @@ -870,7 +870,7 @@ td.resultscontrol img { color:#006699; } -.unavailable { +.unavailable, .tagerror, .tagerror:visited { color: #990033; } diff --git a/koha-tmpl/opac-tmpl/prog/en/js/tags.js b/koha-tmpl/opac-tmpl/prog/en/js/tags.js index e27447de35..83f5be895d 100644 --- a/koha-tmpl/opac-tmpl/prog/en/js/tags.js +++ b/koha-tmpl/opac-tmpl/prog/en/js/tags.js @@ -63,6 +63,10 @@ KOHA.Tags = { $(tagid).html(newstatus); $(tagid).css({display:"inline"}); }, + append_tag_status : function(tagid, newstatus) { + $(tagid).append(newstatus); + $(tagid).css({display:"inline"}); + }, tag_message: { tagsdisabled : function(arg) {return (_("Sorry, tags are not enabled on this system."));}, @@ -70,7 +74,85 @@ KOHA.Tags = { badparam : function(arg) {return (_("Error! Illegal parameter '" +arg+ "'."));}, scrubbed : function(arg) {return (_("Note: your tag contained markup code that was removed. The tag was added as '" +arg+ "'."));}, failed_add_tag : function(arg) {return (_("Error! The add_tag operation failed on '" +arg+ "'. Note: you can only tag an item with a given term once. Check 'My Tags' to see your current tags."));}, - failed_delete : function(arg) {return (_("Error! You cannot delete the tag '" +arg+ "'. Note: you can only delete your own tags."));} - } + failed_delete : function(arg) {return (_("Error! You cannot delete the tag '" +arg+ "'. Note: you can only delete your own tags."));}, + login : function(arg) {return (_("You must be logged in to add tags."));}, + }, + + // Used to tag multiple items at once. The main difference + // is that status is displayed on a per item basis. + add_multitags_button : function(bibarray, tag){ + var mydata = {CGISESSID: readCookie('CGISESSID')}; // Someday this should be OPACSESSID + for (var i = 0; i < bibarray.length; i++) { + var mynewtag = "newtag" + bibarray[i]; + mydata[mynewtag] = tag; + } + var response; // AJAX from server will assign value to response. + $.post( + "/cgi-bin/koha/opac-tags.pl", + mydata, + function(data){ + eval(data); + $(".tagstatus").empty(); + var bibErrors = false; + + // Display the status for each tagged bib + for (var i = 0; i < bibarray.length; i++) { + var bib = bibarray[i]; + var mytagid = "#newtag" + bib; + var status = ""; + + // Number of tags added. + if (response[bib]) { + var added = response[bib]["added"]; + if (added > 0) { + status = "Added " + added + (added == 1 ? " tag" : " tags") + ". "; + KOHA.Tags.set_tag_status(mytagid + "_status", status); + } + + // Show a link that opens an error dialog, if necessary. + var errors = response[bib]["errors"]; + if (errors.length > 0) { + bibErrors = true; + var errid = "tagerr_" + bib; + var errstat = ""; + errstat += "Error" + (errors.length > 1 ? "s" : "") + " adding tag."; + errstat += ""; + KOHA.Tags.append_tag_status(mytagid + "_status", errstat); + var errmsg = ""; + for (var e = 0; e < errors.length; e++){ + if (e) { + errmsg += "\n\n"; + } + errmsg += errors[e]; + } + $("#" + errid).click(function(){ + alert(errmsg); + }); + } + } + } + + if (bibErrors || response["global_errors"]) { + var msg = ""; + if (bibErrors) { + msg = "Unable to add one or more tags."; + } + + // Show global errors in a dialog. + if (response["global_errors"]) { + var global_errors = response["global_errors"]; + var msg; + for (var e = 0; e < global_errors.length; e++) { + msg += "\n\n"; + msg += response.alerts[global_errors[e]]; + } + } + alert(msg); + } + }, + 'script' + ); + return false; + } }; diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-basket.tmpl b/koha-tmpl/opac-tmpl/prog/en/modules/opac-basket.tmpl index f575d7cc87..a102457bb8 100644 --- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-basket.tmpl +++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-basket.tmpl @@ -11,6 +11,52 @@ @@ -66,11 +125,23 @@

Select All Clear All | Selected items : + Remove | Add to a list - + + + + + +

@@ -211,12 +282,21 @@ - @@ -242,6 +322,8 @@ &rft.btitle=&rft.date=&rft.tpages=&rft.isbn=&rft.aucorp=&rft.place=&rft.pub=&rft.edition=&rft.series=&rft.genre="> +
_status" class="tagstatus results_summary" style="display:none">Tag status here.
+ @@ -267,9 +267,9 @@ "/> "> - " - name="checkitem_" disabled="disabled" - id="checkitem_" + " + name="" disabled="disabled" + id="" value="any" /> diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-shelves.tmpl b/koha-tmpl/opac-tmpl/prog/en/modules/opac-shelves.tmpl index a54b34bebb..f6b8449ae9 100644 --- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-shelves.tmpl +++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-shelves.tmpl @@ -3,6 +3,8 @@ -
-
- +
+
+ +
+
+
+ + + +
+
+
A List named already exists!
+
+
ERROR: No barcode given.
+
ERROR: No shelfnumber given.
+ +
+ The list is not empty. +
It has entries. +
Use the "Confirm" button below to confirm deletion. +
+ + +
ERROR: You do not have adequate permission for that action on list .
+ + +
ERROR: No item found with barcode .
+ + +
A record matching barcode has already been added.
+ + +
List Deleted.
+ + +
ERROR: Database error. Delete (list number ) failed.
+ + +
ERROR: List number unrecognized.
+ +
+
+ + -
-
-
- - -
-
-
A List named already exists!
-
-
ERROR: No barcode given.
-
ERROR: No shelfnumber given.
- -
The list is not empty. -
It has entries. -
Use the "Confirm" button below to confirm deletion. -
- - -
ERROR: You do not have adequate permission for that action on list .
- - -
ERROR: No item found with barcode .
- - -
A record matching barcode has already been added.
- - -
List Deleted.
- - -
ERROR: Database error. Delete (list number ) failed.
- - -
ERROR: List number unrecognized.
- -
-
- - +
+ -
- - - - -

Lists /../images/caret.gif" width="16" height="16" alt=">" border="0" />

- - -
- " /> - - -
Select All Clear All | Selected items : +
Select All Clear All | Selected items : + Remove | Add to a list + + +
diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tmpl b/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tmpl index 214a7ba50f..4e015a2ca0 100644 --- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tmpl +++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tmpl @@ -254,11 +254,11 @@ "/> "> - " - name="checkitem_" checked="checked" - id="checkitem_" + " + name="" checked="checked" + id="" value="any" /> - + diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tmpl b/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tmpl index 29b78826bf..35fe0f9ebd 100644 --- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tmpl +++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tmpl @@ -39,6 +39,45 @@ function holdBiblioNums(numList) { } +function tagSelected() { + var checkedBoxes = $(".searchresults :checkbox:checked"); + if ($(checkedBoxes).size() == 0) { + alert(MSG_NO_RECORD_SELECTED); + } else { + $("#tagsel_tag").hide(); + $("#sort_by").hide(); + $("#tagsel_form").show(); + } +} + +function tagCanceled() { + $("#tagsel_form").hide(); + $("#tagsel_tag").show(); + $("#sort_by").show(); +} + +function tagAdded() { + var checkedBoxes = $(".searchresults :checkbox:checked"); + if ($(checkedBoxes).size() == 0) { + alert(MSG_NO_RECORD_SELECTED); + return false; + } + + var tag = $("#tagsel_new").val(); + if (!tag || (tag == "")) { + return false; + } + + var bibs = []; + for (var i = 0; i < $(checkedBoxes).size(); i++) { + var box = $(checkedBoxes).get(i); + bibs[i] = $(box).val(); + } + + KOHA.Tags.add_multitags_button(bibs, tag); + return false; +} + $(document).ready(function(){ var param1 = "
- - - - - - - - - - - - + +

Lists /../images/caret.gif" width="16" height="16" alt=">" border="0" />

+ + +
+ + + | + + &op=modif">Edit List + + + + + + "/> + + + ','win_form','dependant=yes,scrollbars=no,resizable=no,height=300,width=450,top=50,left=100')">Send List + + + + +
- + + " /> + - + +
 Item TypeTitleAuthorCopyright
- " /> -
- - " alt="" title="" /> - - -
+ + + + + + + + + + + + + + + + + + + + + + + +
 Item TypeTitleAuthorCopyright
" + value=" + + + " alt="" title="" /> + + + "> + + + "> + + "> + + + + &rft.btitle=&rft.date=&rft.tpages=&rft.isbn=&rft.aucorp=&rft.place=&rft.pub=&rft.edition=&rft.series=&rft.genre="> +
+
_status" class="tagstatus results_summary" style="display:none">Tag status here.
+
+ +
This List is empty. You can add to your lists from the results of any search!
+ + + - "> + + + + " /> + + " /> - "> - "> - - &rft.btitle=&rft.date=&rft.tpages=&rft.isbn=&rft.aucorp=&rft.place=&rft.pub=&rft.edition=&rft.series=&rft.genre="> - - - - - - - -
This List is empty. You can add to your lists from the results of any search!
- - - + + " value="1" /> + + + + + - - - - " /> - - " /> - -
- " value="1" /> - - - -
- + + +

Lists /../images/caret.gif" width="16" height="16" alt=">" border="0" /> "> /../images/caret.gif" width="16" height="16" alt=">" border="0" /> Editing

+
+ + " /> +
+
    +
  1. " />
  2. +
  3. + + +
  4. +
  5. + + +
  6. +
+
+
">Cancel
+
+ - -

Lists /../images/caret.gif" width="16" height="16" alt=">" border="0" /> "> /../images/caret.gif" width="16" height="16" alt=">" border="0" /> Editing

-
- - " /> -
-
    -
  1. " />
  2. -
  3. -
  4. -
  5. - -
  6. -
-
-
">Cancel
-
- - -

Lists

- - -
- - +
+ + +
+
+ + +
+
+ + - -
-
- Create a New List - -
  1. - - " size="60" /> - - - - " />
  2. -
  3. -
  4. -
  5. -
  6. -
-
-
Cancel
-
- - - - - -
-
- - -
-
- - -
+
diff --git a/opac/opac-basket.pl b/opac/opac-basket.pl index 399e666f3d..154d8afab4 100755 --- a/opac/opac-basket.pl +++ b/opac/opac-basket.pl @@ -48,6 +48,12 @@ my @results; my $num = 1; my $marcflavour = C4::Context->preference('marcflavour'); +if (C4::Context->preference('TagsEnabled')) { + $template->param(TagsEnabled => 1); + foreach (qw(TagsShowOnList TagsInputOnList)) { + C4::Context->preference($_) and $template->param($_ => 1); + } +} foreach my $biblionumber ( @bibs ) { diff --git a/opac/opac-tags.pl b/opac/opac-tags.pl index 2b9dfd7955..76c3abcf07 100755 --- a/opac/opac-tags.pl +++ b/opac/opac-tags.pl @@ -44,6 +44,10 @@ my %newtags = (); my @deltags = (); my %counts = (); my @errors = (); +my $perBibResults = {}; + +# Indexes of @errors that do not apply to a particular biblionumber. +my @globalErrorIndexes = (); sub ajax_auth_cgi ($) { # returns CGI object my $needed_flags = shift; @@ -75,6 +79,7 @@ my $openadds = C4::Context->preference('TagsModeration') ? 0 : 1; my $query = ($is_ajax) ? &ajax_auth_cgi({}) : CGI->new(); unless (C4::Context->preference('TagsEnabled')) { push @errors, {+ tagsdisabled=>1 }; + push @globalErrorIndexes, $#errors; } else { foreach ($query->param) { if (/^newtag(.*)/) { @@ -82,6 +87,7 @@ unless (C4::Context->preference('TagsEnabled')) { unless ($biblionumber =~ /^\d+$/) { $debug and warn "$_ references non numerical biblionumber '$biblionumber'"; push @errors, {+'badparam' => $_ }; + push @globalErrorIndexes, $#errors; next; } $newtags{$biblionumber} = $query->param($_); @@ -109,6 +115,7 @@ if ($is_ajax) { if ($add_op) { unless ($loggedinuser) { push @errors, {+'login' => 1 }; + push @globalErrorIndexes, $#errors; %newtags=(); # zero out any attempted additions @deltags=(); # zero out any attempted deletions } @@ -119,6 +126,7 @@ my @newtags_keys = (keys %newtags); if (scalar @newtags_keys) { $scrubber = C4::Scrubber->new(); foreach my $biblionumber (@newtags_keys) { + my $bibResults = {adds=>0, errors=>[]}; my @values = split /[;,]/, $newtags{$biblionumber}; foreach (@values) { s/^\s*(.+)\s*$/$1/; @@ -126,8 +134,10 @@ if (scalar @newtags_keys) { unless ($clean_tag eq $_) { if ($clean_tag =~ /\S/) { push @errors, {scrubbed=>$clean_tag}; + push @{$bibResults->{errors}}, {scrubbed=>$clean_tag}; } else { push @errors, {scrubbed_all_bad=>1}; + push @{$bibResults->{errors}}, {scrubbed_all_bad=>1}; next; # we don't add it if there's nothing left! } } @@ -136,11 +146,14 @@ if (scalar @newtags_keys) { add_tag($biblionumber,$clean_tag,$loggedinuser) ; if ($result) { $counts{$biblionumber}++; + $bibResults->{adds}++; } else { push @errors, {failed_add_tag=>$clean_tag}; + push @{$bibResults->{errors}}, {failed_add_tag=>$clean_tag}; $debug and warn "add_tag($biblionumber,$clean_tag,$loggedinuser...) returned bad result (" . (defined $result ? $result : 'UNDEF') .")"; } } + $perBibResults->{$biblionumber} = $bibResults; } } my $dels = 0; @@ -156,6 +169,19 @@ if ($is_ajax) { my $sum = 0; foreach (values %counts) {$sum += $_;} my $js_reply = sprintf("response = {\n\tadded: %d,\n\tdeleted: %d,\n\terrors: %d",$sum,$dels,scalar @errors); + + # If no add attempts were made, flag global errors. + if (@globalErrorIndexes) { + $js_reply .= ",\n\tglobal_errors: ["; + my $first = 1; + foreach (@globalErrorIndexes) { + $js_reply .= "," unless $first; + $first = 0; + $js_reply .= "\n\t\t$_"; + } + $js_reply .= "\n\t]"; + } + my $err_string = ''; if (scalar @errors) { $err_string = ",\n\talerts: ["; # open response_function @@ -168,7 +194,25 @@ if ($is_ajax) { } $err_string .= "\n\t]\n"; # close response_function } - output_ajax_with_http_headers($query, "$js_reply\n$err_string};"); + + # Add per-biblionumber results for use on results page + my $js_perbib = ""; + for my $bib (keys %$perBibResults) { + my $bibResult = $perBibResults->{$bib}; + my $js_bibres = ",\n\t$bib: {\n\t\tadded: $bibResult->{adds}"; + $js_bibres .= ",\n\t\terrors: ["; + my $i = 0; + foreach (@{$bibResult->{errors}}) { + $js_bibres .= "," if ($i); + my $key = (keys %$_)[0]; + $js_bibres .= "\n\t\t\t KOHA.Tags.tag_message.$key(\"" . $_->{$key} . '")'; + $i++; + } + $js_bibres .= "\n\t\t]\n\t}"; + $js_perbib .= $js_bibres; + } + + output_ajax_with_http_headers($query, "$js_reply\n$err_string\n$js_perbib\n};"); exit; } -- 2.39.5