summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorByron Jones <glob@mozilla.com>2015-04-14 07:20:00 +0200
committerByron Jones <glob@mozilla.com>2015-04-14 07:20:00 +0200
commite5131c86c22c5577c0e27db04481084caf635dc5 (patch)
tree897a6ef2d1314a1c442e9e8b8ff14a4dd2cf3eb2 /extensions
parent6116f2f7cf32f40c1f943cef3f523ed5955f968d (diff)
downloadbugzilla-e5131c86c22c5577c0e27db04481084caf635dc5.tar.gz
bugzilla-e5131c86c22c5577c0e27db04481084caf635dc5.tar.xz
Bug 1146767: update relative dates without refreshing the page
Diffstat (limited to 'extensions')
-rw-r--r--extensions/BugModal/Extension.pm16
-rw-r--r--extensions/BugModal/lib/ActivityStream.pm15
-rw-r--r--extensions/BugModal/lib/Util.pm31
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/flags.html.tmpl3
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/header.html.tmpl1
-rw-r--r--extensions/BugModal/template/en/default/bug_modal/rel_time.html.tmpl2
-rw-r--r--extensions/BugModal/web/bug_modal.js2
-rw-r--r--extensions/BugModal/web/time_ago.js142
8 files changed, 196 insertions, 16 deletions
diff --git a/extensions/BugModal/Extension.pm b/extensions/BugModal/Extension.pm
index 04d7cb567..0bc96e730 100644
--- a/extensions/BugModal/Extension.pm
+++ b/extensions/BugModal/Extension.pm
@@ -14,12 +14,13 @@ use base qw(Bugzilla::Extension);
use Bugzilla::Extension::BugModal::ActivityStream;
use Bugzilla::Extension::BugModal::MonkeyPatches;
+use Bugzilla::Extension::BugModal::Util qw(date_str_to_time);
use Bugzilla::Constants;
use Bugzilla::User::Setting;
use Bugzilla::Util qw(trick_taint datetime_from html_quote);
use List::MoreUtils qw(any);
use Template::Stash;
-use Time::Duration;
+use Time::Duration qw(ago);
our $VERSION = '1';
@@ -62,7 +63,7 @@ sub template_after_create {
my ($timestamp) = @_;
my $datetime = datetime_from($timestamp)
// return $timestamp;
- return ago(time() - $datetime->epoch);
+ return ago(abs(time() - $datetime->epoch));
};
}, 1
);
@@ -85,6 +86,17 @@ sub template_after_create {
}, 1
);
+ # parse date string and output epoch
+ $context->define_filter(
+ epoch => sub {
+ my ($context) = @_;
+ return sub {
+ my ($date_str) = @_;
+ return date_str_to_time($date_str);
+ };
+ }, 1
+ );
+
# flatten a list of hashrefs to a list of values
# eg. logins = users.pluck("login")
$context->define_vmethod(
diff --git a/extensions/BugModal/lib/ActivityStream.pm b/extensions/BugModal/lib/ActivityStream.pm
index dae6b8ba2..97edf2ee6 100644
--- a/extensions/BugModal/lib/ActivityStream.pm
+++ b/extensions/BugModal/lib/ActivityStream.pm
@@ -12,9 +12,9 @@ package Bugzilla::Bug;
use strict;
use warnings;
+use Bugzilla::Extension::BugModal::Util qw(date_str_to_time);
use Bugzilla::User;
use Bugzilla::Constants;
-use Time::Local;
# returns an arrayref containing all changes to the bug - comments, field
# changes, and duplicates
@@ -51,7 +51,7 @@ sub activity_stream {
_add_activities_to_stream($self, $stream);
_add_duplicates_to_stream($self, $stream);
- my $base_time = _sql_date_to_time($self->creation_ts);
+ my $base_time = date_str_to_time($self->creation_ts);
foreach my $change_set (@$stream) {
$change_set->{id} = $change_set->{comment}
? 'c' . $change_set->{comment}->count
@@ -101,7 +101,7 @@ sub _add_comments_to_stream {
next if $comment->type == CMT_HAS_DUPE;
next if $comment->is_private && !($user->is_insider || $user->id == $comment->author->id);
next if $comment->body eq '' && ($comment->work_time - 0) != 0 && !$user->is_timetracker;
- _add_comment_to_stream($stream, _sql_date_to_time($comment->creation_ts), $comment->author->id, $comment);
+ _add_comment_to_stream($stream, date_str_to_time($comment->creation_ts), $comment->author->id, $comment);
}
}
@@ -237,7 +237,7 @@ sub _add_activities_to_stream {
}
}
- _add_activity_to_stream($stream, _sql_date_to_time($operation->{when}), $operation->{who}->id, $operation);
+ _add_activity_to_stream($stream, date_str_to_time($operation->{when}), $operation->{who}->id, $operation);
}
# prime the visible-bugs cache
@@ -274,11 +274,4 @@ sub _add_duplicates_to_stream {
}
}
-sub _sql_date_to_time {
- my ($date) = @_;
- $date =~ /^(\d{4})[\.\-](\d{2})[\.\-](\d{2}) (\d{2}):(\d{2}):(\d{2})$/
- or die "internal error: invalid date '$date'";
- return timelocal($6, $5, $4, $3, $2 - 1, $1 - 1900);
-}
-
1;
diff --git a/extensions/BugModal/lib/Util.pm b/extensions/BugModal/lib/Util.pm
new file mode 100644
index 000000000..daca86518
--- /dev/null
+++ b/extensions/BugModal/lib/Util.pm
@@ -0,0 +1,31 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::BugModal::Util;
+use strict;
+use warnings;
+
+use base qw(Exporter);
+our @EXPORT_OK = qw(date_str_to_time);
+
+use feature 'state';
+
+use Bugzilla::Util qw(datetime_from);
+use DateTime::TimeZone;
+use Time::Local qw(timelocal);
+
+sub date_str_to_time {
+ my ($date) = @_;
+ # avoid creating a DateTime object
+ if ($date =~ /^(\d{4})[\.\-](\d{2})[\.\-](\d{2}) (\d{2}):(\d{2}):(\d{2})$/) {
+ return timelocal($6, $5, $4, $3, $2 - 1, $1 - 1900);
+ }
+ state $tz //= DateTime::TimeZone->new( name => 'local' );
+ return datetime_from($date, $tz)->epoch;
+}
+
+1;
diff --git a/extensions/BugModal/template/en/default/bug_modal/flags.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/flags.html.tmpl
index 70e7e5c3d..77e04b882 100644
--- a/extensions/BugModal/template/en/default/bug_modal/flags.html.tmpl
+++ b/extensions/BugModal/template/en/default/bug_modal/flags.html.tmpl
@@ -74,7 +74,8 @@
</td>
<td class="flag-name">
- <span class="rel-time" title="[% f.creation_date FILTER time_duration FILTER html %]">
+ <span class="rel-time-title" title="[% f.creation_date FILTER time_duration FILTER html %]"
+ data-time="[% f.creation_date FILTER epoch FILTER none %]">
[% f.type.name FILTER html %]
</span>
</td>
diff --git a/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl
index 70c98641f..9e3742ef8 100644
--- a/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl
+++ b/extensions/BugModal/template/en/default/bug_modal/header.html.tmpl
@@ -49,6 +49,7 @@
# assets
javascript_urls.push(
+ "extensions/BugModal/web/time_ago.js",
"extensions/BugModal/web/bug_modal.js",
"extensions/BugModal/web/ZeroClipboard/ZeroClipboard.min.js",
"js/field.js",
diff --git a/extensions/BugModal/template/en/default/bug_modal/rel_time.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/rel_time.html.tmpl
index 3b31dedeb..26a1b709b 100644
--- a/extensions/BugModal/template/en/default/bug_modal/rel_time.html.tmpl
+++ b/extensions/BugModal/template/en/default/bug_modal/rel_time.html.tmpl
@@ -10,7 +10,7 @@
# ts: timestamp
#%]
-<span class="rel-time" title="[% ts FILTER time("%Y-%m-%d %H:%M %Z") %]">
+<span class="rel-time" title="[% ts FILTER time("%Y-%m-%d %H:%M %Z") %]" data-time="[% ts FILTER epoch FILTER none %]">
[%~
IF content.defined;
content;
diff --git a/extensions/BugModal/web/bug_modal.js b/extensions/BugModal/web/bug_modal.js
index bd529fab1..87d173680 100644
--- a/extensions/BugModal/web/bug_modal.js
+++ b/extensions/BugModal/web/bug_modal.js
@@ -134,7 +134,7 @@ $(function() {
});
// use non-native tooltips for relative times and bug summaries
- $('.rel-time, .bz_bug_link').tooltip({
+ $('.rel-time, .rel-time-title, .bz_bug_link').tooltip({
position: { my: "left top+8", at: "left bottom", collision: "flipfit" },
show: { effect: 'none' },
hide: { effect: 'none' }
diff --git a/extensions/BugModal/web/time_ago.js b/extensions/BugModal/web/time_ago.js
new file mode 100644
index 000000000..59f12692a
--- /dev/null
+++ b/extensions/BugModal/web/time_ago.js
@@ -0,0 +1,142 @@
+/*
+ * this is a port of perl's Time::Duration module, which has the following
+ * license:
+ *
+ * Copyright 2006, Sean M. Burke C<sburke@cpan.org>, all rights reserved. This
+ * program is free software; you can redistribute it and/or modify it under the
+ * same terms as Perl itself.
+ *
+ * This program is distributed in the hope that it will be useful, but without
+ * any warranty; without even the implied warranty of merchantability or
+ * fitness for a particular purpose.
+ */
+
+$(function() {
+ 'use strict';
+
+ function separate(seconds) {
+ // breakdown of seconds into units, starting with the most significant
+
+ var remainder = seconds;
+ var tmp;
+ var wheel = [];
+
+ // years
+ tmp = Math.floor(remainder / (365 * 24 * 60 * 60));
+ wheel.push([ 'year', tmp, 1000000000 ]);
+ remainder -= tmp * (365 * 24 * 60 * 60);
+
+ // days
+ tmp = Math.floor(remainder / (24 * 60 * 60));
+ wheel.push([ 'day', tmp, 365 ]);
+ remainder -= tmp * (24 * 60 * 60);
+
+ // hours
+ tmp = Math.floor(remainder / (60 * 60));
+ wheel.push([ 'hour', tmp, 24 ]);
+ remainder -= tmp * (60 * 60);
+
+ // minutes
+ tmp = Math.floor(remainder / 60);
+ wheel.push([ 'minute', tmp, 60 ]);
+ remainder -= tmp * 60;
+
+ // seconds
+ wheel.push([ 'second', Math.floor(remainder), 60 ]);
+ return wheel;
+ }
+
+ function approximate(precision, wheel) {
+ // now nudge the wheels into an acceptably (im)precise configuration
+ FIX: do {
+ // constraints for leaving this block:
+ // 1) number of nonzero wheels must be <= precision
+ // 2) no wheels can be improperly expressed (like having "60" for mins)
+
+ var nonzero_count = 0;
+ var improperly_expressed = -1;
+
+ for (var i = 0; i < wheel.length; i++) {
+ var tmp = wheel[i];
+ if (tmp[1] == 0) {
+ continue;
+ }
+ ++nonzero_count;
+ if (i == 0) {
+ // the years wheel is never improper or over any limit; skip
+ continue;
+ }
+
+ if (nonzero_count > precision) {
+ // this is one nonzero wheel too many
+
+ // incr previous wheel if we're big enough
+ if (tmp[1] >= (tmp[tmp.length - 1] / 2)) {
+ ++wheel[i - 1][1];
+ }
+
+ // reset this and subsequent wheels to 0
+ for (var j = i; j < wheel.length; j++) {
+ wheel[j][1] = 0;
+ }
+
+ // start over
+ continue FIX;
+
+ } else if (tmp[1] >= tmp[tmp.length - 1]) {
+ // it's an improperly expressed wheel (like "60" on the mins wheel)
+ improperly_expressed = i;
+ }
+ }
+
+ if (improperly_expressed != -1) {
+ // only fix the least-significant improperly expressed wheel (at a time)
+ ++wheel[improperly_expressed - 1][1];
+ wheel[improperly_expressed][1] = 0;
+
+ // start over
+ continue FIX;
+ }
+
+ // otherwise there's not too many nonzero wheels, and there's no
+ //improperly expressed wheels, so fall thru...
+ } while(0);
+
+ return wheel;
+ }
+
+ function render(wheel) {
+ var parts = [];
+ wheel.forEach(function(element, index) {
+ if (element[1] > 0) {
+ parts.push(element[1] + ' ' + element[0]);
+ if (element[1] != 1) {
+ parts[parts.length - 1] += 's';
+ }
+ }
+ });
+
+ if (parts.length == 0) {
+ return "just now";
+ }
+ parts[parts.length - 1] += ' ago';
+ if (parts.length == 1) {
+ return parts[0];
+ }
+ if (parts.length == 2) {
+ return parts[0] + ' and ' + parts[1];
+ }
+ parts[parts.length - 1] = 'and ' + parts[parts.length - 1];
+ return parts.join(', ');
+ }
+
+ window.setInterval(function() {
+ var now = Math.floor(new Date().getTime() / 1000);
+ $('.rel-time').each(function() {
+ $(this).text(render(approximate(2, separate(now - $(this).data('time')))));
+ });
+ $('.rel-time-title').each(function() {
+ $(this).attr('title', render(approximate(2, separate(now - $(this).data('time')))));
+ });
+ }, 60000);
+});