summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Bugzilla/Constants.pm1
-rw-r--r--Bugzilla/Template.pm25
-rwxr-xr-xbuglist.cgi33
-rw-r--r--t/008filter.t2
-rw-r--r--template/en/default/list/list.html.tmpl2
-rw-r--r--template/en/default/list/list.ics.tmpl103
6 files changed, 162 insertions, 4 deletions
diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm
index a1bf74ba0..a61cb4620 100644
--- a/Bugzilla/Constants.pm
+++ b/Bugzilla/Constants.pm
@@ -103,6 +103,7 @@ use constant contenttypes =>
"js" => "application/x-javascript" ,
"csv" => "text/plain" ,
"png" => "image/png" ,
+ "ics" => "text/calendar" ,
};
1;
diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm
index c123154bb..310a18161 100644
--- a/Bugzilla/Template.pm
+++ b/Bugzilla/Template.pm
@@ -293,6 +293,31 @@ sub create {
return $var;
},
+ # iCalendar contentline filter
+ ics => [ sub {
+ my ($context, @args) = @_;
+ return sub {
+ my ($var) = shift;
+ my ($par) = shift @args;
+ my ($output) = "";
+
+ $var =~ s/[\r\n]/ /g;
+ $var =~ s/([;\\\"])/\\$1/g;
+
+ if ($par) {
+ $output = sprintf("%s:%s", $par, $var);
+ } else {
+ $output = $var;
+ }
+
+ $output =~ s/(.{75,75})/$1\n /g;
+
+ return $output;
+ };
+ },
+ 1
+ ],
+
# We force filtering of every variable in key security-critical
# places; we have a none filter for people to use when they
# really, really don't want a variable to be changed.
diff --git a/buglist.cgi b/buglist.cgi
index 0871a8c54..8a02ca490 100755
--- a/buglist.cgi
+++ b/buglist.cgi
@@ -172,6 +172,14 @@ sub DiffDate {
return $date;
}
+sub iCalendarDateTime {
+ my ($datestr) = @_;
+ my $date = str2time($datestr);
+ my ($s,$m,$h,$d,$mo,$y,$wd)= gmtime $date;
+ $date = sprintf "%04d%02d%02dT%02d%02d%02dZ", 1900+$y,$mo+1,$d,$h,$m,$s;
+ return $date;
+}
+
sub LookupNamedQuery {
my ($name) = @_;
confirm_login();
@@ -525,6 +533,9 @@ if ($dotweak) {
push(@selectcolumns, "bug_status") if !grep($_ eq 'bug_status', @selectcolumns);
}
+if ($format->{'extension'} eq 'ics') {
+ push(@selectcolumns, "opendate") if !grep($_ eq 'opendate', @selectcolumns);
+}
################################################################################
# Query Generation
@@ -712,10 +723,23 @@ while (my @row = FetchSQLData()) {
# Process certain values further (i.e. date format conversion).
if ($bug->{'changeddate'}) {
$bug->{'changeddate'} =~
- s/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/$1-$2-$3 $4:$5:$6/;
- $bug->{'changeddate'} = DiffDate($bug->{'changeddate'});
+ s/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/$1-$2-$3 $4:$5:$6/;
+ if ($format->{'extension'} eq 'ics') {
+ $bug->{'changeddate'} = iCalendarDateTime($bug->{'changeddate'});
+ }
+ else {
+ $bug->{'changeddate'} = DiffDate($bug->{'changeddate'});
+ }
+ }
+
+ if ($bug->{'opendate'}) {
+ if ($format->{'extension'} eq 'ics') {
+ $bug->{'opendate'} = iCalendarDateTime($bug->{'opendate'});
+ }
+ else {
+ $bug->{'opendate'} = DiffDate($bug->{'opendate'});
+ }
}
- ($bug->{'opendate'} = DiffDate($bug->{'opendate'})) if $bug->{'opendate'};
# Record the owner, product, and status in the big hashes of those things.
$bugowners->{$bug->{'assigned_to'}} = 1 if $bug->{'assigned_to'};
@@ -800,6 +824,9 @@ $vars->{'splitheader'} = $::COOKIE{'SPLITHEADER'} ? 1 : 0;
$vars->{'quip'} = GetQuip();
$vars->{'currenttime'} = time();
+if ($format->{'extension'} eq 'ics') {
+ $vars->{'currenttime'} = iCalendarDateTime(scalar gmtime $vars->{'currenttime'});
+}
# The following variables are used when the user is making changes to multiple bugs.
if ($dotweak) {
diff --git a/t/008filter.t b/t/008filter.t
index 722802bb8..e0aa5b05f 100644
--- a/t/008filter.t
+++ b/t/008filter.t
@@ -202,7 +202,7 @@ sub directive_ok {
# Note: If a single directive prints two things, and only one is
# filtered, we may not catch that case.
return 1 if $directive =~ /FILTER\ (html|csv|js|url_quote|css_class_quote|
- quoteUrls|time|uri|xml|lower|
+ ics|quoteUrls|time|uri|xml|lower|
unitconvert|none)/x;
return 0;
diff --git a/template/en/default/list/list.html.tmpl b/template/en/default/list/list.html.tmpl
index 82cf5dbfd..c0291024c 100644
--- a/template/en/default/list/list.html.tmpl
+++ b/template/en/default/list/list.html.tmpl
@@ -139,6 +139,8 @@
<td valign="middle">
<a href="buglist.cgi?
[% urlquerypart FILTER html %]&amp;ctype=csv">CSV</a> |
+ <a href="buglist.cgi?
+ [% urlquerypart FILTER html %]&amp;ctype=ics">iCalendar</a> |
<a href="colchange.cgi?
[% urlquerypart FILTER html %]">Change&nbsp;Columns</a> |
diff --git a/template/en/default/list/list.ics.tmpl b/template/en/default/list/list.ics.tmpl
new file mode 100644
index 000000000..27dc1b401
--- /dev/null
+++ b/template/en/default/list/list.ics.tmpl
@@ -0,0 +1,103 @@
+[%# 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.
+ #
+ # 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): William Jon McCann <mccann@jhu.edu>
+ #%]
+[% PROCESS global/variables.none.tmpl %]
+BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+[%+ PROCESS ics_prodid +%]
+VERSION:2.0
+[% FOREACH bug = bugs %]
+BEGIN:VTODO
+[%+ PROCESS ics_dtstart +%]
+[%+ PROCESS ics_summary +%]
+[%+ PROCESS ics_uid base_url=Param('urlbase') bug_id=bug.bug_id +%]
+[%+ PROCESS ics_url base_url=Param('urlbase') bug_id=bug.bug_id +%]
+[%+ PROCESS ics_status bug_status = bug.bug_status +%]
+[%+ PROCESS ics_dtstamp +%]
+[% IF bug.changeddate %]
+[%+ bug.changeddate FILTER ics('LAST-MODIFIED') +%]
+[% END %]
+[% IF bug.percentage_complete %]
+[%+ bug.percentage_complete FILTER format('%d') FILTER ics('PERCENT-COMPLETE') +%]
+[% END %]
+[% IF bug.product %]
+[%+ bug.product FILTER ics('X-BUGZILLA-PRODUCT') +%]
+[% END %]
+[% IF bug.component %]
+[%+ bug.component FILTER ics('X-BUGZILLA-COMPONENT') +%]
+[% END %]
+[% IF bug.version %]
+[%+ bug.version FILTER ics('X-BUGZILLA-VERSION') +%]
+[% END %]
+[% IF bug.keywords %]
+[%+ bug.keywords FILTER ics('X-BUGZILLA-KEYWORDS') +%]
+[% END %]
+END:VTODO
+[% END %]
+END:VCALENDAR
+
+[% BLOCK ics_prodid %]
+ [% "-//Mozilla/Bugzilla $VERSION//EN" FILTER ics('PRODID') %]
+[% END %]
+
+[% BLOCK ics_uid %]
+ [% "${bug_id}@${base_url}" FILTER uri FILTER ics('UID') %]
+[% END %]
+
+[% BLOCK ics_url %]
+ [% "${base_url}show_bug.cgi?id=${bug_id}" FILTER uri FILTER ics('URL;VALUE=URI') %]
+[% END %]
+
+[% BLOCK ics_dtstart %]
+ [% bug.opendate FILTER ics('DTSTART') %]
+[% END %]
+
+[% BLOCK ics_dtstamp %]
+ [% currenttime FILTER ics('DTSTAMP') %]
+[% END %]
+
+[% BLOCK ics_status %]
+ [% status = "" %]
+ [% FOREACH state = closedstates %]
+ [% IF bug_status == state %]
+ [% status = 'COMPLETED' %]
+ [% LAST %]
+ [% END %]
+ [% END %]
+ [% IF NOT status %]
+ [% IF bug_status == 'ASSIGNED' %]
+ [% status = 'IN-PROGRESS' %]
+ [% ELSE %]
+ [% status = 'NEEDS-ACTION' %]
+ [% END %]
+ [% END %]
+ [% status FILTER ics('STATUS') %]
+[% END %]
+
+[% BLOCK ics_summary %]
+ [% IF bug.short_desc %]
+ [% summary = bug.short_desc %]
+ [% ELSIF bug.short_short_desc %]
+ [% summary = bug.short_short_desc %]
+ [% ELSE %]
+ [% summary = "$terms.Bug $bug.bug_id" %]
+ [% END %]
+ [% summary FILTER ics('SUMMARY') %]
+[% END %]