'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',
=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
# 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');
# 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)
# 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 = "";
$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 = "";
ON profiles.userid = bugs_activity.who
WHERE bugs_activity.bug_id = ?
$datepart
+ $attachpart
$suppwhere
ORDER BY bugs_activity.bug_when";
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'},
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'],
],
},
# 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 #
################################################################
}
}
+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__
$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(),
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.
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');
<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%">
--- /dev/null
+[%# 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 %]&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 %]&action=edit">revisit
+ attachment [% attachment.id %]</a>
+ </li>
+</ul>
+
+[% PROCESS global/footer.html.tmpl %]
'obsolete_attachments',
],
+'attachment/midair.html.tmpl' => [
+ 'attachment.id',
+],
+
'attachment/show-multiple.html.tmpl' => [
'a.id',
'flag.status'