From 605c93e9c10d8a3f2272d9fd8c68106d435af608 Mon Sep 17 00:00:00 2001 From: sunnavy Date: Fri, 3 Jan 2025 17:18:08 -0500 Subject: [PATCH 1/2] Use ASCII JSON string for HX-Trigger http header HTTP headers are not supposed to contain non-ASCII characters. With this, the generated string now is like "\u4f60\u597d" and JavaScript natively supports it. --- share/html/Elements/ShowSearch | 2 +- share/html/Helpers/AssetUpdate | 2 +- share/html/Helpers/QuickCreate | 2 +- share/html/Helpers/SavedSearchOptions | 4 ++-- share/html/Helpers/TicketUpdate | 2 +- share/html/Views/Asset/Title | 2 +- share/html/Views/Ticket/Title | 2 +- share/static/js/util.js | 7 +++---- 8 files changed, 11 insertions(+), 12 deletions(-) diff --git a/share/html/Elements/ShowSearch b/share/html/Elements/ShowSearch index 97b11851859..701085a4908 100644 --- a/share/html/Elements/ShowSearch +++ b/share/html/Elements/ShowSearch @@ -244,7 +244,7 @@ my $title = loc(RT::SavedSearch->EscapeDescription($search->Description), $Proce if ( $m->request_path =~ m{^/Views/} ) { $event{triggerChanged} = 'every ' . $refresh_seconds . 's[checkRefreshState(this)]' if $refresh_seconds; $event{widgetTitleChanged} = $title . ( $title_raw // '' ); - $r->headers_out->{'HX-Trigger'} = JSON( \%event, utf8 => 1, ); + $r->headers_out->{'HX-Trigger'} = JSON( \%event, ascii => 1, ); } my @icon_links; diff --git a/share/html/Helpers/AssetUpdate b/share/html/Helpers/AssetUpdate index 3abe4ab489d..e2476755886 100644 --- a/share/html/Helpers/AssetUpdate +++ b/share/html/Helpers/AssetUpdate @@ -123,6 +123,6 @@ $r->headers_out->{'HX-Trigger'} = JSON( actionsChanged => \@results, map { $_ => '' } @events }, - utf8 => 1, + ascii => 1, ) if @events || @results; diff --git a/share/html/Helpers/QuickCreate b/share/html/Helpers/QuickCreate index abb1246a187..66099b722ab 100644 --- a/share/html/Helpers/QuickCreate +++ b/share/html/Helpers/QuickCreate @@ -99,7 +99,7 @@ if ( $ARGS{'QuickCreateSubmit'} ) { { actionsChanged => \@results }, - utf8 => 1, + ascii => 1, ) if @results; } diff --git a/share/html/Helpers/SavedSearchOptions b/share/html/Helpers/SavedSearchOptions index 46539882789..b01991a1b8c 100644 --- a/share/html/Helpers/SavedSearchOptions +++ b/share/html/Helpers/SavedSearchOptions @@ -105,14 +105,14 @@ if ( $SavedSearchId ) { ) ], }, - utf8 => 1, + ascii => 1, ); } else { RT->Logger->error( "Unable to update SearchRefreshIntervale from $ARGS{'SearchRefreshInterval'}: $msg" ); - $r->headers_out->{'HX-Trigger'} = JSON( { actionsChanged => [$msg], }, utf8 => 1, ); + $r->headers_out->{'HX-Trigger'} = JSON( { actionsChanged => [$msg], }, ascii => 1, ); } } } diff --git a/share/html/Helpers/TicketUpdate b/share/html/Helpers/TicketUpdate index eb30fa9fe9e..7ad8fb07db2 100644 --- a/share/html/Helpers/TicketUpdate +++ b/share/html/Helpers/TicketUpdate @@ -178,7 +178,7 @@ $r->headers_out->{'HX-Trigger'} = JSON( actionsChanged => \@Actions, map { $_ => '' } @events }, - utf8 => 1, + ascii => 1, ) if @events || @Actions; diff --git a/share/html/Views/Asset/Title b/share/html/Views/Asset/Title index 7dc1126b49e..5451c553a07 100644 --- a/share/html/Views/Asset/Title +++ b/share/html/Views/Asset/Title @@ -51,7 +51,7 @@ my $asset = RT::Asset->new( $session{CurrentUser} ); $asset->Load($id); my $title = loc( "Asset #[_1]: [_2]", $asset->Id, $asset->Name || '' ); -$r->headers_out->{'HX-Trigger'} = JSON( { titleChanged => $title }, utf8 => 1, ); +$r->headers_out->{'HX-Trigger'} = JSON( { titleChanged => $title }, ascii => 1, ); $m->out($m->interp->apply_escapes($title, 'h')); $m->abort; diff --git a/share/html/Views/Ticket/Title b/share/html/Views/Ticket/Title index cc94f853f43..1f82c493260 100644 --- a/share/html/Views/Ticket/Title +++ b/share/html/Views/Ticket/Title @@ -51,7 +51,7 @@ my $ticket = RT::Ticket->new( $session{CurrentUser} ); $ticket->Load($id); my $title = loc( "#[_1]: [_2]", $ticket->Id, $ticket->Subject || '' ); -$r->headers_out->{'HX-Trigger'} = JSON( { titleChanged => $title }, utf8 => 1, ); +$r->headers_out->{'HX-Trigger'} = JSON( { titleChanged => $title }, ascii => 1, ); $m->out($m->interp->apply_escapes($title, 'h')); $m->abort; diff --git a/share/static/js/util.js b/share/static/js/util.js index 89bf4437e4d..3c4e9d889cf 100644 --- a/share/static/js/util.js +++ b/share/static/js/util.js @@ -817,8 +817,7 @@ jQuery(function() { document.body.addEventListener('actionsChanged', function(evt) { if ( evt.detail.value ) { for ( const action of evt.detail.value ) { - // Need to decode action that is UTF-8 encoded - jQuery.jGrowl(decodeURIComponent(escape(action)), { themeState: 'none' }); + jQuery.jGrowl(action, { themeState: 'none' }); } const history_container = document.querySelector('.history-container'); @@ -854,7 +853,7 @@ jQuery(function() { }); document.body.addEventListener('titleChanged', function(evt) { - document.title = decodeURIComponent(escape(evt.detail.value)); + document.title = evt.detail.value; }); document.body.addEventListener('triggerChanged', function(evt) { @@ -865,7 +864,7 @@ jQuery(function() { document.body.addEventListener('widgetTitleChanged', function(evt) { const title = evt.detail.elt.closest('div.titlebox').querySelector('.titlebox-title a'); if ( title ) { - title.innerHTML = decodeURIComponent(escape(evt.detail.value)); + title.innerHTML = evt.detail.value; } }); From 8a12c969e070adccc331669d23b141d0807c75b1 Mon Sep 17 00:00:00 2001 From: sunnavy Date: Mon, 6 Jan 2025 09:22:30 -0500 Subject: [PATCH 2/2] Test UTF-8 widget title to make sure it is correctly decoded --- t/selenium/basic.t | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/t/selenium/basic.t b/t/selenium/basic.t index 709aa497e75..050c5d5d154 100644 --- a/t/selenium/basic.t +++ b/t/selenium/basic.t @@ -105,6 +105,32 @@ diag "test custom field unique values"; is( $ticket->FirstCustomFieldValue($cf), 456, 'CF value is set' ); } +{ + $s->get_ok('/Prefs/AboutMe.html'); + $s->submit_form_ok( + { + form_name => 'EditAboutMe', + fields => { Lang => 'zh-cn' }, + }, + 'Update Language' + ); + + $s->text_contains( Encode::decode( 'UTF-8', '主页' ), 'Menu has changed to Chinese' ); + $s->get_ok('/'); + $s->text_contains( Encode::decode( 'UTF-8', '我拥有的前10份待处理申请单' ), 'Chinese title is correct' ); + + $s->get_ok('/Prefs/AboutMe.html'); + $s->submit_form_ok( + { + form_name => 'EditAboutMe', + fields => { Lang => '' }, + }, + 'Update Language' + ); + $s->text_contains(q{Lang changed from 'zh-cn' to (no value)}); + +} + $s->logout; done_testing;