]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 99215: Attachments have no midair collision protection - Patch by Frédéric...
authorlpsolit%gmail.com <>
Fri, 30 Nov 2007 01:49:12 +0000 (01:49 +0000)
committerlpsolit%gmail.com <>
Fri, 30 Nov 2007 01:49:12 +0000 (01:49 +0000)
Bugzilla/Attachment.pm
Bugzilla/Bug.pm
Bugzilla/DB/Schema.pm
Bugzilla/Install/DB.pm
attachment.cgi
process_bug.cgi
template/en/default/attachment/edit.html.tmpl
template/en/default/attachment/midair.html.tmpl [new file with mode: 0644]
template/en/default/filterexceptions.pl

index cc3e168934f4b8d6d205580cf371df7587c3701e..e3fe39f3ac172eda9143f7b4e5a2084196d4b91b 100644 (file)
@@ -91,6 +91,7 @@ sub _retrieve {
         'attachments.submitter_id AS _attacher_id',
         Bugzilla->dbh->sql_date_format('attachments.creation_ts',
                                        '%Y.%m.%d %H:%i') . " AS attached",
+        'attachments.modification_time',
         'attachments.filename AS filename',
         'attachments.ispatch AS ispatch',
         'attachments.isurl AS isurl',
@@ -208,6 +209,21 @@ sub attached {
 
 =over
 
+=item C<modification_time>
+
+the date and time on which the attachment was last modified.
+
+=back
+
+=cut
+
+sub modification_time {
+    my $self = shift;
+    return $self->{modification_time};
+}
+
+=over
+
 =item C<filename>
 
 the name of the file the attacher attached
@@ -826,10 +842,10 @@ sub insert_attachment_for_bug {
     # Insert the attachment into the database.
     my $sth = $dbh->do(
         "INSERT INTO attachments
-            (bug_id, creation_ts, filename, description,
+            (bug_id, creation_ts, modification_time, filename, description,
              mimetype, ispatch, isurl, isprivate, submitter_id)
-         VALUES (?,?,?,?,?,?,?,?,?)", undef, ($bug->bug_id, $timestamp, $filename,
-              $description, $contenttype, $cgi->param('ispatch'),
+         VALUES (?,?,?,?,?,?,?,?,?,?)", undef, ($bug->bug_id, $timestamp, $timestamp,
+              $filename, $description, $contenttype, $cgi->param('ispatch'),
               $isurl, $isprivate, $user->id));
     # Retrieve the ID of the newly created attachment record.
     my $attachid = $dbh->bz_last_key('attachments', 'attach_id');
@@ -877,8 +893,9 @@ sub insert_attachment_for_bug {
         # This call must be done before updating the 'attachments' table.
         Bugzilla::Flag::CancelRequests($bug, $obsolete_attachment, $timestamp);
 
-        $dbh->do('UPDATE attachments SET isobsolete = 1 WHERE attach_id = ?',
-                 undef, $obsolete_attachment->id);
+        $dbh->do('UPDATE attachments SET isobsolete = 1, modification_time = ?
+                  WHERE attach_id = ?',
+                 undef, ($timestamp, $obsolete_attachment->id));
 
         $dbh->do('INSERT INTO bugs_activity (bug_id, attach_id, who, bug_when,
                                              fieldid, removed, added)
index a8f1ede5d5869846e1cd4e0cdeb483aabcc940c9..0a45daf1439e4c99e042773ddfe16ec04325db14 100755 (executable)
@@ -2573,11 +2573,11 @@ sub format_comment {
 # Get the activity of a bug, starting from $starttime (if given).
 # This routine assumes ValidateBugID has been previously called.
 sub GetBugActivity {
-    my ($id, $starttime) = @_;
+    my ($bug_id, $attach_id, $starttime) = @_;
     my $dbh = Bugzilla->dbh;
 
     # Arguments passed to the SQL query.
-    my @args = ($id);
+    my @args = ($bug_id);
 
     # Only consider changes since $starttime, if given.
     my $datepart = "";
@@ -2587,6 +2587,12 @@ sub GetBugActivity {
         $datepart = "AND bugs_activity.bug_when > ?";
     }
 
+    my $attachpart = "";
+    if ($attach_id) {
+        push(@args, $attach_id);
+        $attachpart = "AND bugs_activity.attach_id = ?";
+    }
+
     # Only includes attachments the user is allowed to see.
     my $suppjoins = "";
     my $suppwhere = "";
@@ -2616,6 +2622,7 @@ sub GetBugActivity {
             ON profiles.userid = bugs_activity.who
          WHERE bugs_activity.bug_id = ?
                $datepart
+               $attachpart
                $suppwhere
       ORDER BY bugs_activity.bug_when";
 
index 1c6ee352e9d6875225f65a8b7b11b161c8a3458c..61f894f839fe83cbf3604f27ac95b730a2acb934 100644 (file)
@@ -372,6 +372,7 @@ use constant ABSTRACT_SCHEMA => {
                              PRIMARYKEY => 1},
             bug_id       => {TYPE => 'INT3', NOTNULL => 1},
             creation_ts  => {TYPE => 'DATETIME', NOTNULL => 1},
+            modification_time => {TYPE => 'DATETIME', NOTNULL => 1},
             description  => {TYPE => 'MEDIUMTEXT', NOTNULL => 1},
             mimetype     => {TYPE => 'MEDIUMTEXT', NOTNULL => 1},
             ispatch      => {TYPE => 'BOOLEAN'},
@@ -389,6 +390,7 @@ use constant ABSTRACT_SCHEMA => {
         INDEXES => [
             attachments_bug_id_idx => ['bug_id'],
             attachments_creation_ts_idx => ['creation_ts'],
+            attachments_modification_time_idx => ['modification_time'],
             attachments_submitter_id_idx => ['submitter_id', 'bug_id'],
         ],
     },
index 9934e058fb9645dc3a5efa14611815f87b064a75..485c771b149a0155676bf9953e9047b25ebf07fc 100644 (file)
@@ -515,6 +515,9 @@ sub update_table_definitions {
     # 2007-08-21 wurblzap@gmail.com - Bug 365378
     _make_lang_setting_dynamic();
 
+    # 2007-09-09 LpSolit@gmail.com - Bug 99215
+    _fix_attachment_modification_date();
+
     ################################################################
     # New --TABLE-- changes should go *** A B O V E *** this point #
     ################################################################
@@ -2898,6 +2901,31 @@ sub _make_lang_setting_dynamic {
     }
 }
 
+sub _fix_attachment_modification_date {
+    my $dbh = Bugzilla->dbh;
+    if (!$dbh->bz_column_info('attachments', 'modification_time')) {
+        # Allow NULL values till the modification time has been set.
+        $dbh->bz_add_column('attachments', 'modification_time', {TYPE => 'DATETIME'});
+
+        print "Setting the modification time for attachments...\n";
+        $dbh->do('UPDATE attachments SET modification_time = creation_ts');
+
+        # Now force values to be always defined.
+        $dbh->bz_alter_column('attachments', 'modification_time',
+                              {TYPE => 'DATETIME', NOTNULL => 1});
+
+        # Update the modification time for attachments which have been modified.
+        my $attachments =
+          $dbh->selectall_arrayref('SELECT attach_id, MAX(bug_when) FROM bugs_activity
+                                    WHERE attach_id IS NOT NULL ' .
+                                    $dbh->sql_group_by('attach_id'));
+
+        my $sth = $dbh->prepare('UPDATE attachments SET modification_time = ?
+                                 WHERE attach_id = ?');
+        $sth->execute($_->[1], $_->[0]) foreach (@$attachments);
+    }
+}
+
 1;
 
 __END__
index 23841571c7da8b27aeb9a62e75d8e4b352bd5a34..768653c31e0964203790b706c145570c2c40000b 100755 (executable)
@@ -460,6 +460,27 @@ sub update {
     $cgi->param('isobsolete', $cgi->param('isobsolete') ? 1 : 0);
     $cgi->param('isprivate', $cgi->param('isprivate') ? 1 : 0);
 
+    # Now make sure the attachment has not been edited since we loaded the page.
+    if (defined $cgi->param('delta_ts')
+        && $cgi->param('delta_ts') ne $attachment->modification_time)
+    {
+        ($vars->{'operations'}) =
+            Bugzilla::Bug::GetBugActivity($bug->id, $attachment->id, $cgi->param('delta_ts'));
+
+        # If the modification date changed but there is no entry in
+        # the activity table, this means someone commented only.
+        # In this case, there is no reason to midair.
+        if (scalar(@{$vars->{'operations'}})) {
+            $cgi->param('delta_ts', $attachment->modification_time);
+            $vars->{'attachment'} = $attachment;
+
+            print $cgi->header();
+            # Warn the user about the mid-air collision and ask them what to do.
+            $template->process("attachment/midair.html.tmpl", $vars)
+              || ThrowTemplateError($template->error());
+            exit;
+        }
+    }
     # 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(),
@@ -507,11 +528,12 @@ sub update {
                     filename    = ?,
                     ispatch     = ?,
                     isobsolete  = ?,
-                    isprivate   = ?
+                    isprivate   = ?,
+                    modification_time = ?
             WHERE   attach_id   = ?",
             undef, ($description, $contenttype, $filename,
             $cgi->param('ispatch'), $cgi->param('isobsolete'), 
-            $cgi->param('isprivate'), $attachment->id));
+            $cgi->param('isprivate'), $timestamp, $attachment->id));
 
   my $updated_attachment = Bugzilla::Attachment->get($attachment->id);
   # Record changes in the activity table.
index 99ee5ed574b72a8b0d8b3fc3828c9f967a0641c2..a8df416cda5c6300046ed11f7a1acacffe20a4ec 100755 (executable)
@@ -878,7 +878,7 @@ foreach my $id (@idlist) {
     if (defined $cgi->param('delta_ts') && $cgi->param('delta_ts') ne $delta_ts)
     {
         ($vars->{'operations'}) =
-            Bugzilla::Bug::GetBugActivity($id, $cgi->param('delta_ts'));
+            Bugzilla::Bug::GetBugActivity($id, undef, $cgi->param('delta_ts'));
 
         $vars->{'start_at'} = $cgi->param('longdesclength');
 
index 23e104d864f81eb5d1aea57816ac6671460970be..3796b56505dca60468470eecec6da5e36fc05158 100644 (file)
   <input type="hidden" name="id" value="[% attachment.id %]">
   <input type="hidden" name="action" value="update">
   <input type="hidden" name="contenttypemethod" value="manual">
+  <input type="hidden" name="delta_ts" value="[% attachment.modification_time FILTER html %]">
 
   <table class="attachment_info" width="100%">
 
diff --git a/template/en/default/attachment/midair.html.tmpl b/template/en/default/attachment/midair.html.tmpl
new file mode 100644 (file)
index 0000000..8cde9f2
--- /dev/null
@@ -0,0 +1,76 @@
+[%# 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>
+  #                 Frédéric Buclin <LpSolit@gmail.com>
+  #%]
+
+[%# INTERFACE:
+  # operations: array; bug activity since the user last displayed the attachment form,
+  #   used by bug/activity/table.html.tmpl to display recent changes that will
+  #   be overwritten if the user submits these changes.  See that template
+  #   for further documentation.
+  # attachment: object; the attachment being changed.
+  #%]
+
+[%# The global Bugzilla->cgi object is used to obtain form variable values. %]
+[% USE Bugzilla %]
+[% cgi = Bugzilla.cgi %]
+
+[% PROCESS global/variables.none.tmpl %]
+[% PROCESS global/header.html.tmpl title = "Mid-air collision!" %]
+
+<h1>Mid-air collision detected!</h1>
+
+<p>
+  Someone else has made changes to
+  <a href="attachment.cgi?id=[% attachment.id %]&amp;action=edit">attachment [% attachment.id %]</a>
+  of [% "$terms.bug $attachment.bug_id" FILTER bug_link(attachment.bug_id) FILTER none %]
+  at the same time you were trying to. The changes made were:
+</p>
+
+<p>
+  [% PROCESS "bug/activity/table.html.tmpl" incomplete_data=0 %]
+</p>
+
+[% IF cgi.param("comment") %]
+<p>
+  Your comment was:<br>
+  <blockquote><pre>[% cgi.param("comment") FILTER wrap_comment FILTER html %]</pre></blockquote>
+</p>
+[% END %]
+
+<p>
+You have the following choices:
+</p>
+
+<ul>
+  <li>
+    <form method="post" action="attachment.cgi">
+      [% PROCESS "global/hidden-fields.html.tmpl" exclude="^Bugzilla_(login|password)$" %]
+      <input type="submit" id="process" value="Submit my changes anyway">
+        This will cause all of the above changes to be overwritten.
+    </form>
+  </li>
+  <li>
+    Throw away my changes, and
+    <a href="attachment.cgi?id=[% attachment.id %]&amp;action=edit">revisit
+    attachment [% attachment.id %]</a>
+  </li>
+</ul>
+
+[% PROCESS global/footer.html.tmpl %]
index 80957e6c71250286e426da56be4ba7a947588e48..e2acdcbcace068db7fb7fabc6a9ae1a4147166eb 100644 (file)
   'obsolete_attachments',
 ],
 
+'attachment/midair.html.tmpl' => [
+  'attachment.id',
+],
+
 'attachment/show-multiple.html.tmpl' => [
   'a.id',
   'flag.status'