2008-12-15 15:53:33 +03:00
|
|
|
# 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 Everything Solved.
|
|
|
|
# Portions created by Everything Solved are Copyright (C) 2006
|
|
|
|
# Everything Solved. All Rights Reserved.
|
|
|
|
#
|
|
|
|
# Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
|
|
|
|
|
|
|
|
package Bugzilla::Install::Util;
|
|
|
|
|
|
|
|
# The difference between this module and Bugzilla::Util is that this
|
|
|
|
# module may require *only* Bugzilla::Constants and built-in
|
|
|
|
# perl modules.
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
|
|
|
|
use Bugzilla::Constants;
|
2010-05-15 00:02:34 +04:00
|
|
|
use Bugzilla::Extension;
|
2008-12-15 15:53:33 +03:00
|
|
|
|
|
|
|
use File::Basename;
|
|
|
|
use POSIX qw(setlocale LC_CTYPE);
|
|
|
|
use Safe;
|
2009-07-29 15:21:49 +04:00
|
|
|
use Scalar::Util qw(tainted);
|
2008-12-15 15:53:33 +03:00
|
|
|
|
|
|
|
use base qw(Exporter);
|
|
|
|
our @EXPORT_OK = qw(
|
|
|
|
bin_loc
|
|
|
|
get_version_and_os
|
|
|
|
indicate_progress
|
|
|
|
install_string
|
|
|
|
include_languages
|
|
|
|
template_include_path
|
|
|
|
vers_cmp
|
|
|
|
get_console_locale
|
2010-05-15 00:02:34 +04:00
|
|
|
init_console
|
2008-12-15 15:53:33 +03:00
|
|
|
);
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
sub bin_loc
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
my ($bin) = @_;
|
|
|
|
return '' if ON_WINDOWS;
|
|
|
|
# Don't print any errors from "which"
|
|
|
|
open(my $saveerr, ">&STDERR");
|
|
|
|
open(STDERR, '>/dev/null');
|
|
|
|
my $loc = `which $bin`;
|
|
|
|
close(STDERR);
|
|
|
|
open(STDERR, ">&", $saveerr);
|
|
|
|
my $exit_code = $? >> 8; # See the perlvar manpage.
|
|
|
|
return '' if $exit_code > 0;
|
|
|
|
chomp($loc);
|
|
|
|
return $loc;
|
|
|
|
}
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
sub get_version_and_os
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
# Display version information
|
|
|
|
my @os_details = POSIX::uname;
|
|
|
|
# 0 is the name of the OS, 2 is the major version,
|
|
|
|
my $os_name = $os_details[0] . ' ' . $os_details[2];
|
2014-10-14 16:58:57 +04:00
|
|
|
if (ON_WINDOWS)
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
require Win32;
|
|
|
|
$os_name = Win32::GetOSName();
|
|
|
|
}
|
|
|
|
# $os_details[3] is the minor version.
|
2014-10-14 16:58:57 +04:00
|
|
|
return {
|
|
|
|
bz_ver => BUGZILLA_VERSION,
|
|
|
|
perl_ver => sprintf('%vd', $^V),
|
|
|
|
os_name => $os_name,
|
|
|
|
os_ver => $os_details[3],
|
|
|
|
};
|
2008-12-15 15:53:33 +03:00
|
|
|
}
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
sub indicate_progress
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
my ($params) = @_;
|
|
|
|
my $current = $params->{current};
|
|
|
|
my $total = $params->{total};
|
|
|
|
my $every = $params->{every} || 1;
|
|
|
|
print "." if !($current % $every);
|
2014-10-14 16:58:57 +04:00
|
|
|
if ($current == $total || $current % ($every * 60) == 0)
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
print "$current/$total (" . int($current * 100 / $total) . "%)\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
sub install_string
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
my ($string_id, $vars) = @_;
|
2010-05-15 00:02:34 +04:00
|
|
|
_cache()->{install_string_path} ||= template_include_path();
|
|
|
|
my $path = _cache()->{install_string_path};
|
2014-10-14 16:58:57 +04:00
|
|
|
|
2008-12-15 15:53:33 +03:00
|
|
|
my $string_template;
|
|
|
|
# Find the first template that defines this string.
|
2014-10-14 16:58:57 +04:00
|
|
|
foreach my $dir (@$path)
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
my $base = "$dir/setup/strings";
|
2014-10-14 16:58:57 +04:00
|
|
|
if (!defined $string_template)
|
|
|
|
{
|
|
|
|
$string_template = _get_string_from_file($string_id, "$base.txt.pl");
|
|
|
|
}
|
2008-12-15 15:53:33 +03:00
|
|
|
last if defined $string_template;
|
|
|
|
}
|
2014-10-14 16:58:57 +04:00
|
|
|
|
2010-05-17 20:56:09 +04:00
|
|
|
if (!defined $string_template)
|
|
|
|
{
|
|
|
|
# Don't throw an error, it's a stupid way -- <vitalif@yourcmc.ru>
|
|
|
|
$string_template = $string_id;
|
|
|
|
$string_template =~ s/^feature_//so;
|
|
|
|
}
|
2008-12-15 15:53:33 +03:00
|
|
|
|
2010-05-15 00:02:34 +04:00
|
|
|
utf8::decode($string_template) if !utf8::is_utf8($string_template);
|
|
|
|
|
2008-12-15 15:53:33 +03:00
|
|
|
$vars ||= {};
|
|
|
|
my @replace_keys = keys %$vars;
|
2014-10-14 16:58:57 +04:00
|
|
|
foreach my $key (@replace_keys)
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
my $replacement = $vars->{$key};
|
2014-10-14 16:58:57 +04:00
|
|
|
die "'$key' in '$string_id' is tainted: '$replacement'" if tainted($replacement);
|
2008-12-15 15:53:33 +03:00
|
|
|
# We don't want people to start getting clever and inserting
|
|
|
|
# ##variable## into their values. So we check if any other
|
|
|
|
# key is listed in the *replacement* string, before doing
|
|
|
|
# the replacement. This is mostly to protect programmers from
|
|
|
|
# making mistakes.
|
2014-10-14 16:58:57 +04:00
|
|
|
if (grep($replacement =~ /##$key##/, @replace_keys))
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
die "Unsafe replacement for '$key' in '$string_id': '$replacement'";
|
|
|
|
}
|
|
|
|
$string_template =~ s/\Q##$key##\E/$replacement/g;
|
|
|
|
}
|
2010-05-15 00:02:34 +04:00
|
|
|
|
2008-12-15 15:53:33 +03:00
|
|
|
return $string_template;
|
|
|
|
}
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
sub include_languages
|
|
|
|
{
|
2010-05-15 00:02:34 +04:00
|
|
|
# If we are in CGI mode (not in checksetup.pl) and if the function has
|
|
|
|
# been called without any parameter, then we cache the result of this
|
|
|
|
# function in Bugzilla->request_cache. This is done to improve the
|
|
|
|
# performance of the template processing.
|
|
|
|
my $to_be_cached = 0;
|
2014-10-14 16:58:57 +04:00
|
|
|
if (not @_)
|
|
|
|
{
|
2010-05-15 00:02:34 +04:00
|
|
|
my $cache = _cache();
|
2014-10-14 16:58:57 +04:00
|
|
|
if (exists $cache->{include_languages})
|
|
|
|
{
|
2010-05-15 00:02:34 +04:00
|
|
|
return @{ $cache->{include_languages} };
|
|
|
|
}
|
|
|
|
$to_be_cached = 1;
|
|
|
|
}
|
2008-12-15 15:53:33 +03:00
|
|
|
my ($params) = @_;
|
|
|
|
$params ||= {};
|
|
|
|
|
|
|
|
# Basically, the way this works is that we have a list of languages
|
|
|
|
# that we *want*, and a list of languages that Bugzilla actually
|
|
|
|
# supports. The caller tells us what languages they want, by setting
|
2010-05-15 00:02:34 +04:00
|
|
|
# $ENV{HTTP_ACCEPT_LANGUAGE}, using the "LANG" cookie or setting
|
|
|
|
# $params->{only_language}. The languages we support are those
|
|
|
|
# specified in $params->{use_languages}. Otherwise we support every
|
|
|
|
# language installed in the template/ directory.
|
2014-10-14 16:58:57 +04:00
|
|
|
|
2008-12-15 15:53:33 +03:00
|
|
|
my @wanted;
|
2014-10-14 16:58:57 +04:00
|
|
|
if ($params->{only_language})
|
|
|
|
{
|
2010-11-03 20:13:12 +03:00
|
|
|
# We can pass several languages at once as an arrayref
|
|
|
|
# or a single language.
|
2014-10-14 16:58:57 +04:00
|
|
|
if (ref $params->{only_language})
|
|
|
|
{
|
2010-11-03 20:13:12 +03:00
|
|
|
@wanted = @{ $params->{only_language} };
|
|
|
|
}
|
2014-10-14 16:58:57 +04:00
|
|
|
else
|
|
|
|
{
|
2010-11-03 20:13:12 +03:00
|
|
|
@wanted = ($params->{only_language});
|
|
|
|
}
|
2008-12-15 15:53:33 +03:00
|
|
|
}
|
2014-10-14 16:58:57 +04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
@wanted = _sort_accept_language($ENV{HTTP_ACCEPT_LANGUAGE} || '');
|
2010-05-15 00:02:34 +04:00
|
|
|
# Don't use the cookie if we are in "checksetup.pl". The test
|
2014-10-14 16:58:57 +04:00
|
|
|
# with $ENV{SERVER_SOFTWARE} is the same as in
|
2010-05-15 00:02:34 +04:00
|
|
|
# Bugzilla:Util::i_am_cgi.
|
2014-10-14 16:58:57 +04:00
|
|
|
if (exists $ENV{SERVER_SOFTWARE} && defined (my $lang = Bugzilla->cookies->{LANG}))
|
|
|
|
{
|
|
|
|
unshift @wanted, $lang;
|
2010-05-15 00:02:34 +04:00
|
|
|
}
|
2008-12-15 15:53:33 +03:00
|
|
|
}
|
2014-10-14 16:58:57 +04:00
|
|
|
|
2008-12-15 15:53:33 +03:00
|
|
|
my @supported;
|
2014-10-14 16:58:57 +04:00
|
|
|
if (defined $params->{use_languages})
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
@supported = @{$params->{use_languages}};
|
|
|
|
}
|
2014-10-14 16:58:57 +04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
my @dirs = glob(bz_locations()->{templatedir} . "/*");
|
2008-12-15 15:53:33 +03:00
|
|
|
@dirs = map(basename($_), @dirs);
|
|
|
|
@supported = grep($_ ne 'CVS', @dirs);
|
|
|
|
}
|
2014-10-14 16:58:57 +04:00
|
|
|
|
2008-12-15 15:53:33 +03:00
|
|
|
my @usedlanguages;
|
2014-10-14 16:58:57 +04:00
|
|
|
foreach my $wanted (@wanted)
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
# If we support the language we want, or *any version* of
|
|
|
|
# the language we want, it gets pushed into @usedlanguages.
|
|
|
|
#
|
|
|
|
# Per RFC 1766 and RFC 2616, things like 'en' match 'en-us' and
|
|
|
|
# 'en-uk', but not the other way around. (This is unfortunately
|
|
|
|
# not very clearly stated in those RFC; see comment just over 14.5
|
|
|
|
# in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4)
|
2014-10-14 16:58:57 +04:00
|
|
|
if (my @found = grep /^\Q$wanted\E(-.+)?$/i, @supported)
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
push (@usedlanguages, @found);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# We always include English at the bottom if it's not there, even if
|
|
|
|
# somebody removed it from use_languages.
|
2014-10-14 16:58:57 +04:00
|
|
|
if (!grep($_ eq 'en', @usedlanguages))
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
push(@usedlanguages, 'en');
|
|
|
|
}
|
|
|
|
|
2010-05-15 00:02:34 +04:00
|
|
|
# Cache the result if we are in CGI mode and called without parameter
|
|
|
|
# (see the comment at the top of this function).
|
2014-10-14 16:58:57 +04:00
|
|
|
if ($to_be_cached)
|
|
|
|
{
|
2010-05-15 00:02:34 +04:00
|
|
|
_cache()->{include_languages} = \@usedlanguages;
|
|
|
|
}
|
|
|
|
|
2008-12-15 15:53:33 +03:00
|
|
|
return @usedlanguages;
|
|
|
|
}
|
2010-05-15 00:02:34 +04:00
|
|
|
|
|
|
|
# Used by template_include_path
|
2014-10-14 16:58:57 +04:00
|
|
|
sub _template_lang_directories
|
|
|
|
{
|
2010-05-15 00:02:34 +04:00
|
|
|
my ($languages, $templatedir) = @_;
|
|
|
|
my @add = qw(custom default);
|
2014-10-14 16:58:57 +04:00
|
|
|
my $project = bz_locations->{project};
|
2010-05-15 00:02:34 +04:00
|
|
|
unshift(@add, $project) if $project;
|
|
|
|
my @result;
|
2014-10-14 16:58:57 +04:00
|
|
|
foreach my $lang (@$languages)
|
|
|
|
{
|
|
|
|
foreach my $dir (@add)
|
|
|
|
{
|
2010-05-15 00:02:34 +04:00
|
|
|
my $full_dir = "$templatedir/$lang/$dir";
|
2014-10-14 16:58:57 +04:00
|
|
|
if (-d $full_dir)
|
|
|
|
{
|
2010-05-15 00:02:34 +04:00
|
|
|
trick_taint($full_dir);
|
|
|
|
push(@result, $full_dir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return @result;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Used by template_include_path.
|
|
|
|
sub _template_base_directories
|
|
|
|
{
|
|
|
|
my @template_dirs;
|
|
|
|
Bugzilla::Extension::load_all();
|
|
|
|
my $dir;
|
|
|
|
foreach (Bugzilla::Extension::loaded())
|
|
|
|
{
|
|
|
|
$dir = extension_template_dir($_);
|
|
|
|
if (-d $dir)
|
|
|
|
{
|
|
|
|
push @template_dirs, $dir;
|
|
|
|
}
|
|
|
|
}
|
2014-10-14 16:58:57 +04:00
|
|
|
push @template_dirs, bz_locations()->{templatedir};
|
2010-05-15 00:02:34 +04:00
|
|
|
return \@template_dirs;
|
|
|
|
}
|
|
|
|
|
2010-05-17 15:20:32 +04:00
|
|
|
sub template_include_path
|
|
|
|
{
|
2010-05-15 00:02:34 +04:00
|
|
|
my ($params) = @_;
|
2010-11-03 20:13:12 +03:00
|
|
|
my @used_languages = include_languages($params);
|
2008-12-15 15:53:33 +03:00
|
|
|
# Now, we add template directories in the order they will be searched:
|
2014-07-23 15:27:09 +04:00
|
|
|
my $template_dirs = _template_base_directories();
|
2008-12-15 15:53:33 +03:00
|
|
|
my @include_path;
|
2014-10-14 16:58:57 +04:00
|
|
|
foreach my $template_dir (@$template_dirs)
|
|
|
|
{
|
|
|
|
my @lang_dirs = _template_lang_directories(\@used_languages, $template_dir);
|
2010-05-15 00:02:34 +04:00
|
|
|
# Hooks get each set of extension directories separately.
|
2014-10-14 16:58:57 +04:00
|
|
|
if ($params->{hook})
|
|
|
|
{
|
|
|
|
push @include_path, \@lang_dirs if @lang_dirs;
|
2010-05-15 00:02:34 +04:00
|
|
|
}
|
|
|
|
# Whereas everything else just gets a whole INCLUDE_PATH.
|
2014-10-14 16:58:57 +04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
push @include_path, @lang_dirs;
|
2008-12-15 15:53:33 +03:00
|
|
|
}
|
|
|
|
}
|
2014-07-23 15:27:09 +04:00
|
|
|
# Allow to fallback to full template path - not a security risk,
|
|
|
|
# because TT anyway allows to include any file from the FS
|
|
|
|
push @include_path, bz_locations()->{libpath} unless $params->{hook};
|
2008-12-15 15:53:33 +03:00
|
|
|
return \@include_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
# This is taken straight from Sort::Versions 1.5, which is not included
|
|
|
|
# with perl by default.
|
2014-10-14 16:58:57 +04:00
|
|
|
sub vers_cmp
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
my ($a, $b) = @_;
|
2011-08-18 23:49:54 +04:00
|
|
|
$a = '' if !defined $a;
|
|
|
|
$b = '' if !defined $b;
|
2008-12-15 15:53:33 +03:00
|
|
|
|
|
|
|
# Remove leading zeroes - Bug 344661
|
|
|
|
$a =~ s/^0*(\d.+)/$1/;
|
|
|
|
$b =~ s/^0*(\d.+)/$1/;
|
|
|
|
|
|
|
|
my @A = ($a =~ /([-.]|\d+|[^-.\d]+)/g);
|
|
|
|
my @B = ($b =~ /([-.]|\d+|[^-.\d]+)/g);
|
|
|
|
|
|
|
|
my ($A, $B);
|
2014-10-14 16:58:57 +04:00
|
|
|
while (@A && @B)
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
$A = shift @A;
|
|
|
|
$B = shift @B;
|
2014-10-14 16:58:57 +04:00
|
|
|
if ($A eq '-' && $B eq '-')
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
next;
|
2014-10-14 16:58:57 +04:00
|
|
|
}
|
|
|
|
elsif ($A eq '-')
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
return -1;
|
2014-10-14 16:58:57 +04:00
|
|
|
}
|
|
|
|
elsif ($B eq '-')
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
return 1;
|
2014-10-14 16:58:57 +04:00
|
|
|
}
|
|
|
|
elsif ($A eq '.' && $B eq '.')
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
next;
|
2014-10-14 16:58:57 +04:00
|
|
|
}
|
|
|
|
elsif ($A eq '.')
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
return -1;
|
2014-10-14 16:58:57 +04:00
|
|
|
}
|
|
|
|
elsif ($B eq '.')
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
return 1;
|
2014-10-14 16:58:57 +04:00
|
|
|
}
|
|
|
|
elsif ($A =~ /^\d+$/ && $B =~ /^\d+$/)
|
|
|
|
{
|
|
|
|
if ($A =~ /^0/ || $B =~ /^0/)
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
return $A cmp $B if $A cmp $B;
|
2014-10-14 16:58:57 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
return $A <=> $B if $A <=> $B;
|
|
|
|
}
|
2014-10-14 16:58:57 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
$A = uc $A;
|
|
|
|
$B = uc $B;
|
|
|
|
return $A cmp $B if $A cmp $B;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@A <=> @B;
|
|
|
|
}
|
|
|
|
|
|
|
|
######################
|
|
|
|
# Helper Subroutines #
|
|
|
|
######################
|
|
|
|
|
|
|
|
# Used by install_string
|
2014-10-14 16:58:57 +04:00
|
|
|
sub _get_string_from_file
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
my ($string_id, $file) = @_;
|
|
|
|
return undef if !-e $file;
|
|
|
|
my $safe = new Safe;
|
|
|
|
$safe->rdo($file);
|
|
|
|
my %strings = %{$safe->varglob('strings')};
|
|
|
|
return $strings{$string_id};
|
|
|
|
}
|
|
|
|
|
|
|
|
# Make an ordered list out of a HTTP Accept-Language header (see RFC 2616, 14.4)
|
|
|
|
# We ignore '*' and <language-range>;q=0
|
|
|
|
# For languages with the same priority q the order remains unchanged.
|
2014-10-14 16:58:57 +04:00
|
|
|
sub _sort_accept_language
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
my $accept_language = $_[0];
|
|
|
|
|
|
|
|
# clean up string.
|
|
|
|
$accept_language =~ s/[^A-Za-z;q=0-9\.\-,]//g;
|
|
|
|
my @qlanguages;
|
|
|
|
my @languages;
|
2014-10-14 16:58:57 +04:00
|
|
|
foreach(split /,/, $accept_language)
|
|
|
|
{
|
|
|
|
if (m/([A-Za-z\-]+)(?:;q=(\d(?:\.\d+)))?/)
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
my $lang = $1;
|
|
|
|
my $qvalue = $2;
|
|
|
|
$qvalue = 1 if not defined $qvalue;
|
|
|
|
next if $qvalue == 0;
|
|
|
|
$qvalue = 1 if $qvalue > 1;
|
2014-10-14 16:58:57 +04:00
|
|
|
push @qlanguages, { qvalue => $qvalue, language => $lang };
|
2008-12-15 15:53:33 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
return map($_->{language}, (sort { $b->{qvalue} <=> $a->{qvalue} } @qlanguages));
|
2008-12-15 15:53:33 +03:00
|
|
|
}
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
sub get_console_locale
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
require Locale::Language;
|
|
|
|
my $locale = setlocale(LC_CTYPE);
|
|
|
|
my $language;
|
|
|
|
# Some distros set e.g. LC_CTYPE = fr_CH.UTF-8. We clean it up.
|
2014-10-14 16:58:57 +04:00
|
|
|
if ($locale =~ /^([^\.]+)/)
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
$locale = $1;
|
|
|
|
}
|
|
|
|
$locale =~ s/_/-/;
|
|
|
|
# It's pretty sure that there is no language pack of the form fr-CH
|
|
|
|
# installed, so we also include fr as a wanted language.
|
2014-10-14 16:58:57 +04:00
|
|
|
if ($locale =~ /^(\S+)\-/)
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
$language = $1;
|
|
|
|
$locale .= ",$language";
|
|
|
|
}
|
2014-10-14 16:58:57 +04:00
|
|
|
else
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
$language = $locale;
|
|
|
|
}
|
|
|
|
# Some OSs or distributions may have setlocale return a string of the form
|
|
|
|
# German_Germany.1252 (this example taken from a Windows XP system), which
|
|
|
|
# is unsuitable for our needs because Bugzilla works on language codes.
|
|
|
|
# We try and convert them here.
|
2014-10-14 16:58:57 +04:00
|
|
|
if ($language = Locale::Language::language2code($language))
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
$locale .= ",$language";
|
|
|
|
}
|
|
|
|
return $locale;
|
|
|
|
}
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
sub init_console
|
|
|
|
{
|
2010-05-15 00:02:34 +04:00
|
|
|
eval { ON_WINDOWS && require Win32::Console::ANSI; };
|
2014-10-14 16:58:57 +04:00
|
|
|
$ENV{ANSI_COLORS_DISABLED} = 1 if ($@ || !-t *STDOUT);
|
|
|
|
$ENV{HTTP_ACCEPT_LANGUAGE} ||= get_console_locale();
|
2010-05-15 00:02:34 +04:00
|
|
|
prevent_windows_dialog_boxes();
|
|
|
|
}
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
sub prevent_windows_dialog_boxes
|
|
|
|
{
|
2010-03-09 20:12:04 +03:00
|
|
|
# This code comes from http://bugs.activestate.com/show_bug.cgi?id=82183
|
|
|
|
# and prevents Perl modules from popping up dialog boxes, particularly
|
|
|
|
# during checksetup (since loading DBD::Oracle during checksetup when
|
|
|
|
# Oracle isn't installed causes a scary popup and pauses checksetup).
|
|
|
|
#
|
2014-10-14 16:58:57 +04:00
|
|
|
# Win32::API ships with ActiveState by default, though there could
|
2010-03-09 20:12:04 +03:00
|
|
|
# theoretically be a Windows installation without it, I suppose.
|
2014-10-14 16:58:57 +04:00
|
|
|
if (ON_WINDOWS and eval { require Win32::API })
|
|
|
|
{
|
2010-03-09 20:12:04 +03:00
|
|
|
# Call kernel32.SetErrorMode with arguments that mean:
|
|
|
|
# "The system does not display the critical-error-handler message box.
|
|
|
|
# Instead, the system sends the error to the calling process." and
|
|
|
|
# "A child process inherits the error mode of its parent process."
|
2014-10-14 16:58:57 +04:00
|
|
|
my $SetErrorMode = Win32::API->new('kernel32', 'SetErrorMode', 'I', 'I');
|
2010-03-09 20:12:04 +03:00
|
|
|
my $SEM_FAILCRITICALERRORS = 0x0001;
|
|
|
|
my $SEM_NOGPFAULTERRORBOX = 0x0002;
|
|
|
|
$SetErrorMode->Call($SEM_FAILCRITICALERRORS | $SEM_NOGPFAULTERRORBOX);
|
|
|
|
}
|
|
|
|
}
|
2008-12-15 15:53:33 +03:00
|
|
|
|
|
|
|
# This is like request_cache, but it's used only by installation code
|
2010-05-15 00:02:34 +04:00
|
|
|
# for checksetup.pl and things like that.
|
2008-12-15 15:53:33 +03:00
|
|
|
our $_cache = {};
|
2014-10-14 16:58:57 +04:00
|
|
|
sub _cache
|
|
|
|
{
|
2010-05-15 00:02:34 +04:00
|
|
|
# If the normal request_cache is available (which happens any time
|
|
|
|
# after the requirements phase) then we should use that.
|
2014-10-14 16:58:57 +04:00
|
|
|
if (eval { Bugzilla->request_cache; })
|
|
|
|
{
|
2010-05-15 00:02:34 +04:00
|
|
|
return Bugzilla->request_cache;
|
2008-12-15 15:53:33 +03:00
|
|
|
}
|
|
|
|
return $_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
###############################
|
|
|
|
# Copied from Bugzilla::Util #
|
|
|
|
##############################
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
sub trick_taint
|
|
|
|
{
|
2008-12-15 15:53:33 +03:00
|
|
|
require Carp;
|
|
|
|
Carp::confess("Undef to trick_taint") unless defined $_[0];
|
|
|
|
my $match = $_[0] =~ /^(.*)$/s;
|
|
|
|
$_[0] = $match ? $1 : undef;
|
|
|
|
return (defined($_[0]));
|
|
|
|
}
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
sub trim
|
|
|
|
{
|
2010-05-15 00:02:34 +04:00
|
|
|
my ($str) = @_;
|
2014-10-14 16:58:57 +04:00
|
|
|
if ($str)
|
|
|
|
{
|
|
|
|
$str =~ s/^\s+//g;
|
|
|
|
$str =~ s/\s+$//g;
|
2010-05-15 00:02:34 +04:00
|
|
|
}
|
|
|
|
return $str;
|
|
|
|
}
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
1;
|
2008-12-15 15:53:33 +03:00
|
|
|
__END__
|
|
|
|
|
|
|
|
=head1 NAME
|
|
|
|
|
|
|
|
Bugzilla::Install::Util - Utility functions that are useful both during
|
|
|
|
installation and afterwards.
|
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
|
|
|
|
This module contains various subroutines that are used primarily
|
|
|
|
during installation. However, these subroutines can also be useful to
|
|
|
|
non-installation code, so they have been split out into this module.
|
|
|
|
|
|
|
|
The difference between this module and L<Bugzilla::Util> is that this
|
|
|
|
module is safe to C<use> anywhere in Bugzilla, even during installation,
|
|
|
|
because it depends only on L<Bugzilla::Constants> and built-in perl modules.
|
|
|
|
|
|
|
|
None of the subroutines are exported by default--you must explicitly
|
|
|
|
export them.
|
|
|
|
|
|
|
|
=head1 SUBROUTINES
|
|
|
|
|
|
|
|
=over
|
|
|
|
|
|
|
|
=item C<bin_loc>
|
|
|
|
|
|
|
|
On *nix systems, given the name of a binary, returns the path to that
|
|
|
|
binary, if the binary is in the C<PATH>.
|
|
|
|
|
|
|
|
=item C<get_version_and_os>
|
|
|
|
|
|
|
|
Returns a hash containing information about what version of Bugzilla we're
|
|
|
|
running, what perl version we're using, and what OS we're running on.
|
|
|
|
|
|
|
|
=item C<get_console_locale>
|
|
|
|
|
|
|
|
Returns the language to use based on the LC_CTYPE value returned by the OS.
|
|
|
|
If LC_CTYPE is of the form fr-CH, then fr is appended to the list.
|
|
|
|
|
2010-05-15 00:02:34 +04:00
|
|
|
=item C<init_console>
|
|
|
|
|
|
|
|
Sets the C<ANSI_COLORS_DISABLED> and C<HTTP_ACCEPT_LANGUAGE> environment variables.
|
|
|
|
|
2008-12-15 15:53:33 +03:00
|
|
|
=item C<indicate_progress>
|
|
|
|
|
|
|
|
=over
|
|
|
|
|
|
|
|
=item B<Description>
|
|
|
|
|
|
|
|
This prints out lines of dots as a long update is going on, to let the user
|
|
|
|
know where we are and that we're not frozen. A new line of dots will start
|
|
|
|
every 60 dots.
|
|
|
|
|
|
|
|
Sample usage: C<indicate_progress({ total =E<gt> $total, current =E<gt>
|
|
|
|
$count, every =E<gt> 1 })>
|
|
|
|
|
|
|
|
=item B<Sample Output>
|
|
|
|
|
|
|
|
Here's some sample output with C<total = 1000> and C<every = 10>:
|
|
|
|
|
|
|
|
............................................................600/1000 (60%)
|
|
|
|
........................................
|
|
|
|
|
|
|
|
=item B<Params>
|
|
|
|
|
|
|
|
=over
|
|
|
|
|
|
|
|
=item C<total> - The total number of items we're processing.
|
|
|
|
|
|
|
|
=item C<current> - The number of the current item we're processing.
|
|
|
|
|
|
|
|
=item C<every> - How often the function should print out a dot.
|
|
|
|
For example, if this is 10, the function will print out a dot every
|
|
|
|
ten items. Defaults to 1 if not specified.
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
=item B<Returns>: nothing
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
=item C<install_string>
|
|
|
|
|
|
|
|
=over
|
|
|
|
|
|
|
|
=item B<Description>
|
|
|
|
|
|
|
|
This is a very simple method of templating strings for installation.
|
|
|
|
It should only be used by code that has to run before the Template Toolkit
|
|
|
|
can be used. (See the comments at the top of the various L<Bugzilla::Install>
|
|
|
|
modules to find out when it's safe to use Template Toolkit.)
|
|
|
|
|
|
|
|
It pulls strings out of the F<strings.txt.pl> "template" and replaces
|
|
|
|
any variable surrounded by double-hashes (##) with a value you specify.
|
|
|
|
|
|
|
|
This allows for localization of strings used during installation.
|
|
|
|
|
|
|
|
=item B<Example>
|
|
|
|
|
|
|
|
Let's say your template string looks like this:
|
|
|
|
|
|
|
|
The ##animal## jumped over the ##plant##.
|
|
|
|
|
|
|
|
Let's say that string is called 'animal_jump_plant'. So you call the function
|
|
|
|
like this:
|
|
|
|
|
|
|
|
install_string('animal_jump_plant', { animal => 'fox', plant => 'tree' });
|
|
|
|
|
|
|
|
That will output this:
|
|
|
|
|
|
|
|
The fox jumped over the tree.
|
|
|
|
|
|
|
|
=item B<Params>
|
|
|
|
|
|
|
|
=over
|
|
|
|
|
|
|
|
=item C<$string_id> - The name of the string from F<strings.txt.pl>.
|
|
|
|
|
|
|
|
=item C<$vars> - A hashref containing the replacement values for variables
|
|
|
|
inside of the string.
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
=item B<Returns>: The appropriate string, with variables replaced.
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
=item C<template_include_path>
|
|
|
|
|
|
|
|
Used by L<Bugzilla::Template> and L</install_string> to determine the
|
|
|
|
directories where templates are installed. Templates can be installed
|
|
|
|
in many places. They're listed here in the basic order that they're
|
|
|
|
searched:
|
|
|
|
|
|
|
|
=over
|
|
|
|
|
|
|
|
=item extensions/C<$extension>/template/C<$language>/C<$project>
|
|
|
|
|
|
|
|
=item extensions/C<$extension>/template/C<$language>/custom
|
|
|
|
|
|
|
|
=item extensions/C<$extension>/template/C<$language>/default
|
|
|
|
|
|
|
|
=item template/C<$language>/C<$project>
|
|
|
|
|
|
|
|
=item template/C<$language>/custom
|
|
|
|
|
|
|
|
=item template/C<$language>/default
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
C<$project> has to do with installations that are using the C<$ENV{PROJECT}>
|
|
|
|
variable to have different "views" on a single Bugzilla.
|
|
|
|
|
|
|
|
The F<default> directory includes templates shipped with Bugzilla.
|
|
|
|
|
|
|
|
The F<custom> directory is a directory for local installations to override
|
|
|
|
the F<default> templates. Any individual template in F<custom> will
|
|
|
|
override a template of the same name and path in F<default>.
|
|
|
|
|
|
|
|
C<$language> is a language code, C<en> being the default language shipped
|
|
|
|
with Bugzilla. Localizers ship other languages.
|
|
|
|
|
|
|
|
C<$extension> is the name of any directory in the F<extensions/> directory.
|
|
|
|
Each extension has its own directory.
|
|
|
|
|
|
|
|
Note that languages are sorted by the user's preference (as specified
|
|
|
|
in their browser, usually), and extensions are sorted alphabetically.
|
|
|
|
|
|
|
|
=item C<include_languages>
|
|
|
|
|
2014-10-14 16:58:57 +04:00
|
|
|
Used by L<Bugzilla::Template> to determine the languages' list which
|
|
|
|
are compiled with the browser's I<Accept-Language> and the languages
|
2008-12-15 15:53:33 +03:00
|
|
|
of installed templates.
|
|
|
|
|
|
|
|
=item C<vers_cmp>
|
|
|
|
|
|
|
|
=over
|
|
|
|
|
|
|
|
=item B<Description>
|
|
|
|
|
|
|
|
This is a comparison function, like you would use in C<sort>, except that
|
|
|
|
it compares two version numbers. So, for example, 2.10 would be greater
|
|
|
|
than 2.2.
|
|
|
|
|
|
|
|
It's based on versioncmp from L<Sort::Versions>, with some Bugzilla-specific
|
|
|
|
fixes.
|
|
|
|
|
|
|
|
=item B<Params>: C<$a> and C<$b> - The versions you want to compare.
|
|
|
|
|
|
|
|
=item B<Returns>
|
|
|
|
|
|
|
|
C<-1> if C<$a> is less than C<$b>, C<0> if they are equal, or C<1> if C<$a>
|
|
|
|
is greater than C<$b>.
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
=back
|