diff options
-rw-r--r-- | Bugzilla/Attachment.pm | 12 | ||||
-rw-r--r-- | Bugzilla/Config/Attachment.pm | 5 | ||||
-rwxr-xr-x | attachment.cgi | 83 | ||||
-rw-r--r-- | template/en/default/admin/params/attachment.html.tmpl | 4 | ||||
-rw-r--r-- | template/en/default/attachment/confirm-delete.html.tmpl | 91 | ||||
-rw-r--r-- | template/en/default/attachment/delete_reason.txt.tmpl | 33 | ||||
-rw-r--r-- | template/en/default/attachment/edit.html.tmpl | 16 | ||||
-rw-r--r-- | template/en/default/attachment/list.html.tmpl | 8 | ||||
-rw-r--r-- | template/en/default/global/user-error.html.tmpl | 4 |
9 files changed, 247 insertions, 9 deletions
diff --git a/Bugzilla/Attachment.pm b/Bugzilla/Attachment.pm index 350adfd72..00c7ee470 100644 --- a/Bugzilla/Attachment.pm +++ b/Bugzilla/Attachment.pm @@ -339,16 +339,16 @@ sub datasize { # If we have already retrieved the data, return its size. return length($self->{data}) if exists $self->{data}; - ($self->{datasize}) = + $self->{datasize} = Bugzilla->dbh->selectrow_array("SELECT LENGTH(thedata) FROM attach_data WHERE id = ?", - undef, - $self->{id}); + undef, $self->{id}) || 0; - # If there's no attachment data in the database, the attachment - # is stored in a local file, so retrieve its size from the file. - if ($self->{datasize} == 0) { + # If there's no attachment data in the database, either the attachment + # is stored in a local file, and so retrieve its size from the file, + # or the attachment has been deleted. + unless ($self->{datasize}) { if (open(AH, $self->_get_local_filename())) { binmode AH; $self->{datasize} = (stat(AH))[7]; diff --git a/Bugzilla/Config/Attachment.pm b/Bugzilla/Config/Attachment.pm index 449908528..bbaaaa63d 100644 --- a/Bugzilla/Config/Attachment.pm +++ b/Bugzilla/Config/Attachment.pm @@ -41,6 +41,11 @@ sub get_param_list { my $class = shift; my @param_list = ( { + name => 'allow_attachment_deletion', + type => 'b', + default => 0 + }, + { name => 'allow_attach_url', type => 'b', default => 0 diff --git a/attachment.cgi b/attachment.cgi index c212c6f36..fbe0bd054 100755 --- a/attachment.cgi +++ b/attachment.cgi @@ -49,6 +49,7 @@ use Bugzilla::Util; use Bugzilla::Bug; use Bugzilla::Field; use Bugzilla::Attachment; +use Bugzilla::Token; Bugzilla->login(); @@ -103,6 +104,9 @@ elsif ($action eq "update") Bugzilla->login(LOGIN_REQUIRED); update(); } +elsif ($action eq "delete") { + delete_attachment(); +} else { ThrowCodeError("unknown_action", { action => $action }); @@ -1329,3 +1333,82 @@ sub update $template->process("attachment/updated.html.tmpl", $vars) || ThrowTemplateError($template->error()); } + +# Only administrators can delete attachments. +sub delete_attachment { + my $user = Bugzilla->login(LOGIN_REQUIRED); + my $dbh = Bugzilla->dbh; + + print $cgi->header(); + + $user->in_group('admin') + || ThrowUserError('auth_failure', {group => 'admin', + action => 'delete', + object => 'attachment'}); + + Param('allow_attachment_deletion') + || ThrowUserError('attachment_deletion_disabled'); + + # Make sure the administrator is allowed to edit this attachment. + my ($attach_id, $bug_id) = validateID(); + validateCanEdit($attach_id); + validateCanChangeAttachment($attach_id); + + my $attachment = Bugzilla::Attachment->get($attach_id); + $attachment->datasize || ThrowUserError('attachment_removed'); + + # We don't want to let a malicious URL accidentally delete an attachment. + my $token = trim($cgi->param('token')); + if ($token) { + my ($creator_id, $date, $event) = Bugzilla::Token::GetTokenData($token); + unless ($creator_id + && ($creator_id == $user->id) + && ($event eq "attachment$attach_id")) + { + # The token is invalid. + ThrowUserError('token_inexistent'); + } + + # The token is valid. Delete the content of the attachment. + my $msg; + $vars->{'attachid'} = $attach_id; + $vars->{'bugid'} = $bug_id; + $vars->{'date'} = $date; + $vars->{'reason'} = clean_text($cgi->param('reason') || ''); + $vars->{'mailrecipients'} = { 'changer' => $user->login }; + + $template->process("attachment/delete_reason.txt.tmpl", $vars, \$msg) + || ThrowTemplateError($template->error()); + + $dbh->bz_lock_tables('attachments WRITE', 'attach_data WRITE', 'flags WRITE'); + $dbh->do('DELETE FROM attach_data WHERE id = ?', undef, $attach_id); + $dbh->do('UPDATE attachments SET mimetype = ?, ispatch = ?, isurl = ? + WHERE attach_id = ?', undef, ('text/plain', 0, 0, $attach_id)); + $dbh->do('DELETE FROM flags WHERE attach_id = ?', undef, $attach_id); + $dbh->bz_unlock_tables; + + # If the attachment is stored locally, remove it. + if (-e $attachment->_get_local_filename) { + unlink $attachment->_get_local_filename; + } + + # Now delete the token. + Bugzilla::Token::DeleteToken($token); + + # Paste the reason provided by the admin into a comment. + AppendComment($bug_id, $user->id, $msg); + + $template->process("attachment/updated.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } + else { + # Create a token. + $token = Bugzilla::Token::IssueSessionToken('attachment' . $attach_id); + + $vars->{'a'} = $attachment; + $vars->{'token'} = $token; + + $template->process("attachment/confirm-delete.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + } +} diff --git a/template/en/default/admin/params/attachment.html.tmpl b/template/en/default/admin/params/attachment.html.tmpl index ef89c4af0..785d91822 100644 --- a/template/en/default/admin/params/attachment.html.tmpl +++ b/template/en/default/admin/params/attachment.html.tmpl @@ -25,9 +25,13 @@ %] [% param_descs = { + allow_attachment_deletion => "If this option is on, administrators will be able to delete " _ + "the content of attachments.", + allow_attach_url => "If this option is on, it will be possible to " _ "specify a URL when creating an attachment and " _ "treat the URL itself as if it were an attachment.", + maxpatchsize => "The maximum size (in kilobytes) of patches. $terms.Bugzilla will not " _ "accept patches greater than this number of kilobytes in size. " _ "To accept patches of any size (subject to the limitations of " _ diff --git a/template/en/default/attachment/confirm-delete.html.tmpl b/template/en/default/attachment/confirm-delete.html.tmpl new file mode 100644 index 000000000..99007e579 --- /dev/null +++ b/template/en/default/attachment/confirm-delete.html.tmpl @@ -0,0 +1,91 @@ +[%# 1.0@bugzilla.org %] +[%# 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. + # + # Contributor(s): Frédéric Buclin <LpSolit@gmail.com> + #%] + +[%# INTERFACE: + # a: attachment object; attachment the user wants to delete. + # token: string; The token used to identify the session. + #%] + +[% PROCESS global/variables.none.tmpl %] + +[% title = BLOCK %] + Delete Attachment [% a.id FILTER html %] of + [%+ "$terms.Bug " _ a.bug_id FILTER bug_link(a.bug_id) FILTER none %] +[% END %] + +[% PROCESS global/header.html.tmpl title = title %] + +<table border="1" cellpadding="4" cellspacing="0"> + <tr bgcolor="#6666FF"> + <th valign="top" align="left">Field</th> + <th valign="top" align="left">Value</th> + </tr> + <tr> + <td valign="top">Attachment ID:</td> + <td valign="top"> + <a href="attachment.cgi?id=[% a.id FILTER html %]">[% a.id FILTER html %]</a> + </td> + </tr> + <tr> + <td valign="top">File name:</td> + <td valign="top">[% a.filename FILTER html %]</td> + </tr> + <tr> + <td valign="top">Description:</td> + <td valign="top">[% a.description FILTER html %]</td> + </tr> + <tr> + <td valign="top">Contained in [% terms.Bug %]:</td> + <td valign="top">[% a.bug_id FILTER bug_link(a.bug_id) FILTER none %]</td> + </tr> + <tr> + <td valign="top">Creator:</td> + <td valign="top">[% a.attacher.identity FILTER html %]</td> + </tr> + <tr> + <td valign="top">Creation Date:</td> + <td valign="top">[% a.attached FILTER time %]</td> + </tr> +</table> + +<h2>Confirmation</h2> + +<table border="0" cellpadding="20" width="70%" bgcolor="red"> + <tr> + <td> + The content of this attachment will be deleted in a <b>irreversible</b> way. + </td> + </tr> +</table> + +<p>Do you really want to delete this attachment?</p> + +<form action="attachment.cgi" method="POST"> + <label for="reason">Reason of the deletion:</label> + <input type="text" id="reason" name="reason" value="" size="80" maxlength="200"> + <p> + <input type="submit" value="Yes, delete"> + <input type="hidden" name="action" value="delete"> + <input type="hidden" name="id" value="[% a.id FILTER html %]"> + <input type="hidden" name="token" value="[% token FILTER html %]"> +</form> + +<p> + No, cancel this deletion and return to + [%+ "$terms.bug " _ a.bug_id FILTER bug_link(a.bug_id) FILTER none %]. +</p> + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/attachment/delete_reason.txt.tmpl b/template/en/default/attachment/delete_reason.txt.tmpl new file mode 100644 index 000000000..45879f6ca --- /dev/null +++ b/template/en/default/attachment/delete_reason.txt.tmpl @@ -0,0 +1,33 @@ +[%# 1.0@bugzilla.org %] +[%# 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. + # + # Contributor(s): Frédéric Buclin <LpSolit@gmail.com> + #%] + +[%# INTERFACE: + # attachid: ID of the attachment the user wants to delete. + # reason: string; The reason provided by the user. + # date: the date when the request to delete the attachment was made. + #%] + +The content of attachment [% attachid %] has been deleted by + [%+ user.identity %] +[% IF reason %] +who provided the following reason: + +[%+ reason %] +[% ELSE %] +without providing any reason. +[% END %] + +The token used to delete this attachment was generated at [% date FILTER time %]. diff --git a/template/en/default/attachment/edit.html.tmpl b/template/en/default/attachment/edit.html.tmpl index b2b5b13e5..2f02aae34 100644 --- a/template/en/default/attachment/edit.html.tmpl +++ b/template/en/default/attachment/edit.html.tmpl @@ -229,7 +229,12 @@ <b>Filename:</b><br> <input type="text" size="20" name="filename" value="[% attachment.filename FILTER html %]"><br> - <b>Size: </b>[% attachment.datasize FILTER unitconvert %]<br> + <b>Size:</b> + [% IF attachment.datasize %] + [%+ attachment.datasize FILTER unitconvert %] + [% ELSE %] + <em>deleted</em> + [% END %]<br> <b>MIME Type:</b><br> <input type="text" size="20" name="contenttypeentry" @@ -269,10 +274,17 @@ [% IF attachment.ispatch && patchviewerinstalled %] | <a href="attachment.cgi?id=[% attachment.id %]&action=diff">Diff</a> [% END %] + [% IF Param("allow_attachment_deletion") + && user.groups.admin + && attachment.datasize > 0 %] + | <a href="attachment.cgi?id=[% attachment.id %]&action=delete">Delete</a> + [% END %] </small> </td> - [% IF isviewable %] + [% IF !attachment.datasize %] + <td width="75%"><b>The content of this attachment has been deleted.</b></td> + [% ELSIF isviewable %] <td width="75%"> [% INCLUDE global/textarea.html.tmpl id = 'editFrame' diff --git a/template/en/default/attachment/list.html.tmpl b/template/en/default/attachment/list.html.tmpl index 03368ce26..0b42c0c3c 100644 --- a/template/en/default/attachment/list.html.tmpl +++ b/template/en/default/attachment/list.html.tmpl @@ -56,7 +56,13 @@ </a> </td> <td valign="top">[% attachment.attached FILTER time %]</td> - <td valign="top">[% attachment.datasize FILTER unitconvert %]</td> + <td valign="top"> + [% IF attachment.datasize %] + [% attachment.datasize FILTER unitconvert %] + [% ELSE %] + <em>deleted</em> + [% END %] + </td> [% IF show_attachment_flags %] <td valign="top"> diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index 54aa31ee0..c50955db8 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -194,6 +194,10 @@ versions [% END %]. + [% ELSIF error == "attachment_deletion_disabled" %] + [% title = "Attachment Deletion Disabled" %] + Attachment deletion is disabled on this installation. + [% ELSIF error == "attachment_removed" %] [% title = "Attachment Removed" %] The attachment you are attempting to access has been removed. |