Bugzilla – Attachment 170789 Details for
Bug 37637
Use the REST API to display items on the staff edit items view
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 37637: Use the API to load the items table
Bug-37637-Use-the-API-to-load-the-items-table.patch (text/plain), 25.48 KB, created by
Lucas Gass (lukeg)
on 2024-08-27 22:43:45 UTC
(
hide
)
Description:
Bug 37637: Use the API to load the items table
Filename:
MIME Type:
Creator:
Lucas Gass (lukeg)
Created:
2024-08-27 22:43:45 UTC
Size:
25.48 KB
patch
obsolete
>From e5e3d922b7135672af994befb50872fe29bc3463 Mon Sep 17 00:00:00 2001 >From: Sam Lau <samalau@gmail.com> >Date: Mon, 19 Aug 2024 20:44:22 +0000 >Subject: [PATCH] Bug 37637: Use the API to load the items table > >This patch removes the previous use of the database to load the additem table and instead uses the API. > >To test: >1) Test the current datatable behavior: visit an item's detail page and select 'Edit' then 'Edit items' >2) Apply the patches, restart_all, reset_all, clear caches, etc. >3) Again, test the table behaviors. Try adding items, using the actions, filtering and sorting the items, etc. >--- > cataloguing/additem.pl | 3 + > .../prog/en/modules/cataloguing/additem.tt | 41 ++ > .../prog/js/cataloging_additem.js | 493 +++++++++++++++++- > 3 files changed, 511 insertions(+), 26 deletions(-) > >diff --git a/cataloguing/additem.pl b/cataloguing/additem.pl >index 3d361ec1e63..1982fd15844 100755 >--- a/cataloguing/additem.pl >+++ b/cataloguing/additem.pl >@@ -679,6 +679,9 @@ if ($op) { > > # now, build existiing item list > >+my @libraries = $patron->libraries_where_can_edit_items; >+$template->param(can_edit_items_from => \@libraries); >+ > my @items; > for my $item ( $biblio->items->as_list, $biblio->host_items->as_list ) { > my $i = $item->columns_to_str; >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tt >index cf7259975c6..90db8345090 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tt >@@ -5,6 +5,11 @@ > [% USE KohaDates %] > [% USE Price %] > [% USE TablesSettings %] >+[% USE ItemTypes %] >+[% USE To %] >+[% USE tojson = To %] >+[% USE AuthorisedValues %] >+[% USE ClassSources %] > [% PROCESS 'i18n.inc' %] > [% INCLUDE 'doc-head-open.inc' %] > <title>[% FILTER collapse %] >@@ -34,7 +39,39 @@ > [% Asset.js("js/browser.js") | $raw %] > [% INCLUDE 'calendar.inc' %] > [% INCLUDE 'str/cataloging_additem.inc' %] >+[% INCLUDE 'csrf-token.inc' %] > [% Asset.js("js/cataloging_additem.js") | $raw %] >+ <script> >+ const can_edit_items_from = [% To.json(can_edit_items_from || []) | $raw %]; >+ >+ // filter dropdown variables >+ const all_libraries = [% To.json(Branches.all) | $raw %]; >+ const all_item_types = [% To.json(ItemTypes.Get) | $raw %]; >+ const all_damaged_statuses = [% To.json(AuthorisedValues.Get("DAMAGED")) | $raw %]; >+ const all_lost_statuses = [% To.json(AuthorisedValues.Get("LOST")) | $raw %]; >+ const all_stack = [% To.json(AuthorisedValues.Get("STACK")) | $raw %]; >+ const all_collection_codes = [% To.json(AuthorisedValues.Get("CCODE")) | $raw %]; >+ const all_shelivng_locations = [% To.json(AuthorisedValues.Get("LOC")) | $raw %]; >+ const all_not_for_loan_statuses = [% To.json(AuthorisedValues.Get("NOT_LOAN")) | $raw %]; >+ const all_restricted_statuses = [% To.json(AuthorisedValues.Get("RESTRICTED")) | $raw %]; >+ const all_withdrawn = [% To.json(AuthorisedValues.Get("WITHDRAWN")) | $raw %]; >+ const all_class_sources = [ >+ [% FOREACH class_source IN ClassSources.all %] >+ { >+ cn_source: "[% class_source.cn_source | html %]", >+ description: "[% class_source.description | html %]" >+ }, >+ [% END %] >+ ]; >+ >+ var opac_base_url; >+ [% IF ( Koha.Preference('OPACBaseURL') ) %] >+ var opac_base_url = "[% Koha.Preference('OPACBaseURL') | url %]"; >+ [% END %] >+ >+ var is_independent_branches = [% Koha.Preference('IndependentBranches') | html %] && ![% logged_in_user.is_superlibrarian | html %]; >+ var logged_in_branchname = "[% Branches.GetLoggedInBranchname() | html %]"; >+ </script> > [% Asset.js("js/form-submit.js") | $raw %] > <script> > var has_item_groups = "[% item_groups.size | html %]"; >@@ -91,6 +128,10 @@ > [% IF items %] > [% SET date_fields = [ 'dateaccessioned', 'onloan', 'datelastseen', 'datelastborrowed', 'replacementpricedate' ] %] > <div class="page-section"> >+ <span class="show_hide_filters"> >+ <a href="#" class="show_filters"><i class="fa fa-filter"></i> Show filters</a> >+ <a href="#" class="hide_filters" style="display: none;"><i class="fa fa-filter"></i> Hide filters</a> >+ </span> > <table id="itemst"> > <thead> > <tr> >diff --git a/koha-tmpl/intranet-tmpl/prog/js/cataloging_additem.js b/koha-tmpl/intranet-tmpl/prog/js/cataloging_additem.js >index c8a2189b834..7c7bde3f15c 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/cataloging_additem.js >+++ b/koha-tmpl/intranet-tmpl/prog/js/cataloging_additem.js >@@ -4,6 +4,97 @@ var browser = KOHA.browser(searchid, parseInt(biblionumber, 10)); > browser.show(); > > $(document).ready(function(){ >+ table_settings['columns'].unshift( { cannot_be_toggled: "1" } ); >+ build_itemst(false, {}); >+ >+ const filterContainer = document.querySelector('.show_hide_filters'); >+ const showFiltersLink = filterContainer.querySelector('.show_filters'); >+ const hideFiltersLink = filterContainer.querySelector('.hide_filters'); >+ >+ $(".show_filters").on("click",function(e){ >+ e.preventDefault(); >+ showFiltersLink.style.display = 'none'; >+ hideFiltersLink.style.display = 'inline'; >+ $("#itemst thead tr:eq(1)").remove(); >+ build_itemst(true, { destroy: true }); >+ }); >+ >+ $(".hide_filters").on("click",function(e){ >+ e.preventDefault(); >+ showFiltersLink.style.display = 'inline'; >+ hideFiltersLink.style.display = 'none'; >+ $("#itemst thead tr:eq(1)").remove(); >+ build_itemst(false, { destroy: true }); >+ }); >+ >+ // Build the filter dropdowns >+ const libraries_filters = all_libraries.map(e => { >+ e["_id"] = e["branchcode"]; >+ e["_str"] = e["branchname"]; >+ return e; >+ }); >+ const libraries_names = new Map(all_libraries.map( l => [l.branchcode, l.branchname] )); >+ >+ const item_types_filters = all_item_types.map(e => { >+ e["_id"] = e["itemtype"]; >+ e["_str"] = e["translated_description"]; >+ return e; >+ }); >+ >+ const damaged_filters = all_damaged_statuses.map(e => { >+ e["_id"] = e["authorised_value"]; >+ e["_str"] = e["lib"]; >+ return e; >+ }); >+ >+ const lost_filters = all_lost_statuses.map(e => { >+ e["_id"] = e["authorised_value"]; >+ e["_str"] = e["lib"]; >+ return e; >+ }); >+ >+ const stack_filters = all_stack.map(e => { >+ e["_id"] = e["authorised_value"]; >+ e["_str"] = e["lib"]; >+ return e; >+ }); >+ >+ const ccode_filters = all_collection_codes.map(e => { >+ e["_id"] = e["authorised_value"]; >+ e["_str"] = e["lib"]; >+ return e; >+ }); >+ >+ const shelving_location_filters = all_shelivng_locations.map(e => { >+ e["_id"] = e["authorised_value"]; >+ e["_str"] = e["lib"]; >+ return e; >+ }); >+ >+ const not_for_loan_filters = all_not_for_loan_statuses.map(e => { >+ e["_id"] = e["authorised_value"]; >+ e["_str"] = e["lib"]; >+ return e; >+ }); >+ >+ const withdrawn_filters = all_withdrawn.map(e => { >+ e["_id"] = e["authorised_value"]; >+ e["_str"] = e["lib"]; >+ return e; >+ }); >+ >+ >+ const restricted_filters = all_restricted_statuses.map(e => { >+ e["_id"] = e["authorised_value"]; >+ e["_str"] = e["lib"]; >+ return e; >+ }); >+ >+ const class_sources_filters = all_class_sources.map(e => { >+ e["_id"] = e["cn_source"]; >+ e["_str"] = e["description"]; >+ return e; >+ }); > > // Remove the onclick event defined in browser.js, > // otherwise the deletion confirmation will not work correctly >@@ -14,23 +105,7 @@ $(document).ready(function(){ > } > > $("fieldset.rows input, fieldset.rows select").addClass("noEnterSubmit"); >- /* Inline edit/delete links */ > var biblionumber = $("input[name='biblionumber']").val(); >- $("tr.editable").each(function(){ >- $(this).find("td:not(:first)").on('click', function(){ >- var rowid = $(this).parent().attr("id"); >- var num_rowid = rowid.replace("row",""); >- $(".linktools").remove(); >- var edit_link = $('<a href="/cgi-bin/koha/cataloguing/additem.pl?op=edititem&frameworkcode=' + frameworkcode + '&biblionumber=' + biblionumber + '&itemnumber=' + num_rowid + '&searchid=' + searchid + '#edititem"></a>'); >- $(edit_link).text( LABEL_EDIT_ITEM ); >- var delete_link = $('<a class="delete" data-item="'+num_rowid+'" href="#"></a>'); >- $(delete_link).text( LABEL_DELETE_ITEM ); >- var tools_node = $('<span class="linktools"></span>'); >- $(tools_node).append(edit_link); >- $(tools_node).append(delete_link); >- $(this).append(tools_node); >- }); >- }); > > $("#addnewitem").click(function(){ > if ( confirm( MSG_CONFIRM_ADD_ITEM ) ){ >@@ -38,16 +113,6 @@ $(document).ready(function(){ > } > }); > >- // Skip the first column >- table_settings['columns'].unshift( { cannot_be_toggled: "1" } ); >- >- var itemst = KohaTable("itemst", { >- "paging": false, >- "info": false, >- "autoWidth": false, >- "bKohaColumnsUseNames": true >- }, table_settings); >- > var multiCopyControl = $("#add_multiple_copies_span"); > var addMultipleBlock = $("#addmultiple"); > var addSingleBlock = $("#addsingle"); >@@ -203,3 +268,379 @@ function Dopop(link,i) { > var defaultvalue=document.forms[0].field_value[i].value; > var newin=window.open(link+"&result=" + defaultvalue,"valuebuilder",'width=500,height=400,toolbar=false,scrollbars=yes'); > } >+ >+function build_itemst(show_filters, dt_options) { >+ >+ if ( dt_options && dt_options.hasOwnProperty('destroy') ) { >+ let table_id = "#itemst"; >+ if( $.fn.dataTable.isDataTable(table_id) ) { >+ $(table_id).DataTable().destroy(); >+ } >+ dt_options['destroy'] = null; >+ } >+ >+ // assign positions for the filter dropdowns >+ let default_filters = {}; >+ var offset = 4; >+ let filters_options = { >+ [offset] : () => all_libraries, >+ [offset+6] : () => all_stack, >+ [offset+7] : () => all_not_for_loan_statuses, >+ [offset+8] : () => all_damaged_statuses, >+ [offset+9] : () => all_lost_statuses, >+ [offset+10] : () => all_withdrawn, >+ [offset+13] : () => all_restricted_statuses, >+ [offset+16] : () => all_libraries, >+ [offset+18] : () => all_shelivng_locations, >+ [offset+20] : () => all_class_sources, >+ [offset+21] : () => all_collection_codes, >+ [offset+24] : () => all_item_types, >+ }; >+ >+ var biblionumber = $("input[name='biblionumber']").val(); >+ var items_table_url = '/api/v1/biblios/' + biblionumber + '/items?'; >+ var items_table = $("#itemst").kohaTable({ >+ ajax: { >+ url: items_table_url >+ }, >+ order: [], >+ embed: ["+strings"], >+ autoWidth: false, >+ columns: [ >+ { >+ data: function(row, type, val, meta) { >+ let nodes = ''; >+ >+ let can_be_edited = !(is_independent_branches && row.home_library_id != logged_in_branchname); >+ if (can_be_edited) { >+ nodes += '<td>' >+ nodes += '<div class="btn-group dropup">'; >+ nodes += '<a class="btn btn-default btn-xs dropdown-toggle" id="itemactions' + row.item_id + '" role="button" data-toggle="dropdown" href="#">'; >+ nodes += 'Actions <b class="caret"></b>'; >+ nodes += '</a>'; >+ nodes += '<ul class="dropdown-menu" role="menu" aria-labelledby="itemactions' + row.item_id + '">'; >+ if (row.biblio_id != biblionumber) { // Host item >+ nodes += '<li><a href="additem.pl?op=edititem&biblionumber=' + row.biblio_id + '&itemnumber=' + row.item_id + '#edititem">Edit in host</a> <a class="delete" href="/cgi-bin/koha/cataloguing/additem.pl?op=delinkitem&biblionumber=' + row.biblio_id + '&hostitemnumber=' + row.item_id + '&searchid=' + row.searchid + '">Delink</a></li>'; >+ } else { >+ if (can_edit_items_from.includes(row.home_library_id) || !can_edit_items_from.length) { >+ nodes += '<li><a href="additem.pl?op=edititem&biblionumber=' + row.biblio_id + '&itemnumber=' + row.item_id + '&searchid=' + row.searchid + '#edititem">Edit</a></li>'; >+ } >+ nodes += '<li><a href="additem.pl?op=dupeitem&biblionumber=' + row.biblio_id + '&itemnumber=' + row.item_id + '&searchid=' + row.searchid + '#additema">Duplicate</a></li>'; >+ nodes += '<li class="print_label">'; >+ nodes += '<a class="submit-form-link" target="_blank" href="#" data-op="cud-add" data-number_list="' + row.item_id + '" data-number_type="itemnumber" data-method="post" data-action="/cgi-bin/koha/labels/label-edit-batch.pl" data-new_tab="true">Print label</a>'; >+ nodes += '</li>'; >+ if (can_edit_items_from.includes(row.home_library_id) || !can_edit_items_from.length) { >+ let csrfToken = $('meta[name="csrf-token"]').attr('content'); >+ nodes += '<li>'; >+ nodes += '<form id="' + row.item_id + '-delete-item-form" action="/cgi-bin/koha/cataloguing/additem.pl" method="post">'; >+ nodes += '<input type="hidden" name="csrf_token" value="' + csrfToken + '" />'; >+ nodes += '<input type="hidden" name="op" value="cud-delitem" />'; >+ nodes += '<input type="hidden" name="biblionumber" value="' + row.biblio_id + '" />'; >+ nodes += '<input type="hidden" name="itemnumber" value="' + row.item_id + '" />'; >+ nodes += '<input type="hidden" name="searchid" value="' + row.searchid + '" />'; >+ nodes += '</form>'; >+ nodes += '<a class="delete" data-item="' + row.item_id + '" href="#">Delete</a>'; >+ nodes += '</li>'; >+ } >+ } >+ if ( opac_base_url ) { >+ var href = opac_base_url + "/cgi-bin/koha/opac-detail.pl?biblionumber=" + row.biblio_id; >+ nodes+= '<li class="view-in-opac"><a target="_blank" href="' + href + '">OPAC view</a></li>' >+ } >+ nodes += '</ul>'; >+ nodes += '</div>'; >+ nodes += '</td>'; >+ } else { >+ nodes += '<td> </td>' >+ } >+ return nodes; >+ }, >+ searchable: false, >+ orderable: false, >+ }, >+ { >+ data: "external_id", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ if ( row.external_id != null ) { >+ return '<a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=%s&itemnumber=%s#item%s">%s</a>'.format(row.biblio_id, row.item_id, row.item_id, row.external_id); >+ } >+ return ''; >+ } >+ }, >+ { >+ data: "acquisition_date", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return $date(row.acquisition_date); >+ } >+ }, >+ { >+ data: "acquisition_source", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "home_library_id", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return row._strings.home_library_id ? escape_str(row._strings.home_library_id.str) : ""; >+ } >+ }, >+ { >+ data: "purchase_price", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "replacement_price", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "replacement_price_date", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return $date(row.replacement_price_date); >+ } >+ }, >+ { >+ data: "last_checkout_date", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return $date(row.last_checkout_date); >+ } >+ }, >+ { >+ data: "last_seen_date", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return $date(row.last_seen_date); >+ } >+ }, >+ { >+ data: "stack", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return row._strings.stack ? escape_str(row._strings.stack.str) : ""; >+ } >+ }, >+ { >+ data: "not_for_loan_status", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return row._strings.not_for_loan_status ? escape_str(row._strings.not_for_loan_status.str) : ""; >+ } >+ }, >+ { >+ data: "damaged_status", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return row._strings.damaged_status ? escape_str(row._strings.damaged_status.str) : ""; >+ } >+ }, >+ { >+ data: "lost_status", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return row._strings.lost_status ? escape_str(row._strings.lost_status.str) : ""; >+ } >+ }, >+ { >+ data: "withdrawn", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return row._strings.withdrawn ? escape_str(row._strings.withdrawn.str) : ""; >+ } >+ }, >+ { >+ data: "callnumber", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "coded_location_qualifier", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "restricted_status", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return row._strings.restricted_status ? escape_str(row._strings.restricted_status.str) : ""; >+ } >+ }, >+ { >+ data: "public_notes", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "internal_notes", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "holding_library_id", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return row._strings.holding_library_id ? escape_str(row._strings.holding_library_id.str) : ""; >+ } >+ }, >+ { >+ data: "timestamp", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return $date(row.timestamp); >+ } >+ }, >+ { >+ data: "location", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return row._strings.location ? escape_str(row._strings.location.str) : ""; >+ } >+ >+ }, >+ { >+ data: "checked_out_date", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return $date(row.checked_out_date); >+ } >+ }, >+ { >+ data: "call_number_source", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return row._strings.call_number_source ? escape_str(row._strings.call_number_source.str) : ""; >+ } >+ }, >+ { >+ data: "collection_code", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return row._strings.collection_code ? escape_str(row._strings.collection_code.str) : ""; >+ } >+ }, >+ { >+ data: "materials_notes", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "uri", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "item_type_id", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row, meta) { >+ return row._strings.item_type_id ? escape_str(row._strings.item_type_id.str) : ""; >+ } >+ }, >+ { >+ data: "serial_issue_number", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "copy_number", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "inventory_number", >+ searchable: true, >+ orderable: true, >+ }, >+ ], >+ createdRow: function(row, data, dataIndex) { >+ $(row).attr('id', 'row' + data.item_id); >+ >+ let can_be_edited = !(is_independent_branches && data.home_library_id != logged_in_branchname); >+ >+ if (can_be_edited) { >+ $(row).addClass('editable'); >+ } >+ }, >+ initComplete: function( settings, json ){ >+ let table = settings.oInstance.api(); >+ table.columns().every(function(i){ >+ let is_empty = true; >+ let nodes = this.nodes(); >+ nodes.each((td, ii) => { >+ if ( $(td).html() !== '' ) { >+ is_empty = false; >+ return; >+ } >+ }); >+ if ( is_empty ) { >+ table.columns(i).visible(false); >+ } >+ }); >+ >+ }, >+ drawCallback: function(settings){ >+ >+ if (!show_filters && settings._iRecordsDisplay == settings._iRecordsTotal && settings._iDisplayLength >= settings._iRecordsDisplay){ >+ let container = $(this).parent(); >+ container.find(".dt-info").remove(); >+ container.find(".table_entries").remove(); >+ container.find(".dataTables_filter").remove(); >+ container.find(".dt_button_clear_filter").remove(); >+ container.find(".bottom.pager").remove(); >+ } >+ >+ setupRowClickHandlers(); >+ >+ }, >+ ...dt_options, >+ >+ }, table_settings, show_filters, default_filters, filters_options,); >+ >+ return items_table; >+ >+} >+ >+/* Inline edit/delete links */ >+function setupRowClickHandlers() { >+ $("tr.editable").each(function(){ >+ $(this).find("td:not(:first)").off('click').on('click', function(){ >+ $("tr.editable").removeClass('active'); >+ $(this).parent().addClass('active'); >+ >+ var rowid = $(this).parent().attr("id"); >+ var num_rowid = rowid.replace("row",""); >+ $(".linktools").remove(); >+ var edit_link = $('<a href="/cgi-bin/koha/cataloguing/additem.pl?op=edititem&frameworkcode=' + frameworkcode + '&biblionumber=' + biblionumber + '&itemnumber=' + num_rowid + '&searchid=' + searchid + '#edititem"></a>'); >+ $(edit_link).text( LABEL_EDIT_ITEM ); >+ var delete_link = $('<a class="delete" data-item="'+num_rowid+'" href="#"></a>'); >+ $(delete_link).text( LABEL_DELETE_ITEM ); >+ var tools_node = $('<span class="linktools"></span>'); >+ $(tools_node).append(edit_link); >+ $(tools_node).append(delete_link); >+ $(this).append(tools_node); >+ }); >+ }); >+} >-- >2.39.2
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 37637
:
170482
|
170483
|
170507
| 170789 |
170790
|
171953