Commit 645307b7 authored by lpsolit%gmail.com's avatar lpsolit%gmail.com

Bug 284875: Move GetBugLink, GetAttachmentLink, and quoteUrls out of globals.pl…

Bug 284875: Move GetBugLink, GetAttachmentLink, and quoteUrls out of globals.pl - Patch by Frédéric Buclin <LpSolit@gmail.com> r=mkanat a=justdave
parent 0bd92669
......@@ -1221,7 +1221,7 @@ sub ValidateBugAlias {
my $vars = {};
$vars->{'alias'} = $alias;
if ($id) {
$vars->{'bug_link'} = &::GetBugLink($id, $id);
$vars->{'bug_id'} = $id;
ThrowUserError("alias_in_use", $vars);
}
......@@ -1294,19 +1294,12 @@ sub ValidateDependencies {
my @deps = @{$deptree{'dependson'}};
my @blocks = @{$deptree{'blocked'}};
my @union = ();
my @isect = ();
my %union = ();
my %isect = ();
foreach my $b (@deps, @blocks) { $union{$b}++ && $isect{$b}++ }
@union = keys %union;
@isect = keys %isect;
my @isect = keys %isect;
if (scalar(@isect) > 0) {
my $both = "";
foreach my $i (@isect) {
$both .= &::GetBugLink($i, "#" . $i) . " ";
}
ThrowUserError("dependency_loop_multi", { both => $both });
ThrowUserError("dependency_loop_multi", {'deps' => \@isect});
}
return %deps;
}
......
......@@ -218,6 +218,202 @@ sub get_format {
};
}
# This routine quoteUrls contains inspirations from the HTML::FromText CPAN
# module by Gareth Rees <garethr@cre.canon.co.uk>. It has been heavily hacked,
# all that is really recognizable from the original is bits of the regular
# expressions.
# This has been rewritten to be faster, mainly by substituting 'as we go'.
# If you want to modify this routine, read the comments carefully
sub quoteUrls {
my ($text, $curr_bugid) = (@_);
return $text unless $text;
# We use /g for speed, but uris can have other things inside them
# (http://foo/bug#3 for example). Filtering that out filters valid
# bug refs out, so we have to do replacements.
# mailto can't contain space or #, so we don't have to bother for that
# Do this by escaping \0 to \1\0, and replacing matches with \0\0$count\0\0
# \0 is used because its unliklely to occur in the text, so the cost of
# doing this should be very small
# Also, \0 won't appear in the value_quote'd bug title, so we don't have
# to worry about bogus substitutions from there
# escape the 2nd escape char we're using
my $chr1 = chr(1);
$text =~ s/\0/$chr1\0/g;
# However, note that adding the title (for buglinks) can affect things
# In particular, attachment matches go before bug titles, so that titles
# with 'attachment 1' don't double match.
# Dupe checks go afterwards, because that uses ^ and \Z, which won't occur
# if it was subsituted as a bug title (since that always involve leading
# and trailing text)
# Because of entities, its easier (and quicker) to do this before escaping
my @things;
my $count = 0;
my $tmp;
# non-mailto protocols
my $protocol_re = qr/(afs|cid|ftp|gopher|http|https|irc|mid|news|nntp|prospero|telnet|view-source|wais)/i;
$text =~ s~\b(${protocol_re}: # The protocol:
[^\s<>\"]+ # Any non-whitespace
[\w\/]) # so that we end in \w or /
~($tmp = html_quote($1)) &&
($things[$count++] = "<a href=\"$tmp\">$tmp</a>") &&
("\0\0" . ($count-1) . "\0\0")
~egox;
# We have to quote now, otherwise our html is itsself escaped
# THIS MEANS THAT A LITERAL ", <, >, ' MUST BE ESCAPED FOR A MATCH
$text = html_quote($text);
# Color quoted text
$text =~ s~^(&gt;.+)$~<span class="quote">$1</span >~mg;
$text =~ s~</span >\n<span class="quote">~\n~g;
# mailto:
# Use |<nothing> so that $1 is defined regardless
$text =~ s~\b(mailto:|)?([\w\.\-\+\=]+\@[\w\-]+(?:\.[\w\-]+)+)\b
~<a href=\"mailto:$2\">$1$2</a>~igx;
# attachment links - handle both cases separately for simplicity
$text =~ s~((?:^Created\ an\ |\b)attachment\s*\(id=(\d+)\)(\s\[edit\])?)
~($things[$count++] = get_attachment_link($2, $1)) &&
("\0\0" . ($count-1) . "\0\0")
~egmx;
$text =~ s~\b(attachment\s*\#?\s*(\d+))
~($things[$count++] = get_attachment_link($2, $1)) &&
("\0\0" . ($count-1) . "\0\0")
~egmxi;
# Current bug ID this comment belongs to
my $current_bugurl = $curr_bugid ? "show_bug.cgi?id=$curr_bugid" : "";
# This handles bug a, comment b type stuff. Because we're using /g
# we have to do this in one pattern, and so this is semi-messy.
# Also, we can't use $bug_re?$comment_re? because that will match the
# empty string
my $bug_re = qr/bug\s*\#?\s*(\d+)/i;
my $comment_re = qr/comment\s*\#?\s*(\d+)/i;
$text =~ s~\b($bug_re(?:\s*,?\s*$comment_re)?|$comment_re)
~ # We have several choices. $1 here is the link, and $2-4 are set
# depending on which part matched
(defined($2) ? get_bug_link($2,$1,$3) :
"<a href=\"$current_bugurl#c$4\">$1</a>")
~egox;
# Old duplicate markers
$text =~ s~(?<=^\*\*\*\ This\ bug\ has\ been\ marked\ as\ a\ duplicate\ of\ )
(\d+)
(?=\ \*\*\*\Z)
~get_bug_link($1, $1)
~egmx;
# Now remove the encoding hacks
$text =~ s/\0\0(\d+)\0\0/$things[$1]/eg;
$text =~ s/$chr1\0/\0/g;
return $text;
}
# Creates a link to an attachment, including its title.
sub get_attachment_link {
my ($attachid, $link_text) = @_;
my $dbh = Bugzilla->dbh;
detaint_natural($attachid)
|| die "get_attachment_link() called with non-integer attachment number";
my ($bugid, $isobsolete, $desc) =
$dbh->selectrow_array('SELECT bug_id, isobsolete, description
FROM attachments WHERE attach_id = ?',
undef, $attachid);
if ($bugid) {
my $title = "";
my $className = "";
if (Bugzilla->user->can_see_bug($bugid)) {
$title = $desc;
}
if ($isobsolete) {
$className = "bz_obsolete";
}
# Prevent code injection in the title.
$title = value_quote($title);
$link_text =~ s/ \[edit\]$//;
my $linkval = "attachment.cgi?id=$attachid&amp;action=";
# Whitespace matters here because these links are in <pre> tags.
return qq|<span class="$className">|
. qq|<a href="${linkval}view" title="$title">$link_text</a>|
. qq| <a href="${linkval}edit" title="$title">[edit]</a>|
. qq|</span>|;
}
else {
return qq{$link_text};
}
}
# Creates a link to a bug, including its title.
# It takes either two or three parameters:
# - The bug number
# - The link text, to place between the <a>..</a>
# - An optional comment number, for linking to a particular
# comment in the bug
sub get_bug_link {
my ($bug_num, $link_text, $comment_num) = @_;
my $dbh = Bugzilla->dbh;
if (!defined($bug_num) || ($bug_num eq "")) {
return "&lt;missing bug number&gt;";
}
my $quote_bug_num = html_quote($bug_num);
detaint_natural($bug_num) || return "&lt;invalid bug number: $quote_bug_num&gt;";
my ($bug_state, $bug_res, $bug_desc) =
$dbh->selectrow_array('SELECT bugs.bug_status, resolution, short_desc
FROM bugs WHERE bugs.bug_id = ?',
undef, $bug_num);
if ($bug_state) {
# Initialize these variables to be "" so that we don't get warnings
# if we don't change them below (which is highly likely).
my ($pre, $title, $post) = ("", "", "");
$title = $bug_state;
if ($bug_state eq 'UNCONFIRMED') {
$pre = "<i>";
$post = "</i>";
}
elsif (! &::IsOpenedState($bug_state)) {
$pre = '<span class="bz_closed">';
$title .= " $bug_res";
$post = '</span>';
}
if (Bugzilla->user->can_see_bug($bug_num)) {
$title .= " - $bug_desc";
}
# Prevent code injection in the title.
$title = value_quote($title);
my $linkval = "show_bug.cgi?id=$bug_num";
if (defined $comment_num) {
$linkval .= "#c$comment_num";
}
return qq{$pre<a href="$linkval" title="$title">$link_text</a>$post};
}
else {
return qq{$link_text};
}
}
###############################################################################
# Templatization Code
......@@ -406,7 +602,7 @@ sub create {
my ($context, $bug) = @_;
return sub {
my $text = shift;
return &::quoteUrls($text, $bug);
return quoteUrls($text, $bug);
};
},
1
......@@ -416,7 +612,7 @@ sub create {
my ($context, $bug) = @_;
return sub {
my $text = shift;
return &::GetBugLink($bug, $text);
return get_bug_link($bug, $text);
};
},
1
......
......@@ -837,7 +837,6 @@ sub viewall
$vars->{'attachments'} = \@attachments;
$vars->{'bugassignee_id'} = $assignee_id;
$vars->{'bugsummary'} = $bugsummary;
$vars->{'GetBugLink'} = \&GetBugLink;
print $cgi->header();
......@@ -884,7 +883,6 @@ sub enter
$vars->{'attachments'} = \@attachments;
$vars->{'bugassignee_id'} = $assignee_id;
$vars->{'bugsummary'} = $bugsummary;
$vars->{'GetBugLink'} = \&GetBugLink;
SendSQL("SELECT product_id, component_id FROM bugs
WHERE bug_id = $bugid");
......@@ -1145,7 +1143,6 @@ sub edit {
$vars->{'bugsummary'} = $bugsummary;
$vars->{'isviewable'} = $isviewable;
$vars->{'attachments'} = $bugattachments;
$vars->{'GetBugLink'} = \&GetBugLink;
# Determine if PatchReader is installed
eval {
......
......@@ -475,243 +475,6 @@ sub get_component_name {
return $comp;
}
# This routine quoteUrls contains inspirations from the HTML::FromText CPAN
# module by Gareth Rees <garethr@cre.canon.co.uk>. It has been heavily hacked,
# all that is really recognizable from the original is bits of the regular
# expressions.
# This has been rewritten to be faster, mainly by substituting 'as we go'.
# If you want to modify this routine, read the comments carefully
sub quoteUrls {
my ($text, $curr_bugid) = (@_);
return $text unless $text;
# We use /g for speed, but uris can have other things inside them
# (http://foo/bug#3 for example). Filtering that out filters valid
# bug refs out, so we have to do replacements.
# mailto can't contain space or #, so we don't have to bother for that
# Do this by escaping \0 to \1\0, and replacing matches with \0\0$count\0\0
# \0 is used because its unliklely to occur in the text, so the cost of
# doing this should be very small
# Also, \0 won't appear in the value_quote'd bug title, so we don't have
# to worry about bogus substitutions from there
# escape the 2nd escape char we're using
my $chr1 = chr(1);
$text =~ s/\0/$chr1\0/g;
# However, note that adding the title (for buglinks) can affect things
# In particular, attachment matches go before bug titles, so that titles
# with 'attachment 1' don't double match.
# Dupe checks go afterwards, because that uses ^ and \Z, which won't occur
# if it was subsituted as a bug title (since that always involve leading
# and trailing text)
# Because of entities, its easier (and quicker) to do this before escaping
my @things;
my $count = 0;
my $tmp;
# non-mailto protocols
my $protocol_re = qr/(afs|cid|ftp|gopher|http|https|irc|mid|news|nntp|prospero|telnet|view-source|wais)/i;
$text =~ s~\b(${protocol_re}: # The protocol:
[^\s<>\"]+ # Any non-whitespace
[\w\/]) # so that we end in \w or /
~($tmp = html_quote($1)) &&
($things[$count++] = "<a href=\"$tmp\">$tmp</a>") &&
("\0\0" . ($count-1) . "\0\0")
~egox;
# We have to quote now, otherwise our html is itsself escaped
# THIS MEANS THAT A LITERAL ", <, >, ' MUST BE ESCAPED FOR A MATCH
$text = html_quote($text);
# Color quoted text
$text =~ s~^(&gt;.+)$~<span class="quote">$1</span >~mg;
$text =~ s~</span >\n<span class="quote">~\n~g;
# mailto:
# Use |<nothing> so that $1 is defined regardless
$text =~ s~\b(mailto:|)?([\w\.\-\+\=]+\@[\w\-]+(?:\.[\w\-]+)+)\b
~<a href=\"mailto:$2\">$1$2</a>~igx;
# attachment links - handle both cases separately for simplicity
$text =~ s~((?:^Created\ an\ |\b)attachment\s*\(id=(\d+)\)(\s\[edit\])?)
~($things[$count++] = GetAttachmentLink($2, $1)) &&
("\0\0" . ($count-1) . "\0\0")
~egmx;
$text =~ s~\b(attachment\s*\#?\s*(\d+))
~($things[$count++] = GetAttachmentLink($2, $1)) &&
("\0\0" . ($count-1) . "\0\0")
~egmxi;
# Current bug ID this comment belongs to
my $current_bugurl = $curr_bugid ? "show_bug.cgi?id=$curr_bugid" : "";
# This handles bug a, comment b type stuff. Because we're using /g
# we have to do this in one pattern, and so this is semi-messy.
# Also, we can't use $bug_re?$comment_re? because that will match the
# empty string
my $bug_re = qr/bug\s*\#?\s*(\d+)/i;
my $comment_re = qr/comment\s*\#?\s*(\d+)/i;
$text =~ s~\b($bug_re(?:\s*,?\s*$comment_re)?|$comment_re)
~ # We have several choices. $1 here is the link, and $2-4 are set
# depending on which part matched
(defined($2) ? GetBugLink($2,$1,$3) :
"<a href=\"$current_bugurl#c$4\">$1</a>")
~egox;
# Old duplicate markers
$text =~ s~(?<=^\*\*\*\ This\ bug\ has\ been\ marked\ as\ a\ duplicate\ of\ )
(\d+)
(?=\ \*\*\*\Z)
~GetBugLink($1, $1)
~egmx;
# Now remove the encoding hacks
$text =~ s/\0\0(\d+)\0\0/$things[$1]/eg;
$text =~ s/$chr1\0/\0/g;
return $text;
}
# GetAttachmentLink creates a link to an attachment,
# including its title.
sub GetAttachmentLink {
my ($attachid, $link_text) = @_;
detaint_natural($attachid) ||
die "GetAttachmentLink() called with non-integer attachment number";
# If we've run GetAttachmentLink() for this attachment before,
# %::attachlink will contain an anonymous array ref of relevant
# values. If not, we need to get the information from the database.
if (! defined $::attachlink{$attachid}) {
# Make sure any unfetched data from a currently running query
# is saved off rather than overwritten
PushGlobalSQLState();
SendSQL("SELECT bug_id, isobsolete, description
FROM attachments WHERE attach_id = $attachid");
if (MoreSQLData()) {
my ($bugid, $isobsolete, $desc) = FetchSQLData();
my $title = "";
my $className = "";
if (Bugzilla->user->can_see_bug($bugid)) {
$title = $desc;
}
if ($isobsolete) {
$className = "bz_obsolete";
}
$::attachlink{$attachid} = [value_quote($title), $className];
}
else {
# Even if there's nothing in the database, we want to save a blank
# anonymous array in the %::attachlink hash so the query doesn't get
# run again next time we're called for this attachment number.
$::attachlink{$attachid} = [];
}
# All done with this sidetrip
PopGlobalSQLState();
}
# Now that we know we've got all the information we're gonna get, let's
# return the link (which is the whole reason we were called :)
my ($title, $className) = @{$::attachlink{$attachid}};
# $title will be undefined if the attachment didn't exist in the database.
if (defined $title) {
$link_text =~ s/ \[edit\]$//;
my $linkval = "attachment.cgi?id=$attachid&amp;action=";
# Whitespace matters here because these links are in <pre> tags.
return qq|<span class="$className">|
. qq|<a href="${linkval}view" title="$title">$link_text</a>|
. qq| <a href="${linkval}edit" title="$title">[edit]</a>|
. qq|</span>|;
}
else {
return qq{$link_text};
}
}
# GetBugLink creates a link to a bug, including its title.
# It takes either two or three parameters:
# - The bug number
# - The link text, to place between the <a>..</a>
# - An optional comment number, for linking to a particular
# comment in the bug
sub GetBugLink {
my ($bug_num, $link_text, $comment_num) = @_;
if (! defined $bug_num || $bug_num eq "") {
return "&lt;missing bug number&gt;";
}
my $quote_bug_num = html_quote($bug_num);
detaint_natural($bug_num) || return "&lt;invalid bug number: $quote_bug_num&gt;";
# If we've run GetBugLink() for this bug number before, %::buglink
# will contain an anonymous array ref of relevent values, if not
# we need to get the information from the database.
if (! defined $::buglink{$bug_num}) {
# Make sure any unfetched data from a currently running query
# is saved off rather than overwritten
PushGlobalSQLState();
SendSQL("SELECT bugs.bug_status, resolution, short_desc " .
"FROM bugs WHERE bugs.bug_id = $bug_num");
# If the bug exists, save its data off for use later in the sub
if (MoreSQLData()) {
my ($bug_state, $bug_res, $bug_desc) = FetchSQLData();
# Initialize these variables to be "" so that we don't get warnings
# if we don't change them below (which is highly likely).
my ($pre, $title, $post) = ("", "", "");
$title = $bug_state;
if ($bug_state eq 'UNCONFIRMED') {
$pre = "<i>";
$post = "</i>";
}
elsif (! IsOpenedState($bug_state)) {
$pre = '<span class="bz_closed">';
$title .= " $bug_res";
$post = '</span>';
}
if (Bugzilla->user->can_see_bug($bug_num)) {
$title .= " - $bug_desc";
}
$::buglink{$bug_num} = [$pre, value_quote($title), $post];
}
else {
# Even if there's nothing in the database, we want to save a blank
# anonymous array in the %::buglink hash so the query doesn't get
# run again next time we're called for this bug number.
$::buglink{$bug_num} = [];
}
# All done with this sidetrip
PopGlobalSQLState();
}
# Now that we know we've got all the information we're gonna get, let's
# return the link (which is the whole reason we were called :)
my ($pre, $title, $post) = @{$::buglink{$bug_num}};
# $title will be undefined if the bug didn't exist in the database.
if (defined $title) {
my $linkval = "show_bug.cgi?id=$bug_num";
if (defined $comment_num) {
$linkval .= "#c$comment_num";
}
return qq{$pre<a href="$linkval" title="$title">$link_text</a>$post};
}
else {
return qq{$link_text};
}
}
# Returns a list of all the legal values for a field that has a
# list of legal values, like rep_platform or resolution.
sub get_legal_field_values {
......
......@@ -523,7 +523,6 @@ $vars->{'do_report'} = $do_report;
$vars->{'do_depends'} = $do_depends;
$vars->{'check_time'} = \&check_time;
$vars->{'sort_bug_keys'} = \&sort_bug_keys;
$vars->{'GetBugLink'} = \&GetBugLink;
my $format = $template->get_format("bug/summarize-time", undef, $ctype);
......
......@@ -205,7 +205,7 @@ sub directive_ok {
return 1 if $directive =~ /^Hook.process\(/;
# Other functions guaranteed to return OK output
return 1 if $directive =~ /^(time2str|GetBugLink|url)\(/;
return 1 if $directive =~ /^(time2str|url)\(/;
# Safe Template Toolkit virtual methods
return 1 if $directive =~ /\.(length$|size$|push\()/;
......
......@@ -26,7 +26,7 @@
[%# Define strings that will serve as the title and header of this page %]
[% title = BLOCK %]Create New Attachment for [% terms.Bug %] #[% bugid %][% END %]
[% h1 = BLOCK %]Create New Attachment for
[%+ GetBugLink(bugid, "$terms.Bug $bugid") %][% END %]
[%+ "$terms.Bug $bugid" FILTER bug_link(bugid) FILTER none %][% END %]
[% h2 = BLOCK %][% bugsummary FILTER html %][% END %]
[% PROCESS global/header.html.tmpl
......
......@@ -28,7 +28,7 @@
[% END %]
[% h1 = BLOCK %]
Attachment [% attachment.id %] Details for
[%+ GetBugLink(attachment.bug_id, "$terms.Bug ${attachment.bug_id}") %]
[%+ "$terms.Bug ${attachment.bug_id}" FILTER bug_link(attachment.bug_id) FILTER none %]
[% END %]
[% h2 = BLOCK %][% bugsummary FILTER html %][% END %]
......
......@@ -22,7 +22,7 @@
[% PROCESS global/variables.none.tmpl %]
[% filtered_summary = bugsummary FILTER html %]
[% h1 = BLOCK %]View All Attachments for
[%+ GetBugLink(bugid, "$terms.Bug $bugid") %][% END %]
[%+ "$terms.Bug $bugid" FILTER bug_link(bugid) FILTER none %][% END %]
[% PROCESS global/header.html.tmpl
title = "View All Attachments for $terms.Bug #$bugid"
h1 = h1
......
......@@ -21,7 +21,7 @@
[% title = "Time Summary " %]
[% IF do_depends %]
[% title = title _ "for " %]
[% h1 = title _ GetBugLink(ids.0, "$terms.Bug $ids.0") %]
[% h1 = title _ "$terms.Bug $ids.0" FILTER bug_link(ids.0) FILTER none %]
[% title = title _ "$terms.Bug $ids.0: " %]
[% h1 = (h1 _ " (and $terms.bugs blocking it)") IF do_depends %]
[% ELSE %]
......
......@@ -29,7 +29,7 @@
# Simple literals - [% " selected" ...
# Values always used for numbers - [% (i|j|k|n|count) %]
# Params - [% Param(...
# Safe functions - [% (time2str|GetBugLink)...
# Safe functions - [% (time2str)...
# Safe vmethods - [% foo.size %] [% foo.length %]
# [% foo.push() %]
# TT loop variables - [% loop.count %]
......
......@@ -82,8 +82,9 @@
[% ELSIF error == "alias_in_use" %]
[% title = "Alias In Use" %]
[% terms.Bug %] [%+ bug_link FILTER none %] has already taken the alias
<em>[% alias FILTER html %]</em>. Please choose another one.
[% terms.Bug %] [%+ bug_id FILTER bug_link(bug_id) FILTER none %]
has already taken the alias <em>[% alias FILTER html %]</em>.
Please choose another one.
[% ELSIF error == "alias_not_defined" %]
[% title = "Alias Is Not Defined" %]
......@@ -303,8 +304,11 @@
[% title = "Dependency Loop Detected" %]
The following [% terms.bug %](s) would appear on both the "depends on"
and "blocks" parts of the dependency tree if these changes
are committed: [% both FILTER none %]. This would create a circular
dependency, which is not allowed.
are committed:
[% FOREACH dep = deps %]
[%+ dep FILTER bug_link(dep) FILTER none %]
[% END %].
This would create a circular dependency, which is not allowed.
[% ELSIF error == "dependency_loop_single" %]
[% title = "Dependency Loop Detected" %]
......
......@@ -33,7 +33,7 @@
# This means if you change "bug" to "problem", then if you have
# "problem 3" in a comment, it won't become a clickable URL.
# To have that feature, you must edit the quoteUrls function in
# globals.pl (in the base Bugzilla directory).
# Bugzilla/Template.pm.
# Change the line:
# my $bug_re = qr/bug\s*\#?\s*(\d+)/i;
# to something like:
......
......@@ -30,7 +30,7 @@
# Simple literals - [% " selected" ...
# Values always used for numbers - [% (i|j|k|n|count) %]
# Params - [% Param(...
# Safe functions - [% (time2str|GetBugLink)...
# Safe functions - [% (time2str)...
# Safe vmethods - [% foo.size %] [% foo.length %]
# [% foo.push() %]
# TT loop variables - [% loop.count %]
......
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