Commit 79b57226 authored by lpsolit%gmail.com's avatar lpsolit%gmail.com

Bug 346086: [SECURITY] attachment.cgi lets you view descriptions of private…

Bug 346086: [SECURITY] attachment.cgi lets you view descriptions of private attachments even when you are not in the insidergroup - Patch by Frédéric Buclin <LpSolit@gmail.com> r=myk a=justdave
parent b0ddda44
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
# Contributor(s): Terry Weissman <terry@mozilla.org> # Contributor(s): Terry Weissman <terry@mozilla.org>
# Myk Melez <myk@mozilla.org> # Myk Melez <myk@mozilla.org>
# Marc Schumann <wurblzap@gmail.com> # Marc Schumann <wurblzap@gmail.com>
# Frédéric Buclin <LpSolit@gmail.com>
use strict; use strict;
...@@ -475,7 +476,8 @@ sub _validate_data { ...@@ -475,7 +476,8 @@ sub _validate_data {
=item C<get_attachments_by_bug($bug_id)> =item C<get_attachments_by_bug($bug_id)>
Description: retrieves and returns the attachments for the given bug. Description: retrieves and returns the attachments the currently logged in
user can view for the given bug.
Params: C<$bug_id> - integer - the ID of the bug for which Params: C<$bug_id> - integer - the ID of the bug for which
to retrieve and return attachments. to retrieve and return attachments.
...@@ -486,10 +488,22 @@ Returns: a reference to an array of attachment objects. ...@@ -486,10 +488,22 @@ Returns: a reference to an array of attachment objects.
sub get_attachments_by_bug { sub get_attachments_by_bug {
my ($class, $bug_id) = @_; my ($class, $bug_id) = @_;
my $attach_ids = Bugzilla->dbh->selectcol_arrayref("SELECT attach_id my $user = Bugzilla->user;
FROM attachments my $dbh = Bugzilla->dbh;
WHERE bug_id = ?",
undef, $bug_id); # By default, private attachments are not accessible, unless the user
# is in the insider group or submitted the attachment.
my $and_restriction = '';
my @values = ($bug_id);
unless ($user->is_insider) {
$and_restriction = 'AND (isprivate = 0 OR submitter_id = ?)';
push(@values, $user->id);
}
my $attach_ids = $dbh->selectcol_arrayref("SELECT attach_id FROM attachments
WHERE bug_id = ? $and_restriction",
undef, @values);
my $attachments = Bugzilla::Attachment->get_list($attach_ids); my $attachments = Bugzilla::Attachment->get_list($attach_ids);
return $attachments; return $attachments;
} }
...@@ -597,33 +611,41 @@ sub validate_content_type { ...@@ -597,33 +611,41 @@ sub validate_content_type {
=item C<validate_can_edit()> =item C<validate_can_edit()>
Description: validates if the user is allowed to edit the attachment. Description: validates if the user is allowed to view and edit the attachment.
Only the submitter or someone with editbugs privs can edit it. Only the submitter or someone with editbugs privs can edit it.
Only the submitter and users in the insider group can view
private attachments.
Returns: 1 on success. Else an error is thrown. Returns: 1 on success. Else an error is thrown.
=cut =cut
sub validate_can_edit { sub validate_can_edit {
my ($class, $attach_id) = @_; my $attachment = shift;
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user; my $user = Bugzilla->user;
# People in editbugs can edit all attachments # Bug 97729 - the submitter can edit their attachments.
return if $user->in_group('editbugs'); return if ($attachment->attacher->id == $user->id);
# Bug 97729 - the submitter can edit their attachments # Only users in the insider group can view private attachments.
my ($ref) = $dbh->selectrow_array('SELECT attach_id FROM attachments if ($attachment->isprivate && !$user->is_insider) {
WHERE attach_id = ? AND submitter_id = ?', ThrowUserError('illegal_attachment_edit', {attach_id => $attachment->id});
undef, ($attach_id, $user->id)); }
$ref || ThrowUserError('illegal_attachment_edit', { attach_id => $attach_id }); # Users with editbugs privs can edit all attachments.
return if $user->in_group('editbugs');
# If we come here, then this attachment cannot be seen by the user.
ThrowUserError('illegal_attachment_edit', { attach_id => $attachment->id });
} }
=item C<validate_obsolete($bug_id)> =item C<validate_obsolete($bug_id)>
Description: validates if attachments the user wants to mark as obsolete Description: validates if attachments the user wants to mark as obsolete
really belong to the given bug and are not already obsolete. really belong to the given bug and are not already obsolete.
Moreover, a user cannot mark an attachment as obsolete if
he cannot view it (due to restrictions on it).
Params: $bug_id - The bug ID obsolete attachments should belong to. Params: $bug_id - The bug ID obsolete attachments should belong to.
...@@ -636,7 +658,8 @@ sub validate_obsolete { ...@@ -636,7 +658,8 @@ sub validate_obsolete {
my $cgi = Bugzilla->cgi; my $cgi = Bugzilla->cgi;
# Make sure the attachment id is valid and the user has permissions to view # Make sure the attachment id is valid and the user has permissions to view
# the bug to which it is attached. # the bug to which it is attached. Make sure also that the user can view
# the attachment itself.
my @obsolete_attachments; my @obsolete_attachments;
foreach my $attachid ($cgi->param('obsolete')) { foreach my $attachid ($cgi->param('obsolete')) {
my $vars = {}; my $vars = {};
...@@ -650,6 +673,9 @@ sub validate_obsolete { ...@@ -650,6 +673,9 @@ sub validate_obsolete {
# Make sure the attachment exists in the database. # Make sure the attachment exists in the database.
ThrowUserError('invalid_attach_id', $vars) unless $attachment; ThrowUserError('invalid_attach_id', $vars) unless $attachment;
# Check that the user can view and edit this attachment.
$attachment->validate_can_edit;
$vars->{'description'} = $attachment->description; $vars->{'description'} = $attachment->description;
if ($attachment->bug_id != $bug_id) { if ($attachment->bug_id != $bug_id) {
...@@ -662,8 +688,6 @@ sub validate_obsolete { ...@@ -662,8 +688,6 @@ sub validate_obsolete {
ThrowCodeError('attachment_already_obsolete', $vars); ThrowCodeError('attachment_already_obsolete', $vars);
} }
# Check that the user can modify this attachment.
$class->validate_can_edit($attachid);
push(@obsolete_attachments, $attachment); push(@obsolete_attachments, $attachment);
} }
return @obsolete_attachments; return @obsolete_attachments;
...@@ -704,8 +728,10 @@ sub insert_attachment_for_bug { ...@@ -704,8 +728,10 @@ sub insert_attachment_for_bug {
$class->validate_is_patch($throw_error) || return 0; $class->validate_is_patch($throw_error) || return 0;
$class->validate_description($throw_error) || return 0; $class->validate_description($throw_error) || return 0;
if (($attachurl =~ /^(http|https|ftp):\/\/\S+/) if (Bugzilla->params->{'allow_attach_url'}
&& !(defined $cgi->upload('data'))) { && ($attachurl =~ /^(http|https|ftp):\/\/\S+/)
&& !defined $cgi->upload('data'))
{
$filename = ''; $filename = '';
$data = $attachurl; $data = $attachurl;
$isurl = 1; $isurl = 1;
...@@ -729,6 +755,12 @@ sub insert_attachment_for_bug { ...@@ -729,6 +755,12 @@ sub insert_attachment_for_bug {
$isurl = 0; $isurl = 0;
} }
# Check attachments the user tries to mark as obsolete.
my @obsolete_attachments;
if ($cgi->param('obsolete')) {
@obsolete_attachments = $class->validate_obsolete($bug->bug_id);
}
# The order of these function calls is important, as Flag::validate # The order of these function calls is important, as Flag::validate
# assumes User::match_field has ensured that the # assumes User::match_field has ensured that the
# values in the requestee fields are legitimate user email addresses. # values in the requestee fields are legitimate user email addresses.
...@@ -801,12 +833,6 @@ sub insert_attachment_for_bug { ...@@ -801,12 +833,6 @@ sub insert_attachment_for_bug {
close $fh; close $fh;
} }
# Now handle flags.
my @obsolete_attachments;
if ($cgi->param('obsolete')) {
@obsolete_attachments = $class->validate_obsolete($bug->bug_id);
}
# Make existing attachments obsolete. # Make existing attachments obsolete.
my $fieldid = get_field_id('attachments.isobsolete'); my $fieldid = get_field_id('attachments.isobsolete');
......
...@@ -20,6 +20,7 @@ use strict; ...@@ -20,6 +20,7 @@ use strict;
package Bugzilla::Attachment::PatchReader; package Bugzilla::Attachment::PatchReader;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::Attachment;
sub process_diff { sub process_diff {
...@@ -41,32 +42,28 @@ sub process_diff { ...@@ -41,32 +42,28 @@ sub process_diff {
$reader->iterate_string('Attachment ' . $attachment->id, $attachment->data); $reader->iterate_string('Attachment ' . $attachment->id, $attachment->data);
} }
else { else {
$vars->{'other_patches'} = []; my @other_patches = ();
if ($lc->{interdiffbin} && $lc->{diffpath}) { if ($lc->{interdiffbin} && $lc->{diffpath}) {
# Get list of attachments on this bug. # Get the list of attachments that the user can view in this bug.
my @attachments =
@{Bugzilla::Attachment->get_attachments_by_bug($attachment->bug_id)};
# Extract patches only.
@attachments = grep {$_->ispatch == 1} @attachments;
# We want them sorted from newer to older.
@attachments = sort { $b->id <=> $a->id } @attachments;
# Ignore the current patch, but select the one right before it # Ignore the current patch, but select the one right before it
# chronologically. # chronologically.
my $attachment_list =
$dbh->selectall_arrayref('SELECT attach_id, description
FROM attachments
WHERE bug_id = ?
AND ispatch = 1
ORDER BY creation_ts DESC',
undef, $attachment->bug_id);
my $select_next_patch = 0; my $select_next_patch = 0;
foreach (@$attachment_list) { foreach my $attach (@attachments) {
my ($other_id, $other_desc) = @$_; if ($attach->id == $attachment->id) {
if ($other_id == $attachment->id) {
$select_next_patch = 1; $select_next_patch = 1;
} }
else { else {
push(@{$vars->{'other_patches'}}, {'id' => $other_id, push(@other_patches, { 'id' => $attach->id,
'desc' => $other_desc, 'desc' => $attach->description,
'selected' => $select_next_patch}); 'selected' => $select_next_patch });
if ($select_next_patch) { $select_next_patch = 0;
$select_next_patch = 0;
}
} }
} }
} }
...@@ -74,6 +71,7 @@ sub process_diff { ...@@ -74,6 +71,7 @@ sub process_diff {
$vars->{'bugid'} = $attachment->bug_id; $vars->{'bugid'} = $attachment->bug_id;
$vars->{'attachid'} = $attachment->id; $vars->{'attachid'} = $attachment->id;
$vars->{'description'} = $attachment->description; $vars->{'description'} = $attachment->description;
$vars->{'other_patches'} = \@other_patches;
setup_template_patch_reader($last_reader, $format, $context, $vars); setup_template_patch_reader($last_reader, $format, $context, $vars);
# Actually print out the patch. # Actually print out the patch.
......
...@@ -1348,6 +1348,17 @@ sub is_mover { ...@@ -1348,6 +1348,17 @@ sub is_mover {
return $self->{'is_mover'}; return $self->{'is_mover'};
} }
sub is_insider {
my $self = shift;
if (!defined $self->{'is_insider'}) {
my $insider_group = Bugzilla->params->{'insidergroup'};
$self->{'is_insider'} =
($insider_group && $self->in_group($insider_group)) ? 1 : 0;
}
return $self->{'is_insider'};
}
sub get_userlist { sub get_userlist {
my $self = shift; my $self = shift;
...@@ -1886,6 +1897,11 @@ Returns true if the user is in the list of users allowed to move bugs ...@@ -1886,6 +1897,11 @@ Returns true if the user is in the list of users allowed to move bugs
to another database. Note that this method doesn't check whether bug to another database. Note that this method doesn't check whether bug
moving is enabled. moving is enabled.
=item C<is_insider>
Returns true if the user can access private comments and attachments,
i.e. if the 'insidergroup' parameter is set and the user belongs to this group.
=back =back
=head1 CLASS FUNCTIONS =head1 CLASS FUNCTIONS
......
...@@ -137,7 +137,8 @@ sub validateID ...@@ -137,7 +137,8 @@ sub validateID
{ {
my $param = @_ ? $_[0] : 'id'; my $param = @_ ? $_[0] : 'id';
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user;
# If we're not doing interdiffs, check if id wasn't specified and # If we're not doing interdiffs, check if id wasn't specified and
# prompt them with a page that allows them to choose an attachment. # prompt them with a page that allows them to choose an attachment.
# Happens when calling plain attachment.cgi from the urlbar directly # Happens when calling plain attachment.cgi from the urlbar directly
...@@ -158,8 +159,8 @@ sub validateID ...@@ -158,8 +159,8 @@ sub validateID
|| ThrowUserError("invalid_attach_id", { attach_id => $cgi->param($param) }); || ThrowUserError("invalid_attach_id", { attach_id => $cgi->param($param) });
# Make sure the attachment exists in the database. # Make sure the attachment exists in the database.
my ($bugid, $isprivate) = $dbh->selectrow_array( my ($bugid, $isprivate, $submitter_id) = $dbh->selectrow_array(
"SELECT bug_id, isprivate "SELECT bug_id, isprivate, submitter_id
FROM attachments FROM attachments
WHERE attach_id = ?", WHERE attach_id = ?",
undef, $attach_id); undef, $attach_id);
...@@ -167,15 +168,13 @@ sub validateID ...@@ -167,15 +168,13 @@ sub validateID
unless $bugid; unless $bugid;
# Make sure the user is authorized to access this attachment's bug. # Make sure the user is authorized to access this attachment's bug.
ValidateBugID($bugid); ValidateBugID($bugid);
if ($isprivate && Bugzilla->params->{"insidergroup"}) { if ($isprivate && $user->id != $submitter_id && !$user->is_insider) {
Bugzilla->user->in_group(Bugzilla->params->{"insidergroup"}) ThrowUserError('auth_failure', {action => 'access',
|| ThrowUserError("auth_failure", {action => "access", object => 'attachment'});
object => "attachment"});
} }
return ($attach_id,$bugid); return ($attach_id, $bugid);
} }
# Validates format of a diff/interdiff. Takes a list as an parameter, which # Validates format of a diff/interdiff. Takes a list as an parameter, which
...@@ -386,56 +385,27 @@ sub diff { ...@@ -386,56 +385,27 @@ sub diff {
# Display all attachments for a given bug in a series of IFRAMEs within one # Display all attachments for a given bug in a series of IFRAMEs within one
# HTML page. # HTML page.
sub viewall sub viewall {
{
# Retrieve and validate parameters # Retrieve and validate parameters
my $bugid = $cgi->param('bugid'); my $bugid = $cgi->param('bugid');
ValidateBugID($bugid); ValidateBugID($bugid);
my $bug = new Bugzilla::Bug($bugid);
# Retrieve the attachments from the database and write them into an array my $attachments = Bugzilla::Attachment->get_attachments_by_bug($bugid);
# of hashes where each hash represents one attachment.
my $privacy = "";
my $dbh = Bugzilla->dbh;
if ( Bugzilla->params->{"insidergroup"} foreach my $a (@$attachments) {
&& !Bugzilla->user->in_group(Bugzilla->params->{"insidergroup"}) ) $a->{'isviewable'} = isViewable($a->contenttype);
{
$privacy = "AND isprivate < 1 ";
} }
my $attachments = $dbh->selectall_arrayref(
"SELECT attach_id AS attachid, " .
$dbh->sql_date_format('creation_ts', '%Y.%m.%d %H:%i') . " AS date,
mimetype AS contenttype, description, ispatch, isobsolete, isprivate,
LENGTH(thedata) AS datasize
FROM attachments
INNER JOIN attach_data
ON attach_id = id
WHERE bug_id = ? $privacy
ORDER BY attach_id", {'Slice'=>{}}, $bugid);
foreach my $a (@{$attachments})
{
$a->{'isviewable'} = isViewable($a->{'contenttype'});
$a->{'flags'} = Bugzilla::Flag::match({ 'attach_id' => $a->{'attachid'} });
}
# Retrieve the bug summary (for displaying on screen) and assignee. # Define the variables and functions that will be passed to the UI template.
my ($bugsummary, $assignee_id) = $dbh->selectrow_array( $vars->{'bug'} = $bug;
"SELECT short_desc, assigned_to FROM bugs " . $vars->{'attachments'} = $attachments;
"WHERE bug_id = ?", undef, $bugid);
# Define the variables and functions that will be passed to the UI template. print $cgi->header();
$vars->{'bugid'} = $bugid;
$vars->{'attachments'} = $attachments;
$vars->{'bugassignee_id'} = $assignee_id;
$vars->{'bugsummary'} = $bugsummary;
print $cgi->header();
# Generate and return the UI (HTML page) from the appropriate template. # Generate and return the UI (HTML page) from the appropriate template.
$template->process("attachment/show-multiple.html.tmpl", $vars) $template->process("attachment/show-multiple.html.tmpl", $vars)
|| ThrowTemplateError($template->error()); || ThrowTemplateError($template->error());
} }
# Display a form for entering a new attachment. # Display a form for entering a new attachment.
...@@ -586,9 +556,9 @@ sub edit { ...@@ -586,9 +556,9 @@ sub edit {
# Retrieve a list of attachments for this bug as well as a summary of the bug # Retrieve a list of attachments for this bug as well as a summary of the bug
# to use in a navigation bar across the top of the screen. # to use in a navigation bar across the top of the screen.
my $bugattachments = my $bugattachments =
$dbh->selectcol_arrayref('SELECT attach_id FROM attachments Bugzilla::Attachment->get_attachments_by_bug($attachment->bug_id);
WHERE bug_id = ? ORDER BY attach_id', # We only want attachment IDs.
undef, $attachment->bug_id); @$bugattachments = map { $_->id } @$bugattachments;
my ($bugsummary, $product_id, $component_id) = my ($bugsummary, $product_id, $component_id) =
$dbh->selectrow_array('SELECT short_desc, product_id, component_id $dbh->selectrow_array('SELECT short_desc, product_id, component_id
...@@ -629,19 +599,34 @@ sub edit { ...@@ -629,19 +599,34 @@ sub edit {
# Users cannot edit the content of the attachment itself. # Users cannot edit the content of the attachment itself.
sub update sub update
{ {
my $userid = Bugzilla->user->id; my $user = Bugzilla->user;
my $userid = $user->id;
my $dbh = Bugzilla->dbh;
# Retrieve and validate parameters # Retrieve and validate parameters
ValidateComment(scalar $cgi->param('comment')); ValidateComment(scalar $cgi->param('comment'));
my ($attach_id, $bugid) = validateID(); my ($attach_id, $bugid) = validateID();
Bugzilla::Attachment->validate_can_edit($attach_id); my $attachment = Bugzilla::Attachment->get($attach_id);
$attachment->validate_can_edit;
validateCanChangeAttachment($attach_id); validateCanChangeAttachment($attach_id);
Bugzilla::Attachment->validate_description(THROW_ERROR); Bugzilla::Attachment->validate_description(THROW_ERROR);
Bugzilla::Attachment->validate_is_patch(THROW_ERROR); Bugzilla::Attachment->validate_is_patch(THROW_ERROR);
Bugzilla::Attachment->validate_content_type(THROW_ERROR) unless $cgi->param('ispatch'); Bugzilla::Attachment->validate_content_type(THROW_ERROR) unless $cgi->param('ispatch');
validateIsObsolete(); validateIsObsolete();
validatePrivate(); validatePrivate();
my $dbh = Bugzilla->dbh;
# If the submitter of the attachment is not in the insidergroup,
# be sure that he cannot overwrite the private bit.
# This check must be done before calling Bugzilla::Flag*::validate(),
# because they will look at the private bit when checking permissions.
# XXX - This is a ugly hack. Ideally, we shouldn't have to look at the
# old private bit twice (first here, and then below again), but this is
# the less risky change.
unless ($user->is_insider) {
my $oldisprivate = $dbh->selectrow_array('SELECT isprivate FROM attachments
WHERE attach_id = ?', undef, $attach_id);
$cgi->param('isprivate', $oldisprivate);
}
# The order of these function calls is important, as Flag::validate # The order of these function calls is important, as Flag::validate
# assumes User::match_field has ensured that the values in the # assumes User::match_field has ensured that the values in the
...@@ -688,7 +673,6 @@ sub update ...@@ -688,7 +673,6 @@ sub update
# to attachments so that we can delete pending requests if the user # to attachments so that we can delete pending requests if the user
# is obsoleting this attachment without deleting any requests # is obsoleting this attachment without deleting any requests
# the user submits at the same time. # the user submits at the same time.
my $attachment = Bugzilla::Attachment->get($attach_id);
Bugzilla::Flag::process($bug, $attachment, $timestamp, $cgi); Bugzilla::Flag::process($bug, $attachment, $timestamp, $cgi);
# Update the attachment record in the database. # Update the attachment record in the database.
...@@ -799,10 +783,10 @@ sub delete_attachment { ...@@ -799,10 +783,10 @@ sub delete_attachment {
# Make sure the administrator is allowed to edit this attachment. # Make sure the administrator is allowed to edit this attachment.
my ($attach_id, $bug_id) = validateID(); my ($attach_id, $bug_id) = validateID();
Bugzilla::Attachment->validate_can_edit($attach_id); my $attachment = Bugzilla::Attachment->get($attach_id);
$attachment->validate_can_edit;
validateCanChangeAttachment($attach_id); validateCanChangeAttachment($attach_id);
my $attachment = Bugzilla::Attachment->get($attach_id);
$attachment->datasize || ThrowUserError('attachment_removed'); $attachment->datasize || ThrowUserError('attachment_removed');
# We don't want to let a malicious URL accidentally delete an attachment. # We don't want to let a malicious URL accidentally delete an attachment.
......
...@@ -78,13 +78,6 @@ sub queue { ...@@ -78,13 +78,6 @@ sub queue {
my $status = validateStatus($cgi->param('status')); my $status = validateStatus($cgi->param('status'));
my $form_group = validateGroup($cgi->param('group')); my $form_group = validateGroup($cgi->param('group'));
my $attach_join_clause = "flags.attach_id = attachments.attach_id";
if (Bugzilla->params->{"insidergroup"}
&& !Bugzilla->user->in_group(Bugzilla->params->{"insidergroup"}))
{
$attach_join_clause .= " AND attachments.isprivate < 1";
}
my $query = my $query =
# Select columns describing each flag, the bug/attachment on which # Select columns describing each flag, the bug/attachment on which
# it has been set, who set it, and of whom they are requesting it. # it has been set, who set it, and of whom they are requesting it.
...@@ -105,7 +98,7 @@ sub queue { ...@@ -105,7 +98,7 @@ sub queue {
" "
FROM flags FROM flags
LEFT JOIN attachments LEFT JOIN attachments
ON ($attach_join_clause) ON flags.attach_id = attachments.attach_id
INNER JOIN flagtypes INNER JOIN flagtypes
ON flags.type_id = flagtypes.id ON flags.type_id = flagtypes.id
INNER JOIN profiles AS requesters INNER JOIN profiles AS requesters
...@@ -134,7 +127,13 @@ sub queue { ...@@ -134,7 +127,13 @@ sub queue {
(bugs.assigned_to = $userid) " . (bugs.assigned_to = $userid) " .
(Bugzilla->params->{'useqacontact'} ? "OR (Bugzilla->params->{'useqacontact'} ? "OR
(bugs.qa_contact = $userid))" : ")"); (bugs.qa_contact = $userid))" : ")");
unless ($user->is_insider) {
$query .= " AND (attachments.attach_id IS NULL
OR attachments.isprivate = 0
OR attachments.submitter_id = $userid)";
}
# Limit query to pending requests. # Limit query to pending requests.
$query .= " AND flags.status = '?' " unless $status; $query .= " AND flags.status = '?' " unless $status;
......
...@@ -32,11 +32,10 @@ ...@@ -32,11 +32,10 @@
[% END %] [% END %]
<th bgcolor="#cccccc" align="left">Actions</th> <th bgcolor="#cccccc" align="left">Actions</th>
</tr> </tr>
[% canseeprivate = !Param("insidergroup") || user.in_group(Param("insidergroup")) %]
[% count = 0 %] [% count = 0 %]
[% FOREACH attachment = attachments %] [% FOREACH attachment = attachments %]
[% count = count + 1 %] [% count = count + 1 %]
[% IF !attachment.isprivate || canseeprivate %] [% IF !attachment.isprivate || user.is_insider || attachment.attacher.id == user.id %]
<tr [% "class=\"bz_private\"" IF attachment.isprivate %]> <tr [% "class=\"bz_private\"" IF attachment.isprivate %]>
<td valign="top"> <td valign="top">
<a name="a[% count %]" href="attachment.cgi?id=[% attachment.id %]">[% attachment.description FILTER html FILTER obsolete(attachment.isobsolete) %]</a> <a name="a[% count %]" href="attachment.cgi?id=[% attachment.id %]">[% attachment.description FILTER html FILTER obsolete(attachment.isobsolete) %]</a>
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
<table class="attachment_info" cellspacing="0" cellpadding="4" border="1" width="75%"> <table class="attachment_info" cellspacing="0" cellpadding="4" border="1" width="75%">
<tr> <tr>
<td valign="top" bgcolor="#cccccc" colspan="6"> <td valign="top" bgcolor="#cccccc" colspan="6">
<big><b>Attachment #[% a.attachid %]</b></big> <big><b>Attachment #[% a.id %]</b></big>
</td> </td>
</tr> </tr>
<tr> <tr>
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
[% END %] [% END %]
</td> </td>
<td valign="top">[% a.date FILTER time %]</td> <td valign="top">[% a.attached FILTER time %]</td>
<td valign="top">[% a.datasize FILTER unitconvert %]</td> <td valign="top">[% a.datasize FILTER unitconvert %]</td>
<td valign="top"> <td valign="top">
...@@ -76,20 +76,20 @@ ...@@ -76,20 +76,20 @@
</td> </td>
<td valign="top"> <td valign="top">
<a href="attachment.cgi?id=[% a.attachid %]&amp;action=edit">Details</a> <a href="attachment.cgi?id=[% a.id %]&amp;action=edit">Details</a>
</td> </td>
</tr> </tr>
</table> </table>
[% IF a.isviewable %] [% IF a.isviewable %]
<iframe src="attachment.cgi?id=[% a.attachid %]" width="75%" height="350"> <iframe src="attachment.cgi?id=[% a.id %]" width="75%" height="350">
<b>You cannot view the attachment on this page because your browser does not support IFRAMEs. <b>You cannot view the attachment on this page because your browser does not support IFRAMEs.
<a href="attachment.cgi?id=[% a.attachid %]">View the attachment on a separate page</a>.</b> <a href="attachment.cgi?id=[% a.id %]">View the attachment on a separate page</a>.</b>
</iframe> </iframe>
[% ELSE %] [% ELSE %]
<p><b> <p><b>
Attachment cannot be viewed because its MIME type is not text/*, image/*, or application/vnd.mozilla.*. Attachment cannot be viewed because its MIME type is not text/*, image/*, or application/vnd.mozilla.*.
<a href="attachment.cgi?id=[% a.attachid %]">Download the attachment instead</a>. <a href="attachment.cgi?id=[% a.id %]">Download the attachment instead</a>.
</b></p> </b></p>
[% END %] [% END %]
</div> </div>
......
...@@ -436,7 +436,7 @@ ...@@ -436,7 +436,7 @@
], ],
'attachment/show-multiple.html.tmpl' => [ 'attachment/show-multiple.html.tmpl' => [
'a.attachid', 'a.id',
'flag.status' 'flag.status'
], ],
......
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