]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 44595: Implement an interface for administrators to delete attachments - Patch...
authorlpsolit%gmail.com <>
Tue, 18 Apr 2006 03:19:35 +0000 (03:19 +0000)
committerlpsolit%gmail.com <>
Tue, 18 Apr 2006 03:19:35 +0000 (03:19 +0000)
Bugzilla/Attachment.pm
Bugzilla/Config/Attachment.pm
attachment.cgi
template/en/default/admin/params/attachment.html.tmpl
template/en/default/attachment/confirm-delete.html.tmpl [new file with mode: 0644]
template/en/default/attachment/delete_reason.txt.tmpl [new file with mode: 0644]
template/en/default/attachment/edit.html.tmpl
template/en/default/attachment/list.html.tmpl
template/en/default/global/user-error.html.tmpl

index 350adfd728bef4e8d8b79142527d54d3b3450cf9..00c7ee470786b05e3718b0cb05ba46cebc401bac 100644 (file)
@@ -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];
index 449908528a6bbd7d8629f76ab3e54629a35d9964..bbaaaa63d4813ca8d7a4817972021c717d4cdcbb 100644 (file)
@@ -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
index c212c6f36c15a78fed937fede5e98f750596ce03..fbe0bd0543c7ffe06982122a43393a4b821b05e8 100755 (executable)
@@ -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());
+    }
+}
index ef89c4af00ad44e3ea21d37f28fcce5757db096c..785d91822eeb4ea97fc633072c0e5a56d4d1ae96 100644 (file)
 %]
 
 [% 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 (file)
index 0000000..99007e5
--- /dev/null
@@ -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 (file)
index 0000000..45879f6
--- /dev/null
@@ -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 %].
index b2b5b13e5a364720ee34965114f89b63da599796..2f02aae34c5b6ac6ab44b4b180fc52c797170d2c 100644 (file)
           <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"
         [% 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'
index 03368ce26d16fdd5081946adf08fb9fbb8fe2b05..0b42c0c3cd59d6a3704d141da456e10aaa45b55e 100644 (file)
           </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">
index 54aa31ee05943c499cbd9bca5e07bf857bf0ba73..c50955db8c7f27b866d447037a0522c41803ad7a 100644 (file)
       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.