Bugzilla – Attachment 194167 Details for
Bug 35104
We should warn when attempting to save MARC records that contain characters invalid in XML
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 35104: Add context snippet with caret pointer to nonxml_stripped error messages
4498ed7.patch (text/plain), 4.43 KB, created by
Martin Renvoize (ashimema)
on 2026-02-27 19:24:13 UTC
(
hide
)
Description:
Bug 35104: Add context snippet with caret pointer to nonxml_stripped error messages
Filename:
MIME Type:
Creator:
Martin Renvoize (ashimema)
Created:
2026-02-27 19:24:13 UTC
Size:
4.43 KB
patch
obsolete
>From 4498ed73eab6c942d42edeee813636caf29fe749 Mon Sep 17 00:00:00 2001 >From: Martin Renvoize <martin.renvoize@openfifth.co.uk> >Date: Fri, 27 Feb 2026 14:25:14 +0000 >Subject: [PATCH] Bug 35104: Add context snippet with caret pointer to > nonxml_stripped error messages > >Each per-character error message now includes a two-line snippet showing >up to 30 characters of surrounding context, with the offending character >replaced by a [U+XXXX] marker and a caret (^) aligned beneath it: > > 245$a: invalid char value 31 (U+001F) at position 10 > Hello[U+001F]World > ^ > >Adds _context_snippet() helper and refactors _find_nonxml_chars() to use >index-based iteration so the char array can be passed directly to the >snippet helper without re-splitting. >--- > Koha/Biblio/Metadata.pm | 71 ++++++++++++++++++++++++++++++++++------- > 1 file changed, 60 insertions(+), 11 deletions(-) > >diff --git a/Koha/Biblio/Metadata.pm b/Koha/Biblio/Metadata.pm >index a7a202fb9cc..7207eec2f08 100644 >--- a/Koha/Biblio/Metadata.pm >+++ b/Koha/Biblio/Metadata.pm >@@ -132,8 +132,9 @@ sub store { > metadata_id => $self->id, > error_type => 'nonxml_stripped', > message => sprintf( >- "%s: invalid char value %d (U+%04X) at position %d", >- $occ->{field}, $occ->{char_ord}, $occ->{char_ord}, $occ->{position} >+ "%s: invalid char value %d (U+%04X) at position %d\n%s", >+ $occ->{field}, $occ->{char_ord}, $occ->{char_ord}, $occ->{position}, >+ $occ->{snippet} > ), > } > )->store; >@@ -383,6 +384,53 @@ sub _embed_items { > return $record; > } > >+=head3 _context_snippet >+ >+ my $snippet = _context_snippet( \@chars, $pos_0, $char_ord ); >+ >+Given an array-ref of characters C<\@chars>, a 0-based position C<$pos_0> of >+a bad character, and its ordinal C<$char_ord>, returns a two-line string: >+ >+=over 4 >+ >+=item * Line 1 â up to 30 characters of context either side of the bad >+character, with the bad character replaced by a C<[U+XXXX]> marker and >+ellipses when the window is truncated. >+ >+=item * Line 2 â a caret (C<^>) aligned beneath the start of the marker. >+ >+=back >+ >+Example output (4-space indent): >+ >+ ...arr \ [U+0008]$btxt V $2rdac ontent >+ ^ >+ >+=cut >+ >+sub _context_snippet { >+ my ( $chars_ref, $pos_0, $char_ord ) = @_; >+ >+ my $marker = sprintf '[U+%04X]', $char_ord; >+ my $last = $#{$chars_ref}; >+ my $win = 30; >+ >+ my $pre_start = ( $pos_0 > $win ) ? $pos_0 - $win : 0; >+ my $post_end = ( $pos_0 + $win < $last ) ? $pos_0 + $win : $last; >+ >+ my $pre = $pos_0 > 0 ? join( '', @{$chars_ref}[ $pre_start .. $pos_0 - 1 ] ) : ''; >+ my $post = $pos_0 < $last ? join( '', @{$chars_ref}[ $pos_0 + 1 .. $post_end ] ) : ''; >+ >+ my $prefix = ( $pos_0 > $win ) ? '...' : ''; >+ my $suffix = ( $pos_0 + $win < $last ) ? '...' : ''; >+ >+ my $indent = ' '; >+ my $line = "$indent$prefix$pre$marker$post$suffix"; >+ my $caret_col = length($indent) + length($prefix) + length($pre); >+ >+ return "$line\n" . ( ' ' x $caret_col ) . '^'; >+} >+ > =head3 _find_nonxml_chars > > my @occurrences = _find_nonxml_chars( $marcxml_string ); >@@ -399,6 +447,8 @@ the following keys: > > =item * C<position> - 1-based character offset within the field value > >+=item * C<snippet> - two-line string with context window and caret pointer (see C<_context_snippet>) >+ > =back > > The same character class is used as L<C4::Charset/StripNonXmlChars>, so >@@ -416,17 +466,16 @@ sub _find_nonxml_chars { > > my $scan = sub { > my ( $field_ref, $val ) = @_; >- my $pos = 1; >- for my $char ( split //, $val ) { >- if ( $char =~ $non_xml_re ) { >- push @occurrences, >- { >+ my @chars = split //, $val; >+ for my $i ( 0 .. $#chars ) { >+ if ( $chars[$i] =~ $non_xml_re ) { >+ push @occurrences, { > field => $field_ref, >- char_ord => ord($char), >- position => $pos, >- }; >+ char_ord => ord( $chars[$i] ), >+ position => $i + 1, >+ snippet => _context_snippet( \@chars, $i, ord( $chars[$i] ) ), >+ }; > } >- $pos++; > } > }; > >-- >2.53.0 >
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 35104
:
157625
|
157627
|
157628
|
157642
|
159615
|
159620
|
159621
|
159630
|
159810
|
159811
|
159812
|
159813
|
166431
|
173145
|
173146
|
176820
|
176821
|
176850
|
176851
|
176852
|
176860
|
193985
|
193986
|
193987
|
193988
|
193989
|
193990
|
193991
|
193992
|
193993
|
193994
|
193995
|
193996
|
193997
|
193998
|
193999
|
194000
|
194001
|
194004
|
194005
|
194006
|
194007
|
194008
|
194009
|
194010
|
194011
|
194012
|
194013
|
194152
|
194153
|
194154
|
194155
|
194156
|
194157
|
194158
|
194159
|
194160
|
194161
|
194162
|
194163
|
194164
|
194165
|
194166
| 194167 |
194168
|
194169
|
194170
|
194171
|
194172