Bug 330487: Automatic Update Notification for Bugzilla - Patch by Fr�d�ric Buclin <LpSolit@gmail.com> r=mkanat a=justdave

This commit is contained in:
lpsolit%gmail.com 2006-06-12 17:13:35 +00:00
Родитель b6ddf6008a
Коммит a836e8c466
8 изменённых файлов: 305 добавлений и 7 удалений

Просмотреть файл

@ -64,7 +64,7 @@ use base qw(Exporter);
check_sslbase check_priority check_severity check_platform
check_opsys check_shadowdb check_urlbase check_webdotbase
check_netmask check_user_verify_class check_image_converter
check_languages check_mail_delivery_method
check_languages check_mail_delivery_method check_notification
);
# Checking functions for the various values
@ -303,6 +303,20 @@ sub check_mail_delivery_method {
return "";
}
sub check_notification {
my $option = shift;
my @current_version =
($Bugzilla::Config::VERSION =~ m/^(\d+)\.(\d+)(?:(rc|\.)(\d+))?\+?$/);
if ($current_version[1] % 2 && $option eq 'stable_branch_release') {
return "You are currently running a development snapshot, and so your " .
"installation is not based on a branch. If you want to be notified " .
"about the next stable release, you should select " .
"'latest_stable_release' instead";
}
return "";
}
# OK, here are the parameter definitions themselves.
#
# Each definition is a hash with keys:

Просмотреть файл

@ -103,6 +103,15 @@ sub get_param_list {
name => 'shutdownhtml',
type => 'l',
default => ''
},
{
name => 'upgrade_notification',
type => 's',
choices => ['development_snapshot', 'latest_stable_release',
'stable_branch_release', 'disabled'],
default => 'latest_stable_release',
checker => \&check_notification
} );
return @param_list;
}

Просмотреть файл

@ -0,0 +1,196 @@
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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>
package Bugzilla::Update;
use strict;
use Bugzilla::Config qw($datadir);
use constant REMOTE_FILE => 'http://updates.bugzilla.org/bugzilla-update.xml';
use constant LOCAL_FILE => "/bugzilla-update.xml"; # Relative to $datadir.
use constant TIME_INTERVAL => 604800; # Default is one week, in seconds.
use constant TIMEOUT => 5; # Number of seconds before timeout.
# Look for new releases and notify logged in administrators about them.
sub get_notifications {
return if (Bugzilla->params->{'upgrade_notification'} eq 'disabled');
# If the XML::Twig module is missing, we won't be able to parse
# the XML file. So there is no need to go further.
eval("require XML::Twig");
return if $@;
my $local_file = $datadir . LOCAL_FILE;
# Update the local XML file if this one doesn't exist or if
# the last modification time (stat[9]) is older than TIME_INTERVAL.
if (!-e $local_file || (time() - (stat($local_file))[9] > TIME_INTERVAL)) {
# Are we sure we didn't try to refresh this file already
# but we failed because we cannot modify its timestamp?
my $can_alter = 1;
if (-e $local_file) {
# Try to alter its last modification time.
$can_alter = utime(undef, undef, $local_file);
}
if ($can_alter) {
_synchronize_data();
}
else {
return {'error' => 'no_update', 'xml_file' => $local_file};
}
}
# If we cannot access the local XML file, ignore it.
return {'error' => 'no_access', 'xml_file' => $local_file} unless (-r $local_file);
my $twig = XML::Twig->new();
$twig->safe_parsefile($local_file);
# If the XML file is invalid, return.
return {'error' => 'corrupted', 'xml_file' => $local_file} if $@;
my $root = $twig->root;
my @releases;
foreach my $branch ($root->children('branch')) {
my $release = {
'branch_ver' => $branch->{'att'}->{'id'},
'latest_ver' => $branch->{'att'}->{'vid'},
'status' => $branch->{'att'}->{'status'},
'url' => $branch->{'att'}->{'url'},
'date' => $branch->{'att'}->{'date'}
};
push(@releases, $release);
}
# On which branch is the current installation running?
my @current_version =
($Bugzilla::Config::VERSION =~ m/^(\d+)\.(\d+)(?:(rc|\.)(\d+))?\+?$/);
my @release;
if (Bugzilla->params->{'upgrade_notification'} eq 'development_snapshot') {
@release = grep {$_->{'status'} eq 'development'} @releases;
}
elsif (Bugzilla->params->{'upgrade_notification'} eq 'latest_stable_release') {
@release = grep {$_->{'status'} eq 'stable'} @releases;
}
elsif (Bugzilla->params->{'upgrade_notification'} eq 'stable_branch_release') {
# We want the latest stable version for the current branch.
# If we are running a development snapshot, we won't match anything.
my $branch_version = $current_version[0] . '.' . $current_version[1];
# We do a string comparison instead of a numerical one, because
# e.g. 2.2 == 2.20, but 2.2 ne 2.20 (and 2.2 is indeed much older).
@release = grep {$_->{'branch_ver'} eq $branch_version} @releases;
# If the branch is now closed, we should strongly suggest
# to upgrade to the latest stable release available.
if (scalar(@release) && $release[0]->{'status'} eq 'closed') {
@release = grep {$_->{'status'} eq 'stable'} @releases;
return {'data' => $release[0], 'deprecated' => $branch_version};
}
}
else {
# Unknown parameter.
return {'error' => 'unknown_parameter'};
}
# Return if no new release is available.
return unless scalar(@release);
# Only notify the administrator if the latest version available
# is newer than the current one.
my @new_version =
($release[0]->{'latest_ver'} =~ m/^(\d+)\.(\d+)(?:(rc|\.)(\d+))?\+?$/);
# We convert release candidates 'rc' to integers (rc ? 0 : 1) in order
# to compare versions easily.
$current_version[2] = ($current_version[2] && $current_version[2] eq 'rc') ? 0 : 1;
$new_version[2] = ($new_version[2] && $new_version[2] eq 'rc') ? 0 : 1;
my $is_newer = _compare_versions(\@current_version, \@new_version);
return ($is_newer == 1) ? {'data' => $release[0]} : undef;
}
sub _synchronize_data {
eval("require LWP::UserAgent");
return if $@;
my $local_file = $datadir . LOCAL_FILE;
my $ua = LWP::UserAgent->new();
$ua->timeout(TIMEOUT);
$ua->protocols_allowed(['http', 'https']);
$ua->mirror(REMOTE_FILE, $local_file);
# $ua->mirror() forces the modification time of the local XML file
# to match the modification time of the remote one.
# So we have to update it manually to reflect that a newer version
# of the file has effectively been requested. This will avoid
# any new download for the next TIME_INTERVAL.
utime(undef, undef, $local_file);
}
sub _compare_versions {
my ($old_ver, $new_ver) = @_;
while (scalar(@$old_ver) && scalar(@$new_ver)) {
my $old = shift(@$old_ver);
my $new = shift(@$new_ver);
return $new <=> $old if ($new <=> $old);
}
return scalar(@$new_ver) <=> scalar(@$old_ver);
}
1;
__END__
=head1 NAME
Bugzilla::Update - Update routines for Bugzilla
=head1 SYNOPSIS
use Bugzilla::Update;
# Get information about new releases
my $new_release = Bugzilla::Update::get_notifications();
=head1 DESCRIPTION
This module contains all required routines to notify you
about new releases. It downloads an XML file from bugzilla.org
and parses it, in order to display information based on your
preferences. Absolutely no information about the Bugzilla version
you are running is sent to bugzilla.org.
=head1 FUNCTIONS
=over
=item C<get_notifications()>
Description: This function informs you about new releases, if any.
Params: None.
Returns: On success, a reference to a hash with data about
new releases, if any.
On failure, a reference to a hash with the reason
of the failure and the name of the unusable XML file.
=back
=cut

Просмотреть файл

@ -355,6 +355,7 @@ my %ppm_modules = (
'Mail::Base64' => 'MIME-Base64',
'MIME::Tools' => 'MIME-Tools',
'XML::Twig' => 'XML-Twig',
'LWP::UserAgent' => 'LWP-UserAgent'
);
sub install_command {
@ -392,6 +393,7 @@ print "\nThe following Perl modules are optional:\n" unless $silent;
my $gd = have_vers("GD","1.20");
my $chartbase = have_vers("Chart::Base","1.0");
my $xmlparser = have_vers("XML::Twig",0);
my $lwp_ua = have_vers("LWP::UserAgent",0);
my $gdgraph = have_vers("GD::Graph",0);
my $gdtextalign = have_vers("GD::Text::Align",0);
my $patchreader = have_vers("PatchReader","0.9.4");
@ -420,6 +422,12 @@ if (!$xmlparser && !$silent) {
"the XML::Twig module by running (as $::root):\n\n",
" " . install_command("XML::Twig") . "\n\n";
}
if (!$lwp_ua && !$silent) {
print "If you want to use the automatic update notification feature\n",
"you will need to install the LWP::UserAgent module by running\n",
"(as $::root):\n\n",
" " . install_command("LWP::UserAgent") . "\n\n";
}
if (!$imagemagick && !$silent) {
print "If you want to convert BMP image attachments to PNG to conserve\n",
"disk space, you will need to install the ImageMagick application\n",
@ -1420,6 +1428,8 @@ if ($^O !~ /MSWin32/i) {
fixPerms($webdotdir, $<, $webservergid, 007, 1);
fixPerms("$webdotdir/.htaccess", $<, $webservergid, 027);
fixPerms("$datadir/params", $<, $webservergid, 017);
# The web server must be the owner of bugzilla-update.xml.
fixPerms("$datadir/bugzilla-update.xml", $webservergid, $webservergid, 017);
fixPerms('*', $<, $webservergid, 027);
fixPerms('Bugzilla', $<, $webservergid, 027, 1);
fixPerms($templatedir, $<, $webservergid, 027, 1);
@ -1447,6 +1457,7 @@ if ($^O !~ /MSWin32/i) {
chmod 01777, $webdotdir;
fixPerms("$webdotdir/.htaccess", $<, $gid, 022);
fixPerms("$datadir/params", $<, $gid, 011);
fixPerms("$datadir/bugzilla-update.xml", $gid, $gid, 011);
fixPerms('*', $<, $gid, 022);
fixPerms('Bugzilla', $<, $gid, 022, 1);
fixPerms($templatedir, $<, $gid, 022, 1);

Просмотреть файл

@ -19,7 +19,7 @@
# Rights Reserved.
#
# Contributor(s): Jacob Steenhagen <jake@bugzilla.org>
#
# Frédéric Buclin <LpSolit@gmail.com>
###############################################################################
# Script Initialization
@ -32,9 +32,11 @@ use strict;
use lib ".";
require "globals.pl";
# Check whether or not the user is logged in
use Bugzilla::Constants;
Bugzilla->login(LOGIN_OPTIONAL);
use Bugzilla::Update;
# Check whether or not the user is logged in
my $user = Bugzilla->login(LOGIN_OPTIONAL);
###############################################################################
# Main Body Execution
@ -48,10 +50,15 @@ if (Param('sslbase') ne '' and Param('ssl') ne 'never') {
}
my $template = Bugzilla->template;
my $vars = {};
# Return the appropriate HTTP response headers.
print $cgi->header();
if ($user->in_group('admin')) {
$vars->{'release'} = Bugzilla::Update::get_notifications();
}
# Generate and return the UI (HTML page) from the appropriate template.
$template->process("index.html.tmpl")
$template->process("index.html.tmpl", $vars)
|| ThrowTemplateError($template->error());

Просмотреть файл

@ -66,4 +66,17 @@
padding: 1em 0;
}
#new_release
{
border: 2px solid red;
padding: 0.5em 1em;
margin: 1em;
font-weight: bold;
}
#new_release .notice
{
font-size: 80%;
font-weight: normal;
}
/* index page (end) */

Просмотреть файл

@ -70,5 +70,20 @@
shutdownhtml => "If this field is non-empty, then $terms.Bugzilla will be completely " _
"disabled and this text will be displayed instead of all the " _
"$terms.Bugzilla pages." }
"$terms.Bugzilla pages.",
upgrade_notification => "<p>$terms.Bugzilla can inform you when a new release is available. " _
"The notification will appear on the $terms.Bugzilla homepage, " _
"for administrators only.</p>" _
"<ul><li>'development_snapshot' notifies you about the latest " _
"release on the trunk, i.e. development snapshots.</li>" _
"<li>'latest_stable_release' notifies you about the most recent release " _
"available on the most recent stable branch. This branch may be " _
"different from the branch your installation is based on.</li>" _
"<li>'stable_branch_release' notifies you only about new releases " _
"corresponding to the branch your installation is based on. " _
"If you are running a release candidate, you will get " _
"a notification for newer release candidates too.</li>" _
"<li>'disabled' will never notify you about new releases and no " _
"connection will be established to a remote server.</li></ul>" }
%]

Просмотреть файл

@ -23,7 +23,7 @@
#%]
[%# INTERFACE:
# This template has no interface.
# release: a hash containing data about new releases, if any.
#%]
[% PROCESS global/variables.none.tmpl %]
@ -56,6 +56,39 @@ function addSidebar() {
//-->
</script>
[% IF release %]
<div id="new_release">
[% IF release.data %]
[% IF release.deprecated %]
<p>[% terms.Bugzilla %] [%+ release.deprecated FILTER html %] is no longer
supported. You are highly encouraged to upgrade in order to keep your
system secure.</p>
[% END %]
<p>A new [% terms.Bugzilla %] version ([% release.data.latest_ver FILTER html %])
is available at
<a href="[% release.data.url FILTER html %]">[% release.data.url FILTER html %]</a>.<br>
Release date: [% release.data.date FILTER html %]</p>
<p class="notice">This message is only shown to logged in users with admin privs.
You can configure this notification from the
<a href="editparams.cgi?section=core#upgrade_notification">Parameters</a> page.</p>
[% ELSIF release.error == "no_update" %]
<p>The local XML file '[% release.xml_file FILTER html %]' cannot be updated.
Please make sure the web server can edit this file.</p>
[% ELSIF release.error == "no_access" %]
<p>The local XML file '[% release.xml_file FILTER html %]' cannot be read.
Please make sure this file exists and has the correct rights set on it.</p>
[% ELSIF release.error == "corrupted" %]
<p>The local XML file '[% release.xml_file FILTER html %]' has an invalid XML format.
Please delete it and try accessing this page again.</p>
[% ELSIF release.error == "unknown_parameter" %]
<p>'[% Param("upgrade_notification") FILTER html %]' is not a valid notification
parameter. Please check this parameter in the
<a href="editparams.cgi?section=core#upgrade_notification">Parameters</a> page.</p>
[% END %]
</div>
[% END %]
<div id="page-index">
<div class="intro"></div>