Commit 496013d2 authored by myk%mozilla.org's avatar myk%mozilla.org

Fix for bug 103778: Rewrites and templatizes buglist.cgi.

Patch by Myk Melez <myk@mozilla.org>. r=bbaetz,gerv
parent 38551035
...@@ -1201,7 +1201,8 @@ sub PutFooter { ...@@ -1201,7 +1201,8 @@ sub PutFooter {
sub DisplayError { sub DisplayError {
my ($message, $title) = (@_); my ($message, $title) = (@_);
$title ||= "Error"; $title ||= "Error";
$message ||= "An unknown error occurred.";
print "Content-type: text/html\n\n"; print "Content-type: text/html\n\n";
PutHeader($title); PutHeader($title);
......
...@@ -193,6 +193,7 @@ unless (have_vers("Date::Parse",0)) { push @missing,"Date::Parse" } ...@@ -193,6 +193,7 @@ unless (have_vers("Date::Parse",0)) { push @missing,"Date::Parse" }
unless (have_vers("AppConfig","1.52")) { push @missing,"AppConfig" } unless (have_vers("AppConfig","1.52")) { push @missing,"AppConfig" }
unless (have_vers("Template","2.06")) { push @missing,"Template" } unless (have_vers("Template","2.06")) { push @missing,"Template" }
unless (have_vers("Text::Wrap","2001.0131")) { push @missing,"Text::Wrap" } unless (have_vers("Text::Wrap","2001.0131")) { push @missing,"Text::Wrap" }
unless (have_vers("File::Spec", "0.82")) { push @missing,"File::Spec" }
# If CGI::Carp was loaded successfully for version checking, it changes the # If CGI::Carp was loaded successfully for version checking, it changes the
# die and warn handlers, we don't want them changed, so we need to stash the # die and warn handlers, we don't want them changed, so we need to stash the
...@@ -488,6 +489,21 @@ LocalVar('platforms', ' ...@@ -488,6 +489,21 @@ LocalVar('platforms', '
LocalVar('contenttypes', '
#
# The types of content that template files can generate, indexed by file extension.
#
$contenttypes = {
"html" => "text/html" ,
"rdf" => "application/xml" ,
"xml" => "text/xml" ,
"js" => "application/x-javascript" ,
};
');
if ($newstuff ne "") { if ($newstuff ne "") {
print "\nThis version of Bugzilla contains some variables that you may want\n", print "\nThis version of Bugzilla contains some variables that you may want\n",
"to change and adapt to your local settings. Please edit the file\n", "to change and adapt to your local settings. Please edit the file\n",
......
/* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Bugzilla Bug Tracking System.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Myk Melez <myk@mozilla.org>
*/
/* Right align bug IDs. */
.bz_id_column { text-align: right; }
/* Style bug rows according to severity. */
.bz_blocker { color: red; font-weight: bold; }
.bz_critical { color: red; }
.bz_enhancement { font-style: italic; }
/* Style secure bugs if the installation is not using bug groups.
* Installations that *are* using bug groups are likely to be using
* them for almost all bugs, in which case special styling is not
* informative and generally a nuisance.
*/
.bz_secure { color: black; background-color: lightgrey; }
/* Align columns in the "change multiple bugs" form to the right. */
table#form tr th { text-align: right; }
...@@ -35,6 +35,7 @@ sub globals_pl_sillyness { ...@@ -35,6 +35,7 @@ sub globals_pl_sillyness {
my $zz; my $zz;
$zz = @main::SqlStateStack; $zz = @main::SqlStateStack;
$zz = @main::chooseone; $zz = @main::chooseone;
$zz = $main::contenttypes;
$zz = @main::default_column_list; $zz = @main::default_column_list;
$zz = $main::defaultqueryname; $zz = $main::defaultqueryname;
$zz = @main::dontchange; $zz = @main::dontchange;
...@@ -54,8 +55,8 @@ sub globals_pl_sillyness { ...@@ -54,8 +55,8 @@ sub globals_pl_sillyness {
$zz = %main::proddesc; $zz = %main::proddesc;
$zz = @main::prodmaxvotes; $zz = @main::prodmaxvotes;
$zz = $main::superusergroupset; $zz = $main::superusergroupset;
$zz = $main::userid;
$zz = $main::template; $zz = $main::template;
$zz = $main::userid;
$zz = $main::vars; $zz = $main::vars;
} }
...@@ -79,6 +80,9 @@ use Date::Parse; # For str2time(). ...@@ -79,6 +80,9 @@ use Date::Parse; # For str2time().
#use Carp; # for confess #use Carp; # for confess
use RelationSet; use RelationSet;
# Use standard Perl libraries for cross-platform file/directory manipulation.
use File::Spec;
# Some environment variables are not taint safe # Some environment variables are not taint safe
delete @::ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; delete @::ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
...@@ -1611,7 +1615,9 @@ $::template ||= Template->new( ...@@ -1611,7 +1615,9 @@ $::template ||= Template->new(
$var =~ s/\n/\\n/g; $var =~ s/\n/\\n/g;
$var =~ s/\r/\\r/g; $var =~ s/\r/\\r/g;
return $var; return $var;
} } ,
html => \&html_quote ,
} , } ,
} }
) || DisplayError("Template creation failed: " . Template->error()) ) || DisplayError("Template creation failed: " . Template->error())
...@@ -1639,6 +1645,116 @@ $Template::Stash::LIST_OPS->{ containsany } = ...@@ -1639,6 +1645,116 @@ $Template::Stash::LIST_OPS->{ containsany } =
return 0; return 0;
}; };
sub GetOutputFormats {
# Builds a set of possible output formats for a script by looking for
# format files in the appropriate template directories as specified by
# the template include path, the sub-directory parameter, and the
# template name parameter.
# This function is relevant for scripts with one basic function whose
# results can be represented in multiple formats, f.e. buglist.cgi,
# which has one function (query and display of a list of bugs) that can
# be represented in multiple formats (i.e. html, rdf, xml, etc.).
# It is *not* relevant for scripts with several functions but only one
# basic output format, f.e. editattachstatuses.cgi, which not only lists
# statuses but also provides adding, editing, and deleting functions.
# (although it may be possible to make this function applicable under
# these circumstances with minimal modification).
# Format files have names that look like SCRIPT-FORMAT.EXT.tmpl, where
# SCRIPT is the name of the CGI script being invoked, SUBDIR is the name
# of the template sub-directory, FORMAT is the name of the format, and EXT
# is the filename extension identifying the content type of the output.
# When a format file is found, a record for that format is added to
# the hash of format records, indexed by format name, with each record
# containing the name of the format file, its filename extension,
# and its content type (obtained by reference to the $::contenttypes
# hash defined in localconfig).
my ($subdir, $script) = @_;
# A set of output format records, indexed by format name, each record
# containing template, extension, and contenttype fields.
my $formats = {};
# Get the template include path from the template object.
my $includepath = $::template->context->{ LOAD_TEMPLATES }->[0]->include_path();
# Loop over each include directory in reverse so that format files
# earlier in the path override files with the same name later in
# the path (i.e. "custom" formats override "default" ones).
foreach my $path (reverse @$includepath) {
# Get the list of files in the given sub-directory if it exists.
my $dirname = File::Spec->catdir($path, $subdir);
opendir(SUBDIR, $dirname) || next;
my @files = readdir SUBDIR;
closedir SUBDIR;
# Loop over each file in the sub-directory looking for format files
# (files whose name looks like SCRIPT-FORMAT.EXT.tmpl).
foreach my $file (@files) {
if ($file =~ /^$script-(.+)\.(.+)\.(tmpl)$/) {
$formats->{$1} = {
'template' => $file ,
'extension' => $2 ,
'contenttype' => $::contenttypes->{$2} || "text/plain" ,
};
}
}
}
return $formats;
}
sub ValidateOutputFormat {
my ($format, $script, $subdir) = @_;
# If the script name is undefined, assume the script currently being
# executed, deriving its name from Perl's built-in $0 (program name) var.
if (!defined($script)) {
my ($volume, $dirs, $filename) = File::Spec->splitpath($0);
$filename =~ /^(.+)\.cgi$/;
$script = $1
|| DisplayError("Could not determine the name of the script.")
&& exit;
}
# If the format name is undefined or the default format is specified,
# do not do any validation but instead return the default format.
if (!defined($format) || $format eq "default") {
return
{
'template' => "$script.html.tmpl" ,
'extension' => "html" ,
'contenttype' => "text/html" ,
};
}
# If the subdirectory name is undefined, assume the script name.
$subdir = $script if !defined($subdir);
# Get the list of output formats supported by this script.
my $formats = GetOutputFormats($subdir, $script);
# Validate the output format requested by the user.
if (!$formats->{$format}) {
my $escapedname = html_quote($format);
DisplayError("The <em>$escapedname</em> output format is not
supported by this script. Supported formats (besides the
default HTML format) are <em>" .
join("</em>, <em>", map(html_quote($_), keys(%$formats))) .
"</em>.");
exit;
}
# Return the validated output format.
return $formats->{$format};
}
###############################################################################
# Add a "substr" method to the Template Toolkit's "scalar" object # Add a "substr" method to the Template Toolkit's "scalar" object
# that returns a substring of a string. # that returns a substring of a string.
$Template::Stash::SCALAR_OPS->{ substr } = $Template::Stash::SCALAR_OPS->{ substr } =
......
/* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Bugzilla Bug Tracking System.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Myk Melez <myk@mozilla.org>
*/
/* Right align bug IDs. */
.bz_id_column { text-align: right; }
/* Style bug rows according to severity. */
.bz_blocker { color: red; font-weight: bold; }
.bz_critical { color: red; }
.bz_enhancement { font-style: italic; }
/* Style secure bugs if the installation is not using bug groups.
* Installations that *are* using bug groups are likely to be using
* them for almost all bugs, in which case special styling is not
* informative and generally a nuisance.
*/
.bz_secure { color: black; background-color: lightgrey; }
/* Align columns in the "change multiple bugs" form to the right. */
table#form tr th { text-align: right; }
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Myk Melez <myk@mozilla.org>
#%]
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:bz="http://www.bugzilla.org/rdf#">
<bz:result about="[% Param('urlbase') %]buglist.cgi?[% urlquerypart FILTER html %]">
<bz:bugs>
<Seq>
[% FOREACH bug = bugs %]
<li>
<bz:bug about="[% Param('urlbase') %]show_bug.cgi?id=[% bug.id %]">
<bz:id>[% bug.id %]</bz:id>
[% FOREACH column = displaycolumns %]
<bz:[% column %]>[% bug.$column FILTER html %]</bz:[% column %]>
[% END %]
</bz:bug>
</li>
[% END %]
</Seq>
</bz:bugs>
</bz:result>
</RDF>
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Myk Melez <myk@mozilla.org>
#%]
[%############################################################################%]
[%# Initialization #%]
[%############################################################################%]
[% DEFAULT title = "Bug List" %]
[% title = title FILTER html %]
[%############################################################################%]
[%# Bug Table #%]
[%############################################################################%]
<html>
<head>
<title>[% title %]</title>
<link href="css/buglist.css" rel="stylesheet" type="text/css" />
</head>
<body>
[% PROCESS buglist/table.tmpl %]
</body>
</html>
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Myk Melez <myk@mozilla.org>
#%]
[%############################################################################%]
[%# Template Initialization #%]
[%############################################################################%]
[% DEFAULT title = "Bug List" %]
[% style_url = "css/buglist.css" %]
[%############################################################################%]
[%# Page Header #%]
[%############################################################################%]
[% PROCESS global/header
title = title
style = style
%]
<div align="center">
<b>[% currenttime %]</b><br />
[% IF debug %]
<p>[% query FILTER html %]</p>
[% END %]
[% IF quip %]
<a href="quips.cgi"><i>[% quip %]</i></a>
[% END %]
</div>
[% IF toolong %]
<h2>
This list is too long for Bugzilla's little mind; the
Next/Prev/First/Last buttons won't appear on individual bugs.
</h2>
[% END %]
<hr />
[%############################################################################%]
[%# Preceding Status Line #%]
[%############################################################################%]
[% IF bugs.size > 9 %]
[% bugs.size %] bugs found.
[% END %]
[%############################################################################%]
[%# Start of Change Form #%]
[%############################################################################%]
[% IF dotweak %]
<form name="changeform" method="post" action="process_bug.cgi">
[% END %]
[%############################################################################%]
[%# Bug Table #%]
[%############################################################################%]
[% FLUSH %]
[% PROCESS buglist/table.tmpl %]
[%############################################################################%]
[%# Succeeding Status Line #%]
[%############################################################################%]
[% IF bugs.count == 0 %]
Zarro Boogs found.
<p>
<a href="query.cgi">Query Page</a>
&nbsp;&nbsp;<a href="enter_bug.cgi">Enter New Bug</a>
<a href="query.cgi?[% urlquerypart %]">Edit this query</a>
</p>
[% ELSIF bugs.count == 1 %]
One bug found.
[% ELSE %]
[% bugs.size %] bugs found.
[% END %]
<br />
[%############################################################################%]
[%# Rest of Change Form #%]
[%############################################################################%]
[% IF dotweak %]
[% PROCESS "buglist/change-form.tmpl" %]
</form>
<hr />
[% END %]
[%############################################################################%]
[%# Navigation Bar #%]
[%############################################################################%]
[% IF bugs.size > 0 %]
<form method="post" action="long_list.cgi">
<input type="hidden" name="buglist" value="[% buglist %]">
<input type="submit" value="Long Format">
<a href="query.cgi">Query Page</a> &nbsp;&nbsp;
<a href="enter_bug.cgi">Enter New Bug</a> &nbsp;&nbsp;
<a href="colchange.cgi?[% urlquerypart %]">Change Columns</a> &nbsp;&nbsp;
[% IF bugs.size > 1 && caneditbugs && !dotweak %]
<a href="buglist.cgi?[% urlquerypart %]
[%- "&order=$order" FILTER uri html IF order %]&tweak=1">Change Several
Bugs at Once</a>
&nbsp;&nbsp;
[% END %]
[% IF bugowners %]
<a href="mailto:[% bugowners %]">Send Mail to Bug Owners</a> &nbsp;&nbsp;
[% END %]
<a href="query.cgi?[% urlquerypart %]">Edit this Query</a> &nbsp;&nbsp;
</form>
[% END %]
[%############################################################################%]
[%# Page Footer #%]
[%############################################################################%]
[% PROCESS global/footer %]
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Myk Melez <myk@mozilla.org>
#%]
<html>
<head>
<title>Bugzilla is pondering your query</title>
</head>
<body>
<h1 style="margin-top: 20%; text-align: center;">Please stand by ...</h1>
[% IF debug %]
<p>
<code>[% query FILTER html %]</code>
</p>
[% END %]
</body>
</html>
[%# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Myk Melez <myk@mozilla.org>
#%]
[%############################################################################%]
[%# Initialization #%]
[%############################################################################%]
[%# Columns whose titles or values should be abbreviated to make the list
# more compact. For columns whose titles should be abbreviated,
# the shortened title is included. For columns whose values should be
# abbreviated, a maximum length is provided along with the ellipsis that
# should be added to an abbreviated value, if any.
#%]
[% abbrev =
{
"severity" => { size => 3 , title => "Sev" } ,
"priority" => { size => 3 , title => "Pri" } ,
"platform" => { size => 3 , title => "Plt" } ,
"status" => { size => 4 } ,
"reporter" => { size => 45 , ellipsis => "..." } ,
"owner" => { size => 45 , ellipsis => "..." } ,
"qa_contact" => { size => 45 , ellipsis => "..." , title => "QAContact" } ,
"resolution" => { size => 4 } ,
"summary" => { size => 60 , ellipsis => "..." } ,
"status_whiteboard" => { title => "StatusSummary" } ,
"component" => { size => 8 , title => "Comp" } ,
"product" => { size => 8 } ,
"version" => { size => 5 , title => "Vers" } ,
"os" => { size => 4 } ,
"target_milestone" => { title => "TargetM" } ,
}
%]
[%############################################################################%]
[%# Table Header #%]
[%############################################################################%]
[% tableheader = BLOCK %]
<table class="bz_buglist" cellspacing="0" cellpadding="4" width="100%">
<colgroup>
<col class="bz_id_column">
[% FOREACH id = displaycolumns %]
<col class="bz_[% id %]_column">
[% END %]
</colgroup>
<tr align="left">
<th colspan="[% splitheader ? 2 : 1 %]">
<a href="buglist.cgi?[% urlquerypart %]&order=bugs.bug_id">ID</a>
</th>
[% IF splitheader %]
[% FOREACH id = displaycolumns %]
[% NEXT IF loop.count() % 2 == 0 %]
[% column = columns.$id %]
[% PROCESS columnheader %]
[% END %]
</tr><tr align="left"><th>&nbsp;</th>
[% FOREACH id = displaycolumns %]
[% NEXT UNLESS loop.count() % 2 == 0 %]
[% column = columns.$id %]
[% PROCESS columnheader %]
[% END %]
[% ELSE %]
[% FOREACH id = displaycolumns %]
[% column = columns.$id %]
[% PROCESS columnheader %]
[% END %]
[% END %]
</tr>
[% END %]
[% BLOCK columnheader %]
<th colspan="[% splitheader ? 2 : 1 %]">
<a href="buglist.cgi?[% urlquerypart %]&order=
[% column.name FILTER uri html %]
[% ",$order" FILTER uri html IF order %]">
[%- abbrev.$id.title || column.title -%]</a>
</th>
[% END %]
[%############################################################################%]
[%# Bug Table #%]
[%############################################################################%]
[% FOREACH bug = bugs %]
[% FLUSH IF loop.count() % 10 == 1 %]
[%# At the beginning of every hundred bugs in the list, start a new table. %]
[% IF loop.count() % 100 == 1 %]
[% tableheader %]
[% END %]
<tr class="bz_[% bug.severity %] bz_[% bug.priority %] [%+ "bz_secure" IF (bug.groupset && !usebuggroups) %]">
<td>
[% IF dotweak %]<input type="checkbox" name="id_[% bug.id %]">[% END %]
<a href="show_bug.cgi?id=[% bug.id %]">[% bug.id %]</a>
</td>
[% FOREACH column = displaycolumns %]
<td>
[%+ bug.$column.truncate(abbrev.$column.size, abbrev.$column.ellipsis) FILTER html %]
</td>
[% END %]
</tr>
[%# At the end of every hundred bugs in the list, or at the end of the list,
# end the current table.
#%]
[% IF loop.last() || loop.count() % 100 == 0 %]
</table>
[% END %]
[% END %]
...@@ -11,14 +11,23 @@ ...@@ -11,14 +11,23 @@
<html> <html>
<head> <head>
<title>[% title %]</title> <title>[% title %]</title>
[% Param('headerhtml') %] [% Param('headerhtml') %]
[% jscript %] [% jscript %]
[% IF style %] [% IF style %]
<style type="text/css"> <style type="text/css">
[% style %] [% style %]
</style> </style>
[% END %] [% END %]
[% IF style_url %]
<link href="[% style_url %]" rel="stylesheet" type="text/css" />
[% END %]
</head> </head>
<body [% Param('bodyhtml') %][% " " %][% extra %]> <body [% Param('bodyhtml') %][% " " %][% extra %]>
[% PerformSubsts(Param('bannerhtml')) %] [% PerformSubsts(Param('bannerhtml')) %]
......
[% DEFAULT title = "Bugzilla Message" %] [% DEFAULT title = "Bugzilla Message" %]
[% INCLUDE global/header title=title %] [% PROCESS global/header %]
[%# The "header" template automatically displays the contents of a "message" [%# The "header" template automatically displays the contents of a "message"
variable if it finds one, so it is not necessary to display the message variable if it finds one, so it is not necessary to display the message
...@@ -13,4 +13,4 @@ ...@@ -13,4 +13,4 @@
</p> </p>
[% END %] [% END %]
[% INCLUDE global/footer %] [% PROCESS global/footer %]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment