Also move the JavaScript code to a separate file so it does not have to be downloaded with every page.
Signed-off-by: Francois Gouget fgouget@codeweavers.com --- This can be tested on the Wine-devel page (it relies on the CollectionBlock for the formatting of the Received column). Later patches will extend datetime.js so other pages can use it too. --- .../lib/ObjectModel/CGI/CollectionBlock.pm | 56 ++-------- testbot/lib/ObjectModel/CGI/ValueFormatter.pm | 101 ++++++++++++++++-- testbot/web/js/datetime.js | 42 ++++++++ 3 files changed, 148 insertions(+), 51 deletions(-) create mode 100644 testbot/web/js/datetime.js
diff --git a/testbot/lib/ObjectModel/CGI/CollectionBlock.pm b/testbot/lib/ObjectModel/CGI/CollectionBlock.pm index e849d5a59..03f1d18cd 100644 --- a/testbot/lib/ObjectModel/CGI/CollectionBlock.pm +++ b/testbot/lib/ObjectModel/CGI/CollectionBlock.pm @@ -44,7 +44,6 @@ package ObjectModel::CGI::CollectionBlock; use Exporter 'import'; our @EXPORT = qw(new);
-use POSIX qw(strftime); use URI::Escape;
use ObjectModel::CGI::Table; @@ -291,18 +290,7 @@ sub GenerateDataView($$$)
my $PropertyName = $PropertyDescriptor->GetName(); my $Value = $Row->{Item}->$PropertyName; - - if ($PropertyDescriptor->GetClass() eq "Basic" and - $PropertyDescriptor->GetType() eq "DT" and defined $Value) - { - print "<script type='text/javascript'><!--\n", - "ShowDateTime($Value);\n", - "//--></script><noscript><div>", - strftime("%Y-%m-%d %H:%M:%S", localtime($Value)), - "</div></noscript>\n"; - return; - } - print defined $Value ? $self->escapeHTML($Value) : " "; + GenerateValueHTML($self, $PropertyDescriptor, $Value); }
@@ -414,42 +402,20 @@ sub GenerateList($)
my $Collection = $self->{Collection}; my $PropertyDescriptors = $Collection->GetPropertyDescriptors(); - my $HasDT = !1; - foreach my $PropertyDescriptor (@{$PropertyDescriptors}) + + my $Items = $self->{Collection}->GetSortedItems(); + if (@$Items != 0) { - if ($PropertyDescriptor->GetClass() eq "Basic" && - $PropertyDescriptor->GetType() eq "DT") + foreach my $PropertyDescriptor (@$PropertyDescriptors) { - $HasDT = 1; + if ($PropertyDescriptor->GetClass() eq "Basic" and + $PropertyDescriptor->GetType() eq "DT") + { + $self->{EnclosingPage}->GenerateImportJS(GetDateTimeJSFile()); + last; + } } } - if ($HasDT) - { - print <<"EOF"; -<script type='text/javascript'><!--\ -function Pad2(n) -{ - return n < 10 ? '0' + n : n; -} - -function ShowDateTime(Sec1970, Id, Attr) -{ - var Dt = new Date(Sec1970 * 1000); - var Pretty = Dt.getFullYear() + '-' + Pad2(Dt.getMonth() + 1) + '-' + - Pad2(Dt.getDate()) + ' ' + Pad2(Dt.getHours()) + ':' + - Pad2(Dt.getMinutes()) + ':' + Pad2(Dt.getSeconds()) - if (Id != null) - { - document.getElementById(Id).setAttribute(Attr || "title", Pretty); - } - else - { - document.write(Pretty); - } -} -//--></script> -EOF - }
print "<div class='CollectionBlock'>\n"; $self->GenerateFormStart(); diff --git a/testbot/lib/ObjectModel/CGI/ValueFormatter.pm b/testbot/lib/ObjectModel/CGI/ValueFormatter.pm index cad6eee1b..47236c748 100644 --- a/testbot/lib/ObjectModel/CGI/ValueFormatter.pm +++ b/testbot/lib/ObjectModel/CGI/ValueFormatter.pm @@ -22,8 +22,90 @@ use strict; package ObjectModel::CGI::ValueFormatter;
use Exporter 'import'; -our @EXPORT = qw(GenerateValueHTML); +our @EXPORT = qw(GetDateTimeJSFile GenerateDateTime GenerateValueHTML);
+use POSIX qw(strftime); + + +# +# Timestamp formatting +# + +=pod +=over 12 + +=item C<GetDateTimeJSFile()> + +Returns the path to the file containing the JavaScript code to format +timestamps. + +See GenerateDateTime(). + +=back +=cut + +sub GetDateTimeJSFile() +{ + return "/js/datetime.js"; +} + +=pod +=over 12 + +=item C<GenerateDateTime()> + +Returns an HTML snippet that uses the JavaScript ShowDateTime() function +to show a timestamp in ISO 8601 format in the user's local timezone. + +Note that the timestamp will be shown based on the server's timezone in case +JavaScript is disabled. +=over + +=item Sec1970 +The timestamp to display as the number of seconds since the Epoch. + +=item Class +A string containing additional classes for the <span> tag used to show the +date + time. + +=item TagAttrs +A string containing the tag name without the initial bracket, and any +additional attributes except class and timestamp. By default this is 'span'. + +=back +=back +=cut + +sub GenerateDateTime($;$$) +{ + my ($Sec1970, $Class, $TagAttrs) = @_; + + if (defined $Sec1970) + { + my $Tag; + if ($TagAttrs) + { + $Tag = $TagAttrs; + $Tag =~ s/ .*$//; + } + else + { + $TagAttrs = $Tag = "span"; + } + $Class = "$Class " if ($Class); + print "<$TagAttrs class='${Class}datetime' timestamp='$Sec1970'>", + strftime("%Y-%m-%d %H:%M:%S", localtime($Sec1970)), "</$Tag>"; + } + else + { + print " "; + } +} + + +# +# Property value formatting +#
=pod =over 12 @@ -44,13 +126,20 @@ sub GenerateValueHTML($$$) { my ($Parent, $PropertyDescriptor, $Value) = @_;
- if ($PropertyDescriptor->GetClass() eq "Basic" and - $PropertyDescriptor->GetType() eq "B") + if ($PropertyDescriptor->GetClass() eq "Basic") { - print $Value ? "Yes" : "No"; - return; + if ($PropertyDescriptor->GetType() eq "B") + { + print $Value ? "Yes" : "No"; + return; + } + if ($PropertyDescriptor->GetType() eq "DT") + { + GenerateDateTime($Value); + return; + } } - if ($PropertyDescriptor->GetClass() eq "Itemref" and defined $Value) + elsif ($PropertyDescriptor->GetClass() eq "Itemref" and defined $Value) { # Note: This only supports single-key Itemrefs foreach my $ValuePD (@{$Value->GetPropertyDescriptors()}) diff --git a/testbot/web/js/datetime.js b/testbot/web/js/datetime.js new file mode 100644 index 000000000..8f56719ca --- /dev/null +++ b/testbot/web/js/datetime.js @@ -0,0 +1,42 @@ +/* Format timestamps + * + * Copyright 2022 Francois Gouget + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +"use strict"; + +function Pad2(n) +{ + return n < 10 ? '0' + n : n; +} + +function ShowDateTime(dom) +{ + const sec1970 = dom.getAttribute("timestamp"); + const dt = new Date(sec1970 * 1000); + dom.innerHTML = dt.getFullYear() +'-'+ Pad2(dt.getMonth() + 1) +'-'+ + Pad2(dt.getDate()) +' '+ Pad2(dt.getHours()) +':'+ + Pad2(dt.getMinutes()) +':'+ Pad2(dt.getSeconds()) +} + +function init() +{ + document.querySelectorAll(".datetime").forEach(dom => { + ShowDateTime(dom); + }); +} + +window.addEventListener('load', init);
Update ValueFormatter and datetime.js to add support for showing the time with the date as a tooltip.
Signed-off-by: Francois Gouget fgouget@codeweavers.com --- testbot/lib/ObjectModel/CGI/ValueFormatter.pm | 48 ++++++++++++++++++- testbot/web/Activity.pl | 34 +++---------- testbot/web/WineTestBot.css | 1 + testbot/web/js/datetime.js | 13 +++++ 4 files changed, 67 insertions(+), 29 deletions(-)
diff --git a/testbot/lib/ObjectModel/CGI/ValueFormatter.pm b/testbot/lib/ObjectModel/CGI/ValueFormatter.pm index 47236c748..62c1499ab 100644 --- a/testbot/lib/ObjectModel/CGI/ValueFormatter.pm +++ b/testbot/lib/ObjectModel/CGI/ValueFormatter.pm @@ -22,7 +22,8 @@ use strict; package ObjectModel::CGI::ValueFormatter;
use Exporter 'import'; -our @EXPORT = qw(GetDateTimeJSFile GenerateDateTime GenerateValueHTML); +our @EXPORT = qw(GetDateTimeJSFile GenerateDateTime + GenerateTimeTipDate GenerateValueHTML);
use POSIX qw(strftime);
@@ -102,6 +103,51 @@ sub GenerateDateTime($;$$) } }
+=pod +=over 12 + +=item C<GenerateTimeTipDate()> + +Show the timestamp's time with the date as a tooltip. + +The timestamp is shown in the user's timezone if JavaScript is available and +in the server's timezone otherwise. + +The default for TagAttrs is 'a' and it should always be a tag that shows the +content of the title attribute as a tooltip. + +See GenerateDateTime() for more details. + +=back +=cut + +sub GenerateTimeTipDate($;$$) +{ + my ($Sec1970, $Class, $TagAttrs) = @_; + + if (defined $Sec1970) + { + my $Tag; + if ($TagAttrs) + { + $Tag = $TagAttrs; + $Tag =~ s/ .*$//; + } + else + { + $TagAttrs = $Tag = "a"; + } + $Class = "$Class " if ($Class); + print "<$TagAttrs class='${Class}timetipdate' timestamp='$Sec1970' ", + strftime("title='%Y-%m-%d'>%H:%M:%S", localtime($Sec1970)), + "</$Tag>"; + } + else + { + print " "; + } +} +
# # Property value formatting diff --git a/testbot/web/Activity.pl b/testbot/web/Activity.pl index a92435805..ffdf0fef6 100644 --- a/testbot/web/Activity.pl +++ b/testbot/web/Activity.pl @@ -24,9 +24,9 @@ package ActivityPage; use ObjectModel::CGI::FreeFormPage; our @ISA = qw(ObjectModel::CGI::FreeFormPage);
-use POSIX qw(strftime); - use ObjectModel::BasicPropertyDescriptor; +use ObjectModel::CGI::ValueFormatter; + use WineTestBot::Config; use WineTestBot::Activity; use WineTestBot::Log; # For Elapsed() @@ -75,16 +75,6 @@ sub GeneratePage($) $self->SUPER::GeneratePage(); }
-sub _GetHtmlTime($) -{ - my ($Timestamp) = @_; - return "<noscript><div>", - strftime("<a class='title' title='%d'>%H:%M:%S</a>", localtime($Timestamp)), "</div></noscript>\n" . - "<script type='text/javascript'><!--\n" . - "ShowDateTime($Timestamp);\n" . - "//--></script>"; -} - sub _GetHtmlDuration($) { my ($Secs) = @_; @@ -110,25 +100,11 @@ sub GenerateBody($) $self->GenerateFormStart(); print "<div class='ItemProperty'><label>Analyze the activity of the past <div class='ItemValue'><input type='text' name='Hours' maxlength='3' size='3' value='", $self->GetParam("Hours"), "'/></div> hours.</label></div>\n"; $self->GenerateFormEnd(); + $self->GenerateImportJS(GetDateTimeJSFile());
print "<h1>${ProjectName} Test Bot activity</h1>\n"; print "<div class='Content'>\n";
- print <<"EOF"; -<script type='text/javascript'><!--\ -function Pad2(n) -{ - return n < 10 ? '0' + n : n; -} -function ShowDateTime(Sec1970) -{ - var Dt = new Date(Sec1970 * 1000); - document.write('<a class="title" title="' + Pad2(Dt.getDate()) + '">' + Pad2(Dt.getHours()) + ':' + - Pad2(Dt.getMinutes()) + ':' + Pad2(Dt.getSeconds()) + "</a>"); -} -//--></script> -EOF - ### Get the sorted VMs list
my $VMs = CreateVMs(); @@ -162,7 +138,9 @@ EOF next if (!$Group->{statusvms});
my $GroupId = $Group->{id}; - print "<tr><td id='g$GroupId'>", _GetHtmlTime($Group->{start}), "</td>"; + print "<tr><td id='g$GroupId'>"; + GenerateTimeTipDate($Group->{start}); + print "</td>"; if ($Group->{engine}) { print "<td class='Record RecordEngine'>$Group->{engine}</td>\n"; diff --git a/testbot/web/WineTestBot.css b/testbot/web/WineTestBot.css index a50125f8e..0955f58cf 100644 --- a/testbot/web/WineTestBot.css +++ b/testbot/web/WineTestBot.css @@ -408,6 +408,7 @@ pre .log-fullnew { color: red; font-weight: bold; }
a.title { color:inherit; text-decoration: none; } +a.timetipdate { color:inherit; text-decoration: none; }
th.Record { text-align: center; } td.Record { text-align: center; } diff --git a/testbot/web/js/datetime.js b/testbot/web/js/datetime.js index 8f56719ca..47b0feb0f 100644 --- a/testbot/web/js/datetime.js +++ b/testbot/web/js/datetime.js @@ -32,11 +32,24 @@ function ShowDateTime(dom) Pad2(dt.getMinutes()) +':'+ Pad2(dt.getSeconds()) }
+function ShowTimeTipDate(dom) +{ + const sec1970 = dom.getAttribute("timestamp"); + const dt = new Date(sec1970 * 1000); + dom.setAttribute('title', dt.getFullYear() +'-'+ Pad2(dt.getMonth() + 1) + +'-'+ Pad2(dt.getDate())); + dom.innerHTML = Pad2(dt.getHours()) +':'+ Pad2(dt.getMinutes()) +':'+ + Pad2(dt.getSeconds()); +} + function init() { document.querySelectorAll(".datetime").forEach(dom => { ShowDateTime(dom); }); + document.querySelectorAll(".timetipdate").forEach(dom => { + ShowTimeTipDate(dom); + }); }
window.addEventListener('load', init);